1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
local m = require 'lpeglabel'
local matcher = require 'glob.matcher'
local function prop(name, pat)
return m.Cg(m.Cc(true), name) * pat
end
local function object(type, pat)
return m.Ct(
m.Cg(m.Cc(type), 'type') *
m.Cg(pat, 'value')
)
end
local function expect(p, err)
return p + m.T(err)
end
local parser = m.P {
'Main',
['Sp'] = m.S(' \t')^0,
['Slash'] = m.S('/\\')^1,
['Main'] = m.Ct(m.V'Sp' * m.P'{' * m.V'Pattern' * (',' * expect(m.V'Pattern', 'Miss exp after ","'))^0 * m.P'}')
+ m.Ct(m.V'Pattern')
+ m.T'Main Failed'
,
['Pattern'] = m.Ct(m.V'Sp' * prop('neg', m.P'!') * expect(m.V'Unit', 'Miss exp after "!"'))
+ m.Ct(m.V'Unit')
,
['NeedRoot'] = prop('root', (m.P'.' * m.V'Slash' + m.V'Slash')),
['Unit'] = m.V'Sp' * m.V'NeedRoot'^-1 * expect(m.V'Exp', 'Miss exp') * m.V'Sp',
['Exp'] = m.V'Sp' * (m.V'FSymbol' + object('/', m.V'Slash') + m.V'Word')^0 * m.V'Sp',
['Word'] = object('word', m.Ct((m.V'CSymbol' + m.V'Char' - m.V'FSymbol')^1)),
['CSymbol'] = object('*', m.P'*')
+ object('?', m.P'?')
+ object('[]', m.V'Range')
,
['Char'] = object('char', (1 - m.S',{}[]*?/\\')^1),
['FSymbol'] = object('**', m.P'**'),
['RangeWord'] = 1 - m.P']',
['Range'] = m.P'[' * m.Ct(m.V'RangeUnit'^0) * m.P']'^-1,
['RangeUnit'] = m.Ct(m.C(m.V'RangeWord') * m.P'-' * m.C(m.V'RangeWord'))
+ m.V'RangeWord',
}
local mt = {}
mt.__index = mt
mt.__name = 'glob'
function mt:addPattern(pat)
if type(pat) ~= 'string' then
return
end
self.pattern[#self.pattern+1] = pat
if self.options.ignoreCase then
pat = pat:lower()
end
local states, err = parser:match(pat)
if not states then
self.errors[#self.errors+1] = {
pattern = pat,
message = err
}
return
end
for _, state in ipairs(states) do
if state.neg then
self.refused[#self.refused+1] = matcher(state)
else
self.passed[#self.passed+1] = matcher(state)
end
end
end
function mt:setOption(op, val)
if val == nil then
val = true
end
self.options[op] = val
end
function mt:__call(path)
if self.options.ignoreCase then
path = path:lower()
end
for _, refused in ipairs(self.refused) do
if refused(path) then
return false
end
end
for _, passed in ipairs(self.passed) do
if passed(path) then
return true
end
end
return false
end
return function (pattern, options)
local self = setmetatable({
pattern = {},
options = {},
passed = {},
refused = {},
errors = {},
}, mt)
if type(pattern) == 'table' then
for _, pat in ipairs(pattern) do
self:addPattern(pat)
end
else
self:addPattern(pattern)
end
if type(options) == 'table' then
for op, val in pairs(options) do
self:setOption(op, val)
end
end
return self
end
|