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