模棱两可的语法,不是 LALR(1),默认无法解析 yacc 模式
长话短说,您可以使用%glr-parser 声明“修复”此问题,如下所示:
%glr-parser
%%
start: rule1_list
. . .
. . .
把长篇故事写成中等长度的……
Shift-reduce 冲突通常不是错误。通过总是做通常是你想要的转变来解决冲突。大多数或所有现实世界的语法都有移位减少冲突。如果你想要减少,你可以通过优先声明来安排。
但是,在真正模棱两可的语法中,执行移位将使解析器沿两条路径之一向下,其中只有一条最终会在语法中找到一个字符串。在这种情况下,S/R 冲突是一个致命错误。
分析第一个,当解析器在| rule2 NEWLINE rule3_list 的情况下看到换行符时,它可以或者转换到一个新的状态,在这个状态下它应该是一个rule3_list,或者它可以减少一个rule1使用rule1: rule2。由于默认的 shift 选择,它将始终查找 rule3_list。
第二个冲突发生在它在rule3_list: rule3_list . NEWLINE rule3 中看到换行符时。现在它可以或者移动并开始寻找规则 3 或使用 | rule2 NEWLINE rule3_list 减少规则 1。
结果是,如所写,假设终端为“2”和“3”,您只能解析 2 行后跟 3 行。如果您摆弄优先级,则只能解析“2”行,而不能解析“3”行。
最后,我应该补充一点,使用 yacc 生成的 GLR 解析器有点儿杂乱无章。我想它会工作得很好,但它是纯 BFI,解析器分裂,保留两个堆栈,继续沿着两条路径,直到在语法中找到一个字符串。遗憾的是,其他修复也很麻烦:1. 将语法重新表述为 LALR(1),2.在扫描器中添加额外的前瞻,并返回一个复合标记,3。试验你所拥有的语法规则,也许 yacc 可以处理变化。
这就是为什么我实际上并不喜欢 yacc,而是更喜欢手写递归下降或像 PEG 这样更现代的东西。 (See Treetop.)
我尝试了一些(首选)左递归规则,这些规则只是忽略了换行符(这会使你的语法复杂化,制作空格标记......)..这“有效”,虽然我不确定它是否是什么你想要...
%%
start: stmtList
;
stmtList: /* nothing */
| stmtList '2' threeList;
;
threeList: /* nothing */
| threeList '3'
;
%%
int yylex() { int c; do { c = getchar (); } while (c == '\n'); return c; }