-- 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.