将扩展的BNF转换为Bison语法,但具有移位/减少错误

时间:2018-07-25 20:23:53

标签: parsing bison yacc ebnf

背景

我正在为一种类似乳胶的语言编写编译器。到目前为止,我已经编写了lex文件,并且已经可以正常工作了。但是,现在我在处理.y文件中的语法时遇到了问题。

问题

我已经复制了我认为是阻碍我成长的语法部分:

%start document
%%
document: BEGINDOCUMENT documentbody ENDDOCUMENT;
documentbody: contentseq | ws MAKETITLE contentseq | MAKETITLE contentseq;
contentseq: | contentseq content;
content: STRING | ws;
ws: WHITESPACE;

在这种情况下,空格基本上是空格,制表符和换行符的任意组合。

据查看y.output文件了解,由于规则

,产生了shift / reduce错误
documentbody: ... | ws MAKETITLE contentseq | ...

考虑到WHITESPACE令牌,Bison不知道它是否是终端“内容”的一部分,或者是否会后面跟随MAKETITLE令牌。两者都是完全有效的输入,我不确定如何解决此问题。

为清楚起见,原始EBNF规范的解释:

document: BEGINDOCUMENT [ws] [MAKETITLE] contentseq ENDDOCUMENT

换句话说,ws终端和MAKETITLE都是可选的。

示例输入

BEGINDOCUMENT WHITESPACE MAKETITLE STRING ENDDOCUMENT
BEGINDOCUMENT WHITESPACE STRING ENDDOCUMENT
BEGINDOCUMENT MAKETITLE STRING ENDDOCUMENT
BEGINDOCUMENT STRING ENDDOCUMENT

以上所有内容均应被语法接受。

我尝试过的事情

我知道可以通过使用优先级来消除许多冲突,但是我尝试过的任何方法都没有用。我尝试过为每种优先级分配MAKETITLE和WHITESPACE令牌,但这并没有解决问题。

我已经看到了其他与移位/归约相关的问题的建议,以重写语法,以减少歧义,但是我不确定该如何去做-至少在不更改语法接受和不接受语法的情况下接受。

我想到但没有尝试过的一种解决方案是弄混lex文件,但这似乎是一个非常棘手的解决方案,我宁愿在yacc中找到一些解决方法。

2 个答案:

答案 0 :(得分:2)

冲突基本上是由contentseq的可为空性引起的。这迫使解析器在识别更长的contentseq之前先识别出空的contentseq。输入开始BEGINDOCUMENT WHITESPACE时会产生冲突,因为在WHITESPACE之前的那一点上,是否应该减少空contentseq尚不清楚。

您可以通过将contentseq设为不可为空(contentseq: content | contentseq content)来轻松解决该问题,但必须显式处理省略的序列:

documentbody: %empty | contentseq | maketitle optionalcs
contentseq: content | contentseq content
optionalcs: %empty | contentseq
maketitle: WHITESPACE MAKETITLE | MAKETITLE

这是转换EBNF的可选语法[ x ]的普遍问题,尤其是在重复x时。您不能总是依靠能够定义optional-x来实现;您通常必须创建两个右侧,一个带有x,另一个没有。


我看不到ws: WHITESPACE的意义;您可以只使用WHITESPACE令牌而不是ws非终结符。如果您的语法比您显示的语法复杂,那么非终结性语法可能会引发冲突,但是我看不出您粘贴的内容有任何歧义。但是,在上面的示例解决方案中,我删除了冗余的非终端。

答案 1 :(得分:0)

我个人的喜好是避免使用特定于该工具的技巧,并定义语法以更准确地描述我们想要识别的内容。我相信此命令的语法可以识别您想要的文件:

%start document
%token BEGINDOCUMENT ENDDOCUMENT MAKETITLE STRING WS
%%
document: BEGINDOCUMENT documentbody ENDDOCUMENT
        ;

documentbody: prefix title contents
            ;

prefix: 
        | WS
        ;

title: 
    | MAKETITLE
    ;

contents: 
        | STRING contentseq
        ;

contentseq: 
          | contentseq content
          ;

content: STRING 
        | WS
        ;

因此,我们从一些空白的可选前缀开始。随后是可选标题。接下来是内容(由于我们已经认识到前导空白),该内容要么为空,要么为字符串,后跟字符串或空格。

简单,直接和容易理解的人(当然,假设他们完全识别yacc表示法)。