diff options
author | Sérgio Queiroz <sqmedeiros@gmail.com> | 2017-12-18 15:39:00 -0300 |
---|---|---|
committer | Sérgio Queiroz <sqmedeiros@gmail.com> | 2017-12-18 15:39:00 -0300 |
commit | c6b98103f0ab2c4afb7216e73f2eacac58cbf952 (patch) | |
tree | a98bef21fff8702c66deac2315a6a10d7d1c26df | |
parent | 4bdf8d40a9ca5f00e454a38d301a3ab35c9b50d5 (diff) | |
download | lpeglabel-c6b98103f0ab2c4afb7216e73f2eacac58cbf952.tar.gz lpeglabel-c6b98103f0ab2c4afb7216e73f2eacac58cbf952.tar.bz2 lpeglabel-c6b98103f0ab2c4afb7216e73f2eacac58cbf952.zip |
Updating code to use the new syntax/semantics of labels
-rw-r--r-- | lpcode.c | 5 | ||||
-rw-r--r-- | lpprint.c | 12 | ||||
-rw-r--r-- | lptree.c | 10 | ||||
-rw-r--r-- | lpvm.c | 1 | ||||
-rw-r--r-- | relabel.lua | 15 | ||||
-rw-r--r-- | testlabel.lua | 802 |
6 files changed, 272 insertions, 573 deletions
@@ -517,7 +517,8 @@ static int addinstruction (CompileState *compst, Opcode op, int aux) { | |||
517 | static int addoffsetinst (CompileState *compst, Opcode op) { | 517 | static int addoffsetinst (CompileState *compst, Opcode op) { |
518 | int i = addinstruction(compst, op, 0); /* instruction */ | 518 | int i = addinstruction(compst, op, 0); /* instruction */ |
519 | addinstruction(compst, (Opcode)0, 0); /* open space for offset */ | 519 | addinstruction(compst, (Opcode)0, 0); /* open space for offset */ |
520 | assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2); | 520 | assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2 || |
521 | op == IThrowRec); /* labeled failure */ | ||
521 | return i; | 522 | return i; |
522 | } | 523 | } |
523 | 524 | ||
@@ -527,13 +528,13 @@ static void codethrow (CompileState *compst, TTree *throw) { | |||
527 | int recov, aux; | 528 | int recov, aux; |
528 | if (throw->u.s.ps != 0) { | 529 | if (throw->u.s.ps != 0) { |
529 | recov = addoffsetinst(compst, IThrowRec); | 530 | recov = addoffsetinst(compst, IThrowRec); |
531 | assert(sib2(throw)->tag == TRule); | ||
530 | } else { | 532 | } else { |
531 | recov = addinstruction(compst, IThrow, 0); | 533 | recov = addinstruction(compst, IThrow, 0); |
532 | } | 534 | } |
533 | aux = nextinstruction(compst); | 535 | aux = nextinstruction(compst); |
534 | getinstr(compst, aux).i.key = throw->key; /* next instruction keeps only rule name */ | 536 | getinstr(compst, aux).i.key = throw->key; /* next instruction keeps only rule name */ |
535 | getinstr(compst, recov).i.key = sib2(throw)->cap; /* rule number */ | 537 | getinstr(compst, recov).i.key = sib2(throw)->cap; /* rule number */ |
536 | assert(sib2(throw)->tag == TRule); | ||
537 | } | 538 | } |
538 | /* labeled failure */ | 539 | /* labeled failure */ |
539 | 540 | ||
@@ -111,11 +111,6 @@ void printinst (const Instruction *op, const Instruction *p) { | |||
111 | printf("%d", p->i.aux); | 111 | printf("%d", p->i.aux); |
112 | break; | 112 | break; |
113 | } | 113 | } |
114 | case IRecov: case ILabChoice: { /* labeled failure */ | ||
115 | printjmp(op, p); | ||
116 | printcharset((p+2)->buff); | ||
117 | break; | ||
118 | } | ||
119 | default: break; | 114 | default: break; |
120 | } | 115 | } |
121 | printf("\n"); | 116 | printf("\n"); |
@@ -217,16 +212,15 @@ void printtree (TTree *tree, int ident) { | |||
217 | break; | 212 | break; |
218 | } | 213 | } |
219 | case TThrow: { /* labeled failure */ | 214 | case TThrow: { /* labeled failure */ |
215 | if (tree->u.s.ps != 0) | ||
216 | assert(sib2(tree)->tag == TRule); | ||
217 | printf(" key: %d (rule: %d)\n", tree->key, sib2(tree)->cap); | ||
220 | printf(" labels: %d\n", tree->u.label); | 218 | printf(" labels: %d\n", tree->u.label); |
221 | break; | 219 | break; |
222 | } | 220 | } |
223 | default: { | 221 | default: { |
224 | int sibs = numsiblings[tree->tag]; | 222 | int sibs = numsiblings[tree->tag]; |
225 | printf("\n"); | 223 | printf("\n"); |
226 | if (tree->tag == TRecov || tree->tag == TLabChoice) { /* labeled failure */ | ||
227 | printcharset(treelabelset(tree)); | ||
228 | printf("\n"); | ||
229 | } | ||
230 | if (sibs >= 1) { | 224 | if (sibs >= 1) { |
231 | printtree(sib1(tree), ident + 2); | 225 | printtree(sib1(tree), ident + 2); |
232 | if (sibs >= 2) | 226 | if (sibs >= 2) |
@@ -1204,8 +1204,16 @@ static int lp_match (lua_State *L) { | |||
1204 | r = match(L, s, s + i, s + l, code, capture, ptop, &labelf, &sfail); /* labeled failure */ | 1204 | r = match(L, s, s + i, s + l, code, capture, ptop, &labelf, &sfail); /* labeled failure */ |
1205 | if (r == NULL) { /* labeled failure begin */ | 1205 | if (r == NULL) { /* labeled failure begin */ |
1206 | lua_pushnil(L); | 1206 | lua_pushnil(L); |
1207 | if (labelf) | 1207 | if (labelf) { |
1208 | int isnum; | ||
1209 | lua_Integer lInt; | ||
1208 | lua_rawgeti(L, ktableidx(ptop), labelf); | 1210 | lua_rawgeti(L, ktableidx(ptop), labelf); |
1211 | lInt = lua_tointegerx(L, -1, &isnum); | ||
1212 | if (isnum) { | ||
1213 | lua_pop(L, 1); | ||
1214 | lua_pushinteger(L, lInt); | ||
1215 | } | ||
1216 | } | ||
1209 | else | 1217 | else |
1210 | lua_pushstring(L, "fail"); | 1218 | lua_pushstring(L, "fail"); |
1211 | lua_pushinteger(L, sfail - (s + i) + 1); /* subject position related to the error */ | 1219 | lua_pushinteger(L, sfail - (s + i) + 1); /* subject position related to the error */ |
@@ -301,6 +301,7 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e, | |||
301 | luaL_error(L, "labelf is %d", *labelf); | 301 | luaL_error(L, "labelf is %d", *labelf); |
302 | *sfail = s; | 302 | *sfail = s; |
303 | stack = getstackbase(L, ptop); | 303 | stack = getstackbase(L, ptop); |
304 | stack++; | ||
304 | goto fail; | 305 | goto fail; |
305 | } | 306 | } |
306 | case IThrowRec: { /* labeled failure */ | 307 | 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 (...) | |||
241 | return p | 241 | return p |
242 | end | 242 | end |
243 | 243 | ||
244 | local function getlab (f) | ||
245 | if not tlabels[f] then | ||
246 | error("undefined label: " .. f) | ||
247 | end | ||
248 | return tlabels[f] | ||
249 | end | ||
250 | 244 | ||
251 | local exp = m.P{ "Exp", | 245 | local exp = m.P{ "Exp", |
252 | Exp = S * ( m.V"Grammar" | 246 | Exp = S * ( m.V"Grammar" |
@@ -278,7 +272,7 @@ local exp = m.P{ "Exp", | |||
278 | + "=>" * expect(S * m.Cg(Def / getdef * m.Cc(m.Cmt)), | 272 | + "=>" * expect(S * m.Cg(Def / getdef * m.Cc(m.Cmt)), |
279 | "ExpName1") | 273 | "ExpName1") |
280 | ) | 274 | ) |
281 | )^0, function (a,b,f) if f == "lab" then return a + mm.T(getlab(b)) else return f(a,b) end end ); | 275 | )^0, function (a,b,f) if f == "lab" then return a + mm.T(b) else return f(a,b) end end ); |
282 | Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1") | 276 | Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1") |
283 | + String / mm.P | 277 | + String / mm.P |
284 | + Class | 278 | + Class |
@@ -301,7 +295,7 @@ local exp = m.P{ "Exp", | |||
301 | + m.P"." * m.Cc(any) | 295 | + m.P"." * m.Cc(any) |
302 | + (name * -arrow + "<" * expect(name, "ExpName3") | 296 | + (name * -arrow + "<" * expect(name, "ExpName3") |
303 | * expect(">", "MisClose6")) * m.Cb("G") / NT; | 297 | * expect(">", "MisClose6")) * m.Cb("G") / NT; |
304 | Label = num + name / function (f) return getlab(f) end; | 298 | Label = num + name; |
305 | Definition = name * arrow * expect(m.V"Exp", "ExpPatt8"); | 299 | Definition = name * arrow * expect(m.V"Exp", "ExpPatt8"); |
306 | Grammar = m.Cg(m.Cc(true), "G") | 300 | Grammar = m.Cg(m.Cc(true), "G") |
307 | * m.Cf(m.V"Definition" / firstdef * (S * m.Cg(m.V"Definition"))^0, | 301 | * m.Cf(m.V"Definition" / firstdef * (S * m.Cg(m.V"Definition"))^0, |
@@ -342,7 +336,7 @@ end | |||
342 | local function compile (p, defs) | 336 | local function compile (p, defs) |
343 | if mm.type(p) == "pattern" then return p end -- already compiled | 337 | if mm.type(p) == "pattern" then return p end -- already compiled |
344 | p = p .. " " -- for better reporting of column numbers in errors when at EOF | 338 | p = p .. " " -- for better reporting of column numbers in errors when at EOF |
345 | local ok, cp, label, suffix = pcall(function() return pattern:match(p, 1, defs) end) | 339 | local ok, cp, label, poserr = pcall(function() return pattern:match(p, 1, defs) end) |
346 | if not ok and cp then | 340 | if not ok and cp then |
347 | if type(cp) == "string" then | 341 | if type(cp) == "string" then |
348 | cp = cp:gsub("^[^:]+:[^:]+: ", "") | 342 | cp = cp:gsub("^[^:]+:[^:]+: ", "") |
@@ -351,8 +345,7 @@ local function compile (p, defs) | |||
351 | end | 345 | end |
352 | if not cp then | 346 | if not cp then |
353 | local lines = splitlines(p) | 347 | local lines = splitlines(p) |
354 | local line, col = lineno(p, #p - #suffix + 1) | 348 | local line, col = lineno(p, poserr) |
355 | --local line, col = calcline(p, #p - #suffix + 1) | ||
356 | local err = {} | 349 | local err = {} |
357 | tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errmsgs[label]) | 350 | tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errmsgs[label]) |
358 | tinsert(err, lines[line]) | 351 | 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"+" | |||
84 | p = m.T(1) | 84 | p = m.T(1) |
85 | s = "abc" | 85 | s = "abc" |
86 | r, l, poserr = p:match(s) | 86 | r, l, poserr = p:match(s) |
87 | assert(r == nil and l == '1' and poserr == 1) | 87 | assert(r == nil and l == 1 and poserr == 1) |
88 | 88 | ||
89 | -- throws a label, choice does not catch labels | 89 | -- throws a label, choice does not catch labels |
90 | p = m.T(1) + m.P"a" | 90 | p = m.T(1) + m.P"a" |
91 | r, l, poserr = p:match(s) | 91 | r, l, poserr = p:match(s) |
92 | assert(r == nil and l == '1' and poserr == 1) | 92 | assert(r == nil and l == 1 and poserr == 1) |
93 | 93 | ||
94 | -- again throws a label that is not caught by choice | 94 | -- again throws a label that is not caught by choice |
95 | local g = m.P{ | 95 | local g = m.P{ |
@@ -99,15 +99,15 @@ local g = m.P{ | |||
99 | B = m.P"a" | 99 | B = m.P"a" |
100 | } | 100 | } |
101 | r, l, poserr = g:match(s) | 101 | r, l, poserr = g:match(s) |
102 | assert(r == nil and l == '1' and poserr == 1) | 102 | assert(r == nil and l == 1 and poserr == 1) |
103 | 103 | ||
104 | -- throws a label in a position that is not the farthest one | 104 | -- throws a label in a position that is not the farthest one |
105 | -- but it is the position that should be reported | 105 | -- but it is the position that should be reported |
106 | p = m.P(1) * m.P"a" + m.T(11) | 106 | p = m.P(1) * m.P"a" + m.T(11) |
107 | checklabeq({3, nil, nil}, p:match("bac")) | 107 | checklabeq({3, nil, nil}, p:match("bac")) |
108 | checklabeq({nil, '11', 1}, p:match("c")) | 108 | checklabeq({nil, 11, 1}, p:match("c")) |
109 | checklabeq({nil, '11', 1}, p:match("x")) | 109 | checklabeq({nil, 11, 1}, p:match("x")) |
110 | checklabeq({nil, '11', 1}, p:match("kx")) | 110 | checklabeq({nil, 11, 1}, p:match("kx")) |
111 | 111 | ||
112 | 112 | ||
113 | -- throws a label without a corresponding recovery rule | 113 | -- throws a label without a corresponding recovery rule |
@@ -127,7 +127,7 @@ p = m.P{ | |||
127 | [2] = m.P"a" | 127 | [2] = m.P"a" |
128 | } | 128 | } |
129 | r, l, poserr = p:match("abc") | 129 | r, l, poserr = p:match("abc") |
130 | assert(r == nil and l == '2' and poserr == 1) | 130 | assert(r == nil and l == 2 and poserr == 1) |
131 | 131 | ||
132 | -- throws a label with a corresponding recovery rule | 132 | -- throws a label with a corresponding recovery rule |
133 | p = m.P{ | 133 | p = m.P{ |
@@ -198,23 +198,23 @@ assert(r == nil and l == 'fail' and poserr == 1) | |||
198 | -- tests related to predicates | 198 | -- tests related to predicates |
199 | p = #m.T(1) + m.P"a" | 199 | p = #m.T(1) + m.P"a" |
200 | r, l, poserr = p:match("abc") | 200 | r, l, poserr = p:match("abc") |
201 | assert(r == nil and l == '1' and poserr == 1) | 201 | assert(r == nil and l == 1 and poserr == 1) |
202 | 202 | ||
203 | p = ##m.T(1) + m.P"a" | 203 | p = ##m.T(1) + m.P"a" |
204 | r, l, poserr = p:match("abc") | 204 | r, l, poserr = p:match("abc") |
205 | assert(r == nil and l == '1' and poserr == 1) | 205 | assert(r == nil and l == 1 and poserr == 1) |
206 | 206 | ||
207 | p = -m.T(1) * m.P"a" | 207 | p = -m.T(1) * m.P"a" |
208 | r, l, poserr = p:match("abc") | 208 | r, l, poserr = p:match("abc") |
209 | assert(r == nil and l == '1' and poserr == 1) | 209 | assert(r == nil and l == 1 and poserr == 1) |
210 | 210 | ||
211 | p = -m.T(1) * m.P"a" | 211 | p = -m.T(1) * m.P"a" |
212 | r, l, poserr = p:match("bbc") | 212 | r, l, poserr = p:match("bbc") |
213 | assert(r == nil and l == '1' and poserr == 1) | 213 | assert(r == nil and l == 1 and poserr == 1) |
214 | 214 | ||
215 | p = -(-m.T(1)) * m.P"a" | 215 | p = -(-m.T(1)) * m.P"a" |
216 | r, l, poserr = p:match("abc") | 216 | r, l, poserr = p:match("abc") |
217 | assert(r == nil and l == '1' and poserr == 1) | 217 | assert(r == nil and l == 1 and poserr == 1) |
218 | 218 | ||
219 | p = m.P{ | 219 | p = m.P{ |
220 | "S", | 220 | "S", |
@@ -254,7 +254,7 @@ p = m.P{ | |||
254 | ["22"] = m.T(15) | 254 | ["22"] = m.T(15) |
255 | } | 255 | } |
256 | r, l, poserr = p:match("abc") | 256 | r, l, poserr = p:match("abc") |
257 | assert(r == nil and l == '15' and poserr == 1) | 257 | assert(r == nil and l == 15 and poserr == 1) |
258 | 258 | ||
259 | p = m.P{ | 259 | p = m.P{ |
260 | "S", | 260 | "S", |
@@ -262,17 +262,17 @@ p = m.P{ | |||
262 | ["22"] = m.T(15) | 262 | ["22"] = m.T(15) |
263 | } | 263 | } |
264 | r, l, poserr = p:match("abc") | 264 | r, l, poserr = p:match("abc") |
265 | assert(r == nil and l == '15' and poserr == 2) | 265 | assert(r == nil and l == 15 and poserr == 2) |
266 | 266 | ||
267 | 267 | ||
268 | -- tests related to repetition | 268 | -- tests related to repetition |
269 | p = m.T(1)^0 | 269 | p = m.T(1)^0 |
270 | r, l, poserr = p:match("ab") | 270 | r, l, poserr = p:match("ab") |
271 | assert(r == nil and l == '1' and poserr == 1) | 271 | assert(r == nil and l == 1 and poserr == 1) |
272 | 272 | ||
273 | p = (m.P"a" + m.T(1))^0 | 273 | p = (m.P"a" + m.T(1))^0 |
274 | r, l, poserr = p:match("aa") | 274 | r, l, poserr = p:match("aa") |
275 | assert(r == nil and l == '1' and poserr == 3) | 275 | assert(r == nil and l == 1 and poserr == 3) |
276 | 276 | ||
277 | 277 | ||
278 | -- Bug reported by Matthew Allen | 278 | -- Bug reported by Matthew Allen |
@@ -327,7 +327,7 @@ g = m.P{ | |||
327 | assert(g:match("a;a;") == 5) | 327 | assert(g:match("a;a;") == 5) |
328 | 328 | ||
329 | r, l, poserr = g:match("a;a") | 329 | r, l, poserr = g:match("a;a") |
330 | assert(r == nil and l == '2' and poserr == 4) | 330 | assert(r == nil and l == 2 and poserr == 4) |
331 | 331 | ||
332 | 332 | ||
333 | p = m.P{ | 333 | p = m.P{ |
@@ -383,23 +383,18 @@ local eq = sp * m.P"=" | |||
383 | 383 | ||
384 | g = m.P{ | 384 | g = m.P{ |
385 | "S", | 385 | "S", |
386 | S = m.Rec( | 386 | S = m.V"S0" + m.T'L5', -- error |
387 | m.Rec( | 387 | S0 = m.V"S1" + m.V"S2" + #m.V"I" * m.T'L3', |
388 | m.Rec( | 388 | S1 = #(m.V"ID" * eq) * m.T'L2' + sp * #(m.V"ID" * -m.P(1)) * m.T'L1' + #m.V"ID" * m.T'L4', |
389 | m.Rec(m.V"S0", m.V"ID", 1), | 389 | S2 = #(m.V"U"^1 * m.V"ID") * m.T'L4' + #(m.V"U"^1 * m.V"I") * m.T'L3', |
390 | m.V"ID" * eq * m.V"Exp", 2 | ||
391 | ), | ||
392 | m.V"U"^0 * m.V"I" * m.V"ID", 3 | ||
393 | ), | ||
394 | m.V"U"^0 * m.V"ID" * m.V"ID", 4) | ||
395 | + m.T(5), -- error | ||
396 | S0 = m.V"S1" + m.V"S2" + #m.V"I" * m.T(3), | ||
397 | S1 = #(m.V"ID" * eq) * m.T(2) + sp * #(m.V"ID" * -m.P(1)) * m.T(1) + #m.V"ID" * m.T(4), | ||
398 | S2 = #(m.V"U"^1 * m.V"ID") * m.T(4) + #(m.V"U"^1 * m.V"I") * m.T(3), | ||
399 | ID = sp * m.P"a", | 390 | ID = sp * m.P"a", |
400 | U = sp * m.P"unsigned", | 391 | U = sp * m.P"unsigned", |
401 | I = sp * m.P"int", | 392 | I = sp * m.P"int", |
402 | Exp = sp * m.P"E", | 393 | Exp = sp * m.P"E", |
394 | L1 = m.V"ID", | ||
395 | L2 = m.V"ID" * eq * m.V"Exp", | ||
396 | L3 = m.V"U"^0 * m.V"I" * m.V"ID", | ||
397 | L4 = m.V"U"^0 * m.V"ID" * m.V"ID", | ||
403 | } | 398 | } |
404 | 399 | ||
405 | local s = "a" | 400 | local s = "a" |
@@ -415,19 +410,19 @@ assert(g:match(s) == #s + 1) --4 | |||
415 | 410 | ||
416 | s = "b" | 411 | s = "b" |
417 | r, l, poserr = g:match(s) | 412 | r, l, poserr = g:match(s) |
418 | assert(r == nil and l == 5 and poserr == 1) | 413 | assert(r == nil and l == 'L5' and poserr == 1) |
419 | 414 | ||
420 | s = "unsigned" | 415 | s = "unsigned" |
421 | r, l, poserr = g:match(s) | 416 | r, l, poserr = g:match(s) |
422 | assert(r == nil and l == 5 and poserr == 1) | 417 | assert(r == nil and l == 'L5' and poserr == 1) |
423 | 418 | ||
424 | s = "unsigned a" | 419 | s = "unsigned a" |
425 | r, l, poserr = g:match(s) | 420 | r, l, poserr = g:match(s) |
426 | assert(r == nil and l == 5 and poserr == 1) | 421 | assert(r == nil and l == 'L5' and poserr == 1) |
427 | 422 | ||
428 | s = "unsigned int" | 423 | s = "unsigned int" |
429 | r, l, poserr = g:match(s) | 424 | r, l, poserr = g:match(s) |
430 | assert(r == nil and l == 5 and poserr == 1) | 425 | assert(r == nil and l == 'L5' and poserr == 1) |
431 | 426 | ||
432 | 427 | ||
433 | print("+") | 428 | print("+") |
@@ -435,42 +430,61 @@ print("+") | |||
435 | 430 | ||
436 | local re = require 'relabel' | 431 | local re = require 'relabel' |
437 | 432 | ||
438 | g = re.compile[['a' //{4,9} [a-z] | 433 | g = re.compile[['a' / %{l1} |
439 | ]] | 434 | ]] |
440 | assert(g:match("a") == 2) | 435 | assert(g:match("a") == 2) |
441 | r, l, poserr = g:match("b") | 436 | r, l, poserr = g:match("b") |
442 | assert(r == nil and l == 0 and poserr == 1) | 437 | assert(r == nil and l == 'l1' and poserr == 1) |
443 | 438 | ||
444 | g = re.compile[['a' //{4,9} [a-f] //{5, 7} [a-z] | 439 | g = re.compile[['a'^l1 |
445 | ]] | 440 | ]] |
446 | assert(g:match("a") == 2) | 441 | assert(g:match("a") == 2) |
447 | r, l, poserr = g:match("b") | 442 | r, l, poserr = g:match("b") |
448 | assert(r == nil and l == 0 and poserr == 1) | 443 | assert(r == nil and l == 'l1' and poserr == 1) |
449 | 444 | ||
450 | g = re.compile[[%{1} //{4,9} [a-z] | 445 | g = re.compile[[A <- 'a'^B |
446 | B <- [a-f]^C | ||
447 | C <- [a-z] | ||
451 | ]] | 448 | ]] |
452 | r, l, poserr = g:match("a") | 449 | assert(g:match("a") == 2) |
453 | assert(r == nil and l == 1 and poserr == 1) | 450 | assert(g:match("a") == 2) |
451 | assert(g:match("f") == 2) | ||
452 | assert(g:match("g") == 2) | ||
453 | assert(g:match("z") == 2) | ||
454 | r, l, poserr = g:match("A") | ||
455 | assert(r == nil and l == 'fail' and poserr == 1) | ||
454 | 456 | ||
457 | g = re.compile[[A <- %{C} | ||
458 | B <- [a-z] | ||
459 | ]] | ||
460 | r, l, poserr = g:match("a") | ||
461 | assert(r == nil and l == 'C' and poserr == 1) | ||
455 | 462 | ||
456 | g = re.compile[[%{1} //{4,1} [a-f] | 463 | g = re.compile[[A <- %{B} |
464 | B <- [a-z] | ||
457 | ]] | 465 | ]] |
466 | r, l, poserr = g:match("a") | ||
458 | assert(g:match("a") == 2) | 467 | assert(g:match("a") == 2) |
459 | r, l, poserr = g:match("h") | 468 | r, l, poserr = g:match("U") |
460 | assert(r == nil and l == 0 and poserr == 1) | 469 | assert(r == nil and l == 'fail' and poserr == 1) |
470 | |||
471 | |||
472 | g = re.compile[[A <- [a-f] %{B} | ||
473 | B <- [a-c] %{C} | ||
474 | C <- [a-z] | ||
475 | ]] | ||
461 | 476 | ||
462 | g = re.compile[[[a-f]%{9} //{4,9} [a-c]%{7} //{5, 7} [a-z] ]] | ||
463 | r, l, poserr = g:match("a") | 477 | r, l, poserr = g:match("a") |
464 | assert(r == nil and l == 0 and poserr == 2) | 478 | assert(r == nil and l == 'fail' and poserr == 2) |
465 | r, l, poserr = g:match("aa") | 479 | r, l, poserr = g:match("aa") |
466 | assert(r == nil and l == 0 and poserr == 3) | 480 | assert(r == nil and l == 'fail' and poserr == 3) |
467 | assert(g:match("aaa") == 4) | 481 | assert(g:match("aaa") == 4) |
468 | 482 | ||
469 | r, l, poserr = g:match("ad") | 483 | r, l, poserr = g:match("ad") |
470 | assert(r == nil and l == 0 and poserr == 2) | 484 | assert(r == nil and l == 'fail' and poserr == 2) |
471 | 485 | ||
472 | r, l, poserr = g:match("g") | 486 | r, l, poserr = g:match("g") |
473 | assert(r == nil and l == 0 and poserr == 1) | 487 | assert(r == nil and l == 'fail' and poserr == 1) |
474 | 488 | ||
475 | 489 | ||
476 | --[[ grammar based on Figure 8 of paper submitted to SCP (using the recovery operator) | 490 | --[[ 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 | |||
481 | ]] | 495 | ]] |
482 | 496 | ||
483 | g = re.compile([[ | 497 | g = re.compile([[ |
484 | S <- S0 //{1} ID //{2} ID %s* '=' Exp //{3} U* Int ID //{4} U ID ID / %{5} | 498 | S <- S0 / %{L5} |
485 | S0 <- S1 / S2 / &Int %{3} | 499 | S0 <- S1 / S2 / &Int %{L3} |
486 | S1 <- &(ID %s* '=') %{2} / &(ID !.) %{1} / &ID %{4} | 500 | S1 <- &(ID %s* '=') %{L2} / &(ID !.) %{L1} / &ID %{L4} |
487 | S2 <- &(U+ ID) %{4} / &(U+ Int) %{3} | 501 | S2 <- &(U+ ID) %{L4} / &(U+ Int) %{L3} |
488 | ID <- %s* 'a' | ||
489 | U <- %s* 'unsigned' | ||
490 | Int <- %s* 'int' | ||
491 | Exp <- %s* 'E' | ||
492 | ]]) | ||
493 | |||
494 | local s = "a" | ||
495 | assert(g:match(s) == #s + 1) --1 | ||
496 | s = "a = E" | ||
497 | assert(g:match(s) == #s + 1) --2 | ||
498 | s = "int a" | ||
499 | assert(g:match(s) == #s + 1) --3 | ||
500 | s = "unsigned int a" | ||
501 | assert(g:match(s) == #s + 1) --3 | ||
502 | s = "unsigned a a" | ||
503 | assert(g:match(s) == #s + 1) --4 | ||
504 | s = "b" | ||
505 | r, l, poserr = g:match(s) | ||
506 | assert(r == nil and l == 5 and poserr == 1) | ||
507 | s = "unsigned" | ||
508 | r, l, poserr = g:match(s) | ||
509 | assert(r == nil and l == 5 and poserr == 1) | ||
510 | s = "unsigned a" | ||
511 | r, l, poserr = g:match(s) | ||
512 | assert(r == nil and l == 5 and poserr == 1) | ||
513 | s = "unsigned int" | ||
514 | r, l, poserr = g:match(s) | ||
515 | assert(r == nil and l == 5 and poserr == 1) | ||
516 | |||
517 | |||
518 | ------------------------------------------ | ||
519 | -- Tests related to labeled ordered choice | ||
520 | ------------------------------------------ | ||
521 | |||
522 | -- throws a label that is not caught by labeled choice | ||
523 | s = "abc" | ||
524 | p = m.Lc(m.T(2), m.P"a", 1, 3) | ||
525 | r, l, poserr = p:match(s) | ||
526 | assert(r == nil and l == 2 and poserr == 1) | ||
527 | |||
528 | -- modifies previous pattern | ||
529 | -- adds another labeled choice to catch label "2" | ||
530 | p = m.Lc(p, m.P"a", 2) | ||
531 | assert(p:match(s) == 2) | ||
532 | |||
533 | -- throws a label that is caught by labeled choice | ||
534 | p = m.Lc(m.T(25), m.P"a", 25) | ||
535 | assert(p:match(s) == 2) | ||
536 | |||
537 | -- "fail" is label "0" | ||
538 | -- throws the "fail" label that is not caught by the labeled choice | ||
539 | s = "bola" | ||
540 | r, l, poserr = p:match("bola") | ||
541 | assert(r == nil and l == 0 and poserr == 1) | ||
542 | |||
543 | -- labeled choice does not catch "fail" | ||
544 | p = m.Lc(m.P"b", m.P"a", 1) | ||
545 | |||
546 | r, l, poserr = p:match("abc") | ||
547 | assert(r == nil and l == 0 and poserr == 1) | ||
548 | assert(p:match("bola") == 2) | ||
549 | |||
550 | -- labeled choice catches "1" or "3" | ||
551 | p = m.Lc(-m.P"a" * m.T(1) + m.P"a" * m.T(3), m.P"a" + m.P"b", 1, 3) | ||
552 | assert(p:match("abc") == 2) | ||
553 | assert(p:match("bac") == 2) | ||
554 | |||
555 | -- associativity | ||
556 | -- (p1 / %1) /{1} (p2 / %2) /{2} p3 | ||
557 | -- left-associativity | ||
558 | -- ("a" /{1} "b") /{2} "c" | ||
559 | p = m.Lc(m.Lc(m.P"a" + m.T(1), m.P"b" + m.T(2), 1), m.P"c", 2) | ||
560 | assert(p:match("abc") == 2) | ||
561 | assert(p:match("bac") == 2) | ||
562 | assert(p:match("cab") == 2) | ||
563 | r, l, poserr = p:match("dab") | ||
564 | assert(r == nil and l == 0 and poserr == 1) | ||
565 | |||
566 | |||
567 | -- righ-associativity | ||
568 | -- "a" /{1} ("b" /{2} "c") | ||
569 | p = m.Lc(m.P"a" + m.T(1), m.Lc(m.P"b" + m.T(2), m.P"c", 2), 1) | ||
570 | assert(p:match("abc") == 2) | ||
571 | assert(p:match("bac") == 2) | ||
572 | assert(p:match("cab") == 2) | ||
573 | r, l, poserr = p:match("dab") | ||
574 | assert(r == nil and l == 0 and poserr == 1) | ||
575 | |||
576 | |||
577 | -- associativity -> in this case the error thrown by p1 is only | ||
578 | -- recovered when we have a left-associative operator | ||
579 | -- (p1 / %2) /{1} (p2 / %2) /{2} p3 | ||
580 | -- left-associativity | ||
581 | -- ("a" /{1} "b") /{2} "c" | ||
582 | p = m.Lc(m.Lc(m.P"a" + m.T(2), m.P"b" + m.T(2), 1), m.P"c", 2) | ||
583 | assert(p:match("abc") == 2) | ||
584 | r, l, poserr = p:match("bac") | ||
585 | assert(r == nil and l == 0 and poserr == 1) | ||
586 | assert(p:match("cab") == 2) | ||
587 | r, l, poserr = p:match("dab") | ||
588 | assert(r == nil and l == 0 and poserr == 1) | ||
589 | |||
590 | |||
591 | -- righ-associativity | ||
592 | -- "a" /{1} ("b" /{2} "c") | ||
593 | p = m.Lc(m.P"a" + m.T(2), m.Lc(m.P"b" + m.T(2), m.P"c", 2), 1) | ||
594 | assert(p:match("abc") == 2) | ||
595 | r, l, poserr = p:match("bac") | ||
596 | assert(r == nil and l == 2 and poserr == 1) | ||
597 | r, l, poserr = p:match("cab") | ||
598 | assert(r == nil and l == 2 and poserr == 1) | ||
599 | r, l, poserr = p:match("dab") | ||
600 | assert(r == nil and l == 2 and poserr == 1) | ||
601 | |||
602 | |||
603 | |||
604 | --[[ grammar based on Figure 8 of paper submitted to SCP (using labeled choice) | ||
605 | S -> S0 /{1} ID /{2} ID '=' Exp /{3} 'unsigned'* 'int' ID /{4} 'unsigned'* ID ID / %error | ||
606 | S0 -> ID S1 / 'unsigned' S2 / 'int' %3 | ||
607 | S1 -> '=' %2 / !. %1 / ID %4 | ||
608 | S2 -> 'unsigned' S2 / ID %4 / 'int' %3 | ||
609 | ]] | ||
610 | |||
611 | |||
612 | g = re.compile([[ | ||
613 | S <- S0 /{1} ID /{2} ID %s* '=' Exp /{3} U* Int ID /{4} U ID ID / %{5} | ||
614 | S0 <- ID S1 / U S2 / Int %{3} | ||
615 | S1 <- %s* '=' %{2} / !. %{1} / ID %{4} | ||
616 | S2 <- U S2 / ID %{4} / Int %{3} | ||
617 | ID <- %s* 'a' | 502 | ID <- %s* 'a' |
618 | U <- %s* 'unsigned' | 503 | U <- %s* 'unsigned' |
619 | Int <- %s* 'int' | 504 | Int <- %s* 'int' |
620 | Exp <- %s* 'E' | 505 | Exp <- %s* 'E' |
506 | L1 <- ID | ||
507 | L2 <- ID %s* '=' Exp | ||
508 | L3 <- U* Int ID | ||
509 | L4 <- U ID ID | ||
621 | ]]) | 510 | ]]) |
622 | 511 | ||
623 | local s = "a" | 512 | local s = "a" |
@@ -632,57 +521,52 @@ s = "unsigned a a" | |||
632 | assert(g:match(s) == #s + 1) --4 | 521 | assert(g:match(s) == #s + 1) --4 |
633 | s = "b" | 522 | s = "b" |
634 | r, l, poserr = g:match(s) | 523 | r, l, poserr = g:match(s) |
635 | assert(r == nil and l == 5 and poserr == 1) | 524 | assert(r == nil and l == 'L5' and poserr == 1) |
636 | s = "unsigned" | 525 | s = "unsigned" |
637 | r, l, poserr = g:match(s) | 526 | r, l, poserr = g:match(s) |
638 | assert(r == nil and l == 5 and poserr == 1) | 527 | assert(r == nil and l == 'L5' and poserr == 1) |
639 | s = "unsigned a" | 528 | s = "unsigned a" |
640 | r, l, poserr = g:match(s) | 529 | r, l, poserr = g:match(s) |
641 | assert(r == nil and l == 5 and poserr == 1) | 530 | assert(r == nil and l == 'L5' and poserr == 1) |
642 | s = "unsigned int" | 531 | s = "unsigned int" |
643 | r, l, poserr = g:match(s) | 532 | r, l, poserr = g:match(s) |
644 | assert(r == nil and l == 5 and poserr == 1) | 533 | assert(r == nil and l == 'L5' and poserr == 1) |
645 | 534 | ||
646 | 535 | ||
647 | 536 | ||
648 | local terror = { ['cmdSeq'] = "Missing ';' in CmdSeq", | 537 | local terror = { cmdSeq = "Missing ';' in CmdSeq", |
649 | ['ifExp'] = "Error in expresion of 'if'", | 538 | ifExp = "Error in expresion of 'if'", |
650 | ['ifThen'] = "Error matching 'then' keyword", | 539 | ifThen = "Error matching 'then' keyword", |
651 | ['ifThenCmdSeq'] = "Error matching CmdSeq of 'then' branch", | 540 | ifThenCmdSeq = "Error matching CmdSeq of 'then' branch", |
652 | ['ifElseCmdSeq'] = "Error matching CmdSeq of 'else' branch", | 541 | ifElseCmdSeq = "Error matching CmdSeq of 'else' branch", |
653 | ['ifEnd'] = "Error matching 'end' keyword of 'if'", | 542 | ifEnd = "Error matching 'end' keyword of 'if'", |
654 | ['repeatCmdSeq'] = "Error matching CmdSeq of 'repeat'", | 543 | repeatCmdSeq = "Error matching CmdSeq of 'repeat'", |
655 | ['repeatUntil'] = "Error matching 'until' keyword", | 544 | repeatUntil = "Error matching 'until' keyword", |
656 | ['repeatExp'] = "Error matching expression of 'until'", | 545 | repeatExp = "Error matching expression of 'until'", |
657 | ['assignOp'] = "Error matching ':='", | 546 | assignOp = "Error matching ':='", |
658 | ['assignExp'] = "Error matching expression of assignment", | 547 | assignExp = "Error matching expression of assignment", |
659 | ['readName'] = "Error matching 'NAME' after 'read'", | 548 | readName = "Error matching 'NAME' after 'read'", |
660 | ['writeExp'] = "Error matching expression after 'write'", | 549 | writeExp = "Error matching expression after 'write'", |
661 | ['simpleExp'] = "Error matching 'SimpleExp'", | 550 | simpleExp = "Error matching 'SimpleExp'", |
662 | ['term'] = "Error matching 'Term'", | 551 | term = "Error matching 'Term'", |
663 | ['factor'] = "Error matching 'Factor'", | 552 | factor = "Error matching 'Factor'", |
664 | ['openParExp'] = "Error matching expression after '('", | 553 | openParExp = "Error matching expression after '('", |
665 | ['closePar'] = "Error matching ')'", | 554 | closePar = "Error matching ')'", |
666 | ['undefined'] = "Undefined Error"} | 555 | undefined = "Undefined Error"} |
667 | 556 | ||
668 | g = re.compile([[ | 557 | g = re.compile([[ |
669 | Tiny <- CmdSeq //{1} '' -> cmdSeq //{2} '' -> ifExp //{3} '' -> ifThen //{4} '' -> ifThenCmdSeq | 558 | Tiny <- CmdSeq |
670 | //{5} '' -> ifElseCmdSeq //{6} '' -> ifEnd //{7} '' -> repeatCmdSeq | 559 | CmdSeq <- (Cmd SEMICOLON^cmdSeq) (Cmd SEMICOLON^cmdSeq)* |
671 | //{8} '' -> repeatUntil //{9} '' -> repeatExp //{10} '' -> assignOp | ||
672 | //{11} '' -> assignExp //{12} '' -> readName //{13} '' -> writeExp | ||
673 | //{14} '' -> simpleExp //{15} '' -> term //{16} '' -> factor | ||
674 | //{17} '' -> openParExp //{18} '' -> closePar / '' -> undefined | ||
675 | CmdSeq <- (Cmd (SEMICOLON / %{1})) (Cmd (SEMICOLON / %{1}))* | ||
676 | Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd | 560 | Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd |
677 | IfCmd <- IF (Exp / %{2}) (THEN / %{3}) (CmdSeq / %{4}) (ELSE (CmdSeq / %{5}) / '') (END / %{6}) | 561 | IfCmd <- IF Exp^ifExp THEN^ifThen CmdSeq^ifThenCmdSeq (ELSE CmdSeq^ifElseCmdSeq / '') END^ifEnd |
678 | RepeatCmd <- REPEAT (CmdSeq / %{7}) (UNTIL / %{8}) (Exp / %{9}) | 562 | RepeatCmd <- REPEAT CmdSeq^repeatCmdSeq UNTIL^repeatUntil Exp^repeatExp |
679 | AssignCmd <- NAME (ASSIGNMENT / %{10}) (Exp / %{11}) | 563 | AssignCmd <- NAME ASSIGNMENT^assignOp Exp^assignExp |
680 | ReadCmd <- READ (NAME / %{12}) | 564 | ReadCmd <- READ NAME^readName |
681 | WriteCmd <- WRITE (Exp / %{13}) | 565 | WriteCmd <- WRITE Exp^writeExp |
682 | Exp <- SimpleExp ((LESS / EQUAL) (SimpleExp / %{14}) / '') | 566 | Exp <- SimpleExp ((LESS / EQUAL) SimpleExp^simpleExp / '') |
683 | SimpleExp <- Term ((ADD / SUB) (Term / %{15}))* | 567 | SimpleExp <- Term ((ADD / SUB) Term^term)* |
684 | Term <- Factor ((MUL / DIV) (Factor / %{16}))* | 568 | Term <- Factor ((MUL / DIV) Factor^factor)* |
685 | Factor <- OPENPAR (Exp / %{17}) (CLOSEPAR / %{18}) / NUMBER / NAME | 569 | Factor <- OPENPAR Exp^openParExp CLOSEPAR^closePar / NUMBER / NAME |
686 | ADD <- Sp '+' | 570 | ADD <- Sp '+' |
687 | ASSIGNMENT <- Sp ':=' | 571 | ASSIGNMENT <- Sp ':=' |
688 | CLOSEPAR <- Sp ')' | 572 | CLOSEPAR <- Sp ')' |
@@ -724,7 +608,8 @@ assert(g:match(s) == #s + 1) | |||
724 | -- a ';' is missing in 'read a' | 608 | -- a ';' is missing in 'read a' |
725 | s = [[ | 609 | s = [[ |
726 | read a]] | 610 | read a]] |
727 | assert(g:match(s) == terror['cmdSeq']) | 611 | r, l, poserr = g:match(s) |
612 | assert(l == 'cmdSeq') | ||
728 | 613 | ||
729 | 614 | ||
730 | -- a ';' is missing in 'n := n - 1' | 615 | -- a ';' is missing in 'n := n - 1' |
@@ -736,7 +621,8 @@ repeat | |||
736 | n := n - 1 | 621 | n := n - 1 |
737 | until (n < 1); | 622 | until (n < 1); |
738 | write f;]] | 623 | write f;]] |
739 | assert(g:match(s) == terror['cmdSeq']) | 624 | r, l, poserr = g:match(s) |
625 | assert(l == 'cmdSeq') | ||
740 | 626 | ||
741 | 627 | ||
742 | -- IF expression | 628 | -- IF expression |
@@ -744,6 +630,7 @@ s = [[ | |||
744 | if a then a := a + 1; end;]] | 630 | if a then a := a + 1; end;]] |
745 | assert(g:match(s) == #s + 1) | 631 | assert(g:match(s) == #s + 1) |
746 | 632 | ||
633 | |||
747 | -- IF expression | 634 | -- IF expression |
748 | s = [[ | 635 | s = [[ |
749 | if a then a := a + 1; else write 2; end;]] | 636 | if a then a := a + 1; else write 2; end;]] |
@@ -752,114 +639,136 @@ assert(g:match(s) == #s + 1) | |||
752 | -- Error in expression of 'if'. 'A' is not a valida name | 639 | -- Error in expression of 'if'. 'A' is not a valida name |
753 | s = [[ | 640 | s = [[ |
754 | if A then a := a + 1; else write 2; end;]] | 641 | if A then a := a + 1; else write 2; end;]] |
755 | assert(g:match(s) == terror['ifExp']) | 642 | r, l, poserr = g:match(s) |
643 | assert(l == 'ifExp') | ||
756 | 644 | ||
757 | -- Error matching the 'then' keyword | 645 | -- Error matching the 'then' keyword |
758 | s = [[ | 646 | s = [[ |
759 | if a a := a + 1; else write 2; end;]] | 647 | if a a := a + 1; else write 2; end;]] |
760 | assert(g:match(s) == terror['ifThen']) | 648 | r, l, poserr = g:match(s) |
649 | assert(l == 'ifThen') | ||
761 | 650 | ||
762 | -- Error matching the CmdSeq inside of 'then' branch | 651 | -- Error matching the CmdSeq inside of 'then' branch |
763 | s = [[ | 652 | s = [[ |
764 | if a then 3 := 2; else write 2; end;]] | 653 | if a then 3 := 2; else write 2; end;]] |
765 | assert(g:match(s) == terror['ifThenCmdSeq']) | 654 | r, l, poserr = g:match(s) |
655 | assert(l == 'ifThenCmdSeq') | ||
766 | 656 | ||
767 | -- Error matching the CmdSeq inside of 'else' branch | 657 | -- Error matching the CmdSeq inside of 'else' branch |
768 | s = [[ | 658 | s = [[ |
769 | if a then b := 2; else A := 2; end;]] | 659 | if a then b := 2; else A := 2; end;]] |
770 | assert(g:match(s) == terror['ifElseCmdSeq']) | 660 | r, l, poserr = g:match(s) |
661 | assert(l == 'ifElseCmdSeq') | ||
771 | 662 | ||
772 | -- Error matching 'end' of 'if' | 663 | -- Error matching 'end' of 'if' |
773 | s = [[ | 664 | s = [[ |
774 | if a then b := 2; else a := 2; 77;]] | 665 | if a then b := 2; else a := 2; 77;]] |
775 | assert(g:match(s) == terror['ifEnd']) | 666 | r, l, poserr = g:match(s) |
667 | assert(l == 'ifEnd') | ||
776 | 668 | ||
777 | -- Error matching the CmdSeq of 'repeat' | 669 | -- Error matching the CmdSeq of 'repeat' |
778 | s = [[repeat | 670 | s = [[repeat |
779 | F := f * n; | 671 | F := f * n; |
780 | n := n - 1; | 672 | n := n - 1; |
781 | until (n < 1);]] | 673 | until (n < 1);]] |
782 | assert(g:match(s) == terror['repeatCmdSeq']) | 674 | r, l, poserr = g:match(s) |
675 | assert(l == 'repeatCmdSeq') | ||
783 | 676 | ||
784 | -- Error matching 'until' | 677 | -- Error matching 'until' |
785 | s = [[repeat | 678 | s = [[repeat |
786 | f := f * n; | 679 | f := f * n; |
787 | n := n - 1; | 680 | n := n - 1; |
788 | 88 (n < 1);]] | 681 | 88 (n < 1);]] |
789 | assert(g:match(s) == terror['repeatUntil']) | 682 | r, l, poserr = g:match(s) |
683 | assert(l == 'repeatUntil') | ||
790 | 684 | ||
791 | -- Error matching expression of 'until' | 685 | -- Error matching expression of 'until' |
792 | s = [[repeat | 686 | s = [[repeat |
793 | f := f * n; | 687 | f := f * n; |
794 | n := n - 1; | 688 | n := n - 1; |
795 | until ; (n < 1);]] | 689 | until ; (n < 1);]] |
796 | assert(g:match(s) == terror['repeatExp']) | 690 | r, l, poserr = g:match(s) |
691 | assert(l == 'repeatExp') | ||
797 | 692 | ||
798 | -- Error matching ':=' | 693 | -- Error matching ':=' |
799 | s = [[ | 694 | s = [[ |
800 | f = f * n;]] | 695 | f = f * n;]] |
801 | assert(g:match(s) == terror['assignOp']) | 696 | r, l, poserr = g:match(s) |
697 | assert(l == 'assignOp') | ||
802 | 698 | ||
803 | -- Error matching expression of assignment | 699 | -- Error matching expression of assignment |
804 | s = [[ | 700 | s = [[ |
805 | f := A * n;]] | 701 | f := A * n;]] |
806 | assert(g:match(s) == terror['assignExp']) | 702 | r, l, poserr = g:match(s) |
703 | assert(l == 'assignExp') | ||
807 | 704 | ||
808 | -- Error matching 'name' | 705 | -- Error matching 'name' |
809 | s = [[ | 706 | s = [[ |
810 | read 2;]] | 707 | read 2;]] |
811 | assert(g:match(s) == terror['readName']) | 708 | r, l, poserr = g:match(s) |
709 | assert(l == 'readName') | ||
812 | 710 | ||
813 | -- Error matching expression after 'write' | 711 | -- Error matching expression after 'write' |
814 | s = [[ | 712 | s = [[ |
815 | write [a] := 2;]] | 713 | write [a] := 2;]] |
816 | assert(g:match(s) == terror['writeExp']) | 714 | r, l, poserr = g:match(s) |
715 | assert(l == 'writeExp') | ||
817 | 716 | ||
818 | -- Error matching 'SimpleExp' | 717 | -- Error matching 'SimpleExp' |
819 | s = [[ | 718 | s = [[ |
820 | a := a < A;]] | 719 | a := a < A;]] |
821 | assert(g:match(s) == terror['simpleExp']) | 720 | r, l, poserr = g:match(s) |
721 | assert(l == 'simpleExp') | ||
822 | 722 | ||
823 | -- Error matching 'Term' | 723 | -- Error matching 'Term' |
824 | s = [[ | 724 | s = [[ |
825 | a := a + A;]] | 725 | a := a + A;]] |
826 | assert(g:match(s) == terror['term']) | 726 | r, l, poserr = g:match(s) |
727 | assert(l == 'term') | ||
827 | 728 | ||
828 | -- Error matching 'Factor' | 729 | -- Error matching 'Factor' |
829 | s = [[ | 730 | s = [[ |
830 | a := a * A;]] | 731 | a := a * A;]] |
831 | assert(g:match(s) == terror['factor']) | 732 | r, l, poserr = g:match(s) |
733 | assert(l == 'factor') | ||
832 | 734 | ||
833 | -- Error matching expression after '(' | 735 | -- Error matching expression after '(' |
834 | s = [[ | 736 | s = [[ |
835 | a := (A);]] | 737 | a := (A);]] |
836 | assert(g:match(s) == terror['openParExp']) | 738 | r, l, poserr = g:match(s) |
739 | assert(l == 'openParExp') | ||
837 | 740 | ||
838 | -- Error matching ')' | 741 | -- Error matching ')' |
839 | s = [[ | 742 | s = [[ |
840 | a := (a];]] | 743 | a := (a];]] |
841 | assert(g:match(s) == terror['closePar']) | 744 | r, l, poserr = g:match(s) |
745 | assert(l == 'closePar') | ||
842 | 746 | ||
843 | -- Error undefined | 747 | -- Error undefined |
844 | s = [[ | 748 | s = [[ |
845 | A := a;]] | 749 | A := a;]] |
846 | assert(g:match(s) == terror['undefined']) | 750 | r, l, poserr = g:match(s) |
751 | assert(l == 'fail') | ||
847 | 752 | ||
848 | 753 | ||
849 | print("+") | 754 | print("+") |
850 | 755 | ||
851 | 756 | ||
852 | p = m.Rec("a", "b", 3) | 757 | p = m.P{ |
758 | "A", | ||
759 | A = m.P"a", | ||
760 | B = m.P"b" | ||
761 | } | ||
853 | assert(p:match("a") == 2) | 762 | assert(p:match("a") == 2) |
854 | checklabeq({nil, 0, 1}, p:match("b")) | 763 | checklabeq({nil, 0, 1}, p:match("b")) |
855 | checklabeq({nil, 0, 1}, p:match("c")) | 764 | checklabeq({nil, 0, 1}, p:match("c")) |
856 | 765 | ||
857 | p = m.Rec(m.T(3), "b", 1) | 766 | p = m.P{ |
858 | checklabeq({nil, 3, 1}, p:match("a")) | 767 | "A", |
859 | checklabeq({nil, 3, 1}, p:match("b")) | 768 | A = m.T"B", |
860 | 769 | B = m.P"b" | |
861 | p = m.Rec(m.T(3), "b", 3) | 770 | } |
862 | checklabeq({nil, 0, 1}, p:match("a")) | 771 | checklabeq({nil, 'fail', 1}, p:match("a")) |
863 | assert(p:match("b") == 2) | 772 | assert(p:match("b") == 2) |
864 | 773 | ||
865 | --[[ | 774 | --[[ |
@@ -869,9 +778,10 @@ C -> c+ | |||
869 | ]] | 778 | ]] |
870 | g = m.P{ | 779 | g = m.P{ |
871 | "S", | 780 | "S", |
872 | S = m.Rec(m.V"A", (-m.P"c" * m.P(1))^0, 128) * m.V"C", | 781 | S = m.V"A" * m.V"C", |
873 | A = m.P"a"^0 * "b" + m.T(128), | 782 | A = m.P"a"^0 * "b" + m.T'Rec', |
874 | C = m.P"c"^1, | 783 | C = m.P"c"^1, |
784 | Rec = (-m.P"c" * m.P(1))^0 | ||
875 | } | 785 | } |
876 | 786 | ||
877 | assert(g:match("abc") == 4) | 787 | assert(g:match("abc") == 4) |
@@ -892,9 +802,10 @@ C -> c+ | |||
892 | ]] | 802 | ]] |
893 | g = m.P{ | 803 | g = m.P{ |
894 | "S", | 804 | "S", |
895 | S = m.Rec(m.V"A", (-m.P"c" * m.P(1))^0, 99) * m.V"C", | 805 | S = m.V"A" * m.V"C", |
896 | A = m.P"a"^1 * ("b" + m.T(99)), | 806 | A = m.P"a"^1 * ("b" + m.T'Rec'), |
897 | C = m.P"c"^1, | 807 | C = m.P"c"^1, |
808 | Rec = (-m.P"c" * m.P(1))^0 | ||
898 | } | 809 | } |
899 | 810 | ||
900 | assert(g:match("abc") == 4) | 811 | assert(g:match("abc") == 4) |
@@ -916,34 +827,24 @@ lpeg = m | |||
916 | 827 | ||
917 | local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V | 828 | local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V |
918 | local C, Cc, Ct, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt | 829 | local C, Cc, Ct, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt |
919 | local T, Lc = lpeg.T, lpeg.Lc | 830 | local T = lpeg.T |
920 | 831 | ||
921 | local labels = { | 832 | local labels = { |
922 | {"NoExp", "no expression found"}, | 833 | NoExp = "no expression found", |
923 | {"Extra", "extra characters found after the expression"}, | 834 | Extra = "extra characters found after the expression", |
924 | {"ExpTerm", "expected a term after the operator"}, | 835 | ExpTerm = "expected a term after the operator", |
925 | {"ExpExp", "expected an expression after the parenthesis"}, | 836 | ExpExp = "expected an expression after the parenthesis", |
926 | {"MisClose", "missing a closing ')' after the expression"}, | 837 | MisClose = "missing a closing ')' after the expression", |
927 | } | 838 | } |
928 | 839 | ||
929 | local function labelindex(labname) | ||
930 | for i, elem in ipairs(labels) do | ||
931 | if elem[1] == labname then | ||
932 | return i | ||
933 | end | ||
934 | end | ||
935 | error("could not find label: " .. labname) | ||
936 | end | ||
937 | |||
938 | local errors = {} | 840 | local errors = {} |
939 | 841 | ||
940 | local function expect(patt, labname) | 842 | local function expect(patt, labname) |
941 | local i = labelindex(labname) | ||
942 | function recorderror(input, pos) | 843 | function recorderror(input, pos) |
943 | table.insert(errors, {i, pos}) | 844 | table.insert(errors, {labname, pos}) |
944 | return true | 845 | return true |
945 | end | 846 | end |
946 | return patt + Cmt("", recorderror) * T(i) | 847 | return patt + Cmt("", recorderror) * T(labname) |
947 | end | 848 | end |
948 | 849 | ||
949 | local num = R("09")^1 / tonumber | 850 | local num = R("09")^1 / tonumber |
@@ -969,12 +870,14 @@ end | |||
969 | 870 | ||
970 | local g = P { | 871 | local g = P { |
971 | "Exp", | 872 | "Exp", |
972 | Exp = Ct(V"Term" * (C(op) * V"OpRecov")^0) / compute; | 873 | Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute; |
973 | OpRecov = m.Rec(V"Operand", Cc(0), labelindex("ExpTerm")); | ||
974 | Operand = expect(V"Term", "ExpTerm"); | 874 | Operand = expect(V"Term", "ExpTerm"); |
975 | Term = num + V"Group"; | 875 | Term = num + V"Group"; |
976 | Group = "(" * V"InnerExp" * m.Rec(expect(")", "MisClose"), P"", labelindex("MisClose")); | 876 | Group = "(" * V"InnerExp" * expect(")", "MisClose"); |
977 | InnerExp = m.Rec(expect(V"Exp", "ExpExp"), (P(1) - ")")^0 * Cc(0), labelindex("ExpExp")); | 877 | InnerExp = expect(V"Exp", "ExpExp"); |
878 | ExpTerm = Cc(0); | ||
879 | MisClose = P""; | ||
880 | ExpExp = (P(1) - ")")^0 * Cc(0); | ||
978 | } | 881 | } |
979 | 882 | ||
980 | g = expect(g, "NoExp") * expect(-P(1), "Extra") | 883 | g = expect(g, "NoExp") * expect(-P(1), "Extra") |
@@ -987,7 +890,7 @@ local function eval(input) | |||
987 | local out = {} | 890 | local out = {} |
988 | for i, err in ipairs(errors) do | 891 | for i, err in ipairs(errors) do |
989 | local pos = err[2] | 892 | local pos = err[2] |
990 | local msg = labels[err[1]][2] | 893 | local msg = labels[err[1]] |
991 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") | 894 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") |
992 | end | 895 | end |
993 | errors = {} | 896 | errors = {} |
@@ -1022,51 +925,66 @@ assert(e == nil and msg == "syntax error: expected a term after the operator (at | |||
1022 | 925 | ||
1023 | print("+") | 926 | print("+") |
1024 | 927 | ||
1025 | local g = m.P{ | 928 | local g1 = { |
1026 | "S", | 929 | "S1", |
1027 | S = V"End" + V'A' * V'S', | 930 | S1 = V"End" + V'A' * V'S1', |
1028 | A = P'a' + T(1), | 931 | A = P'a' + T'Ea', |
1029 | End = P"." * (-P(1) + T(2)), | 932 | End = P"." * (-P(1) + T'Eend'), |
1030 | } | 933 | } |
1031 | 934 | ||
1032 | assert(g:match("a.") == 3) | 935 | local p1 = m.P(g1) |
1033 | assert(g:match("aa.") == 4) | ||
1034 | assert(g:match(".") == 2) | ||
1035 | checklabeq({nil, 1, 1}, g:match("ba.")) | ||
1036 | checklabeq({nil, 1, 2}, g:match("aba.")) | ||
1037 | checklabeq({nil, 1, 1}, g:match("cba.")) | ||
1038 | checklabeq({nil, 2, 3}, g:match("a.a")) | ||
1039 | 936 | ||
937 | assert(p1:match("a.") == 3) | ||
938 | assert(p1:match("aa.") == 4) | ||
939 | assert(p1:match(".") == 2) | ||
940 | checklabeq({nil, 'Ea', 1}, p1:match("ba.")) | ||
941 | checklabeq({nil, 'Ea', 2}, p1:match("aba.")) | ||
942 | checklabeq({nil, 'Ea', 1}, p1:match("cba.")) | ||
943 | checklabeq({nil, 'Eend', 3}, p1:match("a.a")) | ||
1040 | 944 | ||
1041 | local g2 = m.P{ | 945 | local g2 = { |
1042 | "S", | 946 | "S2", |
1043 | S = m.Rec(g, V"B", 1), | 947 | S2 = m.V"S1", |
1044 | B = P'b'^1 + T(3) | 948 | Ea = P'b'^1 + T'Eb', |
1045 | } | 949 | } |
1046 | 950 | ||
1047 | assert(g2:match("a.") == 3) | 951 | g1[1] = nil -- start rule |
1048 | assert(g2:match("aa.") == 4) | 952 | for k, v in pairs(g1) do |
1049 | assert(g2:match(".") == 2) | 953 | g2[k] = v |
1050 | assert(g2:match("ba.") == 4) | 954 | end |
1051 | assert(g2:match("aba.") == 5) | ||
1052 | checklabeq({nil, 3, 1}, g2:match("cba.")) | ||
1053 | checklabeq({nil, 2, 3}, g2:match("a.a")) | ||
1054 | 955 | ||
1055 | local g3 = m.P{ | 956 | local p2 = m.P(g2) |
1056 | "S", | 957 | |
1057 | S = m.Rec(g2, V"C", 2, 3), | 958 | assert(p2:match("a.") == 3) |
1058 | C = P'c'^1 + T(4) | 959 | assert(p2:match("aa.") == 4) |
960 | assert(p2:match(".") == 2) | ||
961 | assert(p2:match("ba.") == 4) | ||
962 | assert(p2:match("aba.") == 5) | ||
963 | checklabeq({nil, 'Eb', 1}, p2:match("cba.")) | ||
964 | checklabeq({nil, 'Eend', 3}, p2:match("a.a")) | ||
965 | |||
966 | local g3 = { | ||
967 | "S3", | ||
968 | S3 = V'S2', | ||
969 | Eb = P'c'^1 + T'Ec', | ||
970 | Eend = V'Eb', | ||
1059 | } | 971 | } |
972 | g2[1] = nil -- start rule | ||
973 | for k, v in pairs(g2) do | ||
974 | g3[k] = v | ||
975 | end | ||
976 | |||
977 | local p3 = m.P(g3) | ||
1060 | 978 | ||
1061 | assert(g3:match("a.") == 3) | 979 | assert(p3:match("a.") == 3) |
1062 | assert(g3:match("aa.") == 4) | 980 | assert(p3:match("aa.") == 4) |
1063 | assert(g3:match(".") == 2) | 981 | assert(p3:match(".") == 2) |
1064 | assert(g3:match("ba.") == 4) | 982 | assert(p3:match("ba.") == 4) |
1065 | assert(g3:match("aba.") == 5) | 983 | assert(p3:match("aba.") == 5) |
1066 | assert(g3:match("cba.") == 5) | 984 | assert(p3:match("cba.") == 5) |
1067 | checklabeq({nil, 4, 3}, g3:match("a.a")) | 985 | checklabeq({nil, 'Ec', 3}, p3:match("a.a")) |
1068 | checklabeq({nil, 4, 1}, g3:match("dc")) | 986 | checklabeq({nil, 'Ec', 1}, p3:match("dc")) |
1069 | checklabeq({nil, 4, 2}, g3:match(".d")) | 987 | checklabeq({nil, 'Ec', 2}, p3:match(".d")) |
1070 | 988 | ||
1071 | 989 | ||
1072 | -- testing more captures | 990 | -- testing more captures |
@@ -1078,7 +996,11 @@ local g = re.compile[[ | |||
1078 | checkeq({"523", "624", "346", "888"} , {g:match("523 624 346\n888")}) | 996 | checkeq({"523", "624", "346", "888"} , {g:match("523 624 346\n888")}) |
1079 | checkeq({nil, 5, 4}, {g:match("44 a 123")}) | 997 | checkeq({nil, 5, 4}, {g:match("44 a 123")}) |
1080 | 998 | ||
1081 | local g2 = m.Rec(g, ((-m.R("09") * m.P(1))^0) / "58", 5) | 999 | local g2 = re.compile[[ |
1000 | S <- ( %s* &. {A} )* | ||
1001 | A <- [0-9]+ / %{Rec} | ||
1002 | Rec <- ((![0-9] .)*) -> "58" | ||
1003 | ]] | ||
1082 | 1004 | ||
1083 | checkeq({"523", "624", "346", "888"} , {g2:match("523 624 346\n888")}) | 1005 | checkeq({"523", "624", "346", "888"} , {g2:match("523 624 346\n888")}) |
1084 | checkeq({"44", "a ", "58", "123"}, {g2:match("44 a 123")}) | 1006 | checkeq({"44", "a ", "58", "123"}, {g2:match("44 a 123")}) |
@@ -1092,7 +1014,11 @@ local g = re.compile[[ | |||
1092 | checkeq({"523", "624", "346", "888"} , {g:match("523 624 346\n888")}) | 1014 | checkeq({"523", "624", "346", "888"} , {g:match("523 624 346\n888")}) |
1093 | checkeq({nil, 5, 4}, {g:match("44 a 123")}) | 1015 | checkeq({nil, 5, 4}, {g:match("44 a 123")}) |
1094 | 1016 | ||
1095 | local g2 = m.Rec(g, ((-m.R("09") * m.P(1))^0) / "58", 5) | 1017 | local g2 = re.compile[[ |
1018 | S <- ( %s* &. A )* | ||
1019 | A <- {[0-9]+} / %{Rec} | ||
1020 | Rec <- ((![0-9] .)*) -> "58" | ||
1021 | ]] | ||
1096 | 1022 | ||
1097 | checkeq({"523", "624", "346", "888"} , {g2:match("523 624 346\n888")}) | 1023 | checkeq({"523", "624", "346", "888"} , {g2:match("523 624 346\n888")}) |
1098 | checkeq({"44", "58", "123"}, {g2:match("44 a 123")}) | 1024 | checkeq({"44", "58", "123"}, {g2:match("44 a 123")}) |
@@ -1100,7 +1026,7 @@ checkeq({"44", "58", "123"}, {g2:match("44 a 123")}) | |||
1100 | 1026 | ||
1101 | local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V | 1027 | local R, S, P, V = lpeg.R, lpeg.S, lpeg.P, lpeg.V |
1102 | local C, Cc, Ct, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt | 1028 | local C, Cc, Ct, Cmt = lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.Cmt |
1103 | local T, Lc, Rec = lpeg.T, lpeg.Lc, lpeg.Rec | 1029 | local T = lpeg.T |
1104 | 1030 | ||
1105 | local labels = { | 1031 | local labels = { |
1106 | {"NoExp", "no expression found"}, | 1032 | {"NoExp", "no expression found"}, |
@@ -1110,26 +1036,16 @@ local labels = { | |||
1110 | {"MisClose", "missing a closing ')' after the expression"}, | 1036 | {"MisClose", "missing a closing ')' after the expression"}, |
1111 | } | 1037 | } |
1112 | 1038 | ||
1113 | local function labelindex(labname) | ||
1114 | for i, elem in ipairs(labels) do | ||
1115 | if elem[1] == labname then | ||
1116 | return i | ||
1117 | end | ||
1118 | end | ||
1119 | error("could not find label: " .. labname) | ||
1120 | end | ||
1121 | |||
1122 | local errors = {} | 1039 | local errors = {} |
1123 | 1040 | ||
1124 | local function expect(patt, labname, recpatt) | 1041 | local function expect(patt, labname, recpatt) |
1125 | local i = labelindex(labname) | ||
1126 | function recorderror(input, pos) | 1042 | function recorderror(input, pos) |
1127 | table.insert(errors, {i, pos}) | 1043 | table.insert(errors, {labname, pos}) |
1128 | return true | 1044 | return true |
1129 | end | 1045 | end |
1130 | if not recpatt then recpatt = P"" end | 1046 | if not recpatt then recpatt = P"" end |
1131 | --return Rec(patt, Cmt("", recorderror) * recpatt) | 1047 | --return Rec(patt, Cmt("", recorderror) * recpatt) |
1132 | return patt + T(i) | 1048 | return patt + T(labname) |
1133 | end | 1049 | end |
1134 | 1050 | ||
1135 | local num = R("09")^1 / tonumber | 1051 | local num = R("09")^1 / tonumber |
@@ -1155,22 +1071,22 @@ end | |||
1155 | 1071 | ||
1156 | 1072 | ||
1157 | local g = P { | 1073 | local g = P { |
1158 | "Exp", | 1074 | "Exp", |
1159 | Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute, | 1075 | Exp = Ct(V"Term" * (C(op) * V"Operand")^0) / compute, |
1160 | Operand = expect(V"Term", "ExpTerm"), | 1076 | Operand = expect(V"Term", "ExpTerm"), |
1161 | Term = num, | 1077 | Term = num, |
1078 | ExpTerm = Cc(3) | ||
1162 | } | 1079 | } |
1163 | local rg = Rec(g, Cc(3), labelindex("ExpTerm")) | ||
1164 | 1080 | ||
1165 | local function eval(input) | 1081 | local function eval(input) |
1166 | local result, label, suffix = rg:match(input) | 1082 | local result, label, suffix = g:match(input) |
1167 | if #errors == 0 then | 1083 | if #errors == 0 then |
1168 | return result | 1084 | return result |
1169 | else | 1085 | else |
1170 | local out = {} | 1086 | local out = {} |
1171 | for i, err in ipairs(errors) do | 1087 | for i, err in ipairs(errors) do |
1172 | local pos = err[2] | 1088 | local pos = err[2] |
1173 | local msg = labels[err[1]][2] | 1089 | local msg = labels[err[1]] |
1174 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") | 1090 | table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")") |
1175 | end | 1091 | end |
1176 | errors = {} | 1092 | errors = {} |
@@ -1184,218 +1100,4 @@ assert(eval("98-76*54/32") == 37.125) | |||
1184 | assert(eval("1+") == 4) | 1100 | assert(eval("1+") == 4) |
1185 | --> syntax error: expected a term after the operator (at index 3) | 1101 | --> syntax error: expected a term after the operator (at index 3) |
1186 | 1102 | ||
1187 | |||
1188 | -- tests related to the use of '^' in relabel to throw labels | ||
1189 | local errinfo = { | ||
1190 | { 'cmdSeq', "Missing ';' in CmdSeq"}, | ||
1191 | { 'ifExp', "Error in expresion of 'if'"}, | ||
1192 | { 'ifThen', "Error matching 'then' keyword"}, | ||
1193 | { 'ifThenCmdSeq', "Error matching CmdSeq of 'then' branch"}, | ||
1194 | { 'ifElseCmdSeq', "Error matching CmdSeq of 'else' branch"}, | ||
1195 | { 'ifEnd', "Error matching 'end' keyword of 'if'"}, | ||
1196 | { 'repeatCmdSeq', "Error matching CmdSeq of 'repeat'"}, | ||
1197 | { 'repeatUntil', "Error matching 'until' keyword"}, | ||
1198 | { 'repeatExp', "Error matching expression of 'until'"}, | ||
1199 | { 'assignOp', "Error matching ':='"}, | ||
1200 | { 'assignExp', "Error matching expression of assignment"}, | ||
1201 | { 'readName', "Error matching 'NAME' after 'read'"}, | ||
1202 | { 'writeExp', "Error matching expression after 'write'"}, | ||
1203 | { 'simpleExp', "Error matching 'SimpleExp'"}, | ||
1204 | { 'term', "Error matching 'Term'"}, | ||
1205 | { 'factor', "Error matching 'Factor'"}, | ||
1206 | { 'openParExp', "Error matching expression after '('"}, | ||
1207 | { 'closePar', "Error matching ')'"}, | ||
1208 | { 'undefined', "Undefined Error" } | ||
1209 | } | ||
1210 | |||
1211 | |||
1212 | local errmsgs = {} | ||
1213 | local labels = {} | ||
1214 | |||
1215 | for i, err in ipairs(errinfo) do | ||
1216 | errmsgs[i] = err[2] | ||
1217 | labels[err[1]] = i | ||
1218 | end | ||
1219 | |||
1220 | re.setlabels(labels) | ||
1221 | |||
1222 | g = re.compile([[ | ||
1223 | Tiny <- CmdSeq | ||
1224 | CmdSeq <- (Cmd SEMICOLON^cmdSeq) (Cmd SEMICOLON^cmdSeq)* | ||
1225 | Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd | ||
1226 | IfCmd <- IF Exp^ifExp THEN^ifThen CmdSeq^ifThenCmdSeq (ELSE CmdSeq^ifElseCmdSeq / '') END^ifEnd | ||
1227 | RepeatCmd <- REPEAT CmdSeq^repeatCmdSeq UNTIL^repeatUntil Exp^repeatExp | ||
1228 | AssignCmd <- NAME ASSIGNMENT^assignOp Exp^assignExp | ||
1229 | ReadCmd <- READ NAME^readName | ||
1230 | WriteCmd <- WRITE Exp^writeExp | ||
1231 | Exp <- SimpleExp ((LESS / EQUAL) SimpleExp^simpleExp / '') | ||
1232 | SimpleExp <- Term ((ADD / SUB) Term^term)* | ||
1233 | Term <- Factor ((MUL / DIV) Factor^factor)* | ||
1234 | Factor <- OPENPAR Exp^openParExp CLOSEPAR^closePar / NUMBER / NAME | ||
1235 | ADD <- Sp '+' | ||
1236 | ASSIGNMENT <- Sp ':=' | ||
1237 | CLOSEPAR <- Sp ')' | ||
1238 | DIV <- Sp '/' | ||
1239 | IF <- Sp 'if' | ||
1240 | ELSE <- Sp 'else' | ||
1241 | END <- Sp 'end' | ||
1242 | EQUAL <- Sp '=' | ||
1243 | LESS <- Sp '<' | ||
1244 | MUL <- Sp '*' | ||
1245 | NAME <- !RESERVED Sp [a-z]+ | ||
1246 | NUMBER <- Sp [0-9]+ | ||
1247 | OPENPAR <- Sp '(' | ||
1248 | READ <- Sp 'read' | ||
1249 | REPEAT <- Sp 'repeat' | ||
1250 | SEMICOLON <- Sp ';' | ||
1251 | SUB <- Sp '-' | ||
1252 | THEN <- Sp 'then' | ||
1253 | UNTIL <- Sp 'until' | ||
1254 | WRITE <- Sp 'write' | ||
1255 | RESERVED <- (IF / ELSE / END / READ / REPEAT / THEN / UNTIL / WRITE) ![a-z]+ | ||
1256 | Sp <- (%s / %nl)* | ||
1257 | ]]) | ||
1258 | |||
1259 | s = [[ | ||
1260 | n := 5;]] | ||
1261 | assert(g:match(s) == #s + 1) | ||
1262 | |||
1263 | s = [[ | ||
1264 | n := 5; | ||
1265 | f := 1; | ||
1266 | repeat | ||
1267 | f := f * n; | ||
1268 | n := n - 1; | ||
1269 | until (n < 1); | ||
1270 | write f;]] | ||
1271 | assert(g:match(s) == #s + 1) | ||
1272 | |||
1273 | -- a ';' is missing in 'read a' | ||
1274 | s = [[ | ||
1275 | read a]] | ||
1276 | assert(table.pack(g:match(s))[2] == labels['cmdSeq']) | ||
1277 | |||
1278 | |||
1279 | -- a ';' is missing in 'n := n - 1' | ||
1280 | s = [[ | ||
1281 | n := 5; | ||
1282 | f := 1; | ||
1283 | repeat | ||
1284 | f := f * n; | ||
1285 | n := n - 1 | ||
1286 | until (n < 1); | ||
1287 | write f;]] | ||
1288 | assert(table.pack(g:match(s))[2] == labels['cmdSeq']) | ||
1289 | |||
1290 | |||
1291 | -- IF expression | ||
1292 | s = [[ | ||
1293 | if a then a := a + 1; end;]] | ||
1294 | assert(g:match(s) == #s + 1) | ||
1295 | |||
1296 | -- IF expression | ||
1297 | s = [[ | ||
1298 | if a then a := a + 1; else write 2; end;]] | ||
1299 | assert(g:match(s) == #s + 1) | ||
1300 | |||
1301 | -- Error in expression of 'if'. 'A' is not a valida name | ||
1302 | s = [[ | ||
1303 | if A then a := a + 1; else write 2; end;]] | ||
1304 | assert(table.pack(g:match(s))[2] == labels['ifExp']) | ||
1305 | |||
1306 | -- Error matching the 'then' keyword | ||
1307 | s = [[ | ||
1308 | if a a := a + 1; else write 2; end;]] | ||
1309 | assert(table.pack(g:match(s))[2] == labels['ifThen']) | ||
1310 | |||
1311 | -- Error matching the CmdSeq inside of 'then' branch | ||
1312 | s = [[ | ||
1313 | if a then 3 := 2; else write 2; end;]] | ||
1314 | assert(table.pack(g:match(s))[2] == labels['ifThenCmdSeq']) | ||
1315 | |||
1316 | -- Error matching the CmdSeq inside of 'else' branch | ||
1317 | s = [[ | ||
1318 | if a then b := 2; else A := 2; end;]] | ||
1319 | assert(table.pack(g:match(s))[2] == labels['ifElseCmdSeq']) | ||
1320 | |||
1321 | -- Error matching 'end' of 'if' | ||
1322 | s = [[ | ||
1323 | if a then b := 2; else a := 2; 77;]] | ||
1324 | assert(table.pack(g:match(s))[2] == labels['ifEnd']) | ||
1325 | |||
1326 | -- Error matching the CmdSeq of 'repeat' | ||
1327 | s = [[repeat | ||
1328 | F := f * n; | ||
1329 | n := n - 1; | ||
1330 | until (n < 1);]] | ||
1331 | assert(table.pack(g:match(s))[2] == labels['repeatCmdSeq']) | ||
1332 | |||
1333 | -- Error matching 'until' | ||
1334 | s = [[repeat | ||
1335 | f := f * n; | ||
1336 | n := n - 1; | ||
1337 | 88 (n < 1);]] | ||
1338 | assert(table.pack(g:match(s))[2] == labels['repeatUntil']) | ||
1339 | |||
1340 | -- Error matching expression of 'until' | ||
1341 | s = [[repeat | ||
1342 | f := f * n; | ||
1343 | n := n - 1; | ||
1344 | until ; (n < 1);]] | ||
1345 | assert(table.pack(g:match(s))[2] == labels['repeatExp']) | ||
1346 | |||
1347 | -- Error matching ':=' | ||
1348 | s = [[ | ||
1349 | f = f * n;]] | ||
1350 | assert(table.pack(g:match(s))[2] == labels['assignOp']) | ||
1351 | |||
1352 | -- Error matching expression of assignment | ||
1353 | s = [[ | ||
1354 | f := A * n;]] | ||
1355 | assert(table.pack(g:match(s))[2] == labels['assignExp']) | ||
1356 | |||
1357 | -- Error matching 'name' | ||
1358 | s = [[ | ||
1359 | read 2;]] | ||
1360 | assert(table.pack(g:match(s))[2] == labels['readName']) | ||
1361 | |||
1362 | -- Error matching expression after 'write' | ||
1363 | s = [[ | ||
1364 | write [a] := 2;]] | ||
1365 | assert(table.pack(g:match(s))[2] == labels['writeExp']) | ||
1366 | |||
1367 | -- Error matching 'SimpleExp' | ||
1368 | s = [[ | ||
1369 | a := a < A;]] | ||
1370 | assert(table.pack(g:match(s))[2] == labels['simpleExp']) | ||
1371 | |||
1372 | -- Error matching 'Term' | ||
1373 | s = [[ | ||
1374 | a := a + A;]] | ||
1375 | assert(table.pack(g:match(s))[2] == labels['term']) | ||
1376 | |||
1377 | -- Error matching 'Factor' | ||
1378 | s = [[ | ||
1379 | a := a * A;]] | ||
1380 | assert(table.pack(g:match(s))[2] == labels['factor']) | ||
1381 | |||
1382 | -- Error matching expression after '(' | ||
1383 | s = [[ | ||
1384 | a := (A);]] | ||
1385 | assert(table.pack(g:match(s))[2] == labels['openParExp']) | ||
1386 | |||
1387 | -- Error matching ')' | ||
1388 | s = [[ | ||
1389 | a := (a];]] | ||
1390 | assert(table.pack(g:match(s))[2] == labels['closePar']) | ||
1391 | |||
1392 | -- Error undefined | ||
1393 | s = [[ | ||
1394 | A := a;]] | ||
1395 | assert(table.pack(g:match(s))[2] == 0) | ||
1396 | |||
1397 | |||
1398 | |||
1399 | |||
1400 | |||
1401 | print("OK") | 1103 | print("OK") |