Skip to content

Bison实现句尾的可选分号 Optional semicolon grammar in Bison

西风逍遥游 edited this page Nov 8, 2021 · 5 revisions

句尾分号在Bison中一直是一个让人非常困扰的问题,绝大部分复杂的语法中,如果在两个语句中间不插入分号,会导致严重的歧义。这也是编程语言设计中,为何一定要添加分号的原因。试想下面的两条语句

int a = call
(b);

如果看做一条语句,可以认为是一条调用语句,如果看成两条语句,可以认为是一个赋值加一个表达式计算。这个歧义在bison中是不能通过简单的词语法设计来弥补的,原因如下:

  1. 如果词法分析器永远输出'\n', 则必须讨论语法中换号符是可选的,则在语法定义中一定会出现这种结构: expr : expr oplb '+' oplb expr 。在各种位置都添加上oplb表示一个可选的换行符,但这种语法本身同样带有大量歧义,产生的歧义数量甚至比不引入';'还要多。

  2. 如果词法分析器永远不输出'\n',则语法上,上面的例子则永远不能被识别为两条语句,因为bison是贪婪匹配的,永远想移入更多的符号,则用户无法通过分行来实现对语句的切割。

最好的方案是,如果语句确定不需要';'时,词法分析器忽略换行符'\n',而用户在某些语句结尾需要分割时却没有分号';',自动检测我们是否遇到了一个换行符'\n',遇到后将其视为一个分号。

普通的手工写的LL parser由于可以精细控制什么时候lookahead,往往可以采用简单的代码实现。例如:

int getNextToken(bool requireSemicolon) {
    int token = yylex();
    if (token == '\n') 
        if (requireSemicolon) return ';';
        else return getNextToken(false);
    return token;
}

这种逻辑简单易懂,需要分号时,参数设置为true即可,自动将下一个回车符合换成';' 发送给你,如果不是回车,那么说明用户不想在此分割,继续贪婪匹配更长的语句即可。