From c6b98103f0ab2c4afb7216e73f2eacac58cbf952 Mon Sep 17 00:00:00 2001 From: Sérgio Queiroz Date: Mon, 18 Dec 2017 15:39:00 -0300 Subject: Updating code to use the new syntax/semantics of labels --- lpcode.c | 5 +- lpprint.c | 12 +- lptree.c | 10 +- lpvm.c | 1 + relabel.lua | 15 +- testlabel.lua | 802 ++++++++++++++++++---------------------------------------- 6 files changed, 272 insertions(+), 573 deletions(-) diff --git a/lpcode.c b/lpcode.c index 0b813b8..8a68060 100644 --- a/lpcode.c +++ b/lpcode.c @@ -517,7 +517,8 @@ static int addinstruction (CompileState *compst, Opcode op, int aux) { static int addoffsetinst (CompileState *compst, Opcode op) { int i = addinstruction(compst, op, 0); /* instruction */ addinstruction(compst, (Opcode)0, 0); /* open space for offset */ - assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2); + assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2 || + op == IThrowRec); /* labeled failure */ return i; } @@ -527,13 +528,13 @@ static void codethrow (CompileState *compst, TTree *throw) { int recov, aux; if (throw->u.s.ps != 0) { recov = addoffsetinst(compst, IThrowRec); + assert(sib2(throw)->tag == TRule); } else { recov = addinstruction(compst, IThrow, 0); } aux = nextinstruction(compst); getinstr(compst, aux).i.key = throw->key; /* next instruction keeps only rule name */ getinstr(compst, recov).i.key = sib2(throw)->cap; /* rule number */ - assert(sib2(throw)->tag == TRule); } /* labeled failure */ diff --git a/lpprint.c b/lpprint.c index 342a2b6..8d5e087 100644 --- a/lpprint.c +++ b/lpprint.c @@ -110,11 +110,6 @@ void printinst (const Instruction *op, const Instruction *p) { case IThrow: { /* labeled failure */ printf("%d", p->i.aux); break; - } - case IRecov: case ILabChoice: { /* labeled failure */ - printjmp(op, p); - printcharset((p+2)->buff); - break; } default: break; } @@ -217,16 +212,15 @@ void printtree (TTree *tree, int ident) { break; } case TThrow: { /* labeled failure */ + if (tree->u.s.ps != 0) + assert(sib2(tree)->tag == TRule); + printf(" key: %d (rule: %d)\n", tree->key, sib2(tree)->cap); printf(" labels: %d\n", tree->u.label); break; } default: { int sibs = numsiblings[tree->tag]; printf("\n"); - if (tree->tag == TRecov || tree->tag == TLabChoice) { /* labeled failure */ - printcharset(treelabelset(tree)); - printf("\n"); - } if (sibs >= 1) { printtree(sib1(tree), ident + 2); if (sibs >= 2) diff --git a/lptree.c b/lptree.c index 6d6b399..ba091d6 100644 --- a/lptree.c +++ b/lptree.c @@ -1204,8 +1204,16 @@ static int lp_match (lua_State *L) { r = match(L, s, s + i, s + l, code, capture, ptop, &labelf, &sfail); /* labeled failure */ if (r == NULL) { /* labeled failure begin */ lua_pushnil(L); - if (labelf) + if (labelf) { + int isnum; + lua_Integer lInt; lua_rawgeti(L, ktableidx(ptop), labelf); + lInt = lua_tointegerx(L, -1, &isnum); + if (isnum) { + lua_pop(L, 1); + lua_pushinteger(L, lInt); + } + } else lua_pushstring(L, "fail"); lua_pushinteger(L, sfail - (s + i) + 1); /* subject position related to the error */ diff --git a/lpvm.c b/lpvm.c index 251fa0a..20bc545 100644 --- a/lpvm.c +++ b/lpvm.c @@ -301,6 +301,7 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e, luaL_error(L, "labelf is %d", *labelf); *sfail = s; stack = getstackbase(L, ptop); + stack++; goto fail; } case IThrowRec: { /* labeled failure */ diff --git a/relabel.lua b/relabel.lua index abb3c7f..6a676b9 100644 --- a/relabel.lua +++ b/relabel.lua @@ -241,12 +241,6 @@ local function choicerec (...) return p end -local function getlab (f) - if not tlabels[f] then - error("undefined label: " .. f) - end - return tlabels[f] -end local exp = m.P{ "Exp", Exp = S * ( m.V"Grammar" @@ -278,7 +272,7 @@ local exp = m.P{ "Exp", + "=>" * expect(S * m.Cg(Def / getdef * m.Cc(m.Cmt)), "ExpName1") ) - )^0, function (a,b,f) if f == "lab" then return a + mm.T(getlab(b)) else return f(a,b) end end ); + )^0, function (a,b,f) if f == "lab" then return a + mm.T(b) else return f(a,b) end end ); Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1") + String / mm.P + Class @@ -301,7 +295,7 @@ local exp = m.P{ "Exp", + m.P"." * m.Cc(any) + (name * -arrow + "<" * expect(name, "ExpName3") * expect(">", "MisClose6")) * m.Cb("G") / NT; - Label = num + name / function (f) return getlab(f) end; + Label = num + name; Definition = name * arrow * expect(m.V"Exp", "ExpPatt8"); Grammar = m.Cg(m.Cc(true), "G") * m.Cf(m.V"Definition" / firstdef * (S * m.Cg(m.V"Definition"))^0, @@ -342,7 +336,7 @@ end local function compile (p, defs) if mm.type(p) == "pattern" then return p end -- already compiled p = p .. " " -- for better reporting of column numbers in errors when at EOF - local ok, cp, label, suffix = pcall(function() return pattern:match(p, 1, defs) end) + local ok, cp, label, poserr = pcall(function() return pattern:match(p, 1, defs) end) if not ok and cp then if type(cp) == "string" then cp = cp:gsub("^[^:]+:[^:]+: ", "") @@ -351,8 +345,7 @@ local function compile (p, defs) end if not cp then local lines = splitlines(p) - local line, col = lineno(p, #p - #suffix + 1) - --local line, col = calcline(p, #p - #suffix + 1) + local line, col = lineno(p, poserr) local err = {} tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errmsgs[label]) tinsert(err, lines[line]) diff --git a/testlabel.lua b/testlabel.lua index 9e2a586..b834be3 100644 --- a/testlabel.lua +++ b/testlabel.lua @@ -84,12 +84,12 @@ print"+" p = m.T(1) s = "abc" r, l, poserr = p:match(s) -assert(r == nil and l == '1' and poserr == 1) +assert(r == nil and l == 1 and poserr == 1) -- throws a label, choice does not catch labels p = m.T(1) + m.P"a" r, l, poserr = p:match(s) -assert(r == nil and l == '1' and poserr == 1) +assert(r == nil and l == 1 and poserr == 1) -- again throws a label that is not caught by choice local g = m.P{ @@ -99,15 +99,15 @@ local g = m.P{ B = m.P"a" } r, l, poserr = g:match(s) -assert(r == nil and l == '1' and poserr == 1) +assert(r == nil and l == 1 and poserr == 1) -- throws a label in a position that is not the farthest one -- but it is the position that should be reported p = m.P(1) * m.P"a" + m.T(11) checklabeq({3, nil, nil}, p:match("bac")) -checklabeq({nil, '11', 1}, p:match("c")) -checklabeq({nil, '11', 1}, p:match("x")) -checklabeq({nil, '11', 1}, p:match("kx")) +checklabeq({nil, 11, 1}, p:match("c")) +checklabeq({nil, 11, 1}, p:match("x")) +checklabeq({nil, 11, 1}, p:match("kx")) -- throws a label without a corresponding recovery rule @@ -127,7 +127,7 @@ p = m.P{ [2] = m.P"a" } r, l, poserr = p:match("abc") -assert(r == nil and l == '2' and poserr == 1) +assert(r == nil and l == 2 and poserr == 1) -- throws a label with a corresponding recovery rule p = m.P{ @@ -198,23 +198,23 @@ assert(r == nil and l == 'fail' and poserr == 1) -- tests related to predicates p = #m.T(1) + m.P"a" r, l, poserr = p:match("abc") -assert(r == nil and l == '1' and poserr == 1) +assert(r == nil and l == 1 and poserr == 1) p = ##m.T(1) + m.P"a" r, l, poserr = p:match("abc") -assert(r == nil and l == '1' and poserr == 1) +assert(r == nil and l == 1 and poserr == 1) p = -m.T(1) * m.P"a" r, l, poserr = p:match("abc") -assert(r == nil and l == '1' and poserr == 1) +assert(r == nil and l == 1 and poserr == 1) p = -m.T(1) * m.P"a" r, l, poserr = p:match("bbc") -assert(r == nil and l == '1' and poserr == 1) +assert(r == nil and l == 1 and poserr == 1) p = -(-m.T(1)) * m.P"a" r, l, poserr = p:match("abc") -assert(r == nil and l == '1' and poserr == 1) +assert(r == nil and l == 1 and poserr == 1) p = m.P{ "S", @@ -254,7 +254,7 @@ p = m.P{ ["22"] = m.T(15) } r, l, poserr = p:match("abc") -assert(r == nil and l == '15' and poserr == 1) +assert(r == nil and l == 15 and poserr == 1) p = m.P{ "S", @@ -262,17 +262,17 @@ p = m.P{ ["22"] = m.T(15) } r, l, poserr = p:match("abc") -assert(r == nil and l == '15' and poserr == 2) +assert(r == nil and l == 15 and poserr == 2) -- tests related to repetition p = m.T(1)^0 r, l, poserr = p:match("ab") -assert(r == nil and l == '1' and poserr == 1) +assert(r == nil and l == 1 and poserr == 1) p = (m.P"a" + m.T(1))^0 r, l, poserr = p:match("aa") -assert(r == nil and l == '1' and poserr == 3) +assert(r == nil and l == 1 and poserr == 3) -- Bug reported by Matthew Allen @@ -327,7 +327,7 @@ g = m.P{ assert(g:match("a;a;") == 5) r, l, poserr = g:match("a;a") -assert(r == nil and l == '2' and poserr == 4) +assert(r == nil and l == 2 and poserr == 4) p = m.P{ @@ -383,23 +383,18 @@ local eq = sp * m.P"=" g = m.P{ "S", - S = m.Rec( - m.Rec( - m.Rec( - m.Rec(m.V"S0", m.V"ID", 1), - m.V"ID" * eq * m.V"Exp", 2 - ), - m.V"U"^0 * m.V"I" * m.V"ID", 3 - ), - m.V"U"^0 * m.V"ID" * m.V"ID", 4) - + m.T(5), -- error - S0 = m.V"S1" + m.V"S2" + #m.V"I" * m.T(3), - S1 = #(m.V"ID" * eq) * m.T(2) + sp * #(m.V"ID" * -m.P(1)) * m.T(1) + #m.V"ID" * m.T(4), - S2 = #(m.V"U"^1 * m.V"ID") * m.T(4) + #(m.V"U"^1 * m.V"I") * m.T(3), + S = m.V"S0" + m.T'L5', -- error + S0 = m.V"S1" + m.V"S2" + #m.V"I" * m.T'L3', + S1 = #(m.V"ID" * eq) * m.T'L2' + sp * #(m.V"ID" * -m.P(1)) * m.T'L1' + #m.V"ID" * m.T'L4', + S2 = #(m.V"U"^1 * m.V"ID") * m.T'L4' + #(m.V"U"^1 * m.V"I") * m.T'L3', ID = sp * m.P"a", U = sp * m.P"unsigned", I = sp * m.P"int", Exp = sp * m.P"E", + L1 = m.V"ID", + L2 = m.V"ID" * eq * m.V"Exp", + L3 = m.V"U"^0 * m.V"I" * m.V"ID", + L4 = m.V"U"^0 * m.V"ID" * m.V"ID", } local s = "a" @@ -415,19 +410,19 @@ assert(g:match(s) == #s + 1) --4 s = "b" r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) +assert(r == nil and l == 'L5' and poserr == 1) s = "unsigned" r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) +assert(r == nil and l == 'L5' and poserr == 1) s = "unsigned a" r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) +assert(r == nil and l == 'L5' and poserr == 1) s = "unsigned int" r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) +assert(r == nil and l == 'L5' and poserr == 1) print("+") @@ -435,42 +430,61 @@ print("+") local re = require 'relabel' -g = re.compile[['a' //{4,9} [a-z] +g = re.compile[['a' / %{l1} ]] assert(g:match("a") == 2) r, l, poserr = g:match("b") -assert(r == nil and l == 0 and poserr == 1) +assert(r == nil and l == 'l1' and poserr == 1) -g = re.compile[['a' //{4,9} [a-f] //{5, 7} [a-z] +g = re.compile[['a'^l1 ]] assert(g:match("a") == 2) r, l, poserr = g:match("b") -assert(r == nil and l == 0 and poserr == 1) +assert(r == nil and l == 'l1' and poserr == 1) -g = re.compile[[%{1} //{4,9} [a-z] +g = re.compile[[A <- 'a'^B + B <- [a-f]^C + C <- [a-z] ]] -r, l, poserr = g:match("a") -assert(r == nil and l == 1 and poserr == 1) +assert(g:match("a") == 2) +assert(g:match("a") == 2) +assert(g:match("f") == 2) +assert(g:match("g") == 2) +assert(g:match("z") == 2) +r, l, poserr = g:match("A") +assert(r == nil and l == 'fail' and poserr == 1) +g = re.compile[[A <- %{C} + B <- [a-z] +]] +r, l, poserr = g:match("a") +assert(r == nil and l == 'C' and poserr == 1) -g = re.compile[[%{1} //{4,1} [a-f] +g = re.compile[[A <- %{B} + B <- [a-z] ]] +r, l, poserr = g:match("a") assert(g:match("a") == 2) -r, l, poserr = g:match("h") -assert(r == nil and l == 0 and poserr == 1) +r, l, poserr = g:match("U") +assert(r == nil and l == 'fail' and poserr == 1) + + +g = re.compile[[A <- [a-f] %{B} + B <- [a-c] %{C} + C <- [a-z] +]] -g = re.compile[[[a-f]%{9} //{4,9} [a-c]%{7} //{5, 7} [a-z] ]] r, l, poserr = g:match("a") -assert(r == nil and l == 0 and poserr == 2) +assert(r == nil and l == 'fail' and poserr == 2) r, l, poserr = g:match("aa") -assert(r == nil and l == 0 and poserr == 3) +assert(r == nil and l == 'fail' and poserr == 3) assert(g:match("aaa") == 4) r, l, poserr = g:match("ad") -assert(r == nil and l == 0 and poserr == 2) +assert(r == nil and l == 'fail' and poserr == 2) r, l, poserr = g:match("g") -assert(r == nil and l == 0 and poserr == 1) +assert(r == nil and l == 'fail' and poserr == 1) --[[ grammar based on Figure 8 of paper submitted to SCP (using the recovery operator) @@ -481,143 +495,18 @@ S2 -> &('unsigned'+ ID) %4 / & ('unsigned'+ 'int') %3 ]] g = re.compile([[ - S <- S0 //{1} ID //{2} ID %s* '=' Exp //{3} U* Int ID //{4} U ID ID / %{5} - S0 <- S1 / S2 / &Int %{3} - S1 <- &(ID %s* '=') %{2} / &(ID !.) %{1} / &ID %{4} - S2 <- &(U+ ID) %{4} / &(U+ Int) %{3} - ID <- %s* 'a' - U <- %s* 'unsigned' - Int <- %s* 'int' - Exp <- %s* 'E' -]]) - -local s = "a" -assert(g:match(s) == #s + 1) --1 -s = "a = E" -assert(g:match(s) == #s + 1) --2 -s = "int a" -assert(g:match(s) == #s + 1) --3 -s = "unsigned int a" -assert(g:match(s) == #s + 1) --3 -s = "unsigned a a" -assert(g:match(s) == #s + 1) --4 -s = "b" -r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) -s = "unsigned" -r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) -s = "unsigned a" -r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) -s = "unsigned int" -r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) - - ------------------------------------------- --- Tests related to labeled ordered choice ------------------------------------------- - --- throws a label that is not caught by labeled choice -s = "abc" -p = m.Lc(m.T(2), m.P"a", 1, 3) -r, l, poserr = p:match(s) -assert(r == nil and l == 2 and poserr == 1) - --- modifies previous pattern --- adds another labeled choice to catch label "2" -p = m.Lc(p, m.P"a", 2) -assert(p:match(s) == 2) - --- throws a label that is caught by labeled choice -p = m.Lc(m.T(25), m.P"a", 25) -assert(p:match(s) == 2) - --- "fail" is label "0" --- throws the "fail" label that is not caught by the labeled choice -s = "bola" -r, l, poserr = p:match("bola") -assert(r == nil and l == 0 and poserr == 1) - --- labeled choice does not catch "fail" -p = m.Lc(m.P"b", m.P"a", 1) - -r, l, poserr = p:match("abc") -assert(r == nil and l == 0 and poserr == 1) -assert(p:match("bola") == 2) - --- labeled choice catches "1" or "3" -p = m.Lc(-m.P"a" * m.T(1) + m.P"a" * m.T(3), m.P"a" + m.P"b", 1, 3) -assert(p:match("abc") == 2) -assert(p:match("bac") == 2) - --- associativity --- (p1 / %1) /{1} (p2 / %2) /{2} p3 --- left-associativity --- ("a" /{1} "b") /{2} "c" -p = m.Lc(m.Lc(m.P"a" + m.T(1), m.P"b" + m.T(2), 1), m.P"c", 2) -assert(p:match("abc") == 2) -assert(p:match("bac") == 2) -assert(p:match("cab") == 2) -r, l, poserr = p:match("dab") -assert(r == nil and l == 0 and poserr == 1) - - --- righ-associativity --- "a" /{1} ("b" /{2} "c") -p = m.Lc(m.P"a" + m.T(1), m.Lc(m.P"b" + m.T(2), m.P"c", 2), 1) -assert(p:match("abc") == 2) -assert(p:match("bac") == 2) -assert(p:match("cab") == 2) -r, l, poserr = p:match("dab") -assert(r == nil and l == 0 and poserr == 1) - - --- associativity -> in this case the error thrown by p1 is only --- recovered when we have a left-associative operator --- (p1 / %2) /{1} (p2 / %2) /{2} p3 --- left-associativity --- ("a" /{1} "b") /{2} "c" -p = m.Lc(m.Lc(m.P"a" + m.T(2), m.P"b" + m.T(2), 1), m.P"c", 2) -assert(p:match("abc") == 2) -r, l, poserr = p:match("bac") -assert(r == nil and l == 0 and poserr == 1) -assert(p:match("cab") == 2) -r, l, poserr = p:match("dab") -assert(r == nil and l == 0 and poserr == 1) - - --- righ-associativity --- "a" /{1} ("b" /{2} "c") -p = m.Lc(m.P"a" + m.T(2), m.Lc(m.P"b" + m.T(2), m.P"c", 2), 1) -assert(p:match("abc") == 2) -r, l, poserr = p:match("bac") -assert(r == nil and l == 2 and poserr == 1) -r, l, poserr = p:match("cab") -assert(r == nil and l == 2 and poserr == 1) -r, l, poserr = p:match("dab") -assert(r == nil and l == 2 and poserr == 1) - - - ---[[ grammar based on Figure 8 of paper submitted to SCP (using labeled choice) -S -> S0 /{1} ID /{2} ID '=' Exp /{3} 'unsigned'* 'int' ID /{4} 'unsigned'* ID ID / %error -S0 -> ID S1 / 'unsigned' S2 / 'int' %3 -S1 -> '=' %2 / !. %1 / ID %4 -S2 -> 'unsigned' S2 / ID %4 / 'int' %3 -]] - - -g = re.compile([[ - S <- S0 /{1} ID /{2} ID %s* '=' Exp /{3} U* Int ID /{4} U ID ID / %{5} - S0 <- ID S1 / U S2 / Int %{3} - S1 <- %s* '=' %{2} / !. %{1} / ID %{4} - S2 <- U S2 / ID %{4} / Int %{3} + S <- S0 / %{L5} + S0 <- S1 / S2 / &Int %{L3} + S1 <- &(ID %s* '=') %{L2} / &(ID !.) %{L1} / &ID %{L4} + S2 <- &(U+ ID) %{L4} / &(U+ Int) %{L3} ID <- %s* 'a' U <- %s* 'unsigned' Int <- %s* 'int' Exp <- %s* 'E' + L1 <- ID + L2 <- ID %s* '=' Exp + L3 <- U* Int ID + L4 <- U ID ID ]]) local s = "a" @@ -632,57 +521,52 @@ s = "unsigned a a" assert(g:match(s) == #s + 1) --4 s = "b" r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) +assert(r == nil and l == 'L5' and poserr == 1) s = "unsigned" r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) +assert(r == nil and l == 'L5' and poserr == 1) s = "unsigned a" r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) +assert(r == nil and l == 'L5' and poserr == 1) s = "unsigned int" r, l, poserr = g:match(s) -assert(r == nil and l == 5 and poserr == 1) - - - -local terror = { ['cmdSeq'] = "Missing ';' in CmdSeq", - ['ifExp'] = "Error in expresion of 'if'", - ['ifThen'] = "Error matching 'then' keyword", - ['ifThenCmdSeq'] = "Error matching CmdSeq of 'then' branch", - ['ifElseCmdSeq'] = "Error matching CmdSeq of 'else' branch", - ['ifEnd'] = "Error matching 'end' keyword of 'if'", - ['repeatCmdSeq'] = "Error matching CmdSeq of 'repeat'", - ['repeatUntil'] = "Error matching 'until' keyword", - ['repeatExp'] = "Error matching expression of 'until'", - ['assignOp'] = "Error matching ':='", - ['assignExp'] = "Error matching expression of assignment", - ['readName'] = "Error matching 'NAME' after 'read'", - ['writeExp'] = "Error matching expression after 'write'", - ['simpleExp'] = "Error matching 'SimpleExp'", - ['term'] = "Error matching 'Term'", - ['factor'] = "Error matching 'Factor'", - ['openParExp'] = "Error matching expression after '('", - ['closePar'] = "Error matching ')'", - ['undefined'] = "Undefined Error"} +assert(r == nil and l == 'L5' and poserr == 1) + + + +local terror = { cmdSeq = "Missing ';' in CmdSeq", + ifExp = "Error in expresion of 'if'", + ifThen = "Error matching 'then' keyword", + ifThenCmdSeq = "Error matching CmdSeq of 'then' branch", + ifElseCmdSeq = "Error matching CmdSeq of 'else' branch", + ifEnd = "Error matching 'end' keyword of 'if'", + repeatCmdSeq = "Error matching CmdSeq of 'repeat'", + repeatUntil = "Error matching 'until' keyword", + repeatExp = "Error matching expression of 'until'", + assignOp = "Error matching ':='", + assignExp = "Error matching expression of assignment", + readName = "Error matching 'NAME' after 'read'", + writeExp = "Error matching expression after 'write'", + simpleExp = "Error matching 'SimpleExp'", + term = "Error matching 'Term'", + factor = "Error matching 'Factor'", + openParExp = "Error matching expression after '('", + closePar = "Error matching ')'", + undefined = "Undefined Error"} g = re.compile([[ - Tiny <- CmdSeq //{1} '' -> cmdSeq //{2} '' -> ifExp //{3} '' -> ifThen //{4} '' -> ifThenCmdSeq - //{5} '' -> ifElseCmdSeq //{6} '' -> ifEnd //{7} '' -> repeatCmdSeq - //{8} '' -> repeatUntil //{9} '' -> repeatExp //{10} '' -> assignOp - //{11} '' -> assignExp //{12} '' -> readName //{13} '' -> writeExp - //{14} '' -> simpleExp //{15} '' -> term //{16} '' -> factor - //{17} '' -> openParExp //{18} '' -> closePar / '' -> undefined - CmdSeq <- (Cmd (SEMICOLON / %{1})) (Cmd (SEMICOLON / %{1}))* + Tiny <- CmdSeq + CmdSeq <- (Cmd SEMICOLON^cmdSeq) (Cmd SEMICOLON^cmdSeq)* Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd - IfCmd <- IF (Exp / %{2}) (THEN / %{3}) (CmdSeq / %{4}) (ELSE (CmdSeq / %{5}) / '') (END / %{6}) - RepeatCmd <- REPEAT (CmdSeq / %{7}) (UNTIL / %{8}) (Exp / %{9}) - AssignCmd <- NAME (ASSIGNMENT / %{10}) (Exp / %{11}) - ReadCmd <- READ (NAME / %{12}) - WriteCmd <- WRITE (Exp / %{13}) - Exp <- SimpleExp ((LESS / EQUAL) (SimpleExp / %{14}) / '') - SimpleExp <- Term ((ADD / SUB) (Term / %{15}))* - Term <- Factor ((MUL / DIV) (Factor / %{16}))* - Factor <- OPENPAR (Exp / %{17}) (CLOSEPAR / %{18}) / NUMBER / NAME + IfCmd <- IF Exp^ifExp THEN^ifThen CmdSeq^ifThenCmdSeq (ELSE CmdSeq^ifElseCmdSeq / '') END^ifEnd + RepeatCmd <- REPEAT CmdSeq^repeatCmdSeq UNTIL^repeatUntil Exp^repeatExp + AssignCmd <- NAME ASSIGNMENT^assignOp Exp^assignExp + ReadCmd <- READ NAME^readName + WriteCmd <- WRITE Exp^writeExp + Exp <- SimpleExp ((LESS / EQUAL) SimpleExp^simpleExp / '') + SimpleExp <- Term ((ADD / SUB) Term^term)* + Term <- Factor ((MUL / DIV) Factor^factor)* + Factor <- OPENPAR Exp^openParExp CLOSEPAR^closePar / NUMBER / NAME ADD <- Sp '+' ASSIGNMENT <- Sp ':=' CLOSEPAR <- Sp ')' @@ -724,7 +608,8 @@ assert(g:match(s) == #s + 1) -- a ';' is missing in 'read a' s = [[ read a]] -assert(g:match(s) == terror['cmdSeq']) +r, l, poserr = g:match(s) +assert(l == 'cmdSeq') -- a ';' is missing in 'n := n - 1' @@ -736,7 +621,8 @@ repeat n := n - 1 until (n < 1); write f;]] -assert(g:match(s) == terror['cmdSeq']) +r, l, poserr = g:match(s) +assert(l == 'cmdSeq') -- IF expression @@ -744,6 +630,7 @@ s = [[ if a then a := a + 1; end;]] assert(g:match(s) == #s + 1) + -- IF expression s = [[ if a then a := a + 1; else write 2; end;]] @@ -752,114 +639,136 @@ assert(g:match(s) == #s + 1) -- Error in expression of 'if'. 'A' is not a valida name s = [[ if A then a := a + 1; else write 2; end;]] -assert(g:match(s) == terror['ifExp']) +r, l, poserr = g:match(s) +assert(l == 'ifExp') -- Error matching the 'then' keyword s = [[ if a a := a + 1; else write 2; end;]] -assert(g:match(s) == terror['ifThen']) +r, l, poserr = g:match(s) +assert(l == 'ifThen') -- Error matching the CmdSeq inside of 'then' branch s = [[ if a then 3 := 2; else write 2; end;]] -assert(g:match(s) == terror['ifThenCmdSeq']) +r, l, poserr = g:match(s) +assert(l == 'ifThenCmdSeq') -- Error matching the CmdSeq inside of 'else' branch s = [[ if a then b := 2; else A := 2; end;]] -assert(g:match(s) == terror['ifElseCmdSeq']) +r, l, poserr = g:match(s) +assert(l == 'ifElseCmdSeq') -- Error matching 'end' of 'if' s = [[ if a then b := 2; else a := 2; 77;]] -assert(g:match(s) == terror['ifEnd']) +r, l, poserr = g:match(s) +assert(l == 'ifEnd') -- Error matching the CmdSeq of 'repeat' s = [[repeat F := f * n; n := n - 1; until (n < 1);]] -assert(g:match(s) == terror['repeatCmdSeq']) +r, l, poserr = g:match(s) +assert(l == 'repeatCmdSeq') -- Error matching 'until' s = [[repeat f := f * n; n := n - 1; 88 (n < 1);]] -assert(g:match(s) == terror['repeatUntil']) +r, l, poserr = g:match(s) +assert(l == 'repeatUntil') -- Error matching expression of 'until' s = [[repeat f := f * n; n := n - 1; until ; (n < 1);]] -assert(g:match(s) == terror['repeatExp']) +r, l, poserr = g:match(s) +assert(l == 'repeatExp') -- Error matching ':=' s = [[ f = f * n;]] -assert(g:match(s) == terror['assignOp']) +r, l, poserr = g:match(s) +assert(l == 'assignOp') -- Error matching expression of assignment s = [[ f := A * n;]] -assert(g:match(s) == terror['assignExp']) +r, l, poserr = g:match(s) +assert(l == 'assignExp') -- Error matching 'name' s = [[ read 2;]] -assert(g:match(s) == terror['readName']) +r, l, poserr = g:match(s) +assert(l == 'readName') -- Error matching expression after 'write' s = [[ write [a] := 2;]] -assert(g:match(s) == terror['writeExp']) +r, l, poserr = g:match(s) +assert(l == 'writeExp') -- Error matching 'SimpleExp' s = [[ a := a < A;]] -assert(g:match(s) == terror['simpleExp']) +r, l, poserr = g:match(s) +assert(l == 'simpleExp') -- Error matching 'Term' s = [[ a := a + A;]] -assert(g:match(s) == terror['term']) +r, l, poserr = g:match(s) +assert(l == 'term') -- Error matching 'Factor' s = [[ a := a * A;]] -assert(g:match(s) == terror['factor']) +r, l, poserr = g:match(s) +assert(l == 'factor') -- Error matching expression after '(' s = [[ a := (A);]] -assert(g:match(s) == terror['openParExp']) +r, l, poserr = g:match(s) +assert(l == 'openParExp') -- Error matching ')' s = [[ a := (a];]] -assert(g:match(s) == terror['closePar']) +r, l, poserr = g:match(s) +assert(l == 'closePar') -- Error undefined s = [[ A := a;]] -assert(g:match(s) == terror['undefined']) +r, l, poserr = g:match(s) +assert(l == 'fail') print("+") -p = m.Rec("a", "b", 3) +p = m.P{ + "A", + A = m.P"a", + B = m.P"b" +} assert(p:match("a") == 2) checklabeq({nil, 0, 1}, p:match("b")) checklabeq({nil, 0, 1}, p:match("c")) -p = m.Rec(m.T(3), "b", 1) -checklabeq({nil, 3, 1}, p:match("a")) -checklabeq({nil, 3, 1}, p:match("b")) - -p = m.Rec(m.T(3), "b", 3) -checklabeq({nil, 0, 1}, p:match("a")) +p = m.P{ + "A", + A = m.T"B", + B = m.P"b" +} +checklabeq({nil, 'fail', 1}, p:match("a")) assert(p:match("b") == 2) --[[ @@ -869,9 +778,10 @@ C -> c+ ]] g = m.P{ "S", - S = m.Rec(m.V"A", (-m.P"c" * m.P(1))^0, 128) * m.V"C", - A = m.P"a"^0 * "b" + m.T(128), + S = m.V"A" * m.V"C", + A = m.P"a"^0 * "b" + m.T'Rec', C = m.P"c"^1, + Rec = (-m.P"c" * m.P(1))^0 } assert(g:match("abc") == 4) @@ -892,9 +802,10 @@ C -> c+ ]] g = m.P{ "S", - S = m.Rec(m.V"A", (-m.P"c" * m.P(1))^0, 99) * m.V"C", - A = m.P"a"^1 * ("b" + m.T(99)), + S = m.V"A" * m.V"C", + A = m.P"a"^1 * ("b" + m.T'Rec'), C = m.P"c"^1, + Rec = (-m.P"c" * m.P(1))^0 } assert(g:match("abc") == 4) @@ -916,34 +827,24 @@ lpeg = m local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V local C, Cc, Ct, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt -local T, Lc = lpeg.T, lpeg.Lc +local T = lpeg.T local labels = { - {"NoExp", "no expression found"}, - {"Extra", "extra characters found after the expression"}, - {"ExpTerm", "expected a term after the operator"}, - {"ExpExp", "expected an expression after the parenthesis"}, - {"MisClose", "missing a closing ')' after the expression"}, + NoExp = "no expression found", + Extra = "extra characters found after the expression", + ExpTerm = "expected a term after the operator", + ExpExp = "expected an expression after the parenthesis", + MisClose = "missing a closing ')' after the expression", } -local function labelindex(labname) - for i, elem in ipairs(labels) do - if elem[1] == labname then - return i - end - end - error("could not find label: " .. labname) -end - local errors = {} local function expect(patt, labname) - local i = labelindex(labname) function recorderror(input, pos) - table.insert(errors, {i, pos}) + table.insert(errors, {labname, pos}) return true end - return patt + Cmt("", recorderror) * T(i) + return patt + Cmt("", recorderror) * T(labname) end local num = R("09")^1 / tonumber @@ -969,12 +870,14 @@ end local g = P { "Exp", - Exp = Ct(V"Term" * (C(op) * V"OpRecov")^0) / compute; - OpRecov = m.Rec(V"Operand", Cc(0), labelindex("ExpTerm")); + Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute; Operand = expect(V"Term", "ExpTerm"); Term = num + V"Group"; - Group = "(" * V"InnerExp" * m.Rec(expect(")", "MisClose"), P"", labelindex("MisClose")); - InnerExp = m.Rec(expect(V"Exp", "ExpExp"), (P(1) - ")")^0 * Cc(0), labelindex("ExpExp")); + Group = "(" * V"InnerExp" * expect(")", "MisClose"); + InnerExp = expect(V"Exp", "ExpExp"); + ExpTerm = Cc(0); + MisClose = P""; + ExpExp = (P(1) - ")")^0 * Cc(0); } g = expect(g, "NoExp") * expect(-P(1), "Extra") @@ -987,7 +890,7 @@ local function eval(input) local out = {} for i, err in ipairs(errors) do local pos = err[2] - local msg = labels[err[1]][2] + local msg = labels[err[1]] table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") end errors = {} @@ -1022,51 +925,66 @@ assert(e == nil and msg == "syntax error: expected a term after the operator (at print("+") -local g = m.P{ - "S", - S = V"End" + V'A' * V'S', - A = P'a' + T(1), - End = P"." * (-P(1) + T(2)), +local g1 = { + "S1", + S1 = V"End" + V'A' * V'S1', + A = P'a' + T'Ea', + End = P"." * (-P(1) + T'Eend'), } -assert(g:match("a.") == 3) -assert(g:match("aa.") == 4) -assert(g:match(".") == 2) -checklabeq({nil, 1, 1}, g:match("ba.")) -checklabeq({nil, 1, 2}, g:match("aba.")) -checklabeq({nil, 1, 1}, g:match("cba.")) -checklabeq({nil, 2, 3}, g:match("a.a")) +local p1 = m.P(g1) +assert(p1:match("a.") == 3) +assert(p1:match("aa.") == 4) +assert(p1:match(".") == 2) +checklabeq({nil, 'Ea', 1}, p1:match("ba.")) +checklabeq({nil, 'Ea', 2}, p1:match("aba.")) +checklabeq({nil, 'Ea', 1}, p1:match("cba.")) +checklabeq({nil, 'Eend', 3}, p1:match("a.a")) -local g2 = m.P{ - "S", - S = m.Rec(g, V"B", 1), - B = P'b'^1 + T(3) +local g2 = { + "S2", + S2 = m.V"S1", + Ea = P'b'^1 + T'Eb', } -assert(g2:match("a.") == 3) -assert(g2:match("aa.") == 4) -assert(g2:match(".") == 2) -assert(g2:match("ba.") == 4) -assert(g2:match("aba.") == 5) -checklabeq({nil, 3, 1}, g2:match("cba.")) -checklabeq({nil, 2, 3}, g2:match("a.a")) +g1[1] = nil -- start rule +for k, v in pairs(g1) do + g2[k] = v +end -local g3 = m.P{ - "S", - S = m.Rec(g2, V"C", 2, 3), - C = P'c'^1 + T(4) +local p2 = m.P(g2) + +assert(p2:match("a.") == 3) +assert(p2:match("aa.") == 4) +assert(p2:match(".") == 2) +assert(p2:match("ba.") == 4) +assert(p2:match("aba.") == 5) +checklabeq({nil, 'Eb', 1}, p2:match("cba.")) +checklabeq({nil, 'Eend', 3}, p2:match("a.a")) + +local g3 = { + "S3", + S3 = V'S2', + Eb = P'c'^1 + T'Ec', + Eend = V'Eb', } +g2[1] = nil -- start rule +for k, v in pairs(g2) do + g3[k] = v +end + +local p3 = m.P(g3) -assert(g3:match("a.") == 3) -assert(g3:match("aa.") == 4) -assert(g3:match(".") == 2) -assert(g3:match("ba.") == 4) -assert(g3:match("aba.") == 5) -assert(g3:match("cba.") == 5) -checklabeq({nil, 4, 3}, g3:match("a.a")) -checklabeq({nil, 4, 1}, g3:match("dc")) -checklabeq({nil, 4, 2}, g3:match(".d")) +assert(p3:match("a.") == 3) +assert(p3:match("aa.") == 4) +assert(p3:match(".") == 2) +assert(p3:match("ba.") == 4) +assert(p3:match("aba.") == 5) +assert(p3:match("cba.") == 5) +checklabeq({nil, 'Ec', 3}, p3:match("a.a")) +checklabeq({nil, 'Ec', 1}, p3:match("dc")) +checklabeq({nil, 'Ec', 2}, p3:match(".d")) -- testing more captures @@ -1078,7 +996,11 @@ local g = re.compile[[ checkeq({"523", "624", "346", "888"} , {g:match("523 624 346\n888")}) checkeq({nil, 5, 4}, {g:match("44 a 123")}) -local g2 = m.Rec(g, ((-m.R("09") * m.P(1))^0) / "58", 5) +local g2 = re.compile[[ + S <- ( %s* &. {A} )* + A <- [0-9]+ / %{Rec} + Rec <- ((![0-9] .)*) -> "58" +]] checkeq({"523", "624", "346", "888"} , {g2:match("523 624 346\n888")}) checkeq({"44", "a ", "58", "123"}, {g2:match("44 a 123")}) @@ -1092,7 +1014,11 @@ local g = re.compile[[ checkeq({"523", "624", "346", "888"} , {g:match("523 624 346\n888")}) checkeq({nil, 5, 4}, {g:match("44 a 123")}) -local g2 = m.Rec(g, ((-m.R("09") * m.P(1))^0) / "58", 5) +local g2 = re.compile[[ + S <- ( %s* &. A )* + A <- {[0-9]+} / %{Rec} + Rec <- ((![0-9] .)*) -> "58" +]] checkeq({"523", "624", "346", "888"} , {g2:match("523 624 346\n888")}) checkeq({"44", "58", "123"}, {g2:match("44 a 123")}) @@ -1100,7 +1026,7 @@ checkeq({"44", "58", "123"}, {g2:match("44 a 123")}) local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V local C, Cc, Ct, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt -local T, Lc, Rec = lpeg.T, lpeg.Lc, lpeg.Rec +local T = lpeg.T local labels = { {"NoExp", "no expression found"}, @@ -1110,26 +1036,16 @@ local labels = { {"MisClose", "missing a closing ')' after the expression"}, } -local function labelindex(labname) - for i, elem in ipairs(labels) do - if elem[1] == labname then - return i - end - end - error("could not find label: " .. labname) -end - local errors = {} local function expect(patt, labname, recpatt) - local i = labelindex(labname) function recorderror(input, pos) - table.insert(errors, {i, pos}) + table.insert(errors, {labname, pos}) return true end if not recpatt then recpatt = P"" end --return Rec(patt, Cmt("", recorderror) * recpatt) - return patt + T(i) + return patt + T(labname) end local num = R("09")^1 / tonumber @@ -1155,22 +1071,22 @@ end local g = P { -"Exp", -Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute, -Operand = expect(V"Term", "ExpTerm"), -Term = num, + "Exp", + Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute, + Operand = expect(V"Term", "ExpTerm"), + Term = num, + ExpTerm = Cc(3) } -local rg = Rec(g, Cc(3), labelindex("ExpTerm")) local function eval(input) - local result, label, suffix = rg:match(input) + local result, label, suffix = g:match(input) if #errors == 0 then return result else local out = {} for i, err in ipairs(errors) do local pos = err[2] - local msg = labels[err[1]][2] + local msg = labels[err[1]] table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") end errors = {} @@ -1184,218 +1100,4 @@ assert(eval("98-76*54/32") == 37.125) assert(eval("1+") == 4) --> syntax error: expected a term after the operator (at index 3) - --- tests related to the use of '^' in relabel to throw labels -local errinfo = { - { 'cmdSeq', "Missing ';' in CmdSeq"}, - { 'ifExp', "Error in expresion of 'if'"}, - { 'ifThen', "Error matching 'then' keyword"}, - { 'ifThenCmdSeq', "Error matching CmdSeq of 'then' branch"}, - { 'ifElseCmdSeq', "Error matching CmdSeq of 'else' branch"}, - { 'ifEnd', "Error matching 'end' keyword of 'if'"}, - { 'repeatCmdSeq', "Error matching CmdSeq of 'repeat'"}, - { 'repeatUntil', "Error matching 'until' keyword"}, - { 'repeatExp', "Error matching expression of 'until'"}, - { 'assignOp', "Error matching ':='"}, - { 'assignExp', "Error matching expression of assignment"}, - { 'readName', "Error matching 'NAME' after 'read'"}, - { 'writeExp', "Error matching expression after 'write'"}, - { 'simpleExp', "Error matching 'SimpleExp'"}, - { 'term', "Error matching 'Term'"}, - { 'factor', "Error matching 'Factor'"}, - { 'openParExp', "Error matching expression after '('"}, - { 'closePar', "Error matching ')'"}, - { 'undefined', "Undefined Error" } -} - - -local errmsgs = {} -local labels = {} - -for i, err in ipairs(errinfo) do - errmsgs[i] = err[2] - labels[err[1]] = i -end - -re.setlabels(labels) - -g = re.compile([[ - Tiny <- CmdSeq - CmdSeq <- (Cmd SEMICOLON^cmdSeq) (Cmd SEMICOLON^cmdSeq)* - Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd - IfCmd <- IF Exp^ifExp THEN^ifThen CmdSeq^ifThenCmdSeq (ELSE CmdSeq^ifElseCmdSeq / '') END^ifEnd - RepeatCmd <- REPEAT CmdSeq^repeatCmdSeq UNTIL^repeatUntil Exp^repeatExp - AssignCmd <- NAME ASSIGNMENT^assignOp Exp^assignExp - ReadCmd <- READ NAME^readName - WriteCmd <- WRITE Exp^writeExp - Exp <- SimpleExp ((LESS / EQUAL) SimpleExp^simpleExp / '') - SimpleExp <- Term ((ADD / SUB) Term^term)* - Term <- Factor ((MUL / DIV) Factor^factor)* - Factor <- OPENPAR Exp^openParExp CLOSEPAR^closePar / NUMBER / NAME - ADD <- Sp '+' - ASSIGNMENT <- Sp ':=' - CLOSEPAR <- Sp ')' - DIV <- Sp '/' - IF <- Sp 'if' - ELSE <- Sp 'else' - END <- Sp 'end' - EQUAL <- Sp '=' - LESS <- Sp '<' - MUL <- Sp '*' - NAME <- !RESERVED Sp [a-z]+ - NUMBER <- Sp [0-9]+ - OPENPAR <- Sp '(' - READ <- Sp 'read' - REPEAT <- Sp 'repeat' - SEMICOLON <- Sp ';' - SUB <- Sp '-' - THEN <- Sp 'then' - UNTIL <- Sp 'until' - WRITE <- Sp 'write' - RESERVED <- (IF / ELSE / END / READ / REPEAT / THEN / UNTIL / WRITE) ![a-z]+ - Sp <- (%s / %nl)* -]]) - -s = [[ -n := 5;]] -assert(g:match(s) == #s + 1) - -s = [[ -n := 5; -f := 1; -repeat - f := f * n; - n := n - 1; -until (n < 1); -write f;]] -assert(g:match(s) == #s + 1) - --- a ';' is missing in 'read a' -s = [[ -read a]] -assert(table.pack(g:match(s))[2] == labels['cmdSeq']) - - --- a ';' is missing in 'n := n - 1' -s = [[ -n := 5; -f := 1; -repeat - f := f * n; - n := n - 1 -until (n < 1); -write f;]] -assert(table.pack(g:match(s))[2] == labels['cmdSeq']) - - --- IF expression -s = [[ -if a then a := a + 1; end;]] -assert(g:match(s) == #s + 1) - --- IF expression -s = [[ -if a then a := a + 1; else write 2; end;]] -assert(g:match(s) == #s + 1) - --- Error in expression of 'if'. 'A' is not a valida name -s = [[ -if A then a := a + 1; else write 2; end;]] -assert(table.pack(g:match(s))[2] == labels['ifExp']) - --- Error matching the 'then' keyword -s = [[ -if a a := a + 1; else write 2; end;]] -assert(table.pack(g:match(s))[2] == labels['ifThen']) - --- Error matching the CmdSeq inside of 'then' branch -s = [[ -if a then 3 := 2; else write 2; end;]] -assert(table.pack(g:match(s))[2] == labels['ifThenCmdSeq']) - --- Error matching the CmdSeq inside of 'else' branch -s = [[ -if a then b := 2; else A := 2; end;]] -assert(table.pack(g:match(s))[2] == labels['ifElseCmdSeq']) - --- Error matching 'end' of 'if' -s = [[ -if a then b := 2; else a := 2; 77;]] -assert(table.pack(g:match(s))[2] == labels['ifEnd']) - --- Error matching the CmdSeq of 'repeat' -s = [[repeat - F := f * n; - n := n - 1; -until (n < 1);]] -assert(table.pack(g:match(s))[2] == labels['repeatCmdSeq']) - --- Error matching 'until' -s = [[repeat - f := f * n; - n := n - 1; -88 (n < 1);]] -assert(table.pack(g:match(s))[2] == labels['repeatUntil']) - --- Error matching expression of 'until' -s = [[repeat - f := f * n; - n := n - 1; -until ; (n < 1);]] -assert(table.pack(g:match(s))[2] == labels['repeatExp']) - --- Error matching ':=' -s = [[ -f = f * n;]] -assert(table.pack(g:match(s))[2] == labels['assignOp']) - --- Error matching expression of assignment -s = [[ -f := A * n;]] -assert(table.pack(g:match(s))[2] == labels['assignExp']) - --- Error matching 'name' -s = [[ -read 2;]] -assert(table.pack(g:match(s))[2] == labels['readName']) - --- Error matching expression after 'write' -s = [[ -write [a] := 2;]] -assert(table.pack(g:match(s))[2] == labels['writeExp']) - --- Error matching 'SimpleExp' -s = [[ -a := a < A;]] -assert(table.pack(g:match(s))[2] == labels['simpleExp']) - --- Error matching 'Term' -s = [[ -a := a + A;]] -assert(table.pack(g:match(s))[2] == labels['term']) - --- Error matching 'Factor' -s = [[ -a := a * A;]] -assert(table.pack(g:match(s))[2] == labels['factor']) - --- Error matching expression after '(' -s = [[ -a := (A);]] -assert(table.pack(g:match(s))[2] == labels['openParExp']) - --- Error matching ')' -s = [[ -a := (a];]] -assert(table.pack(g:match(s))[2] == labels['closePar']) - --- Error undefined -s = [[ -A := a;]] -assert(table.pack(g:match(s))[2] == 0) - - - - - print("OK") -- cgit v1.2.3-55-g6feb