prpw/lib/lulpeg.lua

2977 lines
88 KiB
Lua
Raw Permalink Normal View History

2024-08-10 00:35:03 +00:00
-- 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.