summaryrefslogtreecommitdiff
path: root/script/src/glob/matcher.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/src/glob/matcher.lua')
-rw-r--r--script/src/glob/matcher.lua151
1 files changed, 151 insertions, 0 deletions
diff --git a/script/src/glob/matcher.lua b/script/src/glob/matcher.lua
new file mode 100644
index 00000000..f4c2b12c
--- /dev/null
+++ b/script/src/glob/matcher.lua
@@ -0,0 +1,151 @@
+local m = require 'lpeglabel'
+
+local Slash = m.S('/\\')^1
+local Symbol = m.S',{}[]*?/\\'
+local Char = 1 - Symbol
+local Path = Char^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
+ return m.C(self:exp(state, 1))
+ 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)
+ return self
+end