解决 C 风格块语句中可选换行符的 shift-reduce 冲突

问题描述

我想为 C 中的块语句的简化版本创建一个语法规则,它匹配括号中的语句列表,开头和结尾处有可选的换行符。此语言中的语句以换行符终止。

可选的换行符使块语句可以跨越多行和单行。即,两者

{ statement }

{
    statement
}

应该支持

目前我的规则如下:

BlockStmt:
    '{' OptionalNewlines BlockStmtList OptionalNewlines '}';


OptionalNewlines:
        OptionalNewlines '\n'
    |   %empty;

支持空块,它们基本上是只有换行符而没有语句的块。这是可能的,因为 BlockStmtList 可以减少到 %empty。

然而,对于只有换行符的空块,这会导致 shift-reduce 冲突,因为换行符可以被开头和结尾的 OptionalNewlines 非终结符匹配。

在只有换行符的空块的情况下,我如何告诉 yacc 优先考虑 OptionalNewlines 之一?

解决方法

除非你在块中间有空行的问题——这是我们许多人喜欢做的——简单的解决方案是只允许空语句(即只包含换行符的语句) . 如果你这样做,你就可以不用担心可选的换行符,只需使用

BlockStmt: '{' BlockStmtList '}';

那绝对是最简单的。但如果它不适合您,请继续阅读。

通常,您不能拥有一系列可选列表,其中两个列表具有相同的元素。这会导致歧义:如果您的语法允许 a* b* a*(为简单起见使用 Kleene *)并且输入是 a,则无法知道空 a* 是在之前还是之后空的 b*。 “可选”元素在很多情况下都是有问题的;通常需要使用非可选元素将空的非终结符扩展为多个规则:

BlockStmt: '{' '}'
         | '{' NewLineList '}'
         | '{' NewLineList StmtList OptionalNewLineList '}'
         | '{' StmtList OptionalNewLineList '}'