diff options
author | Sergio Queiroz <sqmedeiros@gmail.com> | 2016-11-10 16:26:11 -0300 |
---|---|---|
committer | Sergio Queiroz <sqmedeiros@gmail.com> | 2016-11-10 16:26:11 -0300 |
commit | fd28f9d9e54f33bf7ae3a5e12dc71478f9c91aea (patch) | |
tree | 875ab38000e52376583bc13741b18701c6294f80 | |
parent | d84dd6b3659f94b09e67eb90a10e71eb05c5630e (diff) | |
download | lpeglabel-fd28f9d9e54f33bf7ae3a5e12dc71478f9c91aea.tar.gz lpeglabel-fd28f9d9e54f33bf7ae3a5e12dc71478f9c91aea.tar.bz2 lpeglabel-fd28f9d9e54f33bf7ae3a5e12dc71478f9c91aea.zip |
Removing labeled choice, updating testlabel, and disabling an optmization related to Throw
-rw-r--r-- | lpcode.c | 39 | ||||
-rw-r--r-- | lpprint.c | 4 | ||||
-rw-r--r-- | lptree.c | 23 | ||||
-rw-r--r-- | lptree.h | 2 | ||||
-rw-r--r-- | lpvm.c | 11 | ||||
-rw-r--r-- | lpvm.h | 1 | ||||
-rw-r--r-- | relabelrec.lua (renamed from relabel.lua) | 101 | ||||
-rwxr-xr-x | test.lua | 2 | ||||
-rw-r--r-- | testlabel.lua | 453 |
9 files changed, 348 insertions, 288 deletions
@@ -196,7 +196,7 @@ int checkaux (TTree *tree, int pred) { | |||
196 | if (checkaux(sib2(tree), pred)) return 1; | 196 | if (checkaux(sib2(tree), pred)) return 1; |
197 | /* else return checkaux(sib1(tree), pred); */ | 197 | /* else return checkaux(sib1(tree), pred); */ |
198 | tree = sib1(tree); goto tailcall; | 198 | tree = sib1(tree); goto tailcall; |
199 | case TLabChoice: case TRecov: /* labeled failure */ | 199 | case TRecov: /* labeled failure */ |
200 | /* we do not know whether sib2 will be evaluated */ | 200 | /* we do not know whether sib2 will be evaluated */ |
201 | tree = sib1(tree); goto tailcall; | 201 | tree = sib1(tree); goto tailcall; |
202 | case TCapture: case TGrammar: case TRule: | 202 | case TCapture: case TGrammar: case TRule: |
@@ -218,10 +218,10 @@ int fixedlenx (TTree *tree, int count, int len) { | |||
218 | switch (tree->tag) { | 218 | switch (tree->tag) { |
219 | case TChar: case TSet: case TAny: | 219 | case TChar: case TSet: case TAny: |
220 | return len + 1; | 220 | return len + 1; |
221 | case TFalse: case TTrue: case TNot: case TAnd: case TBehind: | 221 | case TFalse: case TTrue: case TNot: case TAnd: case TBehind: |
222 | case TThrow: /* labeled failure */ | ||
223 | return len; | 222 | return len; |
224 | case TRep: case TRunTime: case TOpenCall: | 223 | case TRep: case TRunTime: case TOpenCall: |
224 | case TThrow: /* labeled failure */ | ||
225 | return -1; | 225 | return -1; |
226 | case TCapture: case TRule: case TGrammar: | 226 | case TCapture: case TRule: case TGrammar: |
227 | /* return fixedlenx(sib1(tree), count); */ | 227 | /* return fixedlenx(sib1(tree), count); */ |
@@ -237,7 +237,7 @@ int fixedlenx (TTree *tree, int count, int len) { | |||
237 | /* else return fixedlenx(sib2(tree), count, len); */ | 237 | /* else return fixedlenx(sib2(tree), count, len); */ |
238 | tree = sib2(tree); goto tailcall; | 238 | tree = sib2(tree); goto tailcall; |
239 | } | 239 | } |
240 | case TChoice: case TLabChoice: { /* labeled failure */ | 240 | case TChoice: { |
241 | int n1, n2; | 241 | int n1, n2; |
242 | n1 = fixedlenx(sib1(tree), count, len); | 242 | n1 = fixedlenx(sib1(tree), count, len); |
243 | if (n1 < 0) return -1; | 243 | if (n1 < 0) return -1; |
@@ -287,7 +287,7 @@ static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) { | |||
287 | loopset(i, firstset->cs[i] = follow->cs[i]); /* follow = fullset(?) */ | 287 | loopset(i, firstset->cs[i] = follow->cs[i]); /* follow = fullset(?) */ |
288 | return 1; | 288 | return 1; |
289 | } | 289 | } |
290 | case TChoice: case TLabChoice: { /*(?) labeled failure */ | 290 | case TChoice: { |
291 | Charset csaux; | 291 | Charset csaux; |
292 | int e1 = getfirst(sib1(tree), follow, firstset); | 292 | int e1 = getfirst(sib1(tree), follow, firstset); |
293 | int e2 = getfirst(sib2(tree), follow, &csaux); | 293 | int e2 = getfirst(sib2(tree), follow, &csaux); |
@@ -378,7 +378,7 @@ static int headfail (TTree *tree) { | |||
378 | if (!nofail(sib2(tree))) return 0; | 378 | if (!nofail(sib2(tree))) return 0; |
379 | /* else return headfail(sib1(tree)); */ | 379 | /* else return headfail(sib1(tree)); */ |
380 | tree = sib1(tree); goto tailcall; | 380 | tree = sib1(tree); goto tailcall; |
381 | case TChoice: case TLabChoice: case TRecov: /* labeled failure */ | 381 | case TChoice: case TRecov: /* labeled failure */ |
382 | if (!headfail(sib1(tree))) return 0; | 382 | if (!headfail(sib1(tree))) return 0; |
383 | /* else return headfail(sib2(tree)); */ | 383 | /* else return headfail(sib2(tree)); */ |
384 | tree = sib2(tree); goto tailcall; | 384 | tree = sib2(tree); goto tailcall; |
@@ -398,7 +398,7 @@ static int needfollow (TTree *tree) { | |||
398 | case TChar: case TSet: case TAny: | 398 | case TChar: case TSet: case TAny: |
399 | case TFalse: case TTrue: case TAnd: case TNot: | 399 | case TFalse: case TTrue: case TAnd: case TNot: |
400 | case TRunTime: case TGrammar: case TCall: case TBehind: | 400 | case TRunTime: case TGrammar: case TCall: case TBehind: |
401 | case TThrow: case TLabChoice: case TRecov: /* (?)labeled failure */ | 401 | case TThrow: case TRecov: /* (?)labeled failure */ |
402 | return 0; | 402 | return 0; |
403 | case TChoice: case TRep: | 403 | case TChoice: case TRep: |
404 | return 1; | 404 | return 1; |
@@ -433,7 +433,7 @@ int sizei (const Instruction *i) { | |||
433 | return 2; | 433 | return 2; |
434 | case IThrow: /* labeled failure */ | 434 | case IThrow: /* labeled failure */ |
435 | return 1; | 435 | return 1; |
436 | case ILabChoice: case IRecov: | 436 | case IRecov: |
437 | return (CHARSETINSTSIZE - 1) + 2; /* labeled failure */ | 437 | return (CHARSETINSTSIZE - 1) + 2; /* labeled failure */ |
438 | default: return 1; | 438 | default: return 1; |
439 | } | 439 | } |
@@ -499,7 +499,7 @@ static int addoffsetinst (CompileState *compst, Opcode op) { | |||
499 | int i = addinstruction(compst, op, 0); /* instruction */ | 499 | int i = addinstruction(compst, op, 0); /* instruction */ |
500 | addinstruction(compst, (Opcode)0, 0); /* open space for offset */ | 500 | addinstruction(compst, (Opcode)0, 0); /* open space for offset */ |
501 | assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2 || | 501 | assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2 || |
502 | op == IRecov || op == ILabChoice); /* labeled failure */ | 502 | op == IRecov); /* labeled failure */ |
503 | return i; | 503 | return i; |
504 | } | 504 | } |
505 | 505 | ||
@@ -707,21 +707,6 @@ static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt, | |||
707 | 707 | ||
708 | 708 | ||
709 | /* labeled failure begin */ | 709 | /* labeled failure begin */ |
710 | static void codelabchoice (CompileState *compst, TTree *p1, TTree *p2, int opt, | ||
711 | const Charset *fl, const byte *cs) { | ||
712 | int emptyp2 = (p2->tag == TTrue); | ||
713 | int pcommit; | ||
714 | int test = NOINST; | ||
715 | int pchoice = addoffsetinst(compst, ILabChoice); | ||
716 | addcharset(compst, cs); | ||
717 | codegen(compst, p1, emptyp2, test, fullset); | ||
718 | pcommit = addoffsetinst(compst, ICommit); | ||
719 | jumptohere(compst, pchoice); | ||
720 | jumptohere(compst, test); | ||
721 | codegen(compst, p2, opt, NOINST, fl); | ||
722 | jumptohere(compst, pcommit); | ||
723 | } | ||
724 | |||
725 | static void coderecovery (CompileState *compst, TTree *p1, TTree *p2, int opt, | 710 | static void coderecovery (CompileState *compst, TTree *p1, TTree *p2, int opt, |
726 | const Charset *fl, const byte *cs) { | 711 | const Charset *fl, const byte *cs) { |
727 | int emptyp2 = (p2->tag == TTrue); | 712 | int emptyp2 = (p2->tag == TTrue); |
@@ -970,10 +955,6 @@ static void codegen (CompileState *compst, TTree *tree, int opt, int tt, | |||
970 | addinstruction(compst, IThrow, (byte) tree->u.label); | 955 | addinstruction(compst, IThrow, (byte) tree->u.label); |
971 | break; | 956 | break; |
972 | } | 957 | } |
973 | case TLabChoice: { /* labeled failure */ | ||
974 | codelabchoice(compst, sib1(tree), sib2(tree), opt, fl, treelabelset(tree)); | ||
975 | break; | ||
976 | } | ||
977 | case TRecov: { /* labeled failure */ | 958 | case TRecov: { /* labeled failure */ |
978 | coderecovery(compst, sib1(tree), sib2(tree), opt, fl, treelabelset(tree)); | 959 | coderecovery(compst, sib1(tree), sib2(tree), opt, fl, treelabelset(tree)); |
979 | break; | 960 | break; |
@@ -1000,7 +981,7 @@ static void peephole (CompileState *compst) { | |||
1000 | switch (code[i].i.code) { | 981 | switch (code[i].i.code) { |
1001 | case IChoice: case ICall: case ICommit: case IPartialCommit: | 982 | case IChoice: case ICall: case ICommit: case IPartialCommit: |
1002 | case IBackCommit: case ITestChar: case ITestSet: | 983 | case IBackCommit: case ITestChar: case ITestSet: |
1003 | case ILabChoice: case IRecov: /* labeled failure */ | 984 | case IRecov: /* labeled failure */ |
1004 | case ITestAny: { /* instructions with labels */ | 985 | case ITestAny: { /* instructions with labels */ |
1005 | jumptothere(compst, i, finallabel(code, i)); /* optimize label */ | 986 | jumptothere(compst, i, finallabel(code, i)); /* optimize label */ |
1006 | break; | 987 | break; |
@@ -112,7 +112,7 @@ void printinst (const Instruction *op, const Instruction *p) { | |||
112 | printf("%d", p->i.aux); | 112 | printf("%d", p->i.aux); |
113 | break; | 113 | break; |
114 | } | 114 | } |
115 | case ILabChoice: case IRecov: { /* labeled failure */ | 115 | case IRecov: { /* labeled failure */ |
116 | printjmp(op, p); | 116 | printjmp(op, p); |
117 | printcharset((p+2)->buff); | 117 | printcharset((p+2)->buff); |
118 | break; | 118 | break; |
@@ -223,7 +223,7 @@ void printtree (TTree *tree, int ident) { | |||
223 | default: { | 223 | default: { |
224 | int sibs = numsiblings[tree->tag]; | 224 | int sibs = numsiblings[tree->tag]; |
225 | printf("\n"); | 225 | printf("\n"); |
226 | if (tree->tag == TLabChoice || tree->tag == TRecov) { /* labeled failure */ | 226 | if (tree->tag == TRecov) { /* labeled failure */ |
227 | printcharset(treelabelset(tree)); | 227 | printcharset(treelabelset(tree)); |
228 | printf("\n"); | 228 | printf("\n"); |
229 | } | 229 | } |
@@ -723,37 +723,25 @@ static int lp_behind (lua_State *L) { | |||
723 | */ | 723 | */ |
724 | static int lp_throw (lua_State *L) { | 724 | static int lp_throw (lua_State *L) { |
725 | int label = luaL_checkinteger(L, -1); | 725 | int label = luaL_checkinteger(L, -1); |
726 | luaL_argcheck(L, label >= 0 && label < MAXLABELS, -1, "the number of a label must be between 0 and 255"); | 726 | luaL_argcheck(L, label >= 1 && label < MAXLABELS, -1, "the number of a label must be between 1 and 255"); |
727 | newthrowleaf(L, label); | 727 | newthrowleaf(L, label); |
728 | return 1; | 728 | return 1; |
729 | } | 729 | } |
730 | 730 | ||
731 | /* | 731 | /* |
732 | ** labeled choice function | 732 | ** labeled recovery function |
733 | */ | 733 | */ |
734 | static int lp_labchoice (lua_State *L) { | ||
735 | int n = lua_gettop(L); | ||
736 | TTree *tree = newrootlab2sib(L, TLabChoice); | ||
737 | int i; | ||
738 | for (i = 3; i <= n; i++) { | ||
739 | int d = luaL_checkinteger(L, i); | ||
740 | luaL_argcheck(L, d >= 0 && d < MAXLABELS, i, "the number of a label must be between 0 and 255"); | ||
741 | setlabel(treelabelset(tree), (byte)d); | ||
742 | } | ||
743 | return 1; | ||
744 | } | ||
745 | |||
746 | |||
747 | static int lp_recovery (lua_State *L) { | 734 | static int lp_recovery (lua_State *L) { |
748 | int n = lua_gettop(L); | 735 | int n = lua_gettop(L); |
749 | TTree *tree = newrootlab2sib(L, TRecov); | 736 | TTree *tree = newrootlab2sib(L, TRecov); |
737 | luaL_argcheck(L, n >= 3, 3, "non-nil value expected"); | ||
750 | if (n == 2) { /* catches fail as default */ | 738 | if (n == 2) { /* catches fail as default */ |
751 | /*setlabel(treelabelset(tree), LFAIL); recovery does not catch regular fail */ | 739 | /*setlabel(treelabelset(tree), LFAIL); recovery does not catch regular fail */ |
752 | } else { | 740 | } else { |
753 | int i; | 741 | int i; |
754 | for (i = 3; i <= n; i++) { | 742 | for (i = 3; i <= n; i++) { |
755 | int d = luaL_checkinteger(L, i); | 743 | int d = luaL_checkinteger(L, i); |
756 | luaL_argcheck(L, d >= 0 && d < MAXLABELS, i, "the number of a label must be between 0 and 255"); | 744 | luaL_argcheck(L, d >= 1 && d < MAXLABELS, i, "the number of a label must be between 1 and 255"); |
757 | setlabel(treelabelset(tree), (byte)d); | 745 | setlabel(treelabelset(tree), (byte)d); |
758 | } | 746 | } |
759 | } | 747 | } |
@@ -1089,7 +1077,7 @@ static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed, | |||
1089 | return nb; | 1077 | return nb; |
1090 | /* else return verifyrule(L, sib2(tree), passed, npassed, nb); */ | 1078 | /* else return verifyrule(L, sib2(tree), passed, npassed, nb); */ |
1091 | tree = sib2(tree); goto tailcall; | 1079 | tree = sib2(tree); goto tailcall; |
1092 | case TChoice: case TLabChoice: case TRecov: /* must check both children */ /* labeled failure */ | 1080 | case TChoice: case TRecov: /* must check both children */ /* labeled failure */ |
1093 | nb = verifyrule(L, sib1(tree), passed, npassed, nb); | 1081 | nb = verifyrule(L, sib1(tree), passed, npassed, nb); |
1094 | /* return verifyrule(L, sib2(tree), passed, npassed, nb); */ | 1082 | /* return verifyrule(L, sib2(tree), passed, npassed, nb); */ |
1095 | tree = sib2(tree); goto tailcall; | 1083 | tree = sib2(tree); goto tailcall; |
@@ -1342,7 +1330,6 @@ static struct luaL_Reg pattreg[] = { | |||
1342 | {"setmaxstack", lp_setmax}, | 1330 | {"setmaxstack", lp_setmax}, |
1343 | {"type", lp_type}, | 1331 | {"type", lp_type}, |
1344 | {"T", lp_throw}, /* labeled failure throw */ | 1332 | {"T", lp_throw}, /* labeled failure throw */ |
1345 | {"Lc", lp_labchoice}, /* labeled failure choice */ | ||
1346 | {"Rec", lp_recovery}, /* labeled failure choice */ | 1333 | {"Rec", lp_recovery}, /* labeled failure choice */ |
1347 | {NULL, NULL} | 1334 | {NULL, NULL} |
1348 | }; | 1335 | }; |
@@ -25,7 +25,7 @@ typedef enum TTag { | |||
25 | TBehind, /* match behind */ | 25 | TBehind, /* match behind */ |
26 | TCapture, /* regular capture */ | 26 | TCapture, /* regular capture */ |
27 | TRunTime, /* run-time capture */ | 27 | TRunTime, /* run-time capture */ |
28 | TThrow, TLabChoice, TRecov /* labeled failure */ | 28 | TThrow, TRecov /* labeled failure */ |
29 | } TTag; | 29 | } TTag; |
30 | 30 | ||
31 | /* number of siblings for each tree */ | 31 | /* number of siblings for each tree */ |
@@ -274,17 +274,6 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e, | |||
274 | p += 2; | 274 | p += 2; |
275 | continue; | 275 | continue; |
276 | } | 276 | } |
277 | case ILabChoice: { /* labeled failure */ | ||
278 | if (stack == stacklimit) | ||
279 | stack = doublestack(L, &stacklimit, ptop); | ||
280 | stack->p = p + getoffset(p); | ||
281 | stack->s = s; | ||
282 | stack->ls = (const Labelset *) ((p + 2)->buff); | ||
283 | stack->caplevel = captop; | ||
284 | stack++; | ||
285 | p += (CHARSETINSTSIZE - 1) + 2; | ||
286 | continue; | ||
287 | } | ||
288 | case IRecov: { /* labeled failure */ | 277 | case IRecov: { /* labeled failure */ |
289 | if (stack == stacklimit) | 278 | if (stack == stacklimit) |
290 | stack = doublestack(L, &stacklimit, ptop); | 279 | stack = doublestack(L, &stacklimit, ptop); |
@@ -35,7 +35,6 @@ typedef enum Opcode { | |||
35 | ICloseCapture, | 35 | ICloseCapture, |
36 | ICloseRunTime, | 36 | ICloseRunTime, |
37 | IThrow, /* "fails" with a specific label labeled failure */ | 37 | IThrow, /* "fails" with a specific label labeled failure */ |
38 | ILabChoice, /* labeled choice */ | ||
39 | IRecov /* stack a recovery; next fail with label 'f' will jump to 'offset' */ | 38 | IRecov /* stack a recovery; next fail with label 'f' will jump to 'offset' */ |
40 | } Opcode; | 39 | } Opcode; |
41 | 40 | ||
diff --git a/relabel.lua b/relabelrec.lua index 0e7195e..16ca7f0 100644 --- a/relabel.lua +++ b/relabelrec.lua | |||
@@ -82,15 +82,9 @@ for i, err in ipairs(errinfo) do | |||
82 | labels[err[1]] = i | 82 | labels[err[1]] = i |
83 | end | 83 | end |
84 | 84 | ||
85 | local syntaxerrs = {} | ||
86 | |||
87 | local function expect (pattern, labelname) | 85 | local function expect (pattern, labelname) |
88 | local label = labels[labelname] | 86 | local label = labels[labelname] |
89 | local record = function (input, pos) | 87 | return pattern + m.T(label) |
90 | tinsert(syntaxerrs, { label = label, pos = pos }) | ||
91 | return true | ||
92 | end | ||
93 | return pattern + m.Cmt("", record) * m.T(label) | ||
94 | end | 88 | end |
95 | 89 | ||
96 | 90 | ||
@@ -222,86 +216,34 @@ local function NT (n, b) | |||
222 | end | 216 | end |
223 | end | 217 | end |
224 | 218 | ||
225 | local function labchoice (...) | 219 | local function choicerec (...) |
226 | local t = { ... } | 220 | local t = { ... } |
227 | local n = #t | 221 | local n = #t |
228 | local p = t[1] | 222 | local p = t[1] |
229 | local i = 2 | 223 | local i = 2 |
230 | while i + 1 <= n do | 224 | while i + 1 <= n do |
231 | -- t[i] == nil when there are no labels | 225 | -- t[i] == nil when there are no labels |
232 | p = t[i] and mm.Lc(p, t[i+1], unpack(t[i])) or mt.__add(p, t[i+1]) | 226 | p = t[i] and mm.Rec(p, t[i+1], unpack(t[i])) or mt.__add(p, t[i+1]) |
233 | i = i + 2 | 227 | i = i + 2 |
234 | end | 228 | end |
235 | 229 | ||
236 | return p | 230 | return p |
237 | end | 231 | end |
238 | 232 | ||
239 | -- error recovery | ||
240 | local skip = m.P { "Skip", | ||
241 | Skip = (-m.P"/" * -m.P(name * arrow) * m.V"Ignored")^0 * m.Cc(dummy); | ||
242 | Ignored = m.V"Group" + any; | ||
243 | Group = "(" * (-m.P")" * m.V"Ignored")^0 * ")" | ||
244 | + "{" * (-m.P"}" * m.V"Ignored")^0 * "}" | ||
245 | + "[" * (-m.P"]" * m.V"Ignored")^0 * "]" | ||
246 | + "'" * (-m.P"'" * m.V"Ignored")^0 * "'" | ||
247 | + '"' * (-m.P'"' * m.V"Ignored")^0 * '"'; | ||
248 | } | ||
249 | |||
250 | local ignore = m.Cmt(any, function (input, pos) | ||
251 | return syntaxerrs[#syntaxerrs].pos, dummy | ||
252 | end) | ||
253 | |||
254 | local pointAtStart = m.Cmt(any, function (input, pos) | ||
255 | -- like ignore but makes the last syntax error point at the start | ||
256 | local ret = syntaxerrs[#syntaxerrs].pos | ||
257 | syntaxerrs[#syntaxerrs].pos = pos-1 | ||
258 | return ret, dummy | ||
259 | end) | ||
260 | |||
261 | |||
262 | local function labify (labelnames) | ||
263 | for i, l in ipairs(labelnames) do | ||
264 | labelnames[i] = labels[l] | ||
265 | end | ||
266 | return labelnames | ||
267 | end | ||
268 | |||
269 | local labelset1 = labify { | ||
270 | "ExpPatt2", "ExpPatt3", | ||
271 | "ExpPatt4", "ExpPatt5", "ExpPatt6", "ExpPatt7", | ||
272 | "ExpPatt8", "ExpPattOrClose", | ||
273 | "ExpNum", "ExpCap", | ||
274 | "ExpName1", "ExpName2", "ExpName3", | ||
275 | "ExpNameOrLab", "ExpItem", | ||
276 | "MisClose6", "MisClose7" | ||
277 | } | ||
278 | |||
279 | local labelset2 = labify { | ||
280 | "MisClose1", "MisClose2", "MisClose3", "MisClose4", "MisClose5" | ||
281 | } | ||
282 | |||
283 | local labelset3 = labify { | ||
284 | "ExpPatt1", "ExpLab1", "ExpLab2", "MisClose7" | ||
285 | } | ||
286 | |||
287 | local exp = m.P{ "Exp", | 233 | local exp = m.P{ "Exp", |
288 | Exp = S * ( m.V"Grammar" | 234 | Exp = S * ( m.V"Grammar" |
289 | + (m.V"RecovSeq" * (S * "/" * m.Lc((m.Ct(m.V"Labels") + m.Cc(nil)) | 235 | + (m.V"Seq" * (S * (("//" * m.Ct(m.V"Labels")) + ("/" * m.Cc(nil))) |
290 | * expect(S * m.V"RecovSeq", | 236 | * expect(S * m.V"Seq", "ExpPatt1") |
291 | "ExpPatt1"), | ||
292 | m.Cc(nil) * skip, | ||
293 | unpack(labelset3)) | ||
294 | )^0 | 237 | )^0 |
295 | ) / labchoice); | 238 | ) / choicerec); |
296 | Labels = m.P"{" * expect(S * m.V"Label", "ExpLab1") | 239 | Labels = m.P"{" * expect(S * m.V"Label", "ExpLab1") |
297 | * (S * "," * expect(S * m.V"Label", "ExpLab2"))^0 | 240 | * (S * "," * expect(S * m.V"Label", "ExpLab2"))^0 |
298 | * expect(S * "}", "MisClose7"); | 241 | * expect(S * "}", "MisClose7"); |
299 | RecovSeq = m.Lc(m.V"Seq", skip, unpack(labelset1)); | ||
300 | Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix" * (S * m.V"Prefix")^0, mt.__mul); | 242 | Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix" * (S * m.V"Prefix")^0, mt.__mul); |
301 | Prefix = "&" * expect(S * m.V"Prefix", "ExpPatt2") / mt.__len | 243 | Prefix = "&" * expect(S * m.V"Prefix", "ExpPatt2") / mt.__len |
302 | + "!" * expect(S * m.V"Prefix", "ExpPatt3") / mt.__unm | 244 | + "!" * expect(S * m.V"Prefix", "ExpPatt3") / mt.__unm |
303 | + m.V"Suffix"; | 245 | + m.V"Suffix"; |
304 | Suffix = m.Cf(m.V"RecovPrimary" * | 246 | Suffix = m.Cf(m.V"Primary" * |
305 | ( S * ( m.P"+" * m.Cc(1, mt.__pow) | 247 | ( S * ( m.P"+" * m.Cc(1, mt.__pow) |
306 | + m.P"*" * m.Cc(0, mt.__pow) | 248 | + m.P"*" * m.Cc(0, mt.__pow) |
307 | + m.P"?" * m.Cc(-1, mt.__pow) | 249 | + m.P"?" * m.Cc(-1, mt.__pow) |
@@ -318,13 +260,13 @@ local exp = m.P{ "Exp", | |||
318 | "ExpName1") | 260 | "ExpName1") |
319 | ) | 261 | ) |
320 | )^0, function (a,b,f) return f(a,b) end ); | 262 | )^0, function (a,b,f) return f(a,b) end ); |
321 | RecovPrimary = m.Lc(m.V"Primary", ignore, unpack(labelset2)); | ||
322 | Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1") | 263 | Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1") |
323 | + m.Lc(String / mm.P, pointAtStart, | 264 | + String / mm.P |
324 | labels["MisTerm1"], labels["MisTerm2"]) | 265 | + Class |
325 | + m.Lc(Class, pointAtStart, labels["MisClose8"]) | ||
326 | + defined | 266 | + defined |
327 | + "%" * expect(m.V"Labels", "ExpNameOrLab") / mm.T | 267 | + "%" * expect(m.P"{", "ExpNameOrLab") |
268 | * expect(S * m.V"Label", "ExpLab1") | ||
269 | * expect(S * "}", "MisClose7") / mm.T | ||
328 | + "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5") | 270 | + "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5") |
329 | * expect(S * ":}", "MisClose2") | 271 | * expect(S * ":}", "MisClose2") |
330 | / function (n, p) return mm.Cg(p, n) end | 272 | / function (n, p) return mm.Cg(p, n) end |
@@ -374,23 +316,20 @@ local function compile (p, defs) | |||
374 | if mm.type(p) == "pattern" then return p end -- already compiled | 316 | if mm.type(p) == "pattern" then return p end -- already compiled |
375 | p = p .. " " -- for better reporting of column numbers in errors when at EOF | 317 | p = p .. " " -- for better reporting of column numbers in errors when at EOF |
376 | local ok, cp, label, suffix = pcall(function() return pattern:match(p, 1, defs) end) | 318 | local ok, cp, label, suffix = pcall(function() return pattern:match(p, 1, defs) end) |
377 | if not ok and #syntaxerrs == 0 then | 319 | if not ok and cp then |
378 | if type(cp) == "string" then | 320 | if type(cp) == "string" then |
379 | cp = cp:gsub("^[^:]+:[^:]+: ", "") | 321 | cp = cp:gsub("^[^:]+:[^:]+: ", "") |
380 | end | 322 | end |
381 | error(cp, 3) | 323 | error(cp, 3) |
382 | end | 324 | end |
383 | if #syntaxerrs > 0 then | 325 | if not cp then |
384 | local lines = splitlines(p) | 326 | local lines = splitlines(p) |
385 | local errors = {} | 327 | local line, col = lineno(p, #p - #suffix + 1) |
386 | for i, err in ipairs(syntaxerrs) do | 328 | local err = {} |
387 | local line, col = lineno(p, err.pos) | 329 | tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errmsgs[label]) |
388 | tinsert(errors, "L" .. line .. ":C" .. col .. ": " .. errmsgs[err.label]) | 330 | tinsert(err, lines[line]) |
389 | tinsert(errors, lines[line]) | 331 | tinsert(err, rep(" ", col-1) .. "^") |
390 | tinsert(errors, rep(" ", col-1) .. "^") | 332 | error("syntax error(s) in pattern\n" .. concat(err, "\n"), 3) |
391 | end | ||
392 | syntaxerrs = {} | ||
393 | error("syntax error(s) in pattern\n" .. concat(errors, "\n"), 3) | ||
394 | end | 333 | end |
395 | return cp | 334 | return cp |
396 | end | 335 | end |
@@ -1110,7 +1110,7 @@ checkeq(t, {'a', 'aa', 20, 'a', 'aaa', 'aaa'}) | |||
1110 | -- Tests for 're' module | 1110 | -- Tests for 're' module |
1111 | ------------------------------------------------------------------- | 1111 | ------------------------------------------------------------------- |
1112 | 1112 | ||
1113 | local re = require "relabel" | 1113 | local re = require "relabelrec" |
1114 | 1114 | ||
1115 | local match, compile = re.match, re.compile | 1115 | local match, compile = re.match, re.compile |
1116 | 1116 | ||
diff --git a/testlabel.lua b/testlabel.lua index 25036b2..cb92657 100644 --- a/testlabel.lua +++ b/testlabel.lua | |||
@@ -11,6 +11,15 @@ local function checkeqlab (x, ...) | |||
11 | end | 11 | end |
12 | end | 12 | end |
13 | 13 | ||
14 | local function checkeq (x, y, p) | ||
15 | if p then print(x,y) end | ||
16 | if type(x) ~= "table" then assert(x == y) | ||
17 | else | ||
18 | for k,v in pairs(x) do checkeq(v, y[k], p) end | ||
19 | for k,v in pairs(y) do checkeq(v, x[k], p) end | ||
20 | end | ||
21 | end | ||
22 | |||
14 | -- throws a label | 23 | -- throws a label |
15 | p = m.T(1) | 24 | p = m.T(1) |
16 | s = "abc" | 25 | s = "abc" |
@@ -32,48 +41,97 @@ local g = m.P{ | |||
32 | r, l, serror = g:match(s) | 41 | r, l, serror = g:match(s) |
33 | assert(r == nil and l == 1 and serror == "abc") | 42 | assert(r == nil and l == 1 and serror == "abc") |
34 | 43 | ||
35 | --[==[ TODO: labeled choice does not work anymore | 44 | |
36 | -- throws a label that is not caught by labeled choice | 45 | -- throws a label that is not caught by the recovery operator |
37 | p = m.Lc(m.T(2), m.P"a", 1, 3) | 46 | p = m.Rec(m.T(2), m.P"a", 1, 3) |
38 | r, l, serror = p:match(s) | 47 | r, l, serror = p:match(s) |
39 | assert(r == nil and l == 2 and serror == "abc") | 48 | assert(r == nil and l == 2 and serror == "abc") |
40 | 49 | ||
41 | -- modifies previous pattern | 50 | -- wraps the previous pattern with a recovery that catches label "2" |
42 | -- adds another labeled choice to catch label "2" | 51 | p = m.Rec(p, m.P"a", 2) |
43 | p = m.Lc(p, m.P"a", 2) | ||
44 | assert(p:match(s) == 2) | 52 | assert(p:match(s) == 2) |
45 | 53 | ||
46 | -- throws a label that is caught by labeled choice | 54 | -- throws a label that is caught by recovery |
47 | p = m.Lc(m.T(25), m.P"a", 25) | 55 | p = m.Rec(m.T(25), m.P"a", 25) |
48 | assert(p:match(s) == 2) | 56 | assert(p:match(s) == 2) |
49 | 57 | ||
50 | -- "fail" is label "0" | 58 | -- "fail" is label "0" |
51 | -- throws the "fail" label that is not caught by the labeled choice | 59 | -- throws the "fail" label after the recovery |
52 | s = "bola" | 60 | s = "bola" |
53 | r, l, serror = p:match("bola") | 61 | r, l, serror = p:match("bola") |
54 | assert(r == nil and l == 0 and serror == "bola") | 62 | assert(r == nil and l == 0 and serror == "bola") |
55 | 63 | ||
56 | -- labeled choice does not catch "fail" by default | 64 | -- Recovery does not catch "fail" by default |
57 | p = m.Lc(m.P"b", m.P"a", 1) | 65 | p = m.Rec(m.P"b", m.P"a", 1) |
58 | 66 | ||
59 | r, l, serror = p:match("abc") | 67 | r, l, serror = p:match("abc") |
60 | assert(r == nil and l == 0 and serror == "abc") | 68 | assert(r == nil and l == 0 and serror == "abc") |
61 | 69 | ||
62 | assert(p:match("bola") == 2) | 70 | assert(p:match("bola") == 2) |
63 | 71 | ||
64 | -- labeled choice can catch "fail" | 72 | |
65 | p = m.Lc(m.P"b", m.P"a", 0) | 73 | -- recovery operator catches "1" or "3" |
74 | p = m.Rec((m.P"a" + m.T(1)) * m.T(3), (m.P"a" + m.P"b"), 1, 3) | ||
75 | assert(p:match("aac") == 3) | ||
76 | assert(p:match("abc") == 3) | ||
77 | r, l, serror = p:match("acc") | ||
78 | assert(r == nil and l == 0 and serror == "cc") | ||
79 | |||
80 | --throws 1, recovery pattern matches 'b', throw 3, and rec pat mathces 'a' | ||
81 | assert(p:match("bac") == 3) | ||
82 | |||
83 | r, l, serror = p:match("cab") | ||
84 | assert(r == nil and l == 0 and serror == "cab") | ||
85 | |||
86 | |||
87 | -- associativity | ||
88 | -- (p1 / %1) //{1} (p2 / %2) //{2} p3 | ||
89 | -- left-associativity | ||
90 | -- ("a" //{1} "b") //{2} "c" | ||
91 | p = m.Rec(m.Rec(m.P"a" + m.T(1), m.P"b" + m.T(2), 1), m.P"c", 2) | ||
66 | assert(p:match("abc") == 2) | 92 | assert(p:match("abc") == 2) |
67 | assert(p:match("bola") == 2) | 93 | assert(p:match("bac") == 2) |
94 | assert(p:match("cab") == 2) | ||
95 | r, l, serror = p:match("dab") | ||
96 | assert(r == nil and l == 0 and serror == "dab") | ||
68 | 97 | ||
69 | -- "fail" is label "0" | 98 | |
70 | -- labeled choice catches "fail" or "3" | 99 | -- righ-associativity |
71 | p = m.Lc(m.P"a" * m.T(3), (m.P"a" + m.P"b"), 0, 3) | 100 | -- "a" //{1} ("b" //{2} "c") |
101 | p = m.Rec(m.P"a" + m.T(1), m.Rec(m.P"b" + m.T(2), m.P"c", 2), 1) | ||
72 | assert(p:match("abc") == 2) | 102 | assert(p:match("abc") == 2) |
73 | assert(p:match("bac") == 2) | 103 | assert(p:match("bac") == 2) |
104 | assert(p:match("cab") == 2) | ||
105 | r, l, serror = p:match("dab") | ||
106 | assert(r == nil and l == 0 and serror == "dab") | ||
107 | |||
108 | |||
109 | -- associativity -> in this case the error thrown by p1 is only | ||
110 | -- recovered when we have a left-associative operator | ||
111 | -- (p1 / %2) //{1} (p2 / %2) //{2} p3 | ||
112 | -- left-associativity | ||
113 | -- ("a" //{1} "b") //{2} "c" | ||
114 | p = m.Rec(m.Rec(m.P"a" + m.T(2), m.P"b" + m.T(2), 1), m.P"c", 2) | ||
115 | assert(p:match("abc") == 2) | ||
116 | r, l, serror = p:match("bac") | ||
117 | assert(r == nil and l == 0 and serror == "bac") | ||
118 | assert(p:match("cab") == 2) | ||
119 | r, l, serror = p:match("dab") | ||
120 | assert(r == nil and l == 0 and serror == "dab") | ||
121 | |||
74 | 122 | ||
123 | -- righ-associativity | ||
124 | -- "a" //{1} ("b" //{2} "c") | ||
125 | p = m.Rec(m.P"a" + m.T(2), m.Rec(m.P"b" + m.T(2), m.P"c", 2), 1) | ||
126 | assert(p:match("abc") == 2) | ||
127 | r, l, serror = p:match("bac") | ||
128 | assert(r == nil and l == 2 and serror == "bac") | ||
75 | r, l, serror = p:match("cab") | 129 | r, l, serror = p:match("cab") |
76 | assert(r == nil and l == 0 and serror == "cab") | 130 | assert(r == nil and l == 2 and serror == "cab") |
131 | r, l, serror = p:match("dab") | ||
132 | assert(r == nil and l == 2 and serror == "dab") | ||
133 | |||
134 | |||
77 | 135 | ||
78 | -- tests related to predicates | 136 | -- tests related to predicates |
79 | p = #m.T(1) + m.P"a" | 137 | p = #m.T(1) + m.P"a" |
@@ -84,74 +142,66 @@ p = ##m.T(1) + m.P"a" | |||
84 | r, l, serror = p:match("abc") | 142 | r, l, serror = p:match("abc") |
85 | assert(r == nil and l == 1 and serror == "abc") | 143 | assert(r == nil and l == 1 and serror == "abc") |
86 | 144 | ||
87 | p = #m.T(0) * m.P"a" | ||
88 | assert(p:match("abc") == fail) | ||
89 | |||
90 | p = #m.T(0) + m.P"a" | ||
91 | assert(p:match("abc") == 2) | ||
92 | |||
93 | p = -m.T(1) * m.P"a" | 145 | p = -m.T(1) * m.P"a" |
94 | r, l, serror = p:match("abc") | 146 | r, l, serror = p:match("abc") |
95 | assert(r == nil and l == 1 and serror == "abc") | 147 | assert(r == nil and l == 1 and serror == "abc") |
96 | 148 | ||
149 | p = -m.T(1) * m.P"a" | ||
150 | r, l, serror = p:match("bbc") | ||
151 | assert(r == nil and l == 1 and serror == "bbc") | ||
152 | |||
97 | p = -(-m.T(1)) * m.P"a" | 153 | p = -(-m.T(1)) * m.P"a" |
98 | r, l, serror = p:match("abc") | 154 | r, l, serror = p:match("abc") |
99 | assert(r == nil and l == 1 and serror == "abc") | 155 | assert(r == nil and l == 1 and serror == "abc") |
100 | 156 | ||
101 | p = -m.T(0) * m.P"a" | 157 | p = m.Rec(-m.T(22), m.P"a", 22) |
102 | assert(p:match("abc") == 2) | 158 | r, l, serror = p:match("abc") |
159 | assert(r == nil and l == 0 and serror == "bc") | ||
103 | 160 | ||
104 | p = -m.T(0) + m.P"a" | 161 | assert(p:match("bbc") == 1) |
105 | assert(p:match("abc") == 1) | ||
106 | 162 | ||
107 | p = -(-m.T(0)) + m.P"a" | 163 | p = m.Rec(#m.T(22), m.P"a", 22) |
108 | assert(p:match("abc") == 2) | 164 | assert(p:match("abc") == 1) |
109 | 165 | ||
110 | p = m.Lc(-m.T(22), m.P"a", 22) | 166 | p = #m.Rec(m.T(22), m.P"a", 22) |
111 | assert(p:match("abc") == 2) | 167 | assert(p:match("abc") == 1) |
112 | 168 | ||
113 | p = m.Lc(-m.T(0), m.P"a", 0) | 169 | p = m.Rec(m.T(22), #m.P"a", 22) |
114 | assert(p:match("abc") == 1) | 170 | assert(p:match("abc") == 1) |
115 | 171 | ||
116 | p = m.Lc(#m.T(22), m.P"a", 22) | 172 | p = m.Rec(#m.T(22), m.P"a", 22) |
117 | assert(p:match("abc") == 2) | 173 | r, l, serror = p:match("bbc") |
174 | assert(r == nil and l == 0 and serror == "bbc") | ||
118 | 175 | ||
119 | p = m.Lc(#m.T(0), m.P"a", 0) | ||
120 | assert(p:match("abc") == 2) | ||
121 | 176 | ||
122 | -- tests related to repetition | 177 | -- tests related to repetition |
123 | p = m.T(1)^0 | 178 | p = m.T(1)^0 |
124 | r, l, serror = p:match("ab") | 179 | r, l, serror = p:match("ab") |
125 | assert(r == nil and l == 1 and serror == "ab") | 180 | assert(r == nil and l == 1 and serror == "ab") |
126 | 181 | ||
127 | p = m.T(0)^0 | ||
128 | assert(p:match("ab") == 1) | ||
129 | |||
130 | p = (m.P"a" + m.T(1))^0 | 182 | p = (m.P"a" + m.T(1))^0 |
131 | r, l, serror = p:match("aa") | 183 | r, l, serror = p:match("aa") |
132 | assert(r == nil and l == 1 and serror == "") | 184 | assert(r == nil and l == 1 and serror == "") |
133 | 185 | ||
134 | p = (m.P"a" + m.T(0))^0 | ||
135 | assert(p:match("aa") == 3) | ||
136 | 186 | ||
137 | -- Bug reported by Matthew Allen | 187 | -- Bug reported by Matthew Allen |
138 | -- some optmizations performed by LPeg should not be | 188 | -- some optmizations performed by LPeg should not be |
139 | -- applied in case of labeled choices | 189 | -- applied in case of labeled choices |
140 | p = m.Lc(m.P"A", m.P(true), 1) + m.P("B") | 190 | p = m.Rec(m.P"A", m.P(true), 1) + m.P("B") |
141 | assert(p:match("B") == 2) | 191 | assert(p:match("B") == 2) |
142 | 192 | ||
143 | p = m.Lc(m.P"A", m.P(false), 1) + m.P("B") | 193 | p = m.Rec(m.P"A", m.P(false), 1) + m.P("B") |
144 | assert(p:match("B") == 2) | 194 | assert(p:match("B") == 2) |
145 | 195 | ||
146 | 196 | ||
147 | --[[ | 197 | --[[ |
148 | S -> A /{1} 'a' | 198 | S -> A //{1} 'a' |
149 | A -> B | 199 | A -> B |
150 | B -> %1 | 200 | B -> %1 |
151 | ]] | 201 | ]] |
152 | g = m.P{ | 202 | g = m.P{ |
153 | "S", | 203 | "S", |
154 | S = m.Lc(m.V"A", m.P"a", 1), | 204 | S = m.Rec(m.V"A", m.P"a", 1), |
155 | A = m.V"B", | 205 | A = m.V"B", |
156 | B = m.T(1), | 206 | B = m.T(1), |
157 | } | 207 | } |
@@ -177,66 +227,96 @@ r, l, serror = g:match("a;a") | |||
177 | assert(r == nil and l == 1 and serror == "") | 227 | assert(r == nil and l == 1 and serror == "") |
178 | 228 | ||
179 | 229 | ||
180 | -- %1 /{1,3} %2 /{2} 'a' | 230 | -- %1 //{1,3} %2 //{2} 'a' |
181 | p = m.Lc(m.Lc(m.T(1), m.T(2), 1, 3), m.P"a", 2) | 231 | p = m.Rec(m.Rec(m.T(1), m.T(2), 1, 3), m.P"a", 2) |
182 | assert(p:match("abc") == 2) | 232 | assert(p:match("abc") == 2) |
183 | 233 | ||
184 | r, l, serror = p:match("") | 234 | r, l, serror = p:match("") |
185 | assert(r == nil and l == 0 and serror == "") | 235 | assert(r == nil and l == 0 and serror == "") |
186 | 236 | ||
187 | p = m.Lc(m.T(1), m.Lc(m.T(2), m.P"a", 2), 1, 3) | 237 | p = m.Rec(m.T(1), m.Rec(m.T(2), m.P"a", 2), 1, 3) |
188 | assert(p:match("abc") == 2) | 238 | assert(p:match("abc") == 2) |
189 | 239 | ||
190 | r, l, serror = p:match("") | 240 | r, l, serror = p:match("") |
191 | assert(r == nil and l == 0 and serror == "") | 241 | assert(r == nil and l == 0 and serror == "") |
192 | ]==] | 242 | |
243 | |||
244 | -- Infinte Loop TODO: check the semantics | ||
245 | -- %1 //{1} %1 | ||
246 | p = m.Rec(m.T(1), m.T(1), 1) | ||
247 | --r, l, serror = p:match("ab") | ||
248 | --assert(r == nil and l == 1 and serror == "ab") | ||
249 | |||
250 | -- %1 //{1} 'a' (!. / %1) | ||
251 | p = m.Rec(m.T(1), m.P"a" * (-m.P(1) + m.T(1)), 1) | ||
252 | r, l, serror = p:match("ab") | ||
253 | assert(r == nil and l == 0 and serror == "b") | ||
254 | |||
255 | r, l, serror = p:match("cd") | ||
256 | assert(r == nil and l == 0 and serror == "cd") | ||
257 | |||
258 | -- %1 //{1} . (!. / %1) | ||
259 | p = m.Rec(m.T(1), m.P(1) * (-m.P(1) + m.T(1)), 1) | ||
260 | assert(p:match("abc") == 4) | ||
261 | |||
193 | 262 | ||
194 | -- testing the limit of labels | 263 | -- testing the limit of labels |
195 | p = m.T(0) | 264 | -- can only throw labels between 1 and 255 |
196 | s = "abc" | 265 | local r = pcall(m.Rec, m.P"b", m.P"a", 0) |
197 | r, l, serror = p:match(s) | 266 | assert(r == false) |
198 | assert(r == nil and l == 0 and serror == "abc") | ||
199 | 267 | ||
200 | p = m.T(255) | 268 | local r = pcall(m.Rec, m.P"b", m.P"a", 256) |
201 | s = "abc" | 269 | assert(r == false) |
202 | r, l, serror = p:match(s) | ||
203 | assert(r == nil and l == 255 and serror == "abc") | ||
204 | 270 | ||
205 | local r = pcall(m.T, -1) | 271 | local r = pcall(m.Rec, m.P"b", m.P"a", -1) |
272 | assert(r == false) | ||
273 | |||
274 | local r = pcall(m.T, 0) | ||
206 | assert(r == false) | 275 | assert(r == false) |
207 | 276 | ||
208 | local r = pcall(m.T, 256) | 277 | local r = pcall(m.T, 256) |
209 | assert(r == false) | 278 | assert(r == false) |
210 | 279 | ||
280 | local r = pcall(m.T, -1) | ||
281 | assert(r == false) | ||
282 | |||
283 | |||
284 | local r = m.Rec(m.P"b", m.P"a", 255) | ||
285 | assert(p:match("a") == 2) | ||
286 | |||
287 | p = m.T(255) | ||
288 | s = "abc" | ||
289 | r, l, serror = p:match(s) | ||
290 | assert(r == nil and l == 255 and serror == "abc") | ||
291 | |||
292 | |||
211 | 293 | ||
212 | print("+") | 294 | print("+") |
213 | 295 | ||
214 | --[==[ TODO: labeled choice does not work anymore | ||
215 | --[[ grammar based on Figure 8 of paper submitted to SCP | 296 | --[[ grammar based on Figure 8 of paper submitted to SCP |
216 | S -> S0 /{1} ID /{2} ID '=' Exp /{3} 'unsigned'* 'int' ID /{4} 'unsigned'* ID ID / %error | 297 | S -> S0 //{1} ID //{2} ID '=' Exp //{3} 'unsigned'* 'int' ID //{4} 'unsigned'* ID ID / %error |
217 | S0 -> ID S1 / 'unsigned' S2 / 'int' %3 | 298 | S0 -> S1 / S2 / &'int' %3 |
218 | S1 -> '=' %2 / !. %1 / ID %4 | 299 | S1 -> &(ID '=') %2 / &(ID !.) %1 / &ID %4 |
219 | S2 -> 'unsigned' S2 / ID %4 / 'int' %3 | 300 | S2 -> &('unsigned'+ ID) %4 / & ('unsigned'+ 'int') %3 |
220 | ]] | 301 | ]] |
221 | |||
222 | local sp = m.S" \t\n"^0 | 302 | local sp = m.S" \t\n"^0 |
223 | local eq = sp * m.P"=" | 303 | local eq = sp * m.P"=" |
224 | 304 | ||
225 | g = m.P{ | 305 | g = m.P{ |
226 | "S", | 306 | "S", |
227 | S = m.Lc( | 307 | S = m.Rec( |
228 | m.Lc( | 308 | m.Rec( |
229 | m.Lc( | 309 | m.Rec( |
230 | m.Lc(m.V"S0", m.V"ID" * (m.P(1) + ""), 1), | 310 | m.Rec(m.V"S0", m.V"ID", 1), |
231 | m.V"ID" * eq * m.V"Exp", 2 | 311 | m.V"ID" * eq * m.V"Exp", 2 |
232 | ), | 312 | ), |
233 | m.V"U"^0 * m.V"I" * m.V"ID", 3 | 313 | m.V"U"^0 * m.V"I" * m.V"ID", 3 |
234 | ), | 314 | ), |
235 | m.V"U"^0 * m.V"ID" * m.V"ID", 4) | 315 | m.V"U"^0 * m.V"ID" * m.V"ID", 4) |
236 | + m.T(5), -- error | 316 | + m.T(5), -- error |
237 | S0 = m.V"ID" * m.V"S1" + m.V"U" * m.V"S2" + m.V"I" * m.T(3), | 317 | S0 = m.V"S1" + m.V"S2" + #m.V"I" * m.T(3), |
238 | S1 = eq * m.T(2) + sp * -m.P(1) * m.T(1) + m.V"ID" * m.T(4), | 318 | S1 = #(m.V"ID" * eq) * m.T(2) + sp * #(m.V"ID" * -m.P(1)) * m.T(1) + #m.V"ID" * m.T(4), |
239 | S2 = m.V"U" * m.V"S2" + m.V"ID" * m.T(4) + m.V"I" * m.T(3), | 319 | S2 = #(m.V"U"^1 * m.V"ID") * m.T(4) + #(m.V"U"^1 * m.V"I") * m.T(3), |
240 | ID = sp * m.P"a", | 320 | ID = sp * m.P"a", |
241 | U = sp * m.P"unsigned", | 321 | U = sp * m.P"unsigned", |
242 | I = sp * m.P"int", | 322 | I = sp * m.P"int", |
@@ -273,53 +353,59 @@ assert(r == nil and l == 5 and serror == s) | |||
273 | 353 | ||
274 | print("+") | 354 | print("+") |
275 | 355 | ||
276 | local re = require 'relabel' | ||
277 | 356 | ||
278 | g = re.compile[['a' /{4,9} [a-z] | 357 | local re = require 'relabelrec' |
358 | |||
359 | g = re.compile[['a' //{4,9} [a-z] | ||
279 | ]] | 360 | ]] |
280 | assert(g:match("a") == 2) | 361 | assert(g:match("a") == 2) |
281 | r, l, serror = g:match("b") | 362 | r, l, serror = g:match("b") |
282 | assert(r == nil and l == 0 and serror == "b") | 363 | assert(r == nil and l == 0 and serror == "b") |
283 | 364 | ||
284 | g = re.compile[['a' /{4,9} [a-f] /{5, 7} [a-z] | 365 | g = re.compile[['a' //{4,9} [a-f] //{5, 7} [a-z] |
285 | ]] | 366 | ]] |
286 | assert(g:match("a") == 2) | 367 | assert(g:match("a") == 2) |
287 | r, l, serror = g:match("b") | 368 | r, l, serror = g:match("b") |
288 | assert(r == nil and l == 0 and serror == "b") | 369 | assert(r == nil and l == 0 and serror == "b") |
289 | 370 | ||
290 | g = re.compile[[%{1} /{4,9} [a-z] | 371 | g = re.compile[[%{1} //{4,9} [a-z] |
291 | ]] | 372 | ]] |
292 | r, l, serror = g:match("a") | 373 | r, l, serror = g:match("a") |
293 | assert(r == nil and l == 1 and serror == "a") | 374 | assert(r == nil and l == 1 and serror == "a") |
294 | 375 | ||
295 | 376 | ||
296 | g = re.compile[[%{1} /{4,1} [a-f] | 377 | g = re.compile[[%{1} //{4,1} [a-f] |
297 | ]] | 378 | ]] |
298 | assert(g:match("a") == 2) | 379 | assert(g:match("a") == 2) |
299 | r, l, serror = g:match("h") | 380 | r, l, serror = g:match("h") |
300 | assert(r == nil and l == 0 and serror == "h") | 381 | assert(r == nil and l == 0 and serror == "h") |
301 | 382 | ||
302 | g = re.compile[[[a-f]%{9} /{4,9} [a-c]%{7} /{5, 7} [a-z] ]] | 383 | g = re.compile[[[a-f]%{9} //{4,9} [a-c]%{7} //{5, 7} [a-z] ]] |
303 | assert(g:match("a") == 2) | 384 | r, l, serror = g:match("a") |
304 | assert(g:match("c") == 2) | 385 | assert(r == nil and l == 0 and serror == "") |
305 | r, l, serror = g:match("d") | 386 | r, l, serror = g:match("aa") |
387 | assert(r == nil and l == 0 and serror == "") | ||
388 | assert(g:match("aaa") == 4) | ||
389 | |||
390 | r, l, serror = g:match("ad") | ||
306 | assert(r == nil and l == 0 and serror == "d") | 391 | assert(r == nil and l == 0 and serror == "d") |
392 | |||
307 | r, l, serror = g:match("g") | 393 | r, l, serror = g:match("g") |
308 | assert(r == nil and l == 0 and serror == "g") | 394 | assert(r == nil and l == 0 and serror == "g") |
309 | 395 | ||
396 | |||
310 | --[[ grammar based on Figure 8 of paper submitted to SCP | 397 | --[[ grammar based on Figure 8 of paper submitted to SCP |
311 | S -> S0 /{1} ID /{2} ID '=' Exp /{3} 'unsigned'* 'int' ID /{4} 'unsigned'* ID ID / %error | 398 | S -> S0 //{1} ID //{2} ID '=' Exp //{3} 'unsigned'* 'int' ID //{4} 'unsigned'* ID ID / %error |
312 | S0 -> ID S1 / 'unsigned' S2 / 'int' %3 | 399 | S0 -> S1 / S2 / &'int' %3 |
313 | S1 -> '=' %2 / !. %1 / ID %4 | 400 | S1 -> &(ID '=') %2 / &(ID !.) %1 / &ID %4 |
314 | S2 -> 'unsigned' S2 / ID %4 / 'int' %3 | 401 | S2 -> &('unsigned'+ ID) %4 / & ('unsigned'+ 'int') %3 |
315 | ]] | 402 | ]] |
316 | 403 | ||
317 | |||
318 | g = re.compile([[ | 404 | g = re.compile([[ |
319 | S <- S0 /{1} ID /{2} ID %s* '=' Exp /{3} U* Int ID /{4} U ID ID /{0} %{5} | 405 | S <- S0 //{1} ID //{2} ID %s* '=' Exp //{3} U* Int ID //{4} U ID ID / %{5} |
320 | S0 <- ID S1 / U S2 / Int %{3} | 406 | S0 <- S1 / S2 / &Int %{3} |
321 | S1 <- %s* '=' %{2} / !. %{1} / ID %{4} | 407 | S1 <- &(ID %s* '=') %{2} / &(ID !.) %{1} / &ID %{4} |
322 | S2 <- U S2 / ID %{4} / Int %{3} | 408 | S2 <- &(U+ ID) %{4} / &(U+ Int) %{3} |
323 | ID <- %s* 'a' | 409 | ID <- %s* 'a' |
324 | U <- %s* 'unsigned' | 410 | U <- %s* 'unsigned' |
325 | Int <- %s* 'int' | 411 | Int <- %s* 'int' |
@@ -349,6 +435,8 @@ s = "unsigned int" | |||
349 | r, l, serror = g:match(s) | 435 | r, l, serror = g:match(s) |
350 | assert(r == nil and l == 5 and serror == s) | 436 | assert(r == nil and l == 5 and serror == s) |
351 | 437 | ||
438 | |||
439 | |||
352 | local terror = { ['cmdSeq'] = "Missing ';' in CmdSeq", | 440 | local terror = { ['cmdSeq'] = "Missing ';' in CmdSeq", |
353 | ['ifExp'] = "Error in expresion of 'if'", | 441 | ['ifExp'] = "Error in expresion of 'if'", |
354 | ['ifThen'] = "Error matching 'then' keyword", | 442 | ['ifThen'] = "Error matching 'then' keyword", |
@@ -370,12 +458,12 @@ local terror = { ['cmdSeq'] = "Missing ';' in CmdSeq", | |||
370 | ['undefined'] = "Undefined Error"} | 458 | ['undefined'] = "Undefined Error"} |
371 | 459 | ||
372 | g = re.compile([[ | 460 | g = re.compile([[ |
373 | Tiny <- CmdSeq /{1} '' -> cmdSeq /{2} '' -> ifExp /{3} '' -> ifThen /{4} '' -> ifThenCmdSeq | 461 | Tiny <- CmdSeq //{1} '' -> cmdSeq //{2} '' -> ifExp //{3} '' -> ifThen //{4} '' -> ifThenCmdSeq |
374 | /{5} '' -> ifElseCmdSeq /{6} '' -> ifEnd /{7} '' -> repeatCmdSeq | 462 | //{5} '' -> ifElseCmdSeq //{6} '' -> ifEnd //{7} '' -> repeatCmdSeq |
375 | /{8} '' -> repeatUntil /{9} '' -> repeatExp /{10} '' -> assignOp | 463 | //{8} '' -> repeatUntil //{9} '' -> repeatExp //{10} '' -> assignOp |
376 | /{11} '' -> assignExp /{12} '' -> readName /{13} '' -> writeExp | 464 | //{11} '' -> assignExp //{12} '' -> readName //{13} '' -> writeExp |
377 | /{14} '' -> simpleExp /{15} '' -> term /{16} '' -> factor | 465 | //{14} '' -> simpleExp //{15} '' -> term //{16} '' -> factor |
378 | /{17} '' -> openParExp /{18} '' -> closePar /{0} '' -> undefined | 466 | //{17} '' -> openParExp //{18} '' -> closePar / '' -> undefined |
379 | CmdSeq <- (Cmd (SEMICOLON / %{1})) (Cmd (SEMICOLON / %{1}))* | 467 | CmdSeq <- (Cmd (SEMICOLON / %{1})) (Cmd (SEMICOLON / %{1}))* |
380 | Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd | 468 | Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd |
381 | IfCmd <- IF (Exp / %{2}) (THEN / %{3}) (CmdSeq / %{4}) (ELSE (CmdSeq / %{5}) / '') (END / %{6}) | 469 | IfCmd <- IF (Exp / %{2}) (THEN / %{3}) (CmdSeq / %{4}) (ELSE (CmdSeq / %{5}) / '') (END / %{6}) |
@@ -551,21 +639,14 @@ assert(g:match(s) == terror['undefined']) | |||
551 | 639 | ||
552 | 640 | ||
553 | print("+") | 641 | print("+") |
554 | ]==] | ||
555 | 642 | ||
556 | -- test recovery operator | ||
557 | p = m.Rec("a", "b") | ||
558 | assert(p:match("a") == 2) | ||
559 | --assert(p:match("b") == 2) | ||
560 | checkeqlab({nil, 0, "b"}, p:match("b")) | ||
561 | checkeqlab({nil, 0, "c"}, p:match("c")) | ||
562 | 643 | ||
563 | p = m.Rec("a", "b", 3) | 644 | p = m.Rec("a", "b", 3) |
564 | assert(p:match("a") == 2) | 645 | assert(p:match("a") == 2) |
565 | checkeqlab({nil, 0, "b"}, p:match("b")) | 646 | checkeqlab({nil, 0, "b"}, p:match("b")) |
566 | checkeqlab({nil, 0, "c"}, p:match("c")) | 647 | checkeqlab({nil, 0, "c"}, p:match("c")) |
567 | 648 | ||
568 | p = m.Rec(m.T(3), "b") | 649 | p = m.Rec(m.T(3), "b", 1) |
569 | checkeqlab({nil, 3, "a"}, p:match("a")) | 650 | checkeqlab({nil, 3, "a"}, p:match("a")) |
570 | checkeqlab({nil, 3, "b"}, p:match("b")) | 651 | checkeqlab({nil, 3, "b"}, p:match("b")) |
571 | 652 | ||
@@ -574,56 +655,26 @@ checkeqlab({nil, 0, "a"}, p:match("a")) | |||
574 | assert(p:match("b") == 2) | 655 | assert(p:match("b") == 2) |
575 | 656 | ||
576 | --[[ | 657 | --[[ |
577 | S -> (A //{fail} (!c .)*) C | 658 | S -> (A //{128} (!c .)*) C |
578 | A -> a*b | 659 | A -> a*b / %128 |
579 | C -> c+ | ||
580 | ]] | ||
581 | g = m.P{ | ||
582 | "S", | ||
583 | S = m.Rec(m.V"A", (-m.P"c" * m.P(1))^0) * m.V"C", | ||
584 | A = m.P"a"^0 * "b", | ||
585 | C = m.P"c"^1, | ||
586 | } | ||
587 | |||
588 | assert(g:match("abc") == 4) | ||
589 | assert(g:match("aabc") == 5) | ||
590 | --assert(g:match("aadc") == 5) --old semantics | ||
591 | checkeqlab({nil, 0, "dc"}, g:match("aadc")) --new semantics | ||
592 | assert(g:match("bc") == 3) -- new semantics | ||
593 | checkeqlab({nil, 0, "bc"}, g:match("bbc")) | ||
594 | --assert(g:match("xxc") == 4) old semantics | ||
595 | checkeqlab({nil, 0, "xxc"}, g:match("xxc")) --new semantics | ||
596 | --assert(g:match("c") == 2) --old semantics | ||
597 | checkeqlab({nil, 0, "c"}, g:match("c")) --new semantics | ||
598 | --checkeqlab({nil, 0, ""}, g:match("fail")) --old semantics | ||
599 | checkeqlab({nil, 0, "fail"}, g:match("fail")) --new semantics | ||
600 | --checkeqlab({nil, 0, ""}, g:match("aaxx")) --old semantics | ||
601 | checkeqlab({nil, 0, "xx"}, g:match("aaxx")) --new semantics | ||
602 | |||
603 | |||
604 | --[[ | ||
605 | S -> (A //{0} (!c .)*) C | ||
606 | A -> a*b / ^{0} | ||
607 | C -> c+ | 660 | C -> c+ |
608 | ]] | 661 | ]] |
609 | g = m.P{ | 662 | g = m.P{ |
610 | "S", | 663 | "S", |
611 | S = m.Rec(m.V"A", (-m.P"c" * m.P(1))^0, 0) * m.V"C", --explicitly put 0 in Rec | 664 | S = m.Rec(m.V"A", (-m.P"c" * m.P(1))^0, 128) * m.V"C", |
612 | A = m.P"a"^0 * m.P"b" + m.T(0), | 665 | A = m.P"a"^0 * "b" + m.T(128), |
613 | C = m.P"c"^1, | 666 | C = m.P"c"^1, |
614 | } | 667 | } |
615 | 668 | ||
616 | assert(g:match("abc") == 4) | 669 | assert(g:match("abc") == 4) |
617 | assert(g:match("aabc") == 5) | 670 | assert(g:match("aabc") == 5) |
618 | assert(g:match("aadc") == 5) --updated | 671 | assert(g:match("aadc") == 5) |
619 | assert(g:match("bc") == 3) -- new semantics | 672 | assert(g:match("dc") == 3) |
620 | checkeqlab({nil, 0, "bc"}, g:match("bbc")) | 673 | checkeqlab({nil, 0, "bc"}, g:match("bbc")) |
621 | assert(g:match("xxc") == 4) | 674 | assert(g:match("xxc") == 4) |
622 | assert(g:match("c") == 2) --old semantics updated | 675 | assert(g:match("c") == 2) |
623 | checkeqlab({nil, 0, ""}, g:match("fail")) --old semantics updated | 676 | checkeqlab({nil, 0, ""}, g:match("fail")) |
624 | checkeqlab({nil, 0, ""}, g:match("aaxx")) --old semantics updated | 677 | checkeqlab({nil, 0, ""}, g:match("aaxx")) |
625 | |||
626 | |||
627 | 678 | ||
628 | 679 | ||
629 | --[[ | 680 | --[[ |
@@ -810,6 +861,120 @@ checkeqlab({nil, 4, "dc"}, g3:match("dc")) | |||
810 | checkeqlab({nil, 4, "d"}, g3:match(".d")) | 861 | checkeqlab({nil, 4, "d"}, g3:match(".d")) |
811 | 862 | ||
812 | 863 | ||
864 | -- testing more captures | ||
865 | local g = re.compile[[ | ||
866 | S <- ( %s* &. {A} )* | ||
867 | A <- [0-9]+ / %{5} | ||
868 | ]] | ||
813 | 869 | ||
814 | print("OK") | 870 | checkeq({"523", "624", "346", "888"} , {g:match("523 624 346\n888")}) |
871 | checkeq({nil, 5, "a 123"}, {g:match("44 a 123")}) | ||
872 | |||
873 | local g2 = m.Rec(g, ((-m.R("09") * m.P(1))^0) / "58", 5) | ||
874 | |||
875 | checkeq({"523", "624", "346", "888"} , {g2:match("523 624 346\n888")}) | ||
876 | checkeq({"44", "a ", "58", "123"}, {g2:match("44 a 123")}) | ||
877 | |||
878 | |||
879 | local g = re.compile[[ | ||
880 | S <- ( %s* &. A )* | ||
881 | A <- {[0-9]+} / %{5} | ||
882 | ]] | ||
883 | |||
884 | checkeq({"523", "624", "346", "888"} , {g:match("523 624 346\n888")}) | ||
885 | checkeq({nil, 5, "a 123"}, {g:match("44 a 123")}) | ||
886 | |||
887 | local g2 = m.Rec(g, ((-m.R("09") * m.P(1))^0) / "58", 5) | ||
888 | |||
889 | checkeq({"523", "624", "346", "888"} , {g2:match("523 624 346\n888")}) | ||
890 | checkeq({"44", "58", "123"}, {g2:match("44 a 123")}) | ||
891 | |||
892 | |||
893 | local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V | ||
894 | local C, Cc, Ct, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt | ||
895 | local T, Lc, Rec = lpeg.T, lpeg.Lc, lpeg.Rec | ||
896 | |||
897 | local labels = { | ||
898 | {"NoExp", "no expression found"}, | ||
899 | {"Extra", "extra characters found after the expression"}, | ||
900 | {"ExpTerm", "expected a term after the operator"}, | ||
901 | {"ExpExp", "expected an expression after the parenthesis"}, | ||
902 | {"MisClose", "missing a closing ')' after the expression"}, | ||
903 | } | ||
904 | |||
905 | local function labelindex(labname) | ||
906 | for i, elem in ipairs(labels) do | ||
907 | if elem[1] == labname then | ||
908 | return i | ||
909 | end | ||
910 | end | ||
911 | error("could not find label: " .. labname) | ||
912 | end | ||
913 | |||
914 | local errors = {} | ||
915 | |||
916 | local function expect(patt, labname, recpatt) | ||
917 | local i = labelindex(labname) | ||
918 | function recorderror(input, pos) | ||
919 | table.insert(errors, {i, pos}) | ||
920 | return true | ||
921 | end | ||
922 | if not recpatt then recpatt = P"" end | ||
923 | --return Rec(patt, Cmt("", recorderror) * recpatt) | ||
924 | return patt + T(i) | ||
925 | end | ||
926 | |||
927 | local num = R("09")^1 / tonumber | ||
928 | local op = S("+-*/") | ||
929 | |||
930 | local function compute(tokens) | ||
931 | local result = tokens[1] | ||
932 | for i = 2, #tokens, 2 do | ||
933 | if tokens[i] == '+' then | ||
934 | result = result + tokens[i+1] | ||
935 | elseif tokens[i] == '-' then | ||
936 | result = result - tokens[i+1] | ||
937 | elseif tokens[i] == '*' then | ||
938 | result = result * tokens[i+1] | ||
939 | elseif tokens[i] == '/' then | ||
940 | result = result / tokens[i+1] | ||
941 | else | ||
942 | error('unknown operation: ' .. tokens[i]) | ||
943 | end | ||
944 | end | ||
945 | return result | ||
946 | end | ||
815 | 947 | ||
948 | |||
949 | local g = P { | ||
950 | "Exp", | ||
951 | Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute, | ||
952 | Operand = expect(V"Term", "ExpTerm"), | ||
953 | Term = num, | ||
954 | } | ||
955 | local rg = Rec(g, Cc(3), labelindex("ExpTerm")) | ||
956 | |||
957 | local function eval(input) | ||
958 | local result, label, suffix = rg:match(input) | ||
959 | if #errors == 0 then | ||
960 | return result | ||
961 | else | ||
962 | local out = {} | ||
963 | for i, err in ipairs(errors) do | ||
964 | local pos = err[2] | ||
965 | local msg = labels[err[1]][2] | ||
966 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") | ||
967 | end | ||
968 | errors = {} | ||
969 | return nil, table.concat(out, "\n") | ||
970 | end | ||
971 | end | ||
972 | |||
973 | assert(eval("98-76*54/32") == 37.125) | ||
974 | --> 37.125 | ||
975 | |||
976 | assert(eval("1+") == 4) | ||
977 | --> syntax error: expected a term after the operator (at index 3) | ||
978 | |||
979 | |||
980 | print("OK") | ||