如何制作带有可选前缀 LL(2) 的语法?

问题描述

考虑这个语法

start: lvalue ASSIGN expr SEMICOLON | expr SEMICOLON;
expr: OPENPAREN expr CLOSEPAREN | literal | lvalue;
lvalue: ID lvalue_tail ;
lvalue_tail: OPENBRACK expr CLOSEBRACK | ;
ID : [a-zA-Z]+[a-zA-Z0-9_]* ;

所以 lvalueexpr,但 expr 可能不是 lvalue
使用此输入 var[10],语法将需要 4 次前瞻(ID、openbrack、literal、closebrack)才能确定它应该选择 lvalue expr 还是 expr
我如何制作这样的语法 LL(2)?
注意:这里的expr是简化的,大写字母是终结符。

解决方法

与仅使用 LALR 解析器生成器相比,我不确定减少此语法的 LL 前瞻可以达到什么目的,因为未修改的语法是 LALR(1)。

然而,它可以通过将 expr 分成 lvalue 和我们可能称之为 rvalue 的东西来完成——即,不能在赋值运算符的左侧使用的表达式.这可能会产生类似

start: lvalue start_tail | rvalue SEMICOLON;
start_tail: ASSIGN expr SEMICOLON | SEMICOLON;
expr: lvalue | rvalue;
rvalue: OPENPAREN expr CLOSEPAREN | literal;
lvalue: ID lvalue_tail ;
lvalue_tail: OPENBRACK expr CLOSEBRACK | ;
ID : [a-zA-Z]+[a-zA-Z0-9_]* ;

This grammar is LL(1) 除了是 LL(2)。但是,将其扩展以包含更多语言可能会很棘手。

(如果上面的链接失效了,它只是指向一个在线工具,它验证了语法是 LL(1)。)