160 lines
3.5 KiB
Lua
160 lines
3.5 KiB
Lua
|
local m = require 'lpeglabel'
|
||
|
|
||
|
local Slash = m.S('/\\')^1
|
||
|
local Symbol = m.S',{}[]*?/\\'
|
||
|
local Char = 1 - Symbol
|
||
|
local Path = (1 - m.S[[\/*?"<>|]])^1 * Slash
|
||
|
local NoWord = #(m.P(-1) + Symbol)
|
||
|
local function whatHappened()
|
||
|
return m.Cmt(m.P(1)^1, function (...)
|
||
|
print(...)
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
local mt = {}
|
||
|
mt.__index = mt
|
||
|
mt.__name = 'matcher'
|
||
|
|
||
|
function mt:exp(state, index)
|
||
|
local exp = state[index]
|
||
|
if not exp then
|
||
|
return
|
||
|
end
|
||
|
if exp.type == 'word' then
|
||
|
return self:word(exp, state, index + 1)
|
||
|
elseif exp.type == 'char' then
|
||
|
return self:char(exp, state, index + 1)
|
||
|
elseif exp.type == '**' then
|
||
|
return self:anyPath(exp, state, index + 1)
|
||
|
elseif exp.type == '*' then
|
||
|
return self:anyChar(exp, state, index + 1)
|
||
|
elseif exp.type == '?' then
|
||
|
return self:oneChar(exp, state, index + 1)
|
||
|
elseif exp.type == '[]' then
|
||
|
return self:range(exp, state, index + 1)
|
||
|
elseif exp.type == '/' then
|
||
|
return self:slash(exp, state, index + 1)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mt:word(exp, state, index)
|
||
|
local current = self:exp(exp.value, 1)
|
||
|
local after = self:exp(state, index)
|
||
|
if after then
|
||
|
return current * Slash * after
|
||
|
else
|
||
|
return current
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mt:char(exp, state, index)
|
||
|
local current = m.P(exp.value)
|
||
|
local after = self:exp(state, index)
|
||
|
if after then
|
||
|
return current * after * NoWord
|
||
|
else
|
||
|
return current * NoWord
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mt:anyPath(_, state, index)
|
||
|
local after = self:exp(state, index)
|
||
|
if after then
|
||
|
return m.P {
|
||
|
'Main',
|
||
|
Main = after
|
||
|
+ Path * m.V'Main'
|
||
|
}
|
||
|
else
|
||
|
return Path^0
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mt:anyChar(_, state, index)
|
||
|
local after = self:exp(state, index)
|
||
|
if after then
|
||
|
return m.P {
|
||
|
'Main',
|
||
|
Main = after
|
||
|
+ Char * m.V'Main'
|
||
|
}
|
||
|
else
|
||
|
return Char^0
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mt:oneChar(_, state, index)
|
||
|
local after = self:exp(state, index)
|
||
|
if after then
|
||
|
return Char * after
|
||
|
else
|
||
|
return Char
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mt:range(exp, state, index)
|
||
|
local after = self:exp(state, index)
|
||
|
local ranges = {}
|
||
|
local selects = {}
|
||
|
for _, range in ipairs(exp.value) do
|
||
|
if #range == 1 then
|
||
|
selects[#selects+1] = range[1]
|
||
|
elseif #range == 2 then
|
||
|
ranges[#ranges+1] = range[1] .. range[2]
|
||
|
end
|
||
|
end
|
||
|
local current = m.S(table.concat(selects)) + m.R(table.unpack(ranges))
|
||
|
if after then
|
||
|
return current * after
|
||
|
else
|
||
|
return current
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mt:slash(_, state, index)
|
||
|
local after = self:exp(state, index)
|
||
|
if after then
|
||
|
return after
|
||
|
else
|
||
|
self.needDirectory = true
|
||
|
return nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mt:pattern(state)
|
||
|
if state.root then
|
||
|
local after = self:exp(state, 1)
|
||
|
if after then
|
||
|
return m.C(after)
|
||
|
else
|
||
|
return nil
|
||
|
end
|
||
|
else
|
||
|
return m.C(self:anyPath(nil, state, 1))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mt:isNeedDirectory()
|
||
|
return self.needDirectory == true
|
||
|
end
|
||
|
|
||
|
function mt:isNegative()
|
||
|
return self.state.neg == true
|
||
|
end
|
||
|
|
||
|
function mt:__call(path)
|
||
|
return self.matcher:match(path)
|
||
|
end
|
||
|
|
||
|
return function (state, options)
|
||
|
local self = setmetatable({
|
||
|
options = options,
|
||
|
state = state,
|
||
|
}, mt)
|
||
|
self.matcher = self:pattern(state)
|
||
|
if not self.matcher then
|
||
|
return nil
|
||
|
end
|
||
|
return self
|
||
|
end
|