背景
我正在为一种类似乳胶的语言编写编译器。到目前为止,我已经编写了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中找到一些解决方法。
答案 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表示法)。