foundryvtt-swade-fr/tools/lpeg/lpprint.lua

356 lines
11 KiB
Lua
Raw Permalink Normal View History

2022-05-28 09:30:19 +02:00
--[[
LPEGLJ
lpprint.lua
Tree, code and debug print function (only for debuging)
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 band, rshift, lshift = bit.band, bit.rshift, bit.lshift
ffi.cdef[[
int isprint ( int c );
]]
local RuleLR = 0x10000
local Ruleused = 0x20000
-- {======================================================
-- Printing patterns (for debugging)
-- =======================================================
local TChar = 0
local TSet = 1
local TAny = 2 -- standard PEG elements
local TTrue = 3
local TFalse = 4
local TRep = 5
local TSeq = 6
local TChoice = 7
local TNot = 8
local TAnd = 9
local TCall = 10
local TOpenCall = 11
local TRule = 12 -- sib1 is rule's pattern, sib2 is 'next' rule
local TGrammar = 13 -- sib1 is initial (and first) rule
local TBehind = 14 -- match behind
local TCapture = 15 -- regular capture
local TRunTime = 16 -- run-time capture
local IAny = 0 -- if no char, fail
local IChar = 1 -- if char != aux, fail
local ISet = 2 -- if char not in val, fail
local ITestAny = 3 -- in no char, jump to 'offset'
local ITestChar = 4 -- if char != aux, jump to 'offset'
local ITestSet = 5 -- if char not in val, jump to 'offset'
local ISpan = 6 -- read a span of chars in val
local IBehind = 7 -- walk back 'aux' characters (fail if not possible)
local IRet = 8 -- return from a rule
local IEnd = 9 -- end of pattern
local IChoice = 10 -- stack a choice; next fail will jump to 'offset'
local IJmp = 11 -- jump to 'offset'
local ICall = 12 -- call rule at 'offset'
local IOpenCall = 13 -- call rule number 'offset' (must be closed to a ICall)
local ICommit = 14 -- pop choice and jump to 'offset'
local IPartialCommit = 15 -- update top choice to current position and jump
local IBackCommit = 16 -- "fails" but jump to its own 'offset'
local IFailTwice = 17 -- pop one choice and then fail
local IFail = 18 -- go back to saved state on choice and jump to saved offset
local IGiveup = 19 -- internal use
local IFullCapture = 20 -- complete capture of last 'off' chars
local IOpenCapture = 21 -- start a capture
local ICloseCapture = 22
local ICloseRunTime = 23
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
-- number of siblings for each tree
local numsiblings = {
[TRep] = 1,
[TSeq] = 2,
[TChoice] = 2,
[TNot] = 1,
[TAnd] = 1,
[TRule] = 2,
[TGrammar] = 1,
[TBehind] = 1,
[TCapture] = 1,
[TRunTime] = 1,
}
local names = {
[IAny] = "any",
[IChar] = "char",
[ISet] = "set",
[ITestAny] = "testany",
[ITestChar] = "testchar",
[ITestSet] = "testset",
[ISpan] = "span",
[IBehind] = "behind",
[IRet] = "ret",
[IEnd] = "end",
[IChoice] = "choice",
[IJmp] = "jmp",
[ICall] = "call",
[IOpenCall] = "open_call",
[ICommit] = "commit",
[IPartialCommit] = "partial_commit",
[IBackCommit] = "back_commit",
[IFailTwice] = "failtwice",
[IFail] = "fail",
[IGiveup] = "giveup",
[IFullCapture] = "fullcapture",
[IOpenCapture] = "opencapture",
[ICloseCapture] = "closecapture",
[ICloseRunTime] = "closeruntime"
}
local function printcharset(st)
io.write("[");
local i = 0
while i <= 255 do
local first = i;
while band(st[rshift(i, 5)], lshift(1, band(i, 31))) ~= 0 and i <= 255 do
i = i + 1
end
if i - 1 == first then -- unary range?
io.write(("(%02x)"):format(first))
elseif i - 1 > first then -- non-empty range?
io.write(("(%02x-%02x)"):format(first, i - 1))
end
i = i + 1
end
io.write("]")
end
local modes = {
[Cclose] = "close",
[Cposition] = "position",
[Cconst] = "constant",
[Cbackref] = "backref",
[Carg] = "argument",
[Csimple] = "simple",
[Ctable] = "table",
[Cfunction] = "function",
[Cquery] = "query",
[Cstring] = "string",
[Cnum] = "num",
[Csubst] = "substitution",
[Cfold] = "fold",
[Cruntime] = "runtime",
[Cgroup] = "group"
}
local function printcapkind(kind)
io.write(("%s"):format(modes[kind]))
end
local function printjmp(p, index)
io.write(("-> %d"):format(index + p[index].offset))
end
local function printrulename(p, index, rulenames)
if rulenames and rulenames[index + p[index].offset] then
io.write(' ', rulenames[index + p[index].offset])
end
end
local function printinst(p, index, valuetable, rulenames)
local code = p[index].code
if rulenames and rulenames[index] then
io.write(rulenames[index], '\n')
end
io.write(("%04d: %s "):format(index, names[code]))
if code == IChar then
io.write(("'%s'"):format(string.char(p[index].val)))
elseif code == ITestChar then
io.write(("'%s'"):format(string.char(p[index].val)))
printjmp(p, index)
printrulename(p, index, rulenames)
elseif code == IFullCapture then
printcapkind(band(p[index].val, 0x0f));
io.write((" (size = %d) (idx = %s)"):format(band(rshift(p[index].val, 4), 0xF), tostring(valuetable[p[index].offset])))
elseif code == IOpenCapture then
printcapkind(band(p[index].val, 0x0f))
io.write((" (idx = %s)"):format(tostring(valuetable[p[index].offset])))
elseif code == ISet then
printcharset(valuetable[p[index].val]);
elseif code == ITestSet then
printcharset(valuetable[p[index].val])
printjmp(p, index);
printrulename(p, index, rulenames)
elseif code == ISpan then
printcharset(valuetable[p[index].val]);
elseif code == IOpenCall then
io.write(("-> %d"):format(p[index].offset))
elseif code == IBehind then
io.write(("%d"):format(p[index].val))
elseif code == IJmp or code == ICall or code == ICommit or code == IChoice or
code == IPartialCommit or code == IBackCommit or code == ITestAny then
printjmp(p, index);
if (code == ICall or code == IJmp) and p[index].aux > 0 then
io.write(' ', valuetable[p[index].aux])
else
printrulename(p, index, rulenames)
end
end
io.write("\n")
end
local function printpatt(p, valuetable)
local ruleNames = {}
for i = 0, p.size - 1 do
local code = p.p[i].code
if (code == ICall or code == IJmp) and p.p[i].aux > 0 then
local index = i + p.p[i].offset
ruleNames[index] = valuetable[p.p[i].aux]
end
end
for i = 0, p.size - 1 do
printinst(p.p, i, valuetable, ruleNames)
end
end
local function printcap(cap, index, valuetable)
printcapkind(cap[index].kind)
io.write((" (idx: %s - size: %d) -> %d\n"):format(valuetable[cap[index].idx], cap[index].siz, cap[index].s))
end
local function printcaplist(cap, limit, valuetable)
io.write(">======\n")
local index = 0
while cap[index].s and index < limit do
printcap(cap, index, valuetable)
index = index + 1
end
io.write("=======\n")
end
-- ======================================================
-- {======================================================
-- Printing trees (for debugging)
-- =======================================================
local tagnames = {
[TChar] = "char",
[TSet] = "set",
[TAny] = "any",
[TTrue] = "true",
[TFalse] = "false",
[TRep] = "rep",
[TSeq] = "seq",
[TChoice] = "choice",
[TNot] = "not",
[TAnd] = "and",
[TCall] = "call",
[TOpenCall] = "opencall",
[TRule] = "rule",
[TGrammar] = "grammar",
[TBehind] = "behind",
[TCapture] = "capture",
[TRunTime] = "run-time"
}
local function printtree(tree, ident, index, valuetable)
for i = 1, ident do
io.write(" ")
end
local tag = tree[index].tag
io.write(("%s"):format(tagnames[tag]))
if tag == TChar then
local c = tree[index].val
if ffi.C.isprint(c) then
io.write((" '%c'\n"):format(c))
else
io.write((" (%02X)\n"):format(c))
end
elseif tag == TSet then
printcharset(valuetable[tree[index].val]);
io.write("\n")
elseif tag == TOpenCall or tag == TCall then
io.write((" key: %s\n"):format(tostring(valuetable[tree[index].val])))
elseif tag == TBehind then
io.write((" %d\n"):format(tree[index].val))
printtree(tree, ident + 2, index + 1, valuetable);
elseif tag == TCapture then
io.write((" cap: %s n: %s\n"):format(modes[bit.band(tree[index].cap, 0xffff)], valuetable[tree[index].val]))
printtree(tree, ident + 2, index + 1, valuetable);
elseif tag == TRule then
local extra = bit.band(tree[index].cap, RuleLR) == RuleLR and ' left recursive' or ''
extra = extra .. (bit.band(tree[index].cap, Ruleused) ~= Ruleused and ' not used' or '')
io.write((" n: %d key: %s%s\n"):format(bit.band(tree[index].cap, 0xffff) - 1, valuetable[tree[index].val], extra))
printtree(tree, ident + 2, index + 1, valuetable);
-- do not print next rule as a sibling
elseif tag == TGrammar then
local ruleindex = index + 1
io.write((" %d\n"):format(tree[index].val)) -- number of rules
for i = 1, tree[index].val do
printtree(tree, ident + 2, ruleindex, valuetable);
ruleindex = ruleindex + tree[ruleindex].ps
end
assert(tree[ruleindex].tag == TTrue); -- sentinel
else
local sibs = numsiblings[tree[index].tag] or 0
io.write("\n")
if sibs >= 1 then
printtree(tree, ident + 2, index + 1, valuetable);
if sibs >= 2 then
printtree(tree, ident + 2, index + tree[index].ps, valuetable)
end
end
end
end
-- }====================================================== */
return {
printtree = printtree,
printpatt = printpatt,
printcaplist = printcaplist,
printinst = printinst
}