summaryrefslogtreecommitdiff
path: root/vendor/lpeglj/lpcap.lua
diff options
context:
space:
mode:
authorunwox <me@unwox.com>2024-09-26 17:46:38 +0600
committerunwox <me@unwox.com>2024-09-26 17:46:38 +0600
commit9b82db238f9e2e02a76f95c793f8d6ef2387ecfd (patch)
treecdb2a16d01f09553b560ab1034d53392d07bae42 /vendor/lpeglj/lpcap.lua
init
Diffstat (limited to 'vendor/lpeglj/lpcap.lua')
-rw-r--r--vendor/lpeglj/lpcap.lua625
1 files changed, 625 insertions, 0 deletions
diff --git a/vendor/lpeglj/lpcap.lua b/vendor/lpeglj/lpcap.lua
new file mode 100644
index 0000000..06fbee8
--- /dev/null
+++ b/vendor/lpeglj/lpcap.lua
@@ -0,0 +1,625 @@
+--[[
+LPEGLJ
+lpcap.lua
+Capture functions
+Copyright (C) 2014 Rostislav Sacek.
+based on LPeg v1.0 - PEG pattern matching for Lua
+Lua.org & PUC-Rio written by Roberto Ierusalimschy
+http://www.inf.puc-rio.br/~roberto/lpeg/
+
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
+--]]
+local ffi = require "ffi"
+
+local Cclose = 0
+local Cposition = 1
+local Cconst = 2
+local Cbackref = 3
+local Carg = 4
+local Csimple = 5
+local Ctable = 6
+local Cfunction = 7
+local Cquery = 8
+local Cstring = 9
+local Cnum = 10
+local Csubst = 11
+local Cfold = 12
+local Cruntime = 13
+local Cgroup = 14
+
+local MAXSTRCAPS = 10
+
+local pushcapture
+local addonestring
+
+
+-- Goes back in a list of captures looking for an open capture
+-- corresponding to a close
+
+local function findopen(cs, index)
+ local n = 0; -- number of closes waiting an open
+ while true do
+ index = index - 1
+ if cs.ocap[index].kind == Cclose then
+ n = n + 1 -- one more open to skip
+ elseif cs.ocap[index].siz == 0 then
+ if n == 0 then
+ return index
+ end
+ n = n - 1
+ end
+ end
+end
+
+
+local function checknextcap(cs, captop)
+ local cap = cs.cap;
+ -- not a single capture? ((cap)->siz != 0)
+ if cs.ocap[cap].siz == 0 then
+ local n = 0; -- number of opens waiting a close
+ -- look for corresponding close
+ while true do
+ cap = cap + 1
+ if cap > captop then return end
+ if cs.ocap[cap].kind == Cclose then
+ n = n - 1
+ if n + 1 == 0 then
+ break;
+ end
+ elseif cs.ocap[cap].siz == 0 then
+ n = n + 1
+ end
+ end
+ end
+ cap = cap + 1; -- + 1 to skip last close (or entire single capture)
+ if cap > captop then return end
+ return true
+end
+
+
+-- Go to the next capture
+
+local function nextcap(cs)
+ local cap = cs.cap;
+ -- not a single capture? ((cap)->siz != 0)
+ if cs.ocap[cap].siz == 0 then
+ local n = 0; -- number of opens waiting a close
+ -- look for corresponding close
+ while true do
+ cap = cap + 1
+ if cs.ocap[cap].kind == Cclose then
+ n = n - 1
+ if n + 1 == 0 then
+ break;
+ end
+ elseif cs.ocap[cap].siz == 0 then
+ n = n + 1
+ end
+ end
+ end
+ cs.cap = cap + 1; -- + 1 to skip last close (or entire single capture)
+end
+
+
+-- Push on the Lua stack all values generated by nested captures inside
+-- the current capture. Returns number of values pushed. 'addextra'
+-- makes it push the entire match after all captured values. The
+-- entire match is pushed also if there are no other nested values,
+-- so the function never returns zero.
+
+local function pushnestedvalues(cs, addextra, out, valuetable)
+ local co = cs.cap
+ cs.cap = cs.cap + 1
+ -- no nested captures?
+ if cs.ocap[cs.cap - 1].siz ~= 0 then
+ local st = cs.ocap[co].s
+ local l = cs.ocap[co].siz - 1
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = cs.s and cs.s:sub(st, st + l - 1) or cs.stream(st, st + l - 1)
+ return 1; -- that is it
+ else
+ local n = 0;
+ while cs.ocap[cs.cap].kind ~= Cclose do -- repeat for all nested patterns
+ n = n + pushcapture(cs, out, valuetable);
+ end
+ -- need extra?
+ if addextra or n == 0 then
+ local st = cs.ocap[co].s
+ local l = cs.ocap[cs.cap].s - cs.ocap[co].s
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = cs.s and cs.s:sub(st, st + l - 1) or cs.stream(st, st + l - 1)
+ n = n + 1
+ end
+ cs.cap = cs.cap + 1 -- skip close entry
+ return n;
+ end
+end
+
+
+-- Push only the first value generated by nested captures
+
+local function pushonenestedvalue(cs, out, valuetable)
+ local n = pushnestedvalues(cs, false, out, valuetable)
+ for i = n, 2, -1 do
+ out.out[out.outindex] = nil
+ out.outindex = out.outindex - 1
+ end
+end
+
+
+-- Try to find a named group capture with the name given at the top of
+-- the stack; goes backward from 'cap'.
+
+local function findback(cs, cap, name, valuetable)
+ -- repeat until end of list
+ while cap > 0 do
+ cap = cap - 1
+ local continue
+ if cs.ocap[cap].kind == Cclose then
+ cap = findopen(cs, cap); -- skip nested captures
+ elseif cs.ocap[cap].siz == 0 then
+ continue = true -- opening an enclosing capture: skip and get previous
+ end
+ if not continue and cs.ocap[cap].kind == Cgroup and cs.ocap[cap].idx ~= 0 then
+ local gname = valuetable[cs.ocap[cap].idx] -- get group name
+ -- right group?
+ if name == gname then
+ return cap;
+ end
+ end
+ end
+ error(("back reference '%s' not found"):format(name), 0)
+end
+
+
+-- Back-reference capture. Return number of values pushed.
+
+local function backrefcap(cs, out, valuetable)
+ local curr = cs.cap;
+ local name = valuetable[cs.ocap[cs.cap].idx] -- reference name
+ cs.cap = findback(cs, curr, name, valuetable) -- find corresponding group
+ local n = pushnestedvalues(cs, false, out, valuetable); -- push group's values
+ cs.cap = curr + 1;
+ return n;
+end
+
+
+-- Table capture: creates a new table and populates it with nested
+-- captures.
+
+local function tablecap(cs, out, valuetable)
+ local n = 0;
+ local t = {}
+ cs.cap = cs.cap + 1
+ -- table is empty
+ if cs.ocap[cs.cap - 1].siz == 0 then
+ while cs.ocap[cs.cap].kind ~= Cclose do
+ local subout = { outindex = 0, out = {} }
+ -- named group?
+ if cs.ocap[cs.cap].kind == Cgroup and cs.ocap[cs.cap].idx ~= 0 then
+ local groupname = valuetable[cs.ocap[cs.cap].idx] -- push group name
+ pushonenestedvalue(cs, subout, valuetable)
+ t[groupname] = subout.out[1]
+ else
+ -- not a named group
+ local k = pushcapture(cs, subout, valuetable)
+ -- store all values into table
+ for i = 1, subout.outindex do
+ t[i + n] = subout.out[i]
+ end
+ n = n + k;
+ end
+ end
+ cs.cap = cs.cap + 1 -- skip close entry
+ end
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = t
+ return 1; -- number of values pushed (only the table)
+end
+
+
+-- Table-query capture
+
+local function querycap(cs, out, valuetable)
+ local table = valuetable[cs.ocap[cs.cap].idx]
+ local subout = { outindex = 0, out = {} }
+ pushonenestedvalue(cs, subout, valuetable) -- get nested capture
+ -- query cap. value at table
+ if table[subout.out[1]] ~= nil then
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = table[subout.out[1]]
+ return 1
+ end
+ return 0
+end
+
+
+-- Fold capture
+
+local function foldcap(cs, out, valuetable)
+ local fce = valuetable[cs.ocap[cs.cap].idx]
+ cs.cap = cs.cap + 1
+ -- no nested captures?
+ -- or no nested captures (large subject)?
+ if cs.ocap[cs.cap - 1].siz ~= 0 or
+ cs.ocap[cs.cap].kind == Cclose then
+ error("no initial value for fold capture", 0);
+ end
+ local subout = { outindex = 0; out = {} }
+ local n = pushcapture(cs, subout, valuetable) -- nested captures with no values?
+ if n == 0 then
+ error("no initial value for fold capture", 0);
+ end
+ local acumulator = subout.out[1] -- leave only one result for accumulator
+ while cs.ocap[cs.cap].kind ~= Cclose do
+ local subout = { outindex = 0; out = {} }
+ n = pushcapture(cs, subout, valuetable); -- get next capture's values
+ acumulator = fce(acumulator, unpack(subout.out, 1, subout.outindex)) -- call folding function
+ end
+ cs.cap = cs.cap + 1; -- skip close entry
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = acumulator
+ return 1; -- only accumulator left on the stack
+end
+
+
+local function retcount(...)
+ return select('#', ...), { ... }
+end
+
+
+-- Function capture
+
+local function functioncap(cs, out, valuetable)
+ local fce = valuetable[cs.ocap[cs.cap].idx] -- push function
+ local subout = { outindex = 0, out = {} }
+ local n = pushnestedvalues(cs, false, subout, valuetable); -- push nested captures
+ local count, ret = retcount(fce(unpack(subout.out, 1, n))) -- call function
+ for i = 1, count do
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = ret[i]
+ end
+ return count
+end
+
+
+-- Select capture
+
+local function numcap(cs, out, valuetable)
+ local idx = valuetable[cs.ocap[cs.cap].idx] -- value to select
+ -- no values?
+ if idx == 0 then
+ nextcap(cs); -- skip entire capture
+ return 0; -- no value produced
+ else
+ local subout = { outindex = 0, out = {} }
+ local n = pushnestedvalues(cs, false, subout, valuetable)
+ -- invalid index?
+ if n < idx then
+ error(("no capture '%d'"):format(idx), 0)
+ else
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = subout.out[idx] -- get selected capture
+ return 1;
+ end
+ end
+end
+
+
+-- Calls a runtime capture. Returns number of captures removed by
+-- the call, including the initial Cgroup. (Captures to be added are
+-- on the Lua stack.)
+
+local function runtimecap(cs, close, s, out, valuetable)
+ local open = findopen(cs, close)
+ assert(cs.ocap[open].kind == Cgroup)
+ cs.ocap[close].kind = Cclose; -- closes the group
+ cs.ocap[close].s = s;
+ cs.cap = open;
+ local fce = valuetable[cs.ocap[cs.cap].idx] -- push function to be called
+ local subout = { outindex = 0, out = {} }
+ local n = pushnestedvalues(cs, false, subout, valuetable); -- push nested captures
+ local count, ret = retcount(fce(cs.s or cs.stream, s, unpack(subout.out, 1, n))) -- call dynamic function
+ for i = 1, count do
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = ret[i]
+ end
+ return close - open -- number of captures of all kinds removed
+end
+
+-- Collect values from current capture into array 'cps'. Current
+-- capture must be Cstring (first call) or Csimple (recursive calls).
+-- (In first call, fills %0 with whole match for Cstring.)
+-- Returns number of elements in the array that were filled.
+
+local function getstrcaps(cs, cps, n)
+ local k = n
+ n = n + 1
+ cps[k + 1].isstring = true; -- get string value
+ cps[k + 1].startstr = cs.ocap[cs.cap].s; -- starts here
+ cs.cap = cs.cap + 1
+ -- nested captures?
+ if cs.ocap[cs.cap - 1].siz == 0 then
+ -- traverse them
+ while cs.ocap[cs.cap].kind ~= Cclose do
+ -- too many captures?
+ if n >= MAXSTRCAPS then
+ nextcap(cs); -- skip extra captures (will not need them)
+ elseif cs.ocap[cs.cap].kind == Csimple then
+ -- string?
+ n = getstrcaps(cs, cps, n); -- put info. into array
+ else
+ cps[n + 1].isstring = false; -- not a string
+ cps[n + 1].origcap = cs.cap; -- keep original capture
+ nextcap(cs);
+ n = n + 1;
+ end
+ end
+ cs.cap = cs.cap + 1 -- skip close
+ end
+ cps[k + 1].endstr = cs.ocap[cs.cap - 1].s + cs.ocap[cs.cap - 1].siz - 1 -- ends here
+ return n;
+end
+
+
+-- add next capture value (which should be a string) to buffer 'b'
+
+-- String capture: add result to buffer 'b' (instead of pushing
+-- it into the stack)
+
+local function stringcap(cs, b, valuetable)
+ local cps = {}
+ for i = 1, MAXSTRCAPS do
+ cps[#cps + 1] = {}
+ end
+ local fmt = valuetable[cs.ocap[cs.cap].idx]
+ local n = getstrcaps(cs, cps, 0) - 1; -- collect nested captures
+ local i = 1
+ -- traverse them
+ while i <= #fmt do
+ local c = fmt:sub(i, i)
+ -- not an escape?
+ if c ~= '%' then
+ b[#b + 1] = c -- add it to buffer
+ elseif fmt:sub(i + 1, i + 1) < '0' or fmt:sub(i + 1, i + 1) > '9' then
+ -- not followed by a digit?
+ i = i + 1
+ b[#b + 1] = fmt:sub(i, i)
+ else
+ i = i + 1
+ local l = fmt:sub(i, i) - '0'; -- capture index
+ if l > n then
+ error(("invalid capture index (%d)"):format(l), 0)
+ elseif cps[l + 1].isstring then
+ b[#b + 1] = cs.s and cs.s:sub(cps[l + 1].startstr, cps[l + 1].endstr - cps[l + 1].startstr + cps[l + 1].startstr - 1) or
+ cs.stream(cps[l + 1].startstr, cps[l + 1].endstr - cps[l + 1].startstr + cps[l + 1].startstr - 1)
+ else
+ local curr = cs.cap;
+ cs.cap = cps[l + 1].origcap; -- go back to evaluate that nested capture
+ if not addonestring(cs, b, "capture", valuetable) then
+ error(("no values in capture index %d"):format(l), 0)
+ end
+ cs.cap = curr; -- continue from where it stopped
+ end
+ end
+ i = i + 1
+ end
+end
+
+
+-- Substitution capture: add result to buffer 'b'
+
+local function substcap(cs, b, valuetable)
+ local curr = cs.ocap[cs.cap].s;
+ -- no nested captures?
+ if cs.ocap[cs.cap].siz ~= 0 then
+ -- keep original text
+ b[#b + 1] = cs.s and cs.s:sub(curr, cs.ocap[cs.cap].siz - 1 + curr - 1) or
+ cs.stream(curr, cs.ocap[cs.cap].siz - 1 + curr - 1)
+ else
+ cs.cap = cs.cap + 1 -- skip open entry
+ -- traverse nested captures
+ while cs.ocap[cs.cap].kind ~= Cclose do
+ local next = cs.ocap[cs.cap].s;
+ b[#b + 1] = cs.s and cs.s:sub(curr, next - curr + curr - 1) or
+ cs.stream(curr, next - curr + curr - 1) -- add text up to capture
+ if addonestring(cs, b, "replacement", valuetable) then
+ curr = cs.ocap[cs.cap - 1].s + cs.ocap[cs.cap - 1].siz - 1; -- continue after match
+ else
+ -- no capture value
+ curr = next; -- keep original text in final result
+ end
+ end
+ b[#b + 1] = cs.s and cs.s:sub(curr, curr + cs.ocap[cs.cap].s - curr - 1) or
+ cs.stream(curr, curr + cs.ocap[cs.cap].s - curr - 1) -- add last piece of text
+ end
+ cs.cap = cs.cap + 1 -- go to next capture
+end
+
+
+-- Evaluates a capture and adds its first value to buffer 'b'; returns
+-- whether there was a value
+
+function addonestring(cs, b, what, valuetable)
+ local tag = cs.ocap[cs.cap].kind
+ if tag == Cstring then
+ stringcap(cs, b, valuetable); -- add capture directly to buffer
+ return 1
+ elseif tag == Csubst then
+ substcap(cs, b, valuetable); -- add capture directly to buffer
+ return 1
+ else
+ local subout = { outindex = 0, out = {} }
+ local n = pushcapture(cs, subout, valuetable);
+ if n > 0 then
+ if type(subout.out[1]) ~= 'string' and type(subout.out[1]) ~= 'number' then
+ error(("invalid %s value (a %s)"):format(what, type(subout.out[1])), 0)
+ end
+ b[#b + 1] = subout.out[1]
+ return n
+ end
+ end
+end
+
+
+-- Push all values of the current capture into the stack; returns
+-- number of values pushed
+
+function pushcapture(cs, out, valuetable)
+ local type = cs.ocap[cs.cap].kind
+ if type == Cposition then
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = cs.ocap[cs.cap].s
+ cs.cap = cs.cap + 1;
+ return 1;
+ elseif type == Cconst then
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = valuetable[cs.ocap[cs.cap].idx]
+ cs.cap = cs.cap + 1
+ return 1;
+ elseif type == Carg then
+ local arg = valuetable[cs.ocap[cs.cap].idx]
+ cs.cap = cs.cap + 1
+ if arg > cs.ptopcount then
+ error(("reference to absent extra argument #%d"):format(arg), 0)
+ end
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = cs.ptop[arg]
+ return 1;
+ elseif type == Csimple then
+ local k = pushnestedvalues(cs, true, out, valuetable)
+ local index = out.outindex
+ table.insert(out.out, index - k + 1, out.out[index])
+ out[index + 1] = nil
+ return k;
+ elseif type == Cruntime then
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = valuetable[cs.ocap[cs.cap].idx]
+ cs.cap = cs.cap + 1;
+ return 1;
+ elseif type == Cstring then
+ local b = {}
+ stringcap(cs, b, valuetable)
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = table.concat(b)
+ return 1;
+ elseif type == Csubst then
+ local b = {}
+ substcap(cs, b, valuetable);
+ out.outindex = out.outindex + 1
+ out.out[out.outindex] = table.concat(b)
+ return 1;
+ elseif type == Cgroup then
+ -- anonymous group?
+ if cs.ocap[cs.cap].idx == 0 then
+ return pushnestedvalues(cs, false, out, valuetable); -- add all nested values
+ else
+ -- named group: add no values
+ nextcap(cs); -- skip capture
+ return 0
+ end
+ elseif type == Cbackref then
+ return backrefcap(cs, out, valuetable)
+ elseif type == Ctable then
+ return tablecap(cs, out, valuetable)
+ elseif type == Cfunction then
+ return functioncap(cs, out, valuetable)
+ elseif type == Cnum then
+ return numcap(cs, out, valuetable)
+ elseif type == Cquery then
+ return querycap(cs, out, valuetable)
+ elseif type == Cfold then
+ return foldcap(cs, out, valuetable)
+ else
+ assert(false)
+ end
+end
+
+
+-- Prepare a CapState structure and traverse the entire list of
+-- captures in the stack pushing its results. 's' is the subject
+-- string, 'r' is the final position of the match, and 'ptop'
+-- the index in the stack where some useful values were pushed.
+-- Returns the number of results pushed. (If the list produces no
+-- results, push the final position of the match.)
+
+local function getcaptures(capture, s, stream, r, valuetable, ...)
+ local n = 0;
+ local cs = { cap = 0 }
+ local out = { outindex = 0; out = {} }
+ -- is there any capture?
+ if capture[cs.cap].kind ~= Cclose then
+ cs.ocap = capture
+ cs.s = s;
+ cs.stream = stream
+ cs.ptopcount, cs.ptop = retcount(...)
+ repeat -- collect their values
+ n = n + pushcapture(cs, out, valuetable)
+ until cs.ocap[cs.cap].kind == Cclose
+ end
+ -- no capture values?
+ if n == 0 then
+ if not r then
+ return
+ else
+ return r
+ end
+ end
+ assert(out.outindex < 7998, "(too many captures)")
+ return unpack(out.out, 1, out.outindex)
+end
+
+local function getcapturesruntime(capture, s, stream, notdelete, min, max, captop, valuetable, ...)
+ local n = 0;
+ local cs = { cap = min }
+ local out = { outindex = 0; out = {} }
+ cs.ocap = capture
+ cs.s = s
+ cs.stream = stream
+ cs.ptopcount, cs.ptop = retcount(...)
+ local start = 0
+ repeat -- collect their values
+ if not checknextcap(cs, max) then break end
+ local notdelete = notdelete or capture[cs.cap].kind == Cgroup and capture[cs.cap].idx ~= 0 and capture[cs.cap].candelete == 0
+ pushcapture(cs, out, valuetable)
+ if notdelete then
+ start = cs.cap
+ else
+ n = n + cs.cap - start
+ for i = 0, captop - cs.cap - 1 do
+ ffi.copy(capture + start + i, capture + cs.cap + i, ffi.sizeof('CAPTURE'))
+ end
+ max = max - (cs.cap - start)
+ captop = captop - (cs.cap - start)
+ cs.cap = start
+ end
+ until cs.cap == max
+ assert(out.outindex < 7998, "(too many captures)")
+ return n, out.out, out.outindex
+end
+
+return {
+ getcaptures = getcaptures,
+ runtimecap = runtimecap,
+ getcapturesruntime = getcapturesruntime,
+}
+