diff options
-rw-r--r-- | relabel.lua | 91 | ||||
-rw-r--r-- | testrelabelparser.lua | 174 |
2 files changed, 28 insertions, 237 deletions
diff --git a/relabel.lua b/relabel.lua index ff2e486..7fe8645 100644 --- a/relabel.lua +++ b/relabel.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 | ||
@@ -236,72 +230,20 @@ local function labchoice (...) | |||
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 | ) / labchoice); |
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,11 +260,9 @@ 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.V"Labels", "ExpNameOrLab") / mm.T |
328 | + "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5") | 268 | + "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5") |
@@ -374,23 +314,20 @@ local function compile (p, defs) | |||
374 | if mm.type(p) == "pattern" then return p end -- already compiled | 314 | 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 | 315 | 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) | 316 | local ok, cp, label, suffix = pcall(function() return pattern:match(p, 1, defs) end) |
377 | if not ok and #syntaxerrs == 0 then | 317 | if not ok and cp then |
378 | if type(cp) == "string" then | 318 | if type(cp) == "string" then |
379 | cp = cp:gsub("^[^:]+:[^:]+: ", "") | 319 | cp = cp:gsub("^[^:]+:[^:]+: ", "") |
380 | end | 320 | end |
381 | error(cp, 3) | 321 | error(cp, 3) |
382 | end | 322 | end |
383 | if #syntaxerrs > 0 then | 323 | if not cp then |
384 | local lines = splitlines(p) | 324 | local lines = splitlines(p) |
385 | local errors = {} | 325 | local line, col = lineno(p, #p - #suffix + 1) |
386 | for i, err in ipairs(syntaxerrs) do | 326 | local err = {} |
387 | local line, col = lineno(p, err.pos) | 327 | tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errmsgs[label]) |
388 | tinsert(errors, "L" .. line .. ":C" .. col .. ": " .. errmsgs[err.label]) | 328 | tinsert(err, lines[line]) |
389 | tinsert(errors, lines[line]) | 329 | tinsert(err, rep(" ", col-1) .. "^") |
390 | tinsert(errors, rep(" ", col-1) .. "^") | 330 | 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 | 331 | end |
395 | return cp | 332 | return cp |
396 | end | 333 | end |
diff --git a/testrelabelparser.lua b/testrelabelparser.lua index 1c5bb9f..46e01ee 100644 --- a/testrelabelparser.lua +++ b/testrelabelparser.lua | |||
@@ -199,9 +199,6 @@ testerror([[{ {~ } ~}]], [[ | |||
199 | L1:C5: expected a pattern after '{~' | 199 | L1:C5: expected a pattern after '{~' |
200 | { {~ } ~} | 200 | { {~ } ~} |
201 | ^ | 201 | ^ |
202 | L1:C10: missing closing '}' | ||
203 | { {~ } ~} | ||
204 | ^ | ||
205 | ]]) | 202 | ]]) |
206 | 203 | ||
207 | testerror([[{~ ^_^ ~}]], [[ | 204 | testerror([[{~ ^_^ ~}]], [[ |
@@ -421,9 +418,6 @@ testerror([[{: group: 'p' :}]], [[ | |||
421 | L1:C9: missing closing ':}' | 418 | L1:C9: missing closing ':}' |
422 | {: group: 'p' :} | 419 | {: group: 'p' :} |
423 | ^ | 420 | ^ |
424 | L1:C9: unexpected characters after the pattern | ||
425 | {: group: 'p' :} | ||
426 | ^ | ||
427 | ]]) | 421 | ]]) |
428 | 422 | ||
429 | testerror([[S <- {: 'p' T <- 'q']], [[ | 423 | testerror([[S <- {: 'p' T <- 'q']], [[ |
@@ -447,9 +441,6 @@ testerror([['p' {| 'q' / 'r' }]], [[ | |||
447 | L1:C17: missing closing '|}' | 441 | L1:C17: missing closing '|}' |
448 | 'p' {| 'q' / 'r' } | 442 | 'p' {| 'q' / 'r' } |
449 | ^ | 443 | ^ |
450 | L1:C18: unexpected characters after the pattern | ||
451 | 'p' {| 'q' / 'r' } | ||
452 | ^ | ||
453 | ]]) | 444 | ]]) |
454 | 445 | ||
455 | -- testing MisClose5 | 446 | -- testing MisClose5 |
@@ -485,186 +476,49 @@ L1:C12: missing closing '}' | |||
485 | -- testing MisClose8 | 476 | -- testing MisClose8 |
486 | 477 | ||
487 | testerror([[[]], [[ | 478 | testerror([[[]], [[ |
488 | L1:C1: missing closing ']' | 479 | L1:C2: missing closing ']' |
489 | [ | 480 | [ |
490 | ^ | 481 | ^ |
491 | ]]) | 482 | ]]) |
492 | 483 | ||
493 | testerror([[[^]], [[ | 484 | testerror([[[^]], [[ |
494 | L1:C1: missing closing ']' | 485 | L1:C3: missing closing ']' |
495 | [^ | 486 | [^ |
496 | ^ | 487 | ^ |
497 | ]]) | 488 | ]]) |
498 | 489 | ||
499 | testerror([[[] ]], [[ | 490 | testerror([[[] ]], [[ |
500 | L1:C1: missing closing ']' | 491 | L1:C4: missing closing ']' |
501 | [] | 492 | [] |
502 | ^ | 493 | ^ |
503 | ]]) | 494 | ]]) |
504 | 495 | ||
505 | testerror([[[^] ]], [[ | 496 | testerror([[[^] ]], [[ |
506 | L1:C1: missing closing ']' | 497 | L1:C6: missing closing ']' |
507 | [^] | 498 | [^] |
508 | ^ | 499 | ^ |
509 | ]]) | 500 | ]]) |
510 | 501 | ||
511 | testerror([[[_-___-_|]], [[ | 502 | testerror([[[_-___-_|]], [[ |
512 | L1:C1: missing closing ']' | 503 | L1:C10: missing closing ']' |
513 | [_-___-_| | 504 | [_-___-_| |
514 | ^ | 505 | ^ |
515 | ]]) | 506 | ]]) |
516 | 507 | ||
517 | -- testing MisTerm1 | 508 | -- testing MisTerm1 |
518 | 509 | ||
519 | testerror([['That is the question...]], [[ | 510 | testerror([['That is the question...]], [[ |
520 | L1:C1: missing terminating single quote | 511 | L1:C25: missing terminating single quote |
521 | 'That is the question... | 512 | 'That is the question... |
522 | ^ | 513 | ^ |
523 | ]]) | 514 | ]]) |
524 | 515 | ||
525 | -- testing MisTerm2 | 516 | -- testing MisTerm2 |
526 | 517 | ||
527 | testerror([[Q <- "To be or not to be...]], [[ | 518 | testerror([[Q <- "To be or not to be...]], [[ |
528 | L1:C6: missing terminating double quote | 519 | L1:C28: missing terminating double quote |
529 | Q <- "To be or not to be... | 520 | Q <- "To be or not to be... |
530 | ^ | 521 | ^ |
531 | ]]) | ||
532 | |||
533 | -- testing error recovery, more complex grammars (multiline), | ||
534 | -- and pointer positions in error recovery | ||
535 | |||
536 | testerror([[&'p'/&/!/'p'^'q']], [[ | ||
537 | L1:C7: expected a pattern after '&' | ||
538 | &'p'/&/!/'p'^'q' | ||
539 | ^ | ||
540 | L1:C9: expected a pattern after '!' | ||
541 | &'p'/&/!/'p'^'q' | ||
542 | ^ | ||
543 | L1:C14: expected a number after '^', '+' or '-' (no space) | ||
544 | &'p'/&/!/'p'^'q' | ||
545 | ^ | ||
546 | ]]) | ||
547 | |||
548 | testerror([[ | ||
549 | A <- 'a' (B 'b' | ||
550 | B <- 'x' / ! | ||
551 | C <- 'c' | ||
552 | ]], [[ | ||
553 | L1:C18: missing closing ')' | ||
554 | A <- 'a' (B 'b' | ||
555 | ^ | ||
556 | L2:C15: expected a pattern after '!' | ||
557 | B <- 'x' / ! | ||
558 | ^ | ||
559 | ]]) | ||
560 | |||
561 | testerror([['a' / &@ ('c' / 'd')]], [[ | ||
562 | L1:C8: expected a pattern after '&' | ||
563 | 'a' / &@ ('c' / 'd') | ||
564 | ^ | ||
565 | ]]) | ||
566 | |||
567 | testerror([['x' / & / 'y']], [[ | ||
568 | L1:C8: expected a pattern after '&' | ||
569 | 'x' / & / 'y' | ||
570 | ^ | ||
571 | ]]) | ||
572 | |||
573 | testerror([[&/'p'/!/'q']], [[ | ||
574 | L1:C2: expected a pattern after '&' | ||
575 | &/'p'/!/'q' | ||
576 | ^ | ||
577 | L1:C8: expected a pattern after '!' | ||
578 | &/'p'/!/'q' | ||
579 | ^ | ||
580 | ]]) | ||
581 | |||
582 | testerror([['p'//'q']], [[ | ||
583 | L1:C5: expected a pattern after '/' or the label(s) | ||
584 | 'p'//'q' | ||
585 | ^ | ||
586 | ]]) | ||
587 | |||
588 | testerror([[ | ||
589 | S <- 'forgot to close / T | ||
590 | T <- 'T' & / 't' | ||
591 | ]], [[ | ||
592 | L1:C8: missing terminating single quote | ||
593 | S <- 'forgot to close / T | ||
594 | ^ | ||
595 | L2:C13: expected a pattern after '&' | ||
596 | T <- 'T' & / 't' | ||
597 | ^ | ||
598 | ]]) | ||
599 | |||
600 | testerror([[ | ||
601 | S <- [a-z / T | ||
602 | T <- 'x' / & / 'y' | ||
603 | ]], [[ | ||
604 | L1:C8: missing closing ']' | ||
605 | S <- [a-z / T | ||
606 | ^ | ||
607 | L2:C15: expected a pattern after '&' | ||
608 | T <- 'x' / & / 'y' | ||
609 | ^ | ||
610 | ]]) | ||
611 | |||
612 | testerror([[ | ||
613 | S <- ('p' -- comment | ||
614 | ]], [[ | ||
615 | L1:C12: missing closing ')' | ||
616 | S <- ('p' -- comment | ||
617 | ^ | ||
618 | ]]) | ||
619 | |||
620 | -- an unfortunate second error exists because we don't know | ||
621 | -- what's part of the quotation | ||
622 | testerror([[ | ||
623 | X <- ('p / Q (R | ||
624 | / S)) | ||
625 | Q <- 'q' | ||
626 | R <- 'r' | ||
627 | S <- 's' | ||
628 | ]], [[ | ||
629 | L1:C9: missing terminating single quote | ||
630 | X <- ('p / Q (R | ||
631 | ^ | ||
632 | L2:C9: unexpected characters after the pattern | ||
633 | / S)) | ||
634 | ^ | ||
635 | ]]) | ||
636 | |||
637 | testerror([[ | ||
638 | A <- 'A' /{'lab'} B / ! | ||
639 | |||
640 | B <- %{1, 2 3} 'b' / '6' & / 'B' | ||
641 | |||
642 | C <- A^B | ||
643 | ]], [[ | ||
644 | L1:C14: expected at least one label after '{' | ||
645 | A <- 'A' /{'lab'} B / ! | ||
646 | ^ | ||
647 | L1:C26: expected a pattern after '!' | ||
648 | A <- 'A' /{'lab'} B / ! | ||
649 | ^ | ||
650 | L3:C15: missing closing '}' | ||
651 | B <- %{1, 2 3} 'b' / '6' & / 'B' | ||
652 | ^ | ||
653 | L3:C29: expected a pattern after '&' | ||
654 | B <- %{1, 2 3} 'b' / '6' & / 'B' | ||
655 | ^ | ||
656 | L5:C10: expected a number after '^', '+' or '-' (no space) | ||
657 | C <- A^B | ||
658 | ^ | ||
659 | ]]) | ||
660 | |||
661 | testerror([['p'/{1/'q'/&]], [[ | ||
662 | L1:C7: missing closing '}' | ||
663 | 'p'/{1/'q'/& | ||
664 | ^ | ||
665 | L1:C13: expected a pattern after '&' | ||
666 | 'p'/{1/'q'/& | ||
667 | ^ | ||
668 | ]]) | 522 | ]]) |
669 | 523 | ||
670 | -- testing non-syntax errors | 524 | -- testing non-syntax errors |