Update talents + beasts
parent
e8b4ed9ad6
commit
c044c52b52
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,29 @@
|
||||
|
||||
package.path = package.path .. ";luajson/?.lua"
|
||||
local JSON = require"json"
|
||||
|
||||
local beastref_f = "../compendium/wfrp4e.bestiary.json"
|
||||
local beastdescr_f = "compendium_wfrp4e.bestiary.json"
|
||||
|
||||
local f1 = io.open(beastref_f)
|
||||
local strjson = f1:read("*a")
|
||||
local beastref = JSON.decode(strjson)
|
||||
|
||||
local f2 = io.open(beastdescr_f)
|
||||
strjson = f2:read("*a")
|
||||
f2:close()
|
||||
local beastdescr = JSON.decode(strjson)
|
||||
|
||||
|
||||
for _, beasttext in pairs(beastdescr.entries) do
|
||||
for _, beastgood in pairs(beastref.entries) do
|
||||
if beasttext.id == beastgood.id then
|
||||
beastgood.description = beasttext.description
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local jsonout = JSON.encode( beastref )
|
||||
local fout = io.open("beasts.json", "w+")
|
||||
fout:write( jsonout )
|
||||
fout:close()
|
@ -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,
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,356 @@
|
||||
--[[
|
||||
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",
|
||||