2977 lines
88 KiB
Lua
2977 lines
88 KiB
Lua
|
-- LuLPeg, a pure Lua port of LPeg, Roberto Ierusalimschy's
|
|||
|
-- Parsing Expression Grammars library.
|
|||
|
--
|
|||
|
-- Copyright (C) Pierre-Yves Gerardy.
|
|||
|
-- Released under the Romantic WTF Public License (cf. the LICENSE
|
|||
|
-- file or the end of this file, whichever is present).
|
|||
|
--
|
|||
|
-- See http://www.inf.puc-rio.br/~roberto/lpeg/ for the original.
|
|||
|
--
|
|||
|
-- The re.lua module and the test suite (tests/lpeg.*.*.tests.lua)
|
|||
|
-- are part of the original LPeg distribution.
|
|||
|
local _ENV, loaded, packages, release, require_
|
|||
|
= _ENV or _G, {}, {}, true, require
|
|||
|
|
|||
|
local function require(...)
|
|||
|
local lib = ...
|
|||
|
|
|||
|
-- is it a private file?
|
|||
|
if loaded[lib] then
|
|||
|
return loaded[lib]
|
|||
|
elseif packages[lib] then
|
|||
|
loaded[lib] = packages[lib](lib)
|
|||
|
return loaded[lib]
|
|||
|
else
|
|||
|
return require_(lib)
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['API'] = function (...)
|
|||
|
|
|||
|
local assert, error, ipairs, pairs, pcall, print
|
|||
|
, require, select, tonumber, tostring, type
|
|||
|
= assert, error, ipairs, pairs, pcall, print
|
|||
|
, require, select, tonumber, tostring, type
|
|||
|
local t, u = require"table", require"util"
|
|||
|
local _ENV = u.noglobals() ---------------------------------------------------
|
|||
|
local t_concat = t.concat
|
|||
|
local checkstring, copy, fold, load, map_fold, map_foldr, setify, t_pack, t_unpack
|
|||
|
= u.checkstring, u.copy, u.fold, u.load, u.map_fold, u.map_foldr, u.setify, u.pack, u.unpack
|
|||
|
local
|
|||
|
function charset_error(index, charset)
|
|||
|
error("Character at position ".. index + 1
|
|||
|
.." is not a valid "..charset.." one.",
|
|||
|
2)
|
|||
|
end
|
|||
|
return function(Builder, LL) -- module wrapper -------------------------------
|
|||
|
local cs = Builder.charset
|
|||
|
local constructors, LL_ispattern
|
|||
|
= Builder.constructors, LL.ispattern
|
|||
|
local truept, falsept, Cppt
|
|||
|
= constructors.constant.truept
|
|||
|
, constructors.constant.falsept
|
|||
|
, constructors.constant.Cppt
|
|||
|
local split_int, validate
|
|||
|
= cs.split_int, cs.validate
|
|||
|
local Range, Set, S_union, S_tostring
|
|||
|
= Builder.Range, Builder.set.new
|
|||
|
, Builder.set.union, Builder.set.tostring
|
|||
|
local factorize_choice, factorize_lookahead, factorize_sequence, factorize_unm
|
|||
|
local
|
|||
|
function makechar(c)
|
|||
|
return constructors.aux("char", c)
|
|||
|
end
|
|||
|
local
|
|||
|
function LL_P (...)
|
|||
|
local v, n = (...), select('#', ...)
|
|||
|
if n == 0 then error"bad argument #1 to 'P' (value expected)" end
|
|||
|
local typ = type(v)
|
|||
|
if LL_ispattern(v) then
|
|||
|
return v
|
|||
|
elseif typ == "function" then
|
|||
|
return
|
|||
|
LL.Cmt("", v)
|
|||
|
elseif typ == "string" then
|
|||
|
local success, index = validate(v)
|
|||
|
if not success then
|
|||
|
charset_error(index, cs.name)
|
|||
|
end
|
|||
|
if v == "" then return truept end
|
|||
|
return
|
|||
|
map_foldr(split_int(v), makechar, Builder.sequence)
|
|||
|
elseif typ == "table" then
|
|||
|
local g = copy(v)
|
|||
|
if g[1] == nil then error("grammar has no initial rule") end
|
|||
|
if not LL_ispattern(g[1]) then g[1] = LL.V(g[1]) end
|
|||
|
return
|
|||
|
constructors.none("grammar", g)
|
|||
|
elseif typ == "boolean" then
|
|||
|
return v and truept or falsept
|
|||
|
elseif typ == "number" then
|
|||
|
if v == 0 then
|
|||
|
return truept
|
|||
|
elseif v > 0 then
|
|||
|
return
|
|||
|
constructors.aux("any", v)
|
|||
|
else
|
|||
|
return
|
|||
|
- constructors.aux("any", -v)
|
|||
|
end
|
|||
|
else
|
|||
|
error("bad argument #1 to 'P' (lpeg-pattern expected, got "..typ..")")
|
|||
|
end
|
|||
|
end
|
|||
|
LL.P = LL_P
|
|||
|
local
|
|||
|
function LL_S (set)
|
|||
|
if set == "" then
|
|||
|
return
|
|||
|
falsept
|
|||
|
else
|
|||
|
local success
|
|||
|
set = checkstring(set, "S")
|
|||
|
return
|
|||
|
constructors.aux("set", Set(split_int(set)), set)
|
|||
|
end
|
|||
|
end
|
|||
|
LL.S = LL_S
|
|||
|
local
|
|||
|
function LL_R (...)
|
|||
|
if select('#', ...) == 0 then
|
|||
|
return LL_P(false)
|
|||
|
else
|
|||
|
local range = Range(1,0)--Set("")
|
|||
|
for _, r in ipairs{...} do
|
|||
|
r = checkstring(r, "R")
|
|||
|
assert(#r == 2, "bad argument #1 to 'R' (range must have two characters)")
|
|||
|
range = S_union ( range, Range(t_unpack(split_int(r))) )
|
|||
|
end
|
|||
|
return
|
|||
|
constructors.aux("set", range)
|
|||
|
end
|
|||
|
end
|
|||
|
LL.R = LL_R
|
|||
|
local
|
|||
|
function LL_V (name)
|
|||
|
assert(name ~= nil)
|
|||
|
return
|
|||
|
constructors.aux("ref", name)
|
|||
|
end
|
|||
|
LL.V = LL_V
|
|||
|
do
|
|||
|
local one = setify{"set", "range", "one", "char"}
|
|||
|
local zero = setify{"true", "false", "lookahead", "unm"}
|
|||
|
local forbidden = setify{
|
|||
|
"Carg", "Cb", "C", "Cf",
|
|||
|
"Cg", "Cs", "Ct", "/zero",
|
|||
|
"Clb", "Cmt", "Cc", "Cp",
|
|||
|
"div_string", "div_number", "div_table", "div_function",
|
|||
|
"at least", "at most", "behind"
|
|||
|
}
|
|||
|
local function fixedlen(pt, gram, cycle)
|
|||
|
local typ = pt.pkind
|
|||
|
if forbidden[typ] then return false
|
|||
|
elseif one[typ] then return 1
|
|||
|
elseif zero[typ] then return 0
|
|||
|
elseif typ == "string" then return #pt.as_is
|
|||
|
elseif typ == "any" then return pt.aux
|
|||
|
elseif typ == "choice" then
|
|||
|
local l1, l2 = fixedlen(pt[1], gram, cycle), fixedlen(pt[2], gram, cycle)
|
|||
|
return (l1 == l2) and l1
|
|||
|
elseif typ == "sequence" then
|
|||
|
local l1, l2 = fixedlen(pt[1], gram, cycle), fixedlen(pt[2], gram, cycle)
|
|||
|
return l1 and l2 and l1 + l2
|
|||
|
elseif typ == "grammar" then
|
|||
|
if pt.aux[1].pkind == "ref" then
|
|||
|
return fixedlen(pt.aux[pt.aux[1].aux], pt.aux, {})
|
|||
|
else
|
|||
|
return fixedlen(pt.aux[1], pt.aux, {})
|
|||
|
end
|
|||
|
elseif typ == "ref" then
|
|||
|
if cycle[pt] then return false end
|
|||
|
cycle[pt] = true
|
|||
|
return fixedlen(gram[pt.aux], gram, cycle)
|
|||
|
else
|
|||
|
print(typ,"is not handled by fixedlen()")
|
|||
|
end
|
|||
|
end
|
|||
|
function LL.B (pt)
|
|||
|
pt = LL_P(pt)
|
|||
|
local len = fixedlen(pt)
|
|||
|
assert(len, "A 'behind' pattern takes a fixed length pattern as argument.")
|
|||
|
if len >= 260 then error("Subpattern too long in 'behind' pattern constructor.") end
|
|||
|
return
|
|||
|
constructors.both("behind", pt, len)
|
|||
|
end
|
|||
|
end
|
|||
|
local function nameify(a, b)
|
|||
|
return ('%s:%s'):format(a.id, b.id)
|
|||
|
end
|
|||
|
local
|
|||
|
function choice (a, b)
|
|||
|
local name = nameify(a, b)
|
|||
|
local ch = Builder.ptcache.choice[name]
|
|||
|
if not ch then
|
|||
|
ch = factorize_choice(a, b) or constructors.binary("choice", a, b)
|
|||
|
Builder.ptcache.choice[name] = ch
|
|||
|
end
|
|||
|
return ch
|
|||
|
end
|
|||
|
function LL.__add (a, b)
|
|||
|
return
|
|||
|
choice(LL_P(a), LL_P(b))
|
|||
|
end
|
|||
|
local
|
|||
|
function sequence (a, b)
|
|||
|
local name = nameify(a, b)
|
|||
|
local seq = Builder.ptcache.sequence[name]
|
|||
|
if not seq then
|
|||
|
seq = factorize_sequence(a, b) or constructors.binary("sequence", a, b)
|
|||
|
Builder.ptcache.sequence[name] = seq
|
|||
|
end
|
|||
|
return seq
|
|||
|
end
|
|||
|
Builder.sequence = sequence
|
|||
|
function LL.__mul (a, b)
|
|||
|
return
|
|||
|
sequence(LL_P(a), LL_P(b))
|
|||
|
end
|
|||
|
local
|
|||
|
function LL_lookahead (pt)
|
|||
|
if pt == truept
|
|||
|
or pt == falsept
|
|||
|
or pt.pkind == "unm"
|
|||
|
or pt.pkind == "lookahead"
|
|||
|
then
|
|||
|
return pt
|
|||
|
end
|
|||
|
return
|
|||
|
constructors.subpt("lookahead", pt)
|
|||
|
end
|
|||
|
LL.__len = LL_lookahead
|
|||
|
LL.L = LL_lookahead
|
|||
|
local
|
|||
|
function LL_unm(pt)
|
|||
|
return
|
|||
|
factorize_unm(pt)
|
|||
|
or constructors.subpt("unm", pt)
|
|||
|
end
|
|||
|
LL.__unm = LL_unm
|
|||
|
local
|
|||
|
function LL_sub (a, b)
|
|||
|
a, b = LL_P(a), LL_P(b)
|
|||
|
return LL_unm(b) * a
|
|||
|
end
|
|||
|
LL.__sub = LL_sub
|
|||
|
local
|
|||
|
function LL_repeat (pt, n)
|
|||
|
local success
|
|||
|
success, n = pcall(tonumber, n)
|
|||
|
assert(success and type(n) == "number",
|
|||
|
"Invalid type encountered at right side of '^'.")
|
|||
|
return constructors.both(( n < 0 and "at most" or "at least" ), pt, n)
|
|||
|
end
|
|||
|
LL.__pow = LL_repeat
|
|||
|
for _, cap in pairs{"C", "Cs", "Ct"} do
|
|||
|
LL[cap] = function(pt)
|
|||
|
pt = LL_P(pt)
|
|||
|
return
|
|||
|
constructors.subpt(cap, pt)
|
|||
|
end
|
|||
|
end
|
|||
|
LL["Cb"] = function(aux)
|
|||
|
return
|
|||
|
constructors.aux("Cb", aux)
|
|||
|
end
|
|||
|
LL["Carg"] = function(aux)
|
|||
|
assert(type(aux)=="number", "Number expected as parameter to Carg capture.")
|
|||
|
assert( 0 < aux and aux <= 200, "Argument out of bounds in Carg capture.")
|
|||
|
return
|
|||
|
constructors.aux("Carg", aux)
|
|||
|
end
|
|||
|
local
|
|||
|
function LL_Cp ()
|
|||
|
return Cppt
|
|||
|
end
|
|||
|
LL.Cp = LL_Cp
|
|||
|
local
|
|||
|
function LL_Cc (...)
|
|||
|
return
|
|||
|
constructors.none("Cc", t_pack(...))
|
|||
|
end
|
|||
|
LL.Cc = LL_Cc
|
|||
|
for _, cap in pairs{"Cf", "Cmt"} do
|
|||
|
local msg = "Function expected in "..cap.." capture"
|
|||
|
LL[cap] = function(pt, aux)
|
|||
|
assert(type(aux) == "function", msg)
|
|||
|
pt = LL_P(pt)
|
|||
|
return
|
|||
|
constructors.both(cap, pt, aux)
|
|||
|
end
|
|||
|
end
|
|||
|
local
|
|||
|
function LL_Cg (pt, tag)
|
|||
|
pt = LL_P(pt)
|
|||
|
if tag ~= nil then
|
|||
|
return
|
|||
|
constructors.both("Clb", pt, tag)
|
|||
|
else
|
|||
|
return
|
|||
|
constructors.subpt("Cg", pt)
|
|||
|
end
|
|||
|
end
|
|||
|
LL.Cg = LL_Cg
|
|||
|
local valid_slash_type = setify{"string", "number", "table", "function"}
|
|||
|
local
|
|||
|
function LL_slash (pt, aux)
|
|||
|
if LL_ispattern(aux) then
|
|||
|
error"The right side of a '/' capture cannot be a pattern."
|
|||
|
elseif not valid_slash_type[type(aux)] then
|
|||
|
error("The right side of a '/' capture must be of type "
|
|||
|
.."string, number, table or function.")
|
|||
|
end
|
|||
|
local name
|
|||
|
if aux == 0 then
|
|||
|
name = "/zero"
|
|||
|
else
|
|||
|
name = "div_"..type(aux)
|
|||
|
end
|
|||
|
return
|
|||
|
constructors.both(name, pt, aux)
|
|||
|
end
|
|||
|
LL.__div = LL_slash
|
|||
|
if Builder.proxymt then
|
|||
|
for k, v in pairs(LL) do
|
|||
|
if k:match"^__" then
|
|||
|
Builder.proxymt[k] = v
|
|||
|
end
|
|||
|
end
|
|||
|
else
|
|||
|
LL.__index = LL
|
|||
|
end
|
|||
|
local factorizer
|
|||
|
= Builder.factorizer(Builder, LL)
|
|||
|
factorize_choice, factorize_lookahead, factorize_sequence, factorize_unm =
|
|||
|
factorizer.choice, factorizer.lookahead, factorizer.sequence, factorizer.unm
|
|||
|
end -- module wrapper --------------------------------------------------------
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['analyzer'] = function (...)
|
|||
|
|
|||
|
local u = require"util"
|
|||
|
local nop, weakkey = u.nop, u.weakkey
|
|||
|
local hasVcache, hasCmtcache , lengthcache
|
|||
|
= weakkey{}, weakkey{}, weakkey{}
|
|||
|
return {
|
|||
|
hasV = nop,
|
|||
|
hasCmt = nop,
|
|||
|
length = nop,
|
|||
|
hasCapture = nop
|
|||
|
}
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['charsets'] = function (...)
|
|||
|
|
|||
|
local s, t, u = require"string", require"table", require"util"
|
|||
|
local _ENV = u.noglobals() ----------------------------------------------------
|
|||
|
local copy = u.copy
|
|||
|
local s_char, s_sub, s_byte, t_concat, t_insert
|
|||
|
= s.char, s.sub, s.byte, t.concat, t.insert
|
|||
|
local
|
|||
|
function utf8_offset (byte)
|
|||
|
if byte < 128 then return 0, byte
|
|||
|
elseif byte < 192 then
|
|||
|
error("Byte values between 0x80 to 0xBF cannot start a multibyte sequence")
|
|||
|
elseif byte < 224 then return 1, byte - 192
|
|||
|
elseif byte < 240 then return 2, byte - 224
|
|||
|
elseif byte < 248 then return 3, byte - 240
|
|||
|
elseif byte < 252 then return 4, byte - 248
|
|||
|
elseif byte < 254 then return 5, byte - 252
|
|||
|
else
|
|||
|
error("Byte values between 0xFE and OxFF cannot start a multibyte sequence")
|
|||
|
end
|
|||
|
end
|
|||
|
local
|
|||
|
function utf8_validate (subject, start, finish)
|
|||
|
start = start or 1
|
|||
|
finish = finish or #subject
|
|||
|
local offset, char
|
|||
|
= 0
|
|||
|
for i = start,finish do
|
|||
|
local b = s_byte(subject,i)
|
|||
|
if offset == 0 then
|
|||
|
char = i
|
|||
|
success, offset = pcall(utf8_offset, b)
|
|||
|
if not success then return false, char - 1 end
|
|||
|
else
|
|||
|
if not (127 < b and b < 192) then
|
|||
|
return false, char - 1
|
|||
|
end
|
|||
|
offset = offset -1
|
|||
|
end
|
|||
|
end
|
|||
|
if offset ~= 0 then return nil, char - 1 end -- Incomplete input.
|
|||
|
return true, finish
|
|||
|
end
|
|||
|
local
|
|||
|
function utf8_next_int (subject, i)
|
|||
|
i = i and i+1 or 1
|
|||
|
if i > #subject then return end
|
|||
|
local c = s_byte(subject, i)
|
|||
|
local offset, val = utf8_offset(c)
|
|||
|
for i = i+1, i+offset do
|
|||
|
c = s_byte(subject, i)
|
|||
|
val = val * 64 + (c-128)
|
|||
|
end
|
|||
|
return i + offset, i, val
|
|||
|
end
|
|||
|
local
|
|||
|
function utf8_next_char (subject, i)
|
|||
|
i = i and i+1 or 1
|
|||
|
if i > #subject then return end
|
|||
|
local offset = utf8_offset(s_byte(subject,i))
|
|||
|
return i + offset, i, s_sub(subject, i, i + offset)
|
|||
|
end
|
|||
|
local
|
|||
|
function utf8_split_int (subject)
|
|||
|
local chars = {}
|
|||
|
for _, _, c in utf8_next_int, subject do
|
|||
|
t_insert(chars,c)
|
|||
|
end
|
|||
|
return chars
|
|||
|
end
|
|||
|
local
|
|||
|
function utf8_split_char (subject)
|
|||
|
local chars = {}
|
|||
|
for _, _, c in utf8_next_char, subject do
|
|||
|
t_insert(chars,c)
|
|||
|
end
|
|||
|
return chars
|
|||
|
end
|
|||
|
local
|
|||
|
function utf8_get_int(subject, i)
|
|||
|
if i > #subject then return end
|
|||
|
local c = s_byte(subject, i)
|
|||
|
local offset, val = utf8_offset(c)
|
|||
|
for i = i+1, i+offset do
|
|||
|
c = s_byte(subject, i)
|
|||
|
val = val * 64 + ( c - 128 )
|
|||
|
end
|
|||
|
return val, i + offset + 1
|
|||
|
end
|
|||
|
local
|
|||
|
function split_generator (get)
|
|||
|
if not get then return end
|
|||
|
return function(subject)
|
|||
|
local res = {}
|
|||
|
local o, i = true
|
|||
|
while o do
|
|||
|
o,i = get(subject, i)
|
|||
|
res[#res] = o
|
|||
|
end
|
|||
|
return res
|
|||
|
end
|
|||
|
end
|
|||
|
local
|
|||
|
function merge_generator (char)
|
|||
|
if not char then return end
|
|||
|
return function(ary)
|
|||
|
local res = {}
|
|||
|
for i = 1, #ary do
|
|||
|
t_insert(res,char(ary[i]))
|
|||
|
end
|
|||
|
return t_concat(res)
|
|||
|
end
|
|||
|
end
|
|||
|
local
|
|||
|
function utf8_get_int2 (subject, i)
|
|||
|
local byte, b5, b4, b3, b2, b1 = s_byte(subject, i)
|
|||
|
if byte < 128 then return byte, i + 1
|
|||
|
elseif byte < 192 then
|
|||
|
error("Byte values between 0x80 to 0xBF cannot start a multibyte sequence")
|
|||
|
elseif byte < 224 then
|
|||
|
return (byte - 192)*64 + s_byte(subject, i+1), i+2
|
|||
|
elseif byte < 240 then
|
|||
|
b2, b1 = s_byte(subject, i+1, i+2)
|
|||
|
return (byte-224)*4096 + b2%64*64 + b1%64, i+3
|
|||
|
elseif byte < 248 then
|
|||
|
b3, b2, b1 = s_byte(subject, i+1, i+2, 1+3)
|
|||
|
return (byte-240)*262144 + b3%64*4096 + b2%64*64 + b1%64, i+4
|
|||
|
elseif byte < 252 then
|
|||
|
b4, b3, b2, b1 = s_byte(subject, i+1, i+2, 1+3, i+4)
|
|||
|
return (byte-248)*16777216 + b4%64*262144 + b3%64*4096 + b2%64*64 + b1%64, i+5
|
|||
|
elseif byte < 254 then
|
|||
|
b5, b4, b3, b2, b1 = s_byte(subject, i+1, i+2, 1+3, i+4, i+5)
|
|||
|
return (byte-252)*1073741824 + b5%64*16777216 + b4%64*262144 + b3%64*4096 + b2%64*64 + b1%64, i+6
|
|||
|
else
|
|||
|
error("Byte values between 0xFE and OxFF cannot start a multibyte sequence")
|
|||
|
end
|
|||
|
end
|
|||
|
local
|
|||
|
function utf8_get_char(subject, i)
|
|||
|
if i > #subject then return end
|
|||
|
local offset = utf8_offset(s_byte(subject,i))
|
|||
|
return s_sub(subject, i, i + offset), i + offset + 1
|
|||
|
end
|
|||
|
local
|
|||
|
function utf8_char(c)
|
|||
|
if c < 128 then
|
|||
|
return s_char(c)
|
|||
|
elseif c < 2048 then
|
|||
|
return s_char(192 + c/64, 128 + c%64)
|
|||
|
elseif c < 55296 or 57343 < c and c < 65536 then
|
|||
|
return s_char(224 + c/4096, 128 + c/64%64, 128 + c%64)
|
|||
|
elseif c < 2097152 then
|
|||
|
return s_char(240 + c/262144, 128 + c/4096%64, 128 + c/64%64, 128 + c%64)
|
|||
|
elseif c < 67108864 then
|
|||
|
return s_char(248 + c/16777216, 128 + c/262144%64, 128 + c/4096%64, 128 + c/64%64, 128 + c%64)
|
|||
|
elseif c < 2147483648 then
|
|||
|
return s_char( 252 + c/1073741824,
|
|||
|
128 + c/16777216%64, 128 + c/262144%64, 128 + c/4096%64, 128 + c/64%64, 128 + c%64)
|
|||
|
end
|
|||
|
error("Bad Unicode code point: "..c..".")
|
|||
|
end
|
|||
|
local
|
|||
|
function binary_validate (subject, start, finish)
|
|||
|
start = start or 1
|
|||
|
finish = finish or #subject
|
|||
|
return true, finish
|
|||
|
end
|
|||
|
local
|
|||
|
function binary_next_int (subject, i)
|
|||
|
i = i and i+1 or 1
|
|||
|
if i >= #subject then return end
|
|||
|
return i, i, s_sub(subject, i, i)
|
|||
|
end
|
|||
|
local
|
|||
|
function binary_next_char (subject, i)
|
|||
|
i = i and i+1 or 1
|
|||
|
if i > #subject then return end
|
|||
|
return i, i, s_byte(subject,i)
|
|||
|
end
|
|||
|
local
|
|||
|
function binary_split_int (subject)
|
|||
|
local chars = {}
|
|||
|
for i = 1, #subject do
|
|||
|
t_insert(chars, s_byte(subject,i))
|
|||
|
end
|
|||
|
return chars
|
|||
|
end
|
|||
|
local
|
|||
|
function binary_split_char (subject)
|
|||
|
local chars = {}
|
|||
|
for i = 1, #subject do
|
|||
|
t_insert(chars, s_sub(subject,i,i))
|
|||
|
end
|
|||
|
return chars
|
|||
|
end
|
|||
|
local
|
|||
|
function binary_get_int(subject, i)
|
|||
|
return s_byte(subject, i), i + 1
|
|||
|
end
|
|||
|
local
|
|||
|
function binary_get_char(subject, i)
|
|||
|
return s_sub(subject, i, i), i + 1
|
|||
|
end
|
|||
|
local charsets = {
|
|||
|
binary = {
|
|||
|
name = "binary",
|
|||
|
binary = true,
|
|||
|
validate = binary_validate,
|
|||
|
split_char = binary_split_char,
|
|||
|
split_int = binary_split_int,
|
|||
|
next_char = binary_next_char,
|
|||
|
next_int = binary_next_int,
|
|||
|
get_char = binary_get_char,
|
|||
|
get_int = binary_get_int,
|
|||
|
tochar = s_char
|
|||
|
},
|
|||
|
["UTF-8"] = {
|
|||
|
name = "UTF-8",
|
|||
|
validate = utf8_validate,
|
|||
|
split_char = utf8_split_char,
|
|||
|
split_int = utf8_split_int,
|
|||
|
next_char = utf8_next_char,
|
|||
|
next_int = utf8_next_int,
|
|||
|
get_char = utf8_get_char,
|
|||
|
get_int = utf8_get_int
|
|||
|
}
|
|||
|
}
|
|||
|
return function (Builder)
|
|||
|
local cs = Builder.options.charset or "binary"
|
|||
|
if charsets[cs] then
|
|||
|
Builder.charset = copy(charsets[cs])
|
|||
|
Builder.binary_split_int = binary_split_int
|
|||
|
else
|
|||
|
error("NYI: custom charsets")
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['compat'] = function (...)
|
|||
|
|
|||
|
local _, debug, jit
|
|||
|
_, debug = pcall(require, "debug")
|
|||
|
_, jit = pcall(require, "jit")
|
|||
|
jit = _ and jit
|
|||
|
local compat = {
|
|||
|
debug = debug,
|
|||
|
lua51 = (_VERSION == "Lua 5.1") and not jit,
|
|||
|
lua52 = _VERSION == "Lua 5.2",
|
|||
|
luajit = jit and true or false,
|
|||
|
jit = jit and jit.status(),
|
|||
|
lua52_len = not #setmetatable({},{__len = function()end}),
|
|||
|
proxies = pcall(function()
|
|||
|
local prox = newproxy(true)
|
|||
|
local prox2 = newproxy(prox)
|
|||
|
assert (type(getmetatable(prox)) == "table"
|
|||
|
and (getmetatable(prox)) == (getmetatable(prox2)))
|
|||
|
end),
|
|||
|
_goto = not not(loadstring or load)"::R::"
|
|||
|
}
|
|||
|
return compat
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['compiler'] = function (...)
|
|||
|
local assert, error, pairs, print, rawset, select, setmetatable, tostring, type
|
|||
|
= assert, error, pairs, print, rawset, select, setmetatable, tostring, type
|
|||
|
local s, t, u = require"string", require"table", require"util"
|
|||
|
local _ENV = u.noglobals() ----------------------------------------------------
|
|||
|
local s_byte, s_sub, t_concat, t_insert, t_remove, t_unpack
|
|||
|
= s.byte, s.sub, t.concat, t.insert, t.remove, u.unpack
|
|||
|
local load, map, map_all, t_pack
|
|||
|
= u.load, u.map, u.map_all, u.pack
|
|||
|
local expose = u.expose
|
|||
|
return function(Builder, LL)
|
|||
|
local evaluate, LL_ispattern = LL.evaluate, LL.ispattern
|
|||
|
local charset = Builder.charset
|
|||
|
local compilers = {}
|
|||
|
local
|
|||
|
function compile(pt, ccache)
|
|||
|
if not LL_ispattern(pt) then
|
|||
|
error("pattern expected")
|
|||
|
end
|
|||
|
local typ = pt.pkind
|
|||
|
if typ == "grammar" then
|
|||
|
ccache = {}
|
|||
|
elseif typ == "ref" or typ == "choice" or typ == "sequence" then
|
|||
|
if not ccache[pt] then
|
|||
|
ccache[pt] = compilers[typ](pt, ccache)
|
|||
|
end
|
|||
|
return ccache[pt]
|
|||
|
end
|
|||
|
if not pt.compiled then
|
|||
|
pt.compiled = compilers[pt.pkind](pt, ccache)
|
|||
|
end
|
|||
|
return pt.compiled
|
|||
|
end
|
|||
|
LL.compile = compile
|
|||
|
local
|
|||
|
function clear_captures(ary, ci)
|
|||
|
for i = ci, #ary do ary[i] = nil end
|
|||
|
end
|
|||
|
local LL_compile, LL_evaluate, LL_P
|
|||
|
= LL.compile, LL.evaluate, LL.P
|
|||
|
local function computeidex(i, len)
|
|||
|
if i == 0 or i == 1 or i == nil then return 1
|
|||
|
elseif type(i) ~= "number" then error"number or nil expected for the stating index"
|
|||
|
elseif i > 0 then return i > len and len + 1 or i
|
|||
|
else return len + i < 0 and 1 or len + i + 1
|
|||
|
end
|
|||
|
end
|
|||
|
local function newcaps()
|
|||
|
return {
|
|||
|
kind = {},
|
|||
|
bounds = {},
|
|||
|
openclose = {},
|
|||
|
aux = -- [[DBG]] dbgcaps
|
|||
|
{}
|
|||
|
}
|
|||
|
end
|
|||
|
local
|
|||
|
function _match(dbg, pt, sbj, si, ...)
|
|||
|
if dbg then -------------
|
|||
|
print("@!!! Match !!!@", pt)
|
|||
|
end ---------------------
|
|||
|
pt = LL_P(pt)
|
|||
|
assert(type(sbj) == "string", "string expected for the match subject")
|
|||
|
si = computeidex(si, #sbj)
|
|||
|
if dbg then -------------
|
|||
|
print(("-"):rep(30))
|
|||
|
print(pt.pkind)
|
|||
|
LL.pprint(pt)
|
|||
|
end ---------------------
|
|||
|
local matcher = compile(pt, {})
|
|||
|
local caps = newcaps()
|
|||
|
local matcher_state = {grammars = {}, args = {n = select('#',...),...}, tags = {}}
|
|||
|
local success, final_si, ci = matcher(sbj, si, caps, 1, matcher_state)
|
|||
|
if dbg then -------------
|
|||
|
print("!!! Done Matching !!! success: ", success,
|
|||
|
"final position", final_si, "final cap index", ci,
|
|||
|
"#caps", #caps.openclose)
|
|||
|
end----------------------
|
|||
|
if success then
|
|||
|
clear_captures(caps.kind, ci)
|
|||
|
clear_captures(caps.aux, ci)
|
|||
|
if dbg then -------------
|
|||
|
print("trimmed cap index = ", #caps + 1)
|
|||
|
LL.cprint(caps, sbj, 1)
|
|||
|
end ---------------------
|
|||
|
local values, _, vi = LL_evaluate(caps, sbj, 1, 1)
|
|||
|
if dbg then -------------
|
|||
|
print("#values", vi)
|
|||
|
expose(values)
|
|||
|
end ---------------------
|
|||
|
if vi == 0
|
|||
|
then return final_si
|
|||
|
else return t_unpack(values, 1, vi) end
|
|||
|
else
|
|||
|
if dbg then print("Failed") end
|
|||
|
return nil
|
|||
|
end
|
|||
|
end
|
|||
|
function LL.match(...)
|
|||
|
return _match(false, ...)
|
|||
|
end
|
|||
|
function LL.dmatch(...)
|
|||
|
return _match(true, ...)
|
|||
|
end
|
|||
|
for _, v in pairs{
|
|||
|
"C", "Cf", "Cg", "Cs", "Ct", "Clb",
|
|||
|
"div_string", "div_table", "div_number", "div_function"
|
|||
|
} do
|
|||
|
compilers[v] = load(([=[
|
|||
|
local compile, expose, type, LL = ...
|
|||
|
return function (pt, ccache)
|
|||
|
local matcher, this_aux = compile(pt.pattern, ccache), pt.aux
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local ref_ci = ci
|
|||
|
local kind, bounds, openclose, aux
|
|||
|
= caps.kind, caps.bounds, caps.openclose, caps.aux
|
|||
|
kind [ci] = "XXXX"
|
|||
|
bounds [ci] = si
|
|||
|
openclose [ci] = 0
|
|||
|
caps.aux [ci] = (this_aux or false)
|
|||
|
local success
|
|||
|
success, si, ci
|
|||
|
= matcher(sbj, si, caps, ci + 1, state)
|
|||
|
if success then
|
|||
|
if ci == ref_ci + 1 then
|
|||
|
caps.openclose[ref_ci] = si
|
|||
|
else
|
|||
|
kind [ci] = "XXXX"
|
|||
|
bounds [ci] = si
|
|||
|
openclose [ci] = ref_ci - ci
|
|||
|
aux [ci] = this_aux or false
|
|||
|
ci = ci + 1
|
|||
|
end
|
|||
|
else
|
|||
|
ci = ci - 1
|
|||
|
end
|
|||
|
return success, si, ci
|
|||
|
end
|
|||
|
end]=]):gsub("XXXX", v), v.." compiler")(compile, expose, type, LL)
|
|||
|
end
|
|||
|
compilers["Carg"] = function (pt, ccache)
|
|||
|
local n = pt.aux
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
if state.args.n < n then error("reference to absent argument #"..n) end
|
|||
|
caps.kind [ci] = "value"
|
|||
|
caps.bounds [ci] = si
|
|||
|
if state.args[n] == nil then
|
|||
|
caps.openclose [ci] = 1/0
|
|||
|
caps.aux [ci] = 1/0
|
|||
|
else
|
|||
|
caps.openclose [ci] = si
|
|||
|
caps.aux [ci] = state.args[n]
|
|||
|
end
|
|||
|
return true, si, ci + 1
|
|||
|
end
|
|||
|
end
|
|||
|
for _, v in pairs{
|
|||
|
"Cb", "Cc", "Cp"
|
|||
|
} do
|
|||
|
compilers[v] = load(([=[
|
|||
|
return function (pt, ccache)
|
|||
|
local this_aux = pt.aux
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
caps.kind [ci] = "XXXX"
|
|||
|
caps.bounds [ci] = si
|
|||
|
caps.openclose [ci] = si
|
|||
|
caps.aux [ci] = this_aux or false
|
|||
|
return true, si, ci + 1
|
|||
|
end
|
|||
|
end]=]):gsub("XXXX", v), v.." compiler")(expose)
|
|||
|
end
|
|||
|
compilers["/zero"] = function (pt, ccache)
|
|||
|
local matcher = compile(pt.pattern, ccache)
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local success, nsi = matcher(sbj, si, caps, ci, state)
|
|||
|
clear_captures(caps.aux, ci)
|
|||
|
return success, nsi, ci
|
|||
|
end
|
|||
|
end
|
|||
|
local function pack_Cmt_caps(i,...) return i, t_pack(...) end
|
|||
|
compilers["Cmt"] = function (pt, ccache)
|
|||
|
local matcher, func = compile(pt.pattern, ccache), pt.aux
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local success, Cmt_si, Cmt_ci = matcher(sbj, si, caps, ci, state)
|
|||
|
if not success then
|
|||
|
clear_captures(caps.aux, ci)
|
|||
|
return false, si, ci
|
|||
|
end
|
|||
|
local final_si, values
|
|||
|
if Cmt_ci == ci then
|
|||
|
final_si, values = pack_Cmt_caps(
|
|||
|
func(sbj, Cmt_si, s_sub(sbj, si, Cmt_si - 1))
|
|||
|
)
|
|||
|
else
|
|||
|
clear_captures(caps.aux, Cmt_ci)
|
|||
|
clear_captures(caps.kind, Cmt_ci)
|
|||
|
local cps, _, nn = evaluate(caps, sbj, ci)
|
|||
|
final_si, values = pack_Cmt_caps(
|
|||
|
func(sbj, Cmt_si, t_unpack(cps, 1, nn))
|
|||
|
)
|
|||
|
end
|
|||
|
if not final_si then
|
|||
|
return false, si, ci
|
|||
|
end
|
|||
|
if final_si == true then final_si = Cmt_si end
|
|||
|
if type(final_si) == "number"
|
|||
|
and si <= final_si
|
|||
|
and final_si <= #sbj + 1
|
|||
|
then
|
|||
|
local kind, bounds, openclose, aux
|
|||
|
= caps.kind, caps.bounds, caps.openclose, caps.aux
|
|||
|
for i = 1, values.n do
|
|||
|
kind [ci] = "value"
|
|||
|
bounds [ci] = si
|
|||
|
if values[i] == nil then
|
|||
|
caps.openclose [ci] = 1/0
|
|||
|
caps.aux [ci] = 1/0
|
|||
|
else
|
|||
|
caps.openclose [ci] = final_si
|
|||
|
caps.aux [ci] = values[i]
|
|||
|
end
|
|||
|
ci = ci + 1
|
|||
|
end
|
|||
|
elseif type(final_si) == "number" then
|
|||
|
error"Index out of bounds returned by match-time capture."
|
|||
|
else
|
|||
|
error("Match time capture must return a number, a boolean or nil"
|
|||
|
.." as first argument, or nothing at all.")
|
|||
|
end
|
|||
|
return true, final_si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["string"] = function (pt, ccache)
|
|||
|
local S = pt.aux
|
|||
|
local N = #S
|
|||
|
return function(sbj, si, caps, ci, state)
|
|||
|
local in_1 = si - 1
|
|||
|
for i = 1, N do
|
|||
|
local c
|
|||
|
c = s_byte(sbj,in_1 + i)
|
|||
|
if c ~= S[i] then
|
|||
|
return false, si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
return true, si + N, ci
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["char"] = function (pt, ccache)
|
|||
|
return load(([=[
|
|||
|
local s_byte, s_char = ...
|
|||
|
return function(sbj, si, caps, ci, state)
|
|||
|
local c, nsi = s_byte(sbj, si), si + 1
|
|||
|
if c ~= __C0__ then
|
|||
|
return false, si, ci
|
|||
|
end
|
|||
|
return true, nsi, ci
|
|||
|
end]=]):gsub("__C0__", tostring(pt.aux)))(s_byte, ("").char)
|
|||
|
end
|
|||
|
local
|
|||
|
function truecompiled (sbj, si, caps, ci, state)
|
|||
|
return true, si, ci
|
|||
|
end
|
|||
|
compilers["true"] = function (pt)
|
|||
|
return truecompiled
|
|||
|
end
|
|||
|
local
|
|||
|
function falsecompiled (sbj, si, caps, ci, state)
|
|||
|
return false, si, ci
|
|||
|
end
|
|||
|
compilers["false"] = function (pt)
|
|||
|
return falsecompiled
|
|||
|
end
|
|||
|
local
|
|||
|
function eoscompiled (sbj, si, caps, ci, state)
|
|||
|
return si > #sbj, si, ci
|
|||
|
end
|
|||
|
compilers["eos"] = function (pt)
|
|||
|
return eoscompiled
|
|||
|
end
|
|||
|
local
|
|||
|
function onecompiled (sbj, si, caps, ci, state)
|
|||
|
local char, _ = s_byte(sbj, si), si + 1
|
|||
|
if char
|
|||
|
then return true, si + 1, ci
|
|||
|
else return false, si, ci end
|
|||
|
end
|
|||
|
compilers["one"] = function (pt)
|
|||
|
return onecompiled
|
|||
|
end
|
|||
|
compilers["any"] = function (pt)
|
|||
|
local N = pt.aux
|
|||
|
if N == 1 then
|
|||
|
return onecompiled
|
|||
|
else
|
|||
|
N = pt.aux - 1
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local n = si + N
|
|||
|
if n <= #sbj then
|
|||
|
return true, n + 1, ci
|
|||
|
else
|
|||
|
return false, si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
do
|
|||
|
local function checkpatterns(g)
|
|||
|
for k,v in pairs(g.aux) do
|
|||
|
if not LL_ispattern(v) then
|
|||
|
error(("rule 'A' is not a pattern"):gsub("A", tostring(k)))
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["grammar"] = function (pt, ccache)
|
|||
|
checkpatterns(pt)
|
|||
|
local gram = map_all(pt.aux, compile, ccache)
|
|||
|
local start = gram[1]
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
t_insert(state.grammars, gram)
|
|||
|
local success, nsi, ci = start(sbj, si, caps, ci, state)
|
|||
|
t_remove(state.grammars)
|
|||
|
return success, nsi, ci
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
local dummy_acc = {kind={}, bounds={}, openclose={}, aux={}}
|
|||
|
compilers["behind"] = function (pt, ccache)
|
|||
|
local matcher, N = compile(pt.pattern, ccache), pt.aux
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
if si <= N then return false, si, ci end
|
|||
|
local success = matcher(sbj, si - N, dummy_acc, ci, state)
|
|||
|
dummy_acc.aux = {}
|
|||
|
return success, si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["range"] = function (pt)
|
|||
|
local ranges = pt.aux
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local char, nsi = s_byte(sbj, si), si + 1
|
|||
|
for i = 1, #ranges do
|
|||
|
local r = ranges[i]
|
|||
|
if char and r[char]
|
|||
|
then return true, nsi, ci end
|
|||
|
end
|
|||
|
return false, si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["set"] = function (pt)
|
|||
|
local s = pt.aux
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local char, nsi = s_byte(sbj, si), si + 1
|
|||
|
if s[char]
|
|||
|
then return true, nsi, ci
|
|||
|
else return false, si, ci end
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["range"] = compilers.set
|
|||
|
compilers["ref"] = function (pt, ccache)
|
|||
|
local name = pt.aux
|
|||
|
local ref
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
if not ref then
|
|||
|
if #state.grammars == 0 then
|
|||
|
error(("rule 'XXXX' used outside a grammar"):gsub("XXXX", tostring(name)))
|
|||
|
elseif not state.grammars[#state.grammars][name] then
|
|||
|
error(("rule 'XXXX' undefined in given grammar"):gsub("XXXX", tostring(name)))
|
|||
|
end
|
|||
|
ref = state.grammars[#state.grammars][name]
|
|||
|
end
|
|||
|
local success, nsi, nci = ref(sbj, si, caps, ci, state)
|
|||
|
return success, nsi, nci
|
|||
|
end
|
|||
|
end
|
|||
|
local choice_tpl = [=[
|
|||
|
success, si, ci = XXXX(sbj, si, caps, ci, state)
|
|||
|
if success then
|
|||
|
return true, si, ci
|
|||
|
else
|
|||
|
end]=]
|
|||
|
local function flatten(kind, pt, ccache)
|
|||
|
if pt[2].pkind == kind then
|
|||
|
return compile(pt[1], ccache), flatten(kind, pt[2], ccache)
|
|||
|
else
|
|||
|
return compile(pt[1], ccache), compile(pt[2], ccache)
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["choice"] = function (pt, ccache)
|
|||
|
local choices = {flatten("choice", pt, ccache)}
|
|||
|
local names, chunks = {}, {}
|
|||
|
for i = 1, #choices do
|
|||
|
local m = "ch"..i
|
|||
|
names[#names + 1] = m
|
|||
|
chunks[ #names ] = choice_tpl:gsub("XXXX", m)
|
|||
|
end
|
|||
|
names[#names + 1] = "clear_captures"
|
|||
|
choices[ #names ] = clear_captures
|
|||
|
local compiled = t_concat{
|
|||
|
"local ", t_concat(names, ", "), [=[ = ...
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local aux, success = caps.aux, false
|
|||
|
]=],
|
|||
|
t_concat(chunks,"\n"),[=[--
|
|||
|
return false, si, ci
|
|||
|
end]=]
|
|||
|
}
|
|||
|
return load(compiled, "Choice")(t_unpack(choices))
|
|||
|
end
|
|||
|
local sequence_tpl = [=[
|
|||
|
success, si, ci = XXXX(sbj, si, caps, ci, state)
|
|||
|
if not success then
|
|||
|
return false, ref_si, ref_ci
|
|||
|
end]=]
|
|||
|
compilers["sequence"] = function (pt, ccache)
|
|||
|
local sequence = {flatten("sequence", pt, ccache)}
|
|||
|
local names, chunks = {}, {}
|
|||
|
for i = 1, #sequence do
|
|||
|
local m = "seq"..i
|
|||
|
names[#names + 1] = m
|
|||
|
chunks[ #names ] = sequence_tpl:gsub("XXXX", m)
|
|||
|
end
|
|||
|
names[#names + 1] = "clear_captures"
|
|||
|
sequence[ #names ] = clear_captures
|
|||
|
local compiled = t_concat{
|
|||
|
"local ", t_concat(names, ", "), [=[ = ...
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local ref_si, ref_ci, success = si, ci
|
|||
|
]=],
|
|||
|
t_concat(chunks,"\n"),[=[
|
|||
|
return true, si, ci
|
|||
|
end]=]
|
|||
|
}
|
|||
|
return load(compiled, "Sequence")(t_unpack(sequence))
|
|||
|
end
|
|||
|
compilers["at most"] = function (pt, ccache)
|
|||
|
local matcher, n = compile(pt.pattern, ccache), pt.aux
|
|||
|
n = -n
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local success = true
|
|||
|
for i = 1, n do
|
|||
|
success, si, ci = matcher(sbj, si, caps, ci, state)
|
|||
|
if not success then
|
|||
|
break
|
|||
|
end
|
|||
|
end
|
|||
|
return true, si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["at least"] = function (pt, ccache)
|
|||
|
local matcher, n = compile(pt.pattern, ccache), pt.aux
|
|||
|
if n == 0 then
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local last_si, last_ci
|
|||
|
while true do
|
|||
|
local success
|
|||
|
last_si, last_ci = si, ci
|
|||
|
success, si, ci = matcher(sbj, si, caps, ci, state)
|
|||
|
if not success then
|
|||
|
si, ci = last_si, last_ci
|
|||
|
break
|
|||
|
end
|
|||
|
end
|
|||
|
return true, si, ci
|
|||
|
end
|
|||
|
elseif n == 1 then
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local last_si, last_ci
|
|||
|
local success = true
|
|||
|
success, si, ci = matcher(sbj, si, caps, ci, state)
|
|||
|
if not success then
|
|||
|
return false, si, ci
|
|||
|
end
|
|||
|
while true do
|
|||
|
local success
|
|||
|
last_si, last_ci = si, ci
|
|||
|
success, si, ci = matcher(sbj, si, caps, ci, state)
|
|||
|
if not success then
|
|||
|
si, ci = last_si, last_ci
|
|||
|
break
|
|||
|
end
|
|||
|
end
|
|||
|
return true, si, ci
|
|||
|
end
|
|||
|
else
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local last_si, last_ci
|
|||
|
local success = true
|
|||
|
for _ = 1, n do
|
|||
|
success, si, ci = matcher(sbj, si, caps, ci, state)
|
|||
|
if not success then
|
|||
|
return false, si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
while true do
|
|||
|
local success
|
|||
|
last_si, last_ci = si, ci
|
|||
|
success, si, ci = matcher(sbj, si, caps, ci, state)
|
|||
|
if not success then
|
|||
|
si, ci = last_si, last_ci
|
|||
|
break
|
|||
|
end
|
|||
|
end
|
|||
|
return true, si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["unm"] = function (pt, ccache)
|
|||
|
if pt.pkind == "any" and pt.aux == 1 then
|
|||
|
return eoscompiled
|
|||
|
end
|
|||
|
local matcher = compile(pt.pattern, ccache)
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local success, _, _ = matcher(sbj, si, caps, ci, state)
|
|||
|
return not success, si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
compilers["lookahead"] = function (pt, ccache)
|
|||
|
local matcher = compile(pt.pattern, ccache)
|
|||
|
return function (sbj, si, caps, ci, state)
|
|||
|
local success, _, _ = matcher(sbj, si, caps, ci, state)
|
|||
|
return success, si, ci
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['constructors'] = function (...)
|
|||
|
|
|||
|
local getmetatable, ipairs, newproxy, print, setmetatable
|
|||
|
= getmetatable, ipairs, newproxy, print, setmetatable
|
|||
|
local t, u, compat
|
|||
|
= require"table", require"util", require"compat"
|
|||
|
local t_concat = t.concat
|
|||
|
local copy, getuniqueid, id, map
|
|||
|
, weakkey, weakval
|
|||
|
= u.copy, u.getuniqueid, u.id, u.map
|
|||
|
, u.weakkey, u.weakval
|
|||
|
local _ENV = u.noglobals() ----------------------------------------------------
|
|||
|
local patternwith = {
|
|||
|
constant = {
|
|||
|
"Cp", "true", "false"
|
|||
|
},
|
|||
|
aux = {
|
|||
|
"string", "any",
|
|||
|
"char", "range", "set",
|
|||
|
"ref", "sequence", "choice",
|
|||
|
"Carg", "Cb"
|
|||
|
},
|
|||
|
subpt = {
|
|||
|
"unm", "lookahead", "C", "Cf",
|
|||
|
"Cg", "Cs", "Ct", "/zero"
|
|||
|
},
|
|||
|
both = {
|
|||
|
"behind", "at least", "at most", "Clb", "Cmt",
|
|||
|
"div_string", "div_number", "div_table", "div_function"
|
|||
|
},
|
|||
|
none = "grammar", "Cc"
|
|||
|
}
|
|||
|
return function(Builder, LL) --- module wrapper.
|
|||
|
local S_tostring = Builder.set.tostring
|
|||
|
local newpattern, pattmt
|
|||
|
local next_pattern_id = 1
|
|||
|
if compat.proxies and not compat.lua52_len then
|
|||
|
local proxycache = weakkey{}
|
|||
|
local __index_LL = {__index = LL}
|
|||
|
local baseproxy = newproxy(true)
|
|||
|
pattmt = getmetatable(baseproxy)
|
|||
|
Builder.proxymt = pattmt
|
|||
|
function pattmt:__index(k)
|
|||
|
return proxycache[self][k]
|
|||
|
end
|
|||
|
function pattmt:__newindex(k, v)
|
|||
|
proxycache[self][k] = v
|
|||
|
end
|
|||
|
function LL.getdirect(p) return proxycache[p] end
|
|||
|
function newpattern(cons)
|
|||
|
local pt = newproxy(baseproxy)
|
|||
|
setmetatable(cons, __index_LL)
|
|||
|
proxycache[pt]=cons
|
|||
|
pt.id = "__ptid" .. next_pattern_id
|
|||
|
next_pattern_id = next_pattern_id + 1
|
|||
|
return pt
|
|||
|
end
|
|||
|
else
|
|||
|
if LL.warnings and not compat.lua52_len then
|
|||
|
print("Warning: The `__len` metamethod won't work with patterns, "
|
|||
|
.."use `LL.L(pattern)` for lookaheads.")
|
|||
|
end
|
|||
|
pattmt = LL
|
|||
|
function LL.getdirect (p) return p end
|
|||
|
function newpattern(pt)
|
|||
|
pt.id = "__ptid" .. next_pattern_id
|
|||
|
next_pattern_id = next_pattern_id + 1
|
|||
|
return setmetatable(pt,LL)
|
|||
|
end
|
|||
|
end
|
|||
|
Builder.newpattern = newpattern
|
|||
|
local
|
|||
|
function LL_ispattern(pt) return getmetatable(pt) == pattmt end
|
|||
|
LL.ispattern = LL_ispattern
|
|||
|
function LL.type(pt)
|
|||
|
if LL_ispattern(pt) then
|
|||
|
return "pattern"
|
|||
|
else
|
|||
|
return nil
|
|||
|
end
|
|||
|
end
|
|||
|
local ptcache, meta
|
|||
|
local
|
|||
|
function resetcache()
|
|||
|
ptcache, meta = {}, weakkey{}
|
|||
|
Builder.ptcache = ptcache
|
|||
|
for _, p in ipairs(patternwith.aux) do
|
|||
|
ptcache[p] = weakval{}
|
|||
|
end
|
|||
|
for _, p in ipairs(patternwith.subpt) do
|
|||
|
ptcache[p] = weakval{}
|
|||
|
end
|
|||
|
for _, p in ipairs(patternwith.both) do
|
|||
|
ptcache[p] = {}
|
|||
|
end
|
|||
|
return ptcache
|
|||
|
end
|
|||
|
LL.resetptcache = resetcache
|
|||
|
resetcache()
|
|||
|
local constructors = {}
|
|||
|
Builder.constructors = constructors
|
|||
|
constructors["constant"] = {
|
|||
|
truept = newpattern{ pkind = "true" },
|
|||
|
falsept = newpattern{ pkind = "false" },
|
|||
|
Cppt = newpattern{ pkind = "Cp" }
|
|||
|
}
|
|||
|
local getauxkey = {
|
|||
|
string = function(aux, as_is) return as_is end,
|
|||
|
table = copy,
|
|||
|
set = function(aux, as_is)
|
|||
|
return S_tostring(aux)
|
|||
|
end,
|
|||
|
range = function(aux, as_is)
|
|||
|
return t_concat(as_is, "|")
|
|||
|
end,
|
|||
|
sequence = function(aux, as_is)
|
|||
|
return t_concat(map(getuniqueid, aux),"|")
|
|||
|
end
|
|||
|
}
|
|||
|
getauxkey.choice = getauxkey.sequence
|
|||
|
constructors["aux"] = function(typ, aux, as_is)
|
|||
|
local cache = ptcache[typ]
|
|||
|
local key = (getauxkey[typ] or id)(aux, as_is)
|
|||
|
local res_pt = cache[key]
|
|||
|
if not res_pt then
|
|||
|
res_pt = newpattern{
|
|||
|
pkind = typ,
|
|||
|
aux = aux,
|
|||
|
as_is = as_is
|
|||
|
}
|
|||
|
cache[key] = res_pt
|
|||
|
end
|
|||
|
return res_pt
|
|||
|
end
|
|||
|
constructors["none"] = function(typ, aux)
|
|||
|
return newpattern{
|
|||
|
pkind = typ,
|
|||
|
aux = aux
|
|||
|
}
|
|||
|
end
|
|||
|
constructors["subpt"] = function(typ, pt)
|
|||
|
local cache = ptcache[typ]
|
|||
|
local res_pt = cache[pt.id]
|
|||
|
if not res_pt then
|
|||
|
res_pt = newpattern{
|
|||
|
pkind = typ,
|
|||
|
pattern = pt
|
|||
|
}
|
|||
|
cache[pt.id] = res_pt
|
|||
|
end
|
|||
|
return res_pt
|
|||
|
end
|
|||
|
constructors["both"] = function(typ, pt, aux)
|
|||
|
local cache = ptcache[typ][aux]
|
|||
|
if not cache then
|
|||
|
ptcache[typ][aux] = weakval{}
|
|||
|
cache = ptcache[typ][aux]
|
|||
|
end
|
|||
|
local res_pt = cache[pt.id]
|
|||
|
if not res_pt then
|
|||
|
res_pt = newpattern{
|
|||
|
pkind = typ,
|
|||
|
pattern = pt,
|
|||
|
aux = aux,
|
|||
|
cache = cache -- needed to keep the cache as long as the pattern exists.
|
|||
|
}
|
|||
|
cache[pt.id] = res_pt
|
|||
|
end
|
|||
|
return res_pt
|
|||
|
end
|
|||
|
constructors["binary"] = function(typ, a, b)
|
|||
|
return newpattern{
|
|||
|
a, b;
|
|||
|
pkind = typ,
|
|||
|
}
|
|||
|
end
|
|||
|
end -- module wrapper
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['datastructures'] = function (...)
|
|||
|
local getmetatable, pairs, setmetatable, type
|
|||
|
= getmetatable, pairs, setmetatable, type
|
|||
|
local m, t , u = require"math", require"table", require"util"
|
|||
|
local compat = require"compat"
|
|||
|
local ffi if compat.luajit then
|
|||
|
ffi = require"ffi"
|
|||
|
end
|
|||
|
local _ENV = u.noglobals() ----------------------------------------------------
|
|||
|
local extend, load, u_max
|
|||
|
= u.extend, u.load, u.max
|
|||
|
local m_max, t_concat, t_insert, t_sort
|
|||
|
= m.max, t.concat, t.insert, t.sort
|
|||
|
local structfor = {}
|
|||
|
local byteset_new, isboolset, isbyteset
|
|||
|
local byteset_mt = {}
|
|||
|
local
|
|||
|
function byteset_constructor (upper)
|
|||
|
local set = setmetatable(load(t_concat{
|
|||
|
"return{ [0]=false",
|
|||
|
(", false"):rep(upper),
|
|||
|
" }"
|
|||
|
})(),
|
|||
|
byteset_mt)
|
|||
|
return set
|
|||
|
end
|
|||
|
if compat.jit then
|
|||
|
local struct, boolset_constructor = {v={}}
|
|||
|
function byteset_mt.__index(s,i)
|
|||
|
if i == nil or i > s.upper then return nil end
|
|||
|
return s.v[i]
|
|||
|
end
|
|||
|
function byteset_mt.__len(s)
|
|||
|
return s.upper
|
|||
|
end
|
|||
|
function byteset_mt.__newindex(s,i,v)
|
|||
|
s.v[i] = v
|
|||
|
end
|
|||
|
boolset_constructor = ffi.metatype('struct { int upper; bool v[?]; }', byteset_mt)
|
|||
|
function byteset_new (t)
|
|||
|
if type(t) == "number" then
|
|||
|
local res = boolset_constructor(t+1)
|
|||
|
res.upper = t
|
|||
|
return res
|
|||
|
end
|
|||
|
local upper = u_max(t)
|
|||
|
struct.upper = upper
|
|||
|
if upper > 255 then error"bool_set overflow" end
|
|||
|
local set = boolset_constructor(upper+1)
|
|||
|
set.upper = upper
|
|||
|
for i = 1, #t do set[t[i]] = true end
|
|||
|
return set
|
|||
|
end
|
|||
|
function isboolset(s) return type(s)=="cdata" and ffi.istype(s, boolset_constructor) end
|
|||
|
isbyteset = isboolset
|
|||
|
else
|
|||
|
function byteset_new (t)
|
|||
|
if type(t) == "number" then return byteset_constructor(t) end
|
|||
|
local set = byteset_constructor(u_max(t))
|
|||
|
for i = 1, #t do set[t[i]] = true end
|
|||
|
return set
|
|||
|
end
|
|||
|
function isboolset(s) return false end
|
|||
|
function isbyteset (s)
|
|||
|
return getmetatable(s) == byteset_mt
|
|||
|
end
|
|||
|
end
|
|||
|
local
|
|||
|
function byterange_new (low, high)
|
|||
|
high = ( low <= high ) and high or -1
|
|||
|
local set = byteset_new(high)
|
|||
|
for i = low, high do
|
|||
|
set[i] = true
|
|||
|
end
|
|||
|
return set
|
|||
|
end
|
|||
|
local tmpa, tmpb ={}, {}
|
|||
|
local
|
|||
|
function set_if_not_yet (s, dest)
|
|||
|
if type(s) == "number" then
|
|||
|
dest[s] = true
|
|||
|
return dest
|
|||
|
else
|
|||
|
return s
|
|||
|
end
|
|||
|
end
|
|||
|
local
|
|||
|
function clean_ab (a,b)
|
|||
|
tmpa[a] = nil
|
|||
|
tmpb[b] = nil
|
|||
|
end
|
|||
|
local
|
|||
|
function byteset_union (a ,b)
|
|||
|
local upper = m_max(
|
|||
|
type(a) == "number" and a or #a,
|
|||
|
type(b) == "number" and b or #b
|
|||
|
)
|
|||
|
local A, B
|
|||
|
= set_if_not_yet(a, tmpa)
|
|||
|
, set_if_not_yet(b, tmpb)
|
|||
|
local res = byteset_new(upper)
|
|||
|
for i = 0, upper do
|
|||
|
res[i] = A[i] or B[i] or false
|
|||
|
end
|
|||
|
clean_ab(a,b)
|
|||
|
return res
|
|||
|
end
|
|||
|
local
|
|||
|
function byteset_difference (a, b)
|
|||
|
local res = {}
|
|||
|
for i = 0, 255 do
|
|||
|
res[i] = a[i] and not b[i]
|
|||
|
end
|
|||
|
return res
|
|||
|
end
|
|||
|
local
|
|||
|
function byteset_tostring (s)
|
|||
|
local list = {}
|
|||
|
for i = 0, 255 do
|
|||
|
list[#list+1] = (s[i] == true) and i or nil
|
|||
|
end
|
|||
|
return t_concat(list,", ")
|
|||
|
end
|
|||
|
structfor.binary = {
|
|||
|
set ={
|
|||
|
new = byteset_new,
|
|||
|
union = byteset_union,
|
|||
|
difference = byteset_difference,
|
|||
|
tostring = byteset_tostring
|
|||
|
},
|
|||
|
Range = byterange_new,
|
|||
|
isboolset = isboolset,
|
|||
|
isbyteset = isbyteset,
|
|||
|
isset = isbyteset
|
|||
|
}
|
|||
|
local set_mt = {}
|
|||
|
local
|
|||
|
function set_new (t)
|
|||
|
local set = setmetatable({}, set_mt)
|
|||
|
for i = 1, #t do set[t[i]] = true end
|
|||
|
return set
|
|||
|
end
|
|||
|
local -- helper for the union code.
|
|||
|
function add_elements(a, res)
|
|||
|
for k in pairs(a) do res[k] = true end
|
|||
|
return res
|
|||
|
end
|
|||
|
local
|
|||
|
function set_union (a, b)
|
|||
|
a, b = (type(a) == "number") and set_new{a} or a
|
|||
|
, (type(b) == "number") and set_new{b} or b
|
|||
|
local res = set_new{}
|
|||
|
add_elements(a, res)
|
|||
|
add_elements(b, res)
|
|||
|
return res
|
|||
|
end
|
|||
|
local
|
|||
|
function set_difference(a, b)
|
|||
|
local list = {}
|
|||
|
a, b = (type(a) == "number") and set_new{a} or a
|
|||
|
, (type(b) == "number") and set_new{b} or b
|
|||
|
for el in pairs(a) do
|
|||
|
if a[el] and not b[el] then
|
|||
|
list[#list+1] = el
|
|||
|
end
|
|||
|
end
|
|||
|
return set_new(list)
|
|||
|
end
|
|||
|
local
|
|||
|
function set_tostring (s)
|
|||
|
local list = {}
|
|||
|
for el in pairs(s) do
|
|||
|
t_insert(list,el)
|
|||
|
end
|
|||
|
t_sort(list)
|
|||
|
return t_concat(list, ",")
|
|||
|
end
|
|||
|
local
|
|||
|
function isset (s)
|
|||
|
return (getmetatable(s) == set_mt)
|
|||
|
end
|
|||
|
local
|
|||
|
function range_new (start, finish)
|
|||
|
local list = {}
|
|||
|
for i = start, finish do
|
|||
|
list[#list + 1] = i
|
|||
|
end
|
|||
|
return set_new(list)
|
|||
|
end
|
|||
|
structfor.other = {
|
|||
|
set = {
|
|||
|
new = set_new,
|
|||
|
union = set_union,
|
|||
|
tostring = set_tostring,
|
|||
|
difference = set_difference,
|
|||
|
},
|
|||
|
Range = range_new,
|
|||
|
isboolset = isboolset,
|
|||
|
isbyteset = isbyteset,
|
|||
|
isset = isset,
|
|||
|
isrange = function(a) return false end
|
|||
|
}
|
|||
|
return function(Builder, LL)
|
|||
|
local cs = (Builder.options or {}).charset or "binary"
|
|||
|
if type(cs) == "string" then
|
|||
|
cs = (cs == "binary") and "binary" or "other"
|
|||
|
else
|
|||
|
cs = cs.binary and "binary" or "other"
|
|||
|
end
|
|||
|
return extend(Builder, structfor[cs])
|
|||
|
end
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['evaluator'] = function (...)
|
|||
|
|
|||
|
local select, tonumber, tostring, type
|
|||
|
= select, tonumber, tostring, type
|
|||
|
local s, t, u = require"string", require"table", require"util"
|
|||
|
local s_sub, t_concat
|
|||
|
= s.sub, t.concat
|
|||
|
local t_unpack
|
|||
|
= u.unpack
|
|||
|
local _ENV = u.noglobals() ----------------------------------------------------
|
|||
|
return function(Builder, LL) -- Decorator wrapper
|
|||
|
local eval = {}
|
|||
|
local
|
|||
|
function insert (caps, sbj, vals, ci, vi)
|
|||
|
local openclose, kind = caps.openclose, caps.kind
|
|||
|
while kind[ci] and openclose[ci] >= 0 do
|
|||
|
ci, vi = eval[kind[ci]](caps, sbj, vals, ci, vi)
|
|||
|
end
|
|||
|
return ci, vi
|
|||
|
end
|
|||
|
function eval.C (caps, sbj, vals, ci, vi)
|
|||
|
if caps.openclose[ci] > 0 then
|
|||
|
vals[vi] = s_sub(sbj, caps.bounds[ci], caps.openclose[ci] - 1)
|
|||
|
return ci + 1, vi + 1
|
|||
|
end
|
|||
|
vals[vi] = false -- pad it for now
|
|||
|
local cj, vj = insert(caps, sbj, vals, ci + 1, vi + 1)
|
|||
|
vals[vi] = s_sub(sbj, caps.bounds[ci], caps.bounds[cj] - 1)
|
|||
|
return cj + 1, vj
|
|||
|
end
|
|||
|
local
|
|||
|
function lookback (caps, label, ci)
|
|||
|
local aux, openclose, kind= caps.aux, caps.openclose, caps.kind
|
|||
|
repeat
|
|||
|
ci = ci - 1
|
|||
|
local auxv, oc = aux[ci], openclose[ci]
|
|||
|
if oc < 0 then ci = ci + oc end
|
|||
|
if oc ~= 0 and kind[ci] == "Clb" and label == auxv then
|
|||
|
return ci
|
|||
|
end
|
|||
|
until ci == 1
|
|||
|
label = type(label) == "string" and "'"..label.."'" or tostring(label)
|
|||
|
error("back reference "..label.." not found")
|
|||
|
end
|
|||
|
function eval.Cb (caps, sbj, vals, ci, vi)
|
|||
|
local Cb_ci = lookback(caps, caps.aux[ci], ci)
|
|||
|
Cb_ci, vi = eval.Cg(caps, sbj, vals, Cb_ci, vi)
|
|||
|
return ci + 1, vi
|
|||
|
end
|
|||
|
function eval.Cc (caps, sbj, vals, ci, vi)
|
|||
|
local these_values = caps.aux[ci]
|
|||
|
for i = 1, these_values.n do
|
|||
|
vi, vals[vi] = vi + 1, these_values[i]
|
|||
|
end
|
|||
|
return ci + 1, vi
|
|||
|
end
|
|||
|
eval["Cf"] = function() error("NYI: Cf") end
|
|||
|
function eval.Cf (caps, sbj, vals, ci, vi)
|
|||
|
if caps.openclose[ci] > 0 then
|
|||
|
error"No First Value"
|
|||
|
end
|
|||
|
local func, Cf_vals, Cf_vi = caps.aux[ci], {}
|
|||
|
ci = ci + 1
|
|||
|
ci, Cf_vi = eval[caps.kind[ci]](caps, sbj, Cf_vals, ci, 1)
|
|||
|
if Cf_vi == 1 then
|
|||
|
error"No first value"
|
|||
|
end
|
|||
|
local result = Cf_vals[1]
|
|||
|
while caps.kind[ci] and caps.openclose[ci] >= 0 do
|
|||
|
ci, Cf_vi = eval[caps.kind[ci]](caps, sbj, Cf_vals, ci, 1)
|
|||
|
result = func(result, t_unpack(Cf_vals, 1, Cf_vi - 1))
|
|||
|
end
|
|||
|
vals[vi] = result
|
|||
|
return ci +1, vi + 1
|
|||
|
end
|
|||
|
function eval.Cg (caps, sbj, vals, ci, vi)
|
|||
|
if caps.openclose[ci] > 0 then
|
|||
|
vals[vi] = s_sub(sbj, caps.bounds[ci], caps.openclose[ci] - 1)
|
|||
|
return ci + 1, vi + 1
|
|||
|
end
|
|||
|
local cj, vj = insert(caps, sbj, vals, ci + 1, vi)
|
|||
|
if vj == vi then
|
|||
|
vals[vj] = s_sub(sbj, caps.bounds[ci], caps.bounds[cj] - 1)
|
|||
|
vj = vj + 1
|
|||
|
end
|
|||
|
return cj + 1, vj
|
|||
|
end
|
|||
|
function eval.Clb (caps, sbj, vals, ci, vi)
|
|||
|
local oc = caps.openclose
|
|||
|
if oc[ci] > 0 then
|
|||
|
return ci + 1, vi
|
|||
|
end
|
|||
|
local depth = 0
|
|||
|
repeat
|
|||
|
if oc[ci] == 0 then depth = depth + 1
|
|||
|
elseif oc[ci] < 0 then depth = depth - 1
|
|||
|
end
|
|||
|
ci = ci + 1
|
|||
|
until depth == 0
|
|||
|
return ci, vi
|
|||
|
end
|
|||
|
function eval.Cp (caps, sbj, vals, ci, vi)
|
|||
|
vals[vi] = caps.bounds[ci]
|
|||
|
return ci + 1, vi + 1
|
|||
|
end
|
|||
|
function eval.Ct (caps, sbj, vals, ci, vi)
|
|||
|
local aux, openclose, kind = caps. aux, caps.openclose, caps.kind
|
|||
|
local tbl_vals = {}
|
|||
|
vals[vi] = tbl_vals
|
|||
|
if openclose[ci] > 0 then
|
|||
|
return ci + 1, vi + 1
|
|||
|
end
|
|||
|
local tbl_vi, Clb_vals = 1, {}
|
|||
|
ci = ci + 1
|
|||
|
while kind[ci] and openclose[ci] >= 0 do
|
|||
|
if kind[ci] == "Clb" then
|
|||
|
local label, Clb_vi = aux[ci], 1
|
|||
|
ci, Clb_vi = eval.Cg(caps, sbj, Clb_vals, ci, 1)
|
|||
|
if Clb_vi ~= 1 then tbl_vals[label] = Clb_vals[1] end
|
|||
|
else
|
|||
|
ci, tbl_vi = eval[kind[ci]](caps, sbj, tbl_vals, ci, tbl_vi)
|
|||
|
end
|
|||
|
end
|
|||
|
return ci + 1, vi + 1
|
|||
|
end
|
|||
|
local inf = 1/0
|
|||
|
function eval.value (caps, sbj, vals, ci, vi)
|
|||
|
local val
|
|||
|
if caps.aux[ci] ~= inf or caps.openclose[ci] ~= inf
|
|||
|
then val = caps.aux[ci]
|
|||
|
end
|
|||
|
vals[vi] = val
|
|||
|
return ci + 1, vi + 1
|
|||
|
end
|
|||
|
function eval.Cs (caps, sbj, vals, ci, vi)
|
|||
|
if caps.openclose[ci] > 0 then
|
|||
|
vals[vi] = s_sub(sbj, caps.bounds[ci], caps.openclose[ci] - 1)
|
|||
|
else
|
|||
|
local bounds, kind, openclose = caps.bounds, caps.kind, caps.openclose
|
|||
|
local start, buffer, Cs_vals, bi, Cs_vi = bounds[ci], {}, {}, 1, 1
|
|||
|
local last
|
|||
|
ci = ci + 1
|
|||
|
while openclose[ci] >= 0 do
|
|||
|
last = bounds[ci]
|
|||
|
buffer[bi] = s_sub(sbj, start, last - 1)
|
|||
|
bi = bi + 1
|
|||
|
ci, Cs_vi = eval[kind[ci]](caps, sbj, Cs_vals, ci, 1)
|
|||
|
if Cs_vi > 1 then
|
|||
|
buffer[bi] = Cs_vals[1]
|
|||
|
bi = bi + 1
|
|||
|
start = openclose[ci-1] > 0 and openclose[ci-1] or bounds[ci-1]
|
|||
|
else
|
|||
|
start = last
|
|||
|
end
|
|||
|
end
|
|||
|
buffer[bi] = s_sub(sbj, start, bounds[ci] - 1)
|
|||
|
vals[vi] = t_concat(buffer)
|
|||
|
end
|
|||
|
return ci + 1, vi + 1
|
|||
|
end
|
|||
|
local
|
|||
|
function insert_divfunc_results(acc, val_i, ...)
|
|||
|
local n = select('#', ...)
|
|||
|
for i = 1, n do
|
|||
|
val_i, acc[val_i] = val_i + 1, select(i, ...)
|
|||
|
end
|
|||
|
return val_i
|
|||
|
end
|
|||
|
function eval.div_function (caps, sbj, vals, ci, vi)
|
|||
|
local func = caps.aux[ci]
|
|||
|
local params, divF_vi
|
|||
|
if caps.openclose[ci] > 0 then
|
|||
|
params, divF_vi = {s_sub(sbj, caps.bounds[ci], caps.openclose[ci] - 1)}, 2
|
|||
|
else
|
|||
|
params = {}
|
|||
|
ci, divF_vi = insert(caps, sbj, params, ci + 1, 1)
|
|||
|
end
|
|||
|
ci = ci + 1 -- skip the closed or closing node.
|
|||
|
vi = insert_divfunc_results(vals, vi, func(t_unpack(params, 1, divF_vi - 1)))
|
|||
|
return ci, vi
|
|||
|
end
|
|||
|
function eval.div_number (caps, sbj, vals, ci, vi)
|
|||
|
local this_aux = caps.aux[ci]
|
|||
|
local divN_vals, divN_vi
|
|||
|
if caps.openclose[ci] > 0 then
|
|||
|
divN_vals, divN_vi = {s_sub(sbj, caps.bounds[ci], caps.openclose[ci] - 1)}, 2
|
|||
|
else
|
|||
|
divN_vals = {}
|
|||
|
ci, divN_vi = insert(caps, sbj, divN_vals, ci + 1, 1)
|
|||
|
end
|
|||
|
ci = ci + 1 -- skip the closed or closing node.
|
|||
|
if this_aux >= divN_vi then error("no capture '"..this_aux.."' in /number capture.") end
|
|||
|
vals[vi] = divN_vals[this_aux]
|
|||
|
return ci, vi + 1
|
|||
|
end
|
|||
|
local function div_str_cap_refs (caps, ci)
|
|||
|
local opcl = caps.openclose
|
|||
|
local refs = {open=caps.bounds[ci]}
|
|||
|
if opcl[ci] > 0 then
|
|||
|
refs.close = opcl[ci]
|
|||
|
return ci + 1, refs, 0
|
|||
|
end
|
|||
|
local first_ci = ci
|
|||
|
local depth = 1
|
|||
|
ci = ci + 1
|
|||
|
repeat
|
|||
|
local oc = opcl[ci]
|
|||
|
if depth == 1 and oc >= 0 then refs[#refs+1] = ci end
|
|||
|
if oc == 0 then
|
|||
|
depth = depth + 1
|
|||
|
elseif oc < 0 then
|
|||
|
depth = depth - 1
|
|||
|
end
|
|||
|
ci = ci + 1
|
|||
|
until depth == 0
|
|||
|
refs.close = caps.bounds[ci - 1]
|
|||
|
return ci, refs, #refs
|
|||
|
end
|
|||
|
function eval.div_string (caps, sbj, vals, ci, vi)
|
|||
|
local n, refs
|
|||
|
local cached
|
|||
|
local cached, divS_vals = {}, {}
|
|||
|
local the_string = caps.aux[ci]
|
|||
|
ci, refs, n = div_str_cap_refs(caps, ci)
|
|||
|
vals[vi] = the_string:gsub("%%([%d%%])", function (d)
|
|||
|
if d == "%" then return "%" end
|
|||
|
d = tonumber(d)
|
|||
|
if not cached[d] then
|
|||
|
if d > n then
|
|||
|
error("no capture at index "..d.." in /string capture.")
|
|||
|
end
|
|||
|
if d == 0 then
|
|||
|
cached[d] = s_sub(sbj, refs.open, refs.close - 1)
|
|||
|
else
|
|||
|
local _, vi = eval[caps.kind[refs[d]]](caps, sbj, divS_vals, refs[d], 1)
|
|||
|
if vi == 1 then error("no values in capture at index"..d.." in /string capture.") end
|
|||
|
cached[d] = divS_vals[1]
|
|||
|
end
|
|||
|
end
|
|||
|
return cached[d]
|
|||
|
end)
|
|||
|
return ci, vi + 1
|
|||
|
end
|
|||
|
function eval.div_table (caps, sbj, vals, ci, vi)
|
|||
|
local this_aux = caps.aux[ci]
|
|||
|
local key
|
|||
|
if caps.openclose[ci] > 0 then
|
|||
|
key = s_sub(sbj, caps.bounds[ci], caps.openclose[ci] - 1)
|
|||
|
else
|
|||
|
local divT_vals, _ = {}
|
|||
|
ci, _ = insert(caps, sbj, divT_vals, ci + 1, 1)
|
|||
|
key = divT_vals[1]
|
|||
|
end
|
|||
|
ci = ci + 1
|
|||
|
if this_aux[key] then
|
|||
|
vals[vi] = this_aux[key]
|
|||
|
return ci, vi + 1
|
|||
|
else
|
|||
|
return ci, vi
|
|||
|
end
|
|||
|
end
|
|||
|
function LL.evaluate (caps, sbj, ci)
|
|||
|
local vals = {}
|
|||
|
local _, vi = insert(caps, sbj, vals, ci, 1)
|
|||
|
return vals, 1, vi - 1
|
|||
|
end
|
|||
|
end -- Decorator wrapper
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['factorizer'] = function (...)
|
|||
|
local ipairs, pairs, print, setmetatable
|
|||
|
= ipairs, pairs, print, setmetatable
|
|||
|
local u = require"util"
|
|||
|
local id, nop, setify, weakkey
|
|||
|
= u.id, u.nop, u.setify, u.weakkey
|
|||
|
local _ENV = u.noglobals() ----------------------------------------------------
|
|||
|
local
|
|||
|
function process_booleans(a, b, opts)
|
|||
|
local id, brk = opts.id, opts.brk
|
|||
|
if a == id then return true, b
|
|||
|
elseif b == id then return true, a
|
|||
|
elseif a == brk then return true, brk
|
|||
|
else return false end
|
|||
|
end
|
|||
|
local unary = setify{
|
|||
|
"unm", "lookahead", "C", "Cf",
|
|||
|
"Cg", "Cs", "Ct", "/zero"
|
|||
|
}
|
|||
|
local unary_aux = setify{
|
|||
|
"behind", "at least", "at most", "Clb", "Cmt",
|
|||
|
"div_string", "div_number", "div_table", "div_function"
|
|||
|
}
|
|||
|
local unifiable = setify{"char", "set", "range"}
|
|||
|
local hasCmt; hasCmt = setmetatable({}, {__mode = "k", __index = function(self, pt)
|
|||
|
local kind, res = pt.pkind, false
|
|||
|
if kind == "Cmt"
|
|||
|
or kind == "ref"
|
|||
|
then
|
|||
|
res = true
|
|||
|
elseif unary[kind] or unary_aux[kind] then
|
|||
|
res = hasCmt[pt.pattern]
|
|||
|
elseif kind == "choice" or kind == "sequence" then
|
|||
|
res = hasCmt[pt[1]] or hasCmt[pt[2]]
|
|||
|
end
|
|||
|
hasCmt[pt] = res
|
|||
|
return res
|
|||
|
end})
|
|||
|
return function (Builder, LL) --------------------------------------------------
|
|||
|
if Builder.options.factorize == false then
|
|||
|
return {
|
|||
|
choice = nop,
|
|||
|
sequence = nop,
|
|||
|
lookahead = nop,
|
|||
|
unm = nop
|
|||
|
}
|
|||
|
end
|
|||
|
local constructors, LL_P = Builder.constructors, LL.P
|
|||
|
local truept, falsept
|
|||
|
= constructors.constant.truept
|
|||
|
, constructors.constant.falsept
|
|||
|
local --Range, Set,
|
|||
|
S_union
|
|||
|
= --Builder.Range, Builder.set.new,
|
|||
|
Builder.set.union
|
|||
|
local mergeable = setify{"char", "set"}
|
|||
|
local type2cons = {
|
|||
|
["/zero"] = "__div",
|
|||
|
["div_number"] = "__div",
|
|||
|
["div_string"] = "__div",
|
|||
|
["div_table"] = "__div",
|
|||
|
["div_function"] = "__div",
|
|||
|
["at least"] = "__pow",
|
|||
|
["at most"] = "__pow",
|
|||
|
["Clb"] = "Cg",
|
|||
|
}
|
|||
|
local
|
|||
|
function choice (a, b)
|
|||
|
do -- handle the identity/break properties of true and false.
|
|||
|
local hasbool, res = process_booleans(a, b, { id = falsept, brk = truept })
|
|||
|
if hasbool then return res end
|
|||
|
end
|
|||
|
local ka, kb = a.pkind, b.pkind
|
|||
|
if a == b and not hasCmt[a] then
|
|||
|
return a
|
|||
|
elseif ka == "choice" then -- correct associativity without blowing up the stack
|
|||
|
local acc, i = {}, 1
|
|||
|
while a.pkind == "choice" do
|
|||
|
acc[i], a, i = a[1], a[2], i + 1
|
|||
|
end
|
|||
|
acc[i] = a
|
|||
|
for j = i, 1, -1 do
|
|||
|
b = acc[j] + b
|
|||
|
end
|
|||
|
return b
|
|||
|
elseif mergeable[ka] and mergeable[kb] then
|
|||
|
return constructors.aux("set", S_union(a.aux, b.aux))
|
|||
|
elseif mergeable[ka] and kb == "any" and b.aux == 1
|
|||
|
or mergeable[kb] and ka == "any" and a.aux == 1 then
|
|||
|
return ka == "any" and a or b
|
|||
|
elseif ka == kb then
|
|||
|
if (unary[ka] or unary_aux[ka]) and ( a.aux == b.aux ) then
|
|||
|
return LL[type2cons[ka] or ka](a.pattern + b.pattern, a.aux)
|
|||
|
elseif ( ka == kb ) and ka == "sequence" then
|
|||
|
if a[1] == b[1] and not hasCmt[a[1]] then
|
|||
|
return a[1] * (a[2] + b[2])
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
return false
|
|||
|
end
|
|||
|
local
|
|||
|
function lookahead (pt)
|
|||
|
return pt
|
|||
|
end
|
|||
|
local
|
|||
|
function sequence(a, b)
|
|||
|
do
|
|||
|
local hasbool, res = process_booleans(a, b, { id = truept, brk = falsept })
|
|||
|
if hasbool then return res end
|
|||
|
end
|
|||
|
local ka, kb = a.pkind, b.pkind
|
|||
|
if ka == "sequence" then -- correct associativity without blowing up the stack
|
|||
|
local acc, i = {}, 1
|
|||
|
while a.pkind == "sequence" do
|
|||
|
acc[i], a, i = a[1], a[2], i + 1
|
|||
|
end
|
|||
|
acc[i] = a
|
|||
|
for j = i, 1, -1 do
|
|||
|
b = acc[j] * b
|
|||
|
end
|
|||
|
return b
|
|||
|
elseif (ka == "one" or ka == "any") and (kb == "one" or kb == "any") then
|
|||
|
return LL_P(a.aux + b.aux)
|
|||
|
end
|
|||
|
return false
|
|||
|
end
|
|||
|
local
|
|||
|
function unm (pt)
|
|||
|
if pt == truept then return falsept
|
|||
|
elseif pt == falsept then return truept
|
|||
|
elseif pt.pkind == "unm" then return #pt.pattern
|
|||
|
elseif pt.pkind == "lookahead" then return -pt.pattern
|
|||
|
end
|
|||
|
end
|
|||
|
return {
|
|||
|
choice = choice,
|
|||
|
lookahead = lookahead,
|
|||
|
sequence = sequence,
|
|||
|
unm = unm
|
|||
|
}
|
|||
|
end
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['init'] = function (...)
|
|||
|
|
|||
|
local getmetatable, setmetatable, pcall
|
|||
|
= getmetatable, setmetatable, pcall
|
|||
|
local u = require"util"
|
|||
|
local copy, map, nop, t_unpack
|
|||
|
= u.copy, u.map, u.nop, u.unpack
|
|||
|
local API, charsets, compiler, constructors
|
|||
|
, datastructures, evaluator, factorizer
|
|||
|
, locale, printers, re
|
|||
|
= t_unpack(map(require,
|
|||
|
{ "API", "charsets", "compiler", "constructors"
|
|||
|
, "datastructures", "evaluator", "factorizer"
|
|||
|
, "locale", "printers", "re" }))
|
|||
|
local _, package = pcall(require, "package")
|
|||
|
local _ENV = u.noglobals() ----------------------------------------------------
|
|||
|
local VERSION = "0.12"
|
|||
|
local LuVERSION = "0.1.0"
|
|||
|
local function global(self, env) setmetatable(env,{__index = self}) end
|
|||
|
local function register(self, env)
|
|||
|
pcall(function()
|
|||
|
package.loaded.lpeg = self
|
|||
|
package.loaded.re = self.re
|
|||
|
end)
|
|||
|
if env then
|
|||
|
env.lpeg, env.re = self, self.re
|
|||
|
end
|
|||
|
return self
|
|||
|
end
|
|||
|
local
|
|||
|
function LuLPeg(options)
|
|||
|
options = options and copy(options) or {}
|
|||
|
local Builder, LL
|
|||
|
= { options = options, factorizer = factorizer }
|
|||
|
, { new = LuLPeg
|
|||
|
, version = function () return VERSION end
|
|||
|
, luversion = function () return LuVERSION end
|
|||
|
, setmaxstack = nop --Just a stub, for compatibility.
|
|||
|
}
|
|||
|
LL.util = u
|
|||
|
LL.global = global
|
|||
|
LL.register = register
|
|||
|
;-- Decorate the LuLPeg object.
|
|||
|
charsets(Builder, LL)
|
|||
|
datastructures(Builder, LL)
|
|||
|
printers(Builder, LL)
|
|||
|
constructors(Builder, LL)
|
|||
|
API(Builder, LL)
|
|||
|
evaluator(Builder, LL)
|
|||
|
;(options.compiler or compiler)(Builder, LL)
|
|||
|
locale(Builder, LL)
|
|||
|
LL.re = re(Builder, LL)
|
|||
|
return LL
|
|||
|
end -- LuLPeg
|
|||
|
local LL = LuLPeg()
|
|||
|
return LL
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['locale'] = function (...)
|
|||
|
|
|||
|
local extend = require"util".extend
|
|||
|
local _ENV = require"util".noglobals() ----------------------------------------
|
|||
|
return function(Builder, LL) -- Module wrapper {-------------------------------
|
|||
|
local R, S = LL.R, LL.S
|
|||
|
local locale = {}
|
|||
|
locale["cntrl"] = R"\0\31" + "\127"
|
|||
|
locale["digit"] = R"09"
|
|||
|
locale["lower"] = R"az"
|
|||
|
locale["print"] = R" ~" -- 0x20 to 0xee
|
|||
|
locale["space"] = S" \f\n\r\t\v" -- \f == form feed (for a printer), \v == vtab
|
|||
|
locale["upper"] = R"AZ"
|
|||
|
locale["alpha"] = locale["lower"] + locale["upper"]
|
|||
|
locale["alnum"] = locale["alpha"] + locale["digit"]
|
|||
|
locale["graph"] = locale["print"] - locale["space"]
|
|||
|
locale["punct"] = locale["graph"] - locale["alnum"]
|
|||
|
locale["xdigit"] = locale["digit"] + R"af" + R"AF"
|
|||
|
function LL.locale (t)
|
|||
|
return extend(t or {}, locale)
|
|||
|
end
|
|||
|
end -- Module wrapper --------------------------------------------------------}
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['match'] = function (...)
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['optimizer'] = function (...)
|
|||
|
-- Nothing for now.
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['printers'] = function (...)
|
|||
|
return function(Builder, LL)
|
|||
|
local ipairs, pairs, print, tostring, type
|
|||
|
= ipairs, pairs, print, tostring, type
|
|||
|
local s, t, u = require"string", require"table", require"util"
|
|||
|
local S_tostring = Builder.set.tostring
|
|||
|
local _ENV = u.noglobals() ----------------------------------------------------
|
|||
|
local s_char, s_sub, t_concat
|
|||
|
= s.char, s.sub, t.concat
|
|||
|
local expose, load, map
|
|||
|
= u.expose, u.load, u.map
|
|||
|
local escape_index = {
|
|||
|
["\f"] = "\\f",
|
|||
|
["\n"] = "\\n",
|
|||
|
["\r"] = "\\r",
|
|||
|
["\t"] = "\\t",
|
|||
|
["\v"] = "\\v",
|
|||
|
["\127"] = "\\ESC"
|
|||
|
}
|
|||
|
local function flatten(kind, list)
|
|||
|
if list[2].pkind == kind then
|
|||
|
return list[1], flatten(kind, list[2])
|
|||
|
else
|
|||
|
return list[1], list[2]
|
|||
|
end
|
|||
|
end
|
|||
|
for i = 0, 8 do escape_index[s_char(i)] = "\\"..i end
|
|||
|
for i = 14, 31 do escape_index[s_char(i)] = "\\"..i end
|
|||
|
local
|
|||
|
function escape( str )
|
|||
|
return str:gsub("%c", escape_index)
|
|||
|
end
|
|||
|
local
|
|||
|
function set_repr (set)
|
|||
|
return s_char(load("return "..S_tostring(set))())
|
|||
|
end
|
|||
|
local printers = {}
|
|||
|
local
|
|||
|
function LL_pprint (pt, offset, prefix)
|
|||
|
return printers[pt.pkind](pt, offset, prefix)
|
|||
|
end
|
|||
|
function LL.pprint (pt0)
|
|||
|
local pt = LL.P(pt0)
|
|||
|
print"\nPrint pattern"
|
|||
|
LL_pprint(pt, "", "")
|
|||
|
print"--- /pprint\n"
|
|||
|
return pt0
|
|||
|
end
|
|||
|
for k, v in pairs{
|
|||
|
string = [[ "P( \""..escape(pt.as_is).."\" )" ]],
|
|||
|
char = [[ "P( \""..escape(to_char(pt.aux)).."\" )"]],
|
|||
|
["true"] = [[ "P( true )" ]],
|
|||
|
["false"] = [[ "P( false )" ]],
|
|||
|
eos = [[ "~EOS~" ]],
|
|||
|
one = [[ "P( one )" ]],
|
|||
|
any = [[ "P( "..pt.aux.." )" ]],
|
|||
|
set = [[ "S( "..'"'..escape(set_repr(pt.aux))..'"'.." )" ]],
|
|||
|
["function"] = [[ "P( "..pt.aux.." )" ]],
|
|||
|
ref = [[
|
|||
|
"V( ",
|
|||
|
(type(pt.aux) == "string" and "\""..pt.aux.."\"")
|
|||
|
or tostring(pt.aux)
|
|||
|
, " )"
|
|||
|
]],
|
|||
|
range = [[
|
|||
|
"R( ",
|
|||
|
escape(t_concat(map(
|
|||
|
pt.as_is,
|
|||
|
function(e) return '"'..e..'"' end)
|
|||
|
, ", "))
|
|||
|
," )"
|
|||
|
]]
|
|||
|
} do
|
|||
|
printers[k] = load(([==[
|
|||
|
local k, map, t_concat, to_char, escape, set_repr = ...
|
|||
|
return function (pt, offset, prefix)
|
|||
|
print(t_concat{offset,prefix,XXXX})
|
|||
|
end
|
|||
|
]==]):gsub("XXXX", v), k.." printer")(k, map, t_concat, s_char, escape, set_repr)
|
|||
|
end
|
|||
|
for k, v in pairs{
|
|||
|
["behind"] = [[ LL_pprint(pt.pattern, offset, "B ") ]],
|
|||
|
["at least"] = [[ LL_pprint(pt.pattern, offset, pt.aux.." ^ ") ]],
|
|||
|
["at most"] = [[ LL_pprint(pt.pattern, offset, pt.aux.." ^ ") ]],
|
|||
|
unm = [[LL_pprint(pt.pattern, offset, "- ")]],
|
|||
|
lookahead = [[LL_pprint(pt.pattern, offset, "# ")]],
|
|||
|
choice = [[
|
|||
|
print(offset..prefix.."+")
|
|||
|
local ch, i = {}, 1
|
|||
|
while pt.pkind == "choice" do
|
|||
|
ch[i], pt, i = pt[1], pt[2], i + 1
|
|||
|
end
|
|||
|
ch[i] = pt
|
|||
|
map(ch, LL_pprint, offset.." :", "")
|
|||
|
]],
|
|||
|
sequence = [=[
|
|||
|
print(offset..prefix.."*")
|
|||
|
local acc, p2 = {}
|
|||
|
offset = offset .. " |"
|
|||
|
while true do
|
|||
|
if pt.pkind ~= "sequence" then -- last element
|
|||
|
if pt.pkind == "char" then
|
|||
|
acc[#acc + 1] = pt.aux
|
|||
|
print(offset..'P( "'..s.char(u.unpack(acc))..'" )')
|
|||
|
else
|
|||
|
if #acc ~= 0 then
|
|||
|
print(offset..'P( "'..s.char(u.unpack(acc))..'" )')
|
|||
|
end
|
|||
|
LL_pprint(pt, offset, "")
|
|||
|
end
|
|||
|
break
|
|||
|
elseif pt[1].pkind == "char" then
|
|||
|
acc[#acc + 1] = pt[1].aux
|
|||
|
elseif #acc ~= 0 then
|
|||
|
print(offset..'P( "'..s.char(u.unpack(acc))..'" )')
|
|||
|
acc = {}
|
|||
|
LL_pprint(pt[1], offset, "")
|
|||
|
else
|
|||
|
LL_pprint(pt[1], offset, "")
|
|||
|
end
|
|||
|
pt = pt[2]
|
|||
|
end
|
|||
|
]=],
|
|||
|
grammar = [[
|
|||
|
print(offset..prefix.."Grammar")
|
|||
|
for k, pt in pairs(pt.aux) do
|
|||
|
local prefix = ( type(k)~="string"
|
|||
|
and tostring(k)
|
|||
|
or "\""..k.."\"" )
|
|||
|
LL_pprint(pt, offset.." ", prefix .. " = ")
|
|||
|
end
|
|||
|
]]
|
|||
|
} do
|
|||
|
printers[k] = load(([[
|
|||
|
local map, LL_pprint, pkind, s, u, flatten = ...
|
|||
|
return function (pt, offset, prefix)
|
|||
|
XXXX
|
|||
|
end
|
|||
|
]]):gsub("XXXX", v), k.." printer")(map, LL_pprint, type, s, u, flatten)
|
|||
|
end
|
|||
|
for _, cap in pairs{"C", "Cs", "Ct"} do
|
|||
|
printers[cap] = function (pt, offset, prefix)
|
|||
|
print(offset..prefix..cap)
|
|||
|
LL_pprint(pt.pattern, offset.." ", "")
|
|||
|
end
|
|||
|
end
|
|||
|
for _, cap in pairs{"Cg", "Clb", "Cf", "Cmt", "div_number", "/zero", "div_function", "div_table"} do
|
|||
|
printers[cap] = function (pt, offset, prefix)
|
|||
|
print(offset..prefix..cap.." "..tostring(pt.aux or ""))
|
|||
|
LL_pprint(pt.pattern, offset.." ", "")
|
|||
|
end
|
|||
|
end
|
|||
|
printers["div_string"] = function (pt, offset, prefix)
|
|||
|
print(offset..prefix..'/string "'..tostring(pt.aux or "")..'"')
|
|||
|
LL_pprint(pt.pattern, offset.." ", "")
|
|||
|
end
|
|||
|
for _, cap in pairs{"Carg", "Cp"} do
|
|||
|
printers[cap] = function (pt, offset, prefix)
|
|||
|
print(offset..prefix..cap.."( "..tostring(pt.aux).." )")
|
|||
|
end
|
|||
|
end
|
|||
|
printers["Cb"] = function (pt, offset, prefix)
|
|||
|
print(offset..prefix.."Cb( \""..pt.aux.."\" )")
|
|||
|
end
|
|||
|
printers["Cc"] = function (pt, offset, prefix)
|
|||
|
print(offset..prefix.."Cc(" ..t_concat(map(pt.aux, tostring),", ").." )")
|
|||
|
end
|
|||
|
local cprinters = {}
|
|||
|
local padding = " "
|
|||
|
local function padnum(n)
|
|||
|
n = tostring(n)
|
|||
|
n = n .."."..((" "):rep(4 - #n))
|
|||
|
return n
|
|||
|
end
|
|||
|
local function _cprint(caps, ci, indent, sbj, n)
|
|||
|
local openclose, kind = caps.openclose, caps.kind
|
|||
|
indent = indent or 0
|
|||
|
while kind[ci] and openclose[ci] >= 0 do
|
|||
|
if caps.openclose[ci] > 0 then
|
|||
|
print(t_concat({
|
|||
|
padnum(n),
|
|||
|
padding:rep(indent),
|
|||
|
caps.kind[ci],
|
|||
|
": start = ", tostring(caps.bounds[ci]),
|
|||
|
" finish = ", tostring(caps.openclose[ci]),
|
|||
|
caps.aux[ci] and " aux = " or "",
|
|||
|
caps.aux[ci] and (
|
|||
|
type(caps.aux[ci]) == "string"
|
|||
|
and '"'..tostring(caps.aux[ci])..'"'
|
|||
|
or tostring(caps.aux[ci])
|
|||
|
) or "",
|
|||
|
" \t", s_sub(sbj, caps.bounds[ci], caps.openclose[ci] - 1)
|
|||
|
}))
|
|||
|
if type(caps.aux[ci]) == "table" then expose(caps.aux[ci]) end
|
|||
|
else
|
|||
|
local kind = caps.kind[ci]
|
|||
|
local start = caps.bounds[ci]
|
|||
|
print(t_concat({
|
|||
|
padnum(n),
|
|||
|
padding:rep(indent), kind,
|
|||
|
": start = ", start,
|
|||
|
caps.aux[ci] and " aux = " or "",
|
|||
|
caps.aux[ci] and (
|
|||
|
type(caps.aux[ci]) == "string"
|
|||
|
and '"'..tostring(caps.aux[ci])..'"'
|
|||
|
or tostring(caps.aux[ci])
|
|||
|
) or ""
|
|||
|
}))
|
|||
|
ci, n = _cprint(caps, ci + 1, indent + 1, sbj, n + 1)
|
|||
|
print(t_concat({
|
|||
|
padnum(n),
|
|||
|
padding:rep(indent),
|
|||
|
"/", kind,
|
|||
|
" finish = ", tostring(caps.bounds[ci]),
|
|||
|
" \t", s_sub(sbj, start, (caps.bounds[ci] or 1) - 1)
|
|||
|
}))
|
|||
|
end
|
|||
|
n = n + 1
|
|||
|
ci = ci + 1
|
|||
|
end
|
|||
|
return ci, n
|
|||
|
end
|
|||
|
function LL.cprint (caps, ci, sbj)
|
|||
|
ci = ci or 1
|
|||
|
print"\nCapture Printer:\n================"
|
|||
|
_cprint(caps, ci, 0, sbj, 1)
|
|||
|
print"================\n/Cprinter\n"
|
|||
|
end
|
|||
|
return { pprint = LL.pprint,cprint = LL.cprint }
|
|||
|
end -- module wrapper ---------------------------------------------------------
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['re'] = function (...)
|
|||
|
|
|||
|
return function(Builder, LL)
|
|||
|
local tonumber, type, print, error = tonumber, type, print, error
|
|||
|
local setmetatable = setmetatable
|
|||
|
local m = LL
|
|||
|
local mm = m
|
|||
|
local mt = getmetatable(mm.P(0))
|
|||
|
local version = _VERSION
|
|||
|
if version == "Lua 5.2" then _ENV = nil end
|
|||
|
local any = m.P(1)
|
|||
|
local Predef = { nl = m.P"\n" }
|
|||
|
local mem
|
|||
|
local fmem
|
|||
|
local gmem
|
|||
|
local function updatelocale ()
|
|||
|
mm.locale(Predef)
|
|||
|
Predef.a = Predef.alpha
|
|||
|
Predef.c = Predef.cntrl
|
|||
|
Predef.d = Predef.digit
|
|||
|
Predef.g = Predef.graph
|
|||
|
Predef.l = Predef.lower
|
|||
|
Predef.p = Predef.punct
|
|||
|
Predef.s = Predef.space
|
|||
|
Predef.u = Predef.upper
|
|||
|
Predef.w = Predef.alnum
|
|||
|
Predef.x = Predef.xdigit
|
|||
|
Predef.A = any - Predef.a
|
|||
|
Predef.C = any - Predef.c
|
|||
|
Predef.D = any - Predef.d
|
|||
|
Predef.G = any - Predef.g
|
|||
|
Predef.L = any - Predef.l
|
|||
|
Predef.P = any - Predef.p
|
|||
|
Predef.S = any - Predef.s
|
|||
|
Predef.U = any - Predef.u
|
|||
|
Predef.W = any - Predef.w
|
|||
|
Predef.X = any - Predef.x
|
|||
|
mem = {} -- restart memoization
|
|||
|
fmem = {}
|
|||
|
gmem = {}
|
|||
|
local mt = {__mode = "v"}
|
|||
|
setmetatable(mem, mt)
|
|||
|
setmetatable(fmem, mt)
|
|||
|
setmetatable(gmem, mt)
|
|||
|
end
|
|||
|
updatelocale()
|
|||
|
local function getdef (id, defs)
|
|||
|
local c = defs and defs[id]
|
|||
|
if not c then error("undefined name: " .. id) end
|
|||
|
return c
|
|||
|
end
|
|||
|
local function patt_error (s, i)
|
|||
|
local msg = (#s < i + 20) and s:sub(i)
|
|||
|
or s:sub(i,i+20) .. "..."
|
|||
|
msg = ("pattern error near '%s'"):format(msg)
|
|||
|
error(msg, 2)
|
|||
|
end
|
|||
|
local function mult (p, n)
|
|||
|
local np = mm.P(true)
|
|||
|
while n >= 1 do
|
|||
|
if n%2 >= 1 then np = np * p end
|
|||
|
p = p * p
|
|||
|
n = n/2
|
|||
|
end
|
|||
|
return np
|
|||
|
end
|
|||
|
local function equalcap (s, i, c)
|
|||
|
if type(c) ~= "string" then return nil end
|
|||
|
local e = #c + i
|
|||
|
if s:sub(i, e - 1) == c then return e else return nil end
|
|||
|
end
|
|||
|
local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
|
|||
|
local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0
|
|||
|
local arrow = S * "<-"
|
|||
|
local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
|
|||
|
name = m.C(name)
|
|||
|
local Def = name * m.Carg(1)
|
|||
|
local num = m.C(m.R"09"^1) * S / tonumber
|
|||
|
local String = "'" * m.C((any - "'")^0) * "'" +
|
|||
|
'"' * m.C((any - '"')^0) * '"'
|
|||
|
local defined = "%" * Def / function (c,Defs)
|
|||
|
local cat = Defs and Defs[c] or Predef[c]
|
|||
|
if not cat then error ("name '" .. c .. "' undefined") end
|
|||
|
return cat
|
|||
|
end
|
|||
|
local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
|
|||
|
local item = defined + Range + m.C(any)
|
|||
|
local Class =
|
|||
|
"["
|
|||
|
* (m.C(m.P"^"^-1)) -- optional complement symbol
|
|||
|
* m.Cf(item * (item - "]")^0, mt.__add) /
|
|||
|
function (c, p) return c == "^" and any - p or p end
|
|||
|
* "]"
|
|||
|
local function adddef (t, k, exp)
|
|||
|
if t[k] then
|
|||
|
error("'"..k.."' already defined as a rule")
|
|||
|
else
|
|||
|
t[k] = exp
|
|||
|
end
|
|||
|
return t
|
|||
|
end
|
|||
|
local function firstdef (n, r) return adddef({n}, n, r) end
|
|||
|
local function NT (n, b)
|
|||
|
if not b then
|
|||
|
error("rule '"..n.."' used outside a grammar")
|
|||
|
else return mm.V(n)
|
|||
|
end
|
|||
|
end
|
|||
|
local exp = m.P{ "Exp",
|
|||
|
Exp = S * ( m.V"Grammar"
|
|||
|
+ m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) );
|
|||
|
Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul)
|
|||
|
* (m.L(seq_follow) + patt_error);
|
|||
|
Prefix = "&" * S * m.V"Prefix" / mt.__len
|
|||
|
+ "!" * S * m.V"Prefix" / mt.__unm
|
|||
|
+ m.V"Suffix";
|
|||
|
Suffix = m.Cf(m.V"Primary" * S *
|
|||
|
( ( m.P"+" * m.Cc(1, mt.__pow)
|
|||
|
+ m.P"*" * m.Cc(0, mt.__pow)
|
|||
|
+ m.P"?" * m.Cc(-1, mt.__pow)
|
|||
|
+ "^" * ( m.Cg(num * m.Cc(mult))
|
|||
|
+ m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow))
|
|||
|
)
|
|||
|
+ "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div))
|
|||
|
+ m.P"{}" * m.Cc(nil, m.Ct)
|
|||
|
+ m.Cg(Def / getdef * m.Cc(mt.__div))
|
|||
|
)
|
|||
|
+ "=>" * S * m.Cg(Def / getdef * m.Cc(m.Cmt))
|
|||
|
) * S
|
|||
|
)^0, function (a,b,f) return f(a,b) end );
|
|||
|
Primary = "(" * m.V"Exp" * ")"
|
|||
|
+ String / mm.P
|
|||
|
+ Class
|
|||
|
+ defined
|
|||
|
+ "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" /
|
|||
|
function (n, p) return mm.Cg(p, n) end
|
|||
|
+ "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
|
|||
|
+ m.P"{}" / mm.Cp
|
|||
|
+ "{~" * m.V"Exp" * "~}" / mm.Cs
|
|||
|
+ "{|" * m.V"Exp" * "|}" / mm.Ct
|
|||
|
+ "{" * m.V"Exp" * "}" / mm.C
|
|||
|
+ m.P"." * m.Cc(any)
|
|||
|
+ (name * -arrow + "<" * name * ">") * m.Cb("G") / NT;
|
|||
|
Definition = name * arrow * m.V"Exp";
|
|||
|
Grammar = m.Cg(m.Cc(true), "G") *
|
|||
|
m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0,
|
|||
|
adddef) / mm.P
|
|||
|
}
|
|||
|
local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)
|
|||
|
local function compile (p, defs)
|
|||
|
if mm.type(p) == "pattern" then return p end -- already compiled
|
|||
|
local cp = pattern:match(p, 1, defs)
|
|||
|
if not cp then error("incorrect pattern", 3) end
|
|||
|
return cp
|
|||
|
end
|
|||
|
local function match (s, p, i)
|
|||
|
local cp = mem[p]
|
|||
|
if not cp then
|
|||
|
cp = compile(p)
|
|||
|
mem[p] = cp
|
|||
|
end
|
|||
|
return cp:match(s, i or 1)
|
|||
|
end
|
|||
|
local function find (s, p, i)
|
|||
|
local cp = fmem[p]
|
|||
|
if not cp then
|
|||
|
cp = compile(p) / 0
|
|||
|
cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
|
|||
|
fmem[p] = cp
|
|||
|
end
|
|||
|
local i, e = cp:match(s, i or 1)
|
|||
|
if i then return i, e - 1
|
|||
|
else return i
|
|||
|
end
|
|||
|
end
|
|||
|
local function gsub (s, p, rep)
|
|||
|
local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
|
|||
|
gmem[p] = g
|
|||
|
local cp = g[rep]
|
|||
|
if not cp then
|
|||
|
cp = compile(p)
|
|||
|
cp = mm.Cs((cp / rep + 1)^0)
|
|||
|
g[rep] = cp
|
|||
|
end
|
|||
|
return cp:match(s)
|
|||
|
end
|
|||
|
local re = {
|
|||
|
compile = compile,
|
|||
|
match = match,
|
|||
|
find = find,
|
|||
|
gsub = gsub,
|
|||
|
updatelocale = updatelocale,
|
|||
|
}
|
|||
|
return re
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
--=============================================================================
|
|||
|
do local _ENV = _ENV
|
|||
|
packages['util'] = function (...)
|
|||
|
|
|||
|
local getmetatable, setmetatable, load, loadstring, next
|
|||
|
, pairs, pcall, print, rawget, rawset, select, tostring
|
|||
|
, type, unpack
|
|||
|
= getmetatable, setmetatable, load, loadstring, next
|
|||
|
, pairs, pcall, print, rawget, rawset, select, tostring
|
|||
|
, type, unpack
|
|||
|
local m, s, t = require"math", require"string", require"table"
|
|||
|
local m_max, s_match, s_gsub, t_concat, t_insert
|
|||
|
= m.max, s.match, s.gsub, t.concat, t.insert
|
|||
|
local compat = require"compat"
|
|||
|
local
|
|||
|
function nop () end
|
|||
|
local noglobals, getglobal, setglobal if pcall and not compat.lua52 and not release then
|
|||
|
local function errR (_,i)
|
|||
|
error("illegal global read: " .. tostring(i), 2)
|
|||
|
end
|
|||
|
local function errW (_,i, v)
|
|||
|
error("illegal global write: " .. tostring(i)..": "..tostring(v), 2)
|
|||
|
end
|
|||
|
local env = setmetatable({}, { __index=errR, __newindex=errW })
|
|||
|
noglobals = function()
|
|||
|
pcall(setfenv, 3, env)
|
|||
|
end
|
|||
|
function getglobal(k) rawget(env, k) end
|
|||
|
function setglobal(k, v) rawset(env, k, v) end
|
|||
|
else
|
|||
|
noglobals = nop
|
|||
|
end
|
|||
|
local _ENV = noglobals() ------------------------------------------------------
|
|||
|
local util = {
|
|||
|
nop = nop,
|
|||
|
noglobals = noglobals,
|
|||
|
getglobal = getglobal,
|
|||
|
setglobal = setglobal
|
|||
|
}
|
|||
|
util.unpack = t.unpack or unpack
|
|||
|
util.pack = t.pack or function(...) return { n = select('#', ...), ... } end
|
|||
|
if compat.lua51 then
|
|||
|
local old_load = load
|
|||
|
function util.load (ld, source, mode, env)
|
|||
|
local fun
|
|||
|
if type (ld) == 'string' then
|
|||
|
fun = loadstring (ld)
|
|||
|
else
|
|||
|
fun = old_load (ld, source)
|
|||
|
end
|
|||
|
if env then
|
|||
|
setfenv (fun, env)
|
|||
|
end
|
|||
|
return fun
|
|||
|
end
|
|||
|
else
|
|||
|
util.load = load
|
|||
|
end
|
|||
|
if compat.luajit and compat.jit then
|
|||
|
function util.max (ary)
|
|||
|
local max = 0
|
|||
|
for i = 1, #ary do
|
|||
|
max = m_max(max,ary[i])
|
|||
|
end
|
|||
|
return max
|
|||
|
end
|
|||
|
elseif compat.luajit then
|
|||
|
local t_unpack = util.unpack
|
|||
|
function util.max (ary)
|
|||
|
local len = #ary
|
|||
|
if len <=30 or len > 10240 then
|
|||
|
local max = 0
|
|||
|
for i = 1, #ary do
|
|||
|
local j = ary[i]
|
|||
|
if j > max then max = j end
|
|||
|
end
|
|||
|
return max
|
|||
|
else
|
|||
|
return m_max(t_unpack(ary))
|
|||
|
end
|
|||
|
end
|
|||
|
else
|
|||
|
local t_unpack = util.unpack
|
|||
|
local safe_len = 1000
|
|||
|
function util.max(array)
|
|||
|
local len = #array
|
|||
|
if len == 0 then return -1 end -- FIXME: shouldn't this be `return -1`?
|
|||
|
local off = 1
|
|||
|
local off_end = safe_len
|
|||
|
local max = array[1] -- seed max.
|
|||
|
repeat
|
|||
|
if off_end > len then off_end = len end
|
|||
|
local seg_max = m_max(t_unpack(array, off, off_end))
|
|||
|
if seg_max > max then
|
|||
|
max = seg_max
|
|||
|
end
|
|||
|
off = off + safe_len
|
|||
|
off_end = off_end + safe_len
|
|||
|
until off >= len
|
|||
|
return max
|
|||
|
end
|
|||
|
end
|
|||
|
local
|
|||
|
function setmode(t,mode)
|
|||
|
local mt = getmetatable(t) or {}
|
|||
|
if mt.__mode then
|
|||
|
error("The mode has already been set on table "..tostring(t)..".")
|
|||
|
end
|
|||
|
mt.__mode = mode
|
|||
|
return setmetatable(t, mt)
|
|||
|
end
|
|||
|
util.setmode = setmode
|
|||
|
function util.weakboth (t)
|
|||
|
return setmode(t,"kv")
|
|||
|
end
|
|||
|
function util.weakkey (t)
|
|||
|
return setmode(t,"k")
|
|||
|
end
|
|||
|
function util.weakval (t)
|
|||
|
return setmode(t,"v")
|
|||
|
end
|
|||
|
function util.strip_mt (t)
|
|||
|
return setmetatable(t, nil)
|
|||
|
end
|
|||
|
local getuniqueid
|
|||
|
do
|
|||
|
local N, index = 0, {}
|
|||
|
function getuniqueid(v)
|
|||
|
if not index[v] then
|
|||
|
N = N + 1
|
|||
|
index[v] = N
|
|||
|
end
|
|||
|
return index[v]
|
|||
|
end
|
|||
|
end
|
|||
|
util.getuniqueid = getuniqueid
|
|||
|
do
|
|||
|
local counter = 0
|
|||
|
function util.gensym ()
|
|||
|
counter = counter + 1
|
|||
|
return "___SYM_"..counter
|
|||
|
end
|
|||
|
end
|
|||
|
function util.passprint (...) print(...) return ... end
|
|||
|
local val_to_str_, key_to_str, table_tostring, cdata_to_str, t_cache
|
|||
|
local multiplier = 2
|
|||
|
local
|
|||
|
function val_to_string (v, indent)
|
|||
|
indent = indent or 0
|
|||
|
t_cache = {} -- upvalue.
|
|||
|
local acc = {}
|
|||
|
val_to_str_(v, acc, indent, indent)
|
|||
|
local res = t_concat(acc, "")
|
|||
|
return res
|
|||
|
end
|
|||
|
util.val_to_str = val_to_string
|
|||
|
function val_to_str_ ( v, acc, indent, str_indent )
|
|||
|
str_indent = str_indent or 1
|
|||
|
if "string" == type( v ) then
|
|||
|
v = s_gsub( v, "\n", "\n" .. (" "):rep( indent * multiplier + str_indent ) )
|
|||
|
if s_match( s_gsub( v,"[^'\"]",""), '^"+$' ) then
|
|||
|
acc[#acc+1] = t_concat{ "'", "", v, "'" }
|
|||
|
else
|
|||
|
acc[#acc+1] = t_concat{'"', s_gsub(v,'"', '\\"' ), '"' }
|
|||
|
end
|
|||
|
elseif "cdata" == type( v ) then
|
|||
|
cdata_to_str( v, acc, indent )
|
|||
|
elseif "table" == type(v) then
|
|||
|
if t_cache[v] then
|
|||
|
acc[#acc+1] = t_cache[v]
|
|||
|
else
|
|||
|
t_cache[v] = tostring( v )
|
|||
|
table_tostring( v, acc, indent )
|
|||
|
end
|
|||
|
else
|
|||
|
acc[#acc+1] = tostring( v )
|
|||
|
end
|
|||
|
end
|
|||
|
function key_to_str ( k, acc, indent )
|
|||
|
if "string" == type( k ) and s_match( k, "^[_%a][_%a%d]*$" ) then
|
|||
|
acc[#acc+1] = s_gsub( k, "\n", (" "):rep( indent * multiplier + 1 ) .. "\n" )
|
|||
|
else
|
|||
|
acc[#acc+1] = "[ "
|
|||
|
val_to_str_( k, acc, indent )
|
|||
|
acc[#acc+1] = " ]"
|
|||
|
end
|
|||
|
end
|
|||
|
function cdata_to_str(v, acc, indent)
|
|||
|
acc[#acc+1] = ( " " ):rep( indent * multiplier )
|
|||
|
acc[#acc+1] = "["
|
|||
|
print(#acc)
|
|||
|
for i = 0, #v do
|
|||
|
if i % 16 == 0 and i ~= 0 then
|
|||
|
acc[#acc+1] = "\n"
|
|||
|
acc[#acc+1] = (" "):rep(indent * multiplier + 2)
|
|||
|
end
|
|||
|
acc[#acc+1] = v[i] and 1 or 0
|
|||
|
acc[#acc+1] = i ~= #v and ", " or ""
|
|||
|
end
|
|||
|
print(#acc, acc[1], acc[2])
|
|||
|
acc[#acc+1] = "]"
|
|||
|
end
|
|||
|
function table_tostring ( tbl, acc, indent )
|
|||
|
acc[#acc+1] = t_cache[tbl]
|
|||
|
acc[#acc+1] = "{\n"
|
|||
|
for k, v in pairs( tbl ) do
|
|||
|
local str_indent = 1
|
|||
|
acc[#acc+1] = (" "):rep((indent + 1) * multiplier)
|
|||
|
key_to_str( k, acc, indent + 1)
|
|||
|
if acc[#acc] == " ]"
|
|||
|
and acc[#acc - 2] == "[ "
|
|||
|
then str_indent = 8 + #acc[#acc - 1]
|
|||
|
end
|
|||
|
acc[#acc+1] = " = "
|
|||
|
val_to_str_( v, acc, indent + 1, str_indent)
|
|||
|
acc[#acc+1] = "\n"
|
|||
|
end
|
|||
|
acc[#acc+1] = ( " " ):rep( indent * multiplier )
|
|||
|
acc[#acc+1] = "}"
|
|||
|
end
|
|||
|
function util.expose(v) print(val_to_string(v)) return v end
|
|||
|
function util.map (ary, func, ...)
|
|||
|
if type(ary) == "function" then ary, func = func, ary end
|
|||
|
local res = {}
|
|||
|
for i = 1,#ary do
|
|||
|
res[i] = func(ary[i], ...)
|
|||
|
end
|
|||
|
return res
|
|||
|
end
|
|||
|
function util.selfmap (ary, func, ...)
|
|||
|
if type(ary) == "function" then ary, func = func, ary end
|
|||
|
for i = 1,#ary do
|
|||
|
ary[i] = func(ary[i], ...)
|
|||
|
end
|
|||
|
return ary
|
|||
|
end
|
|||
|
local
|
|||
|
function map_all (tbl, func, ...)
|
|||
|
if type(tbl) == "function" then tbl, func = func, tbl end
|
|||
|
local res = {}
|
|||
|
for k, v in next, tbl do
|
|||
|
res[k]=func(v, ...)
|
|||
|
end
|
|||
|
return res
|
|||
|
end
|
|||
|
util.map_all = map_all
|
|||
|
local
|
|||
|
function fold (ary, func, acc)
|
|||
|
local i0 = 1
|
|||
|
if not acc then
|
|||
|
acc = ary[1]
|
|||
|
i0 = 2
|
|||
|
end
|
|||
|
for i = i0, #ary do
|
|||
|
acc = func(acc,ary[i])
|
|||
|
end
|
|||
|
return acc
|
|||
|
end
|
|||
|
util.fold = fold
|
|||
|
local
|
|||
|
function foldr (ary, func, acc)
|
|||
|
local offset = 0
|
|||
|
if not acc then
|
|||
|
acc = ary[#ary]
|
|||
|
offset = 1
|
|||
|
end
|
|||
|
for i = #ary - offset, 1 , -1 do
|
|||
|
acc = func(ary[i], acc)
|
|||
|
end
|
|||
|
return acc
|
|||
|
end
|
|||
|
util.foldr = foldr
|
|||
|
local
|
|||
|
function map_fold(ary, mfunc, ffunc, acc)
|
|||
|
local i0 = 1
|
|||
|
if not acc then
|
|||
|
acc = mfunc(ary[1])
|
|||
|
i0 = 2
|
|||
|
end
|
|||
|
for i = i0, #ary do
|
|||
|
acc = ffunc(acc,mfunc(ary[i]))
|
|||
|
end
|
|||
|
return acc
|
|||
|
end
|
|||
|
util.map_fold = map_fold
|
|||
|
local
|
|||
|
function map_foldr(ary, mfunc, ffunc, acc)
|
|||
|
local offset = 0
|
|||
|
if not acc then
|
|||
|
acc = mfunc(ary[#acc])
|
|||
|
offset = 1
|
|||
|
end
|
|||
|
for i = #ary - offset, 1 , -1 do
|
|||
|
acc = ffunc(mfunc(ary[i], acc))
|
|||
|
end
|
|||
|
return acc
|
|||
|
end
|
|||
|
util.map_foldr = map_fold
|
|||
|
function util.zip(a1, a2)
|
|||
|
local res, len = {}, m_max(#a1,#a2)
|
|||
|
for i = 1,len do
|
|||
|
res[i] = {a1[i], a2[i]}
|
|||
|
end
|
|||
|
return res
|
|||
|
end
|
|||
|
function util.zip_all(t1, t2)
|
|||
|
local res = {}
|
|||
|
for k,v in pairs(t1) do
|
|||
|
res[k] = {v, t2[k]}
|
|||
|
end
|
|||
|
for k,v in pairs(t2) do
|
|||
|
if res[k] == nil then
|
|||
|
res[k] = {t1[k], v}
|
|||
|
end
|
|||
|
end
|
|||
|
return res
|
|||
|
end
|
|||
|
function util.filter(ary,func)
|
|||
|
local res = {}
|
|||
|
for i = 1,#ary do
|
|||
|
if func(ary[i]) then
|
|||
|
t_insert(res, ary[i])
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
local
|
|||
|
function id (...) return ... end
|
|||
|
util.id = id
|
|||
|
local function AND (a,b) return a and b end
|
|||
|
local function OR (a,b) return a or b end
|
|||
|
function util.copy (tbl) return map_all(tbl, id) end
|
|||
|
function util.all (ary, mfunc)
|
|||
|
if mfunc then
|
|||
|
return map_fold(ary, mfunc, AND)
|
|||
|
else
|
|||
|
return fold(ary, AND)
|
|||
|
end
|
|||
|
end
|
|||
|
function util.any (ary, mfunc)
|
|||
|
if mfunc then
|
|||
|
return map_fold(ary, mfunc, OR)
|
|||
|
else
|
|||
|
return fold(ary, OR)
|
|||
|
end
|
|||
|
end
|
|||
|
function util.get(field)
|
|||
|
return function(tbl) return tbl[field] end
|
|||
|
end
|
|||
|
function util.lt(ref)
|
|||
|
return function(val) return val < ref end
|
|||
|
end
|
|||
|
function util.compose(f,g)
|
|||
|
return function(...) return f(g(...)) end
|
|||
|
end
|
|||
|
function util.extend (destination, ...)
|
|||
|
for i = 1, select('#', ...) do
|
|||
|
for k,v in pairs((select(i, ...))) do
|
|||
|
destination[k] = v
|
|||
|
end
|
|||
|
end
|
|||
|
return destination
|
|||
|
end
|
|||
|
function util.setify (t)
|
|||
|
local set = {}
|
|||
|
for i = 1, #t do
|
|||
|
set[t[i]]=true
|
|||
|
end
|
|||
|
return set
|
|||
|
end
|
|||
|
function util.arrayify (...) return {...} end
|
|||
|
local
|
|||
|
function _checkstrhelper(s)
|
|||
|
return s..""
|
|||
|
end
|
|||
|
function util.checkstring(s, func)
|
|||
|
local success, str = pcall(_checkstrhelper, s)
|
|||
|
if not success then
|
|||
|
if func == nil then func = "?" end
|
|||
|
error("bad argument to '"
|
|||
|
..tostring(func)
|
|||
|
.."' (string expected, got "
|
|||
|
..type(s)
|
|||
|
..")",
|
|||
|
2)
|
|||
|
end
|
|||
|
return str
|
|||
|
end
|
|||
|
return util
|
|||
|
|
|||
|
end
|
|||
|
end
|
|||
|
return require"init"
|
|||
|
|
|||
|
|
|||
|
|
|||
|
-- The Romantic WTF public license.
|
|||
|
-- --------------------------------
|
|||
|
-- a.k.a. version "<3" or simply v3
|
|||
|
--
|
|||
|
--
|
|||
|
-- Dear user,
|
|||
|
--
|
|||
|
-- The LuLPeg library
|
|||
|
--
|
|||
|
-- \
|
|||
|
-- '.,__
|
|||
|
-- \ /
|
|||
|
-- '/,__
|
|||
|
-- /
|
|||
|
-- /
|
|||
|
-- /
|
|||
|
-- has been / released
|
|||
|
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
|||
|
-- under the Romantic WTF Public License.
|
|||
|
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~`,´ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
|||
|
-- I hereby grant you an irrevocable license to
|
|||
|
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
|||
|
-- do what the gentle caress you want to
|
|||
|
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
|||
|
-- with this lovely
|
|||
|
-- ~ ~ ~ ~ ~ ~ ~ ~
|
|||
|
-- / library...
|
|||
|
-- / ~ ~ ~ ~
|
|||
|
-- / Love,
|
|||
|
-- # / ','
|
|||
|
-- ####### ·
|
|||
|
-- #####
|
|||
|
-- ###
|
|||
|
-- #
|
|||
|
--
|
|||
|
-- -- Pierre-Yves
|
|||
|
--
|
|||
|
--
|
|||
|
--
|
|||
|
-- P.S.: Even though I poured my heart into this work,
|
|||
|
-- I _cannot_ provide any warranty regarding
|
|||
|
-- its fitness for _any_ purpose. You
|
|||
|
-- acknowledge that I will not be held liable
|
|||
|
-- for any damage its use could incur.
|
|||
|
--
|
|||
|
-- -----------------------------------------------------------------------------
|
|||
|
--
|
|||
|
-- LuLPeg, Copyright (C) 2013 Pierre-Yves Gérardy.
|
|||
|
--
|
|||
|
-- The `re` module and lpeg.*.*.test.lua,
|
|||
|
-- Copyright (C) 2013 Lua.org, PUC-Rio.
|
|||
|
--
|
|||
|
-- 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.
|
|||
|
|