我在理解移位/缩小语法的问题时遇到了问题,我知道这种语法没有含糊之处。这个案例是if else类型之一,但它不是'悬空的'问题,因为我有强制的END子句来分隔代码块。
这是gppg的语法(它是一个像编译器编译器一样的Bison ......而且不是回声):
%output=program.cs
%start program
%token FOR
%token END
%token THINGS
%token WHILE
%token SET
%token IF
%token ELSEIF
%token ELSE
%%
program : statements
;
statements : /*empty */
| statements stmt
;
stmt : flow
| THINGS
;
flow : '#' IF '(' ')' statements else
;
else : '#' END
| '#' ELSE statements '#' END
| elseifs
;
elseifs : elseifs '#' ELSEIF statements else
| '#' ELSEIF statements else
;
以下是冲突输出:
// Parser Conflict Information for grammar file "program.y"
Shift/Reduce conflict on symbol "'#'", parser will shift
Reduce 10: else -> elseifs
Shift "'#'": State-22 -> State-23
Items for From-state State 22
10 else: elseifs .
-lookahead: '#', THINGS, EOF
11 elseifs: elseifs . '#' ELSEIF statements else
Items for Next-state State 23
11 elseifs: elseifs '#' . ELSEIF statements else
// End conflict information for parser
我已经切换了所有内容,我知道如何解决它,但该解决方案涉及放弃'elseif'上的左递归以进行正确的递归。
我已经浏览过我在互联网上发现的有关此问题的所有简明文档(我在最后发布了一些链接),但仍未找到优雅的解决方案。我知道ANTLR,我现在不想考虑它。请将您的解决方案限制为Yacc / Bison解析器。
我很欣赏优雅的解决方案,我设法通过消除/ * empty * /规则来复制所有需要空列表的内容,但是在更大的语法中我正在努力它最终就像'sparghetti语法综合症'。
以下是一些链接:
http://nitsan.org/~maratb/cs164/bison.html
答案 0 :(得分:6)
您修订的ELSEIF规则没有条件标记 - 它应该名义上添加了'('和')'。
更严重的是,您现在有了
的规则elsebody : else
| elseifs else
;
和
elseifs : /* Nothing */
| elseifs ...something...
;
不需要'不';没有'elseifs','elsebody'会隐含地处理它。
我非常倾向于使用规则'opt_elseifs','opt_else'和'end':
flow : '#' IF '(' ')' statements opt_elseifs opt_else end
;
opt_elseifs : /* Nothing */
| opt_elseifs '#' ELSIF '(' ')' statements
;
opt_else : /* Nothing */
| '#' ELSE statements
;
end : '#' END
;
我没有通过解析器生成器运行它,但我发现这相对容易理解。
答案 1 :(得分:2)
我认为问题出在elseifs子句中。
elseifs : elseifs '#' ELSEIF statements else
| '#' ELSEIF statements else
;
我认为第一个版本不是必需的,因为else子句无论如何都会引用elseif:
else : '#' END
| '#' ELSE statements '#' END
| elseifs
;
如果你更改了elseifs会发生什么?:
elseifs : '#' ELSEIF statements else
;
答案 2 :(得分:1)
以上Jonathan的答案似乎是最好的,但由于它不适合你,我有一些建议,你可以尝试,这将有助于你调试错误。
首先,您是否考虑过将哈希/尖锐符号作为令牌本身的一部分(即#END,#IF等)?因此,它们被词法分析器取出,这意味着它们不必包含在解析器中。
其次,我会敦促你重写规则而不重复任何令牌流。 (“不要重复自己”原则的一部分。)因此规则“'''ELSEIF语句”应该只存在于该文件中的一个位置(不是上面的两个)。
最后,我建议您研究IF / ELSEIF / ELSE令牌的优先级和关联性。我知道你应该能够编写一个不需要这个的解析器,但在这种情况下你可能需要它。
答案 3 :(得分:0)
我仍然在转换东西,我的原始问题有一些错误,因为 elseifs 序列最后还有一个 else 错误。这是另一个问题,这次我得到两个转移/减少冲突:
flow : '#' IF '(' ')' statements elsebody
;
elsebody : else
| elseifs else
;
else : '#' ELSE statements '#' END
| '#' END
;
elseifs : /* empty */
| elseifs '#' ELSEIF statements
;
现在的冲突是:
// Parser Conflict Information for grammar file "program.y"
Shift/Reduce conflict on symbol "'#'", parser will shift
Reduce 12: elseifs -> /* empty */
Shift "'#'": State-10 -> State-13
Items for From-state State 10
7 flow: '#' IF '(' ')' statements . elsebody
4 statements: statements . stmt
Items for Next-state State 13
10 else: '#' . ELSE statements '#' END
11 else: '#' . END
7 flow: '#' . IF '(' ')' statements elsebody
Shift/Reduce conflict on symbol "'#'", parser will shift
Reduce 13: elseifs -> elseifs, '#', ELSEIF, statements
Shift "'#'": State-24 -> State-6
Items for From-state State 24
13 elseifs: elseifs '#' ELSEIF statements .
-lookahead: '#'
4 statements: statements . stmt
Items for Next-state State 6
7 flow: '#' . IF '(' ')' statements elsebody
// End conflict information for parser
空洞的规则只会加剧我非常害怕的gppg。但它们看起来很自然,我一直在尝试它们。
我已经知道正确的递归解决了 1800 INFORMATION 所说的问题。但是我正在 elseifs子句上寻找左递归的解决方案。
答案 4 :(得分:0)
elsebody : elseifs else
| elseifs
;
elseifs : /* empty */
| elseifs '#' ELSEIF statements
;
else : '#' ELSE statements '#' END
;
我认为这应该继续进行并且总是终止。
答案 5 :(得分:0)
好的 - 这是if块的语法(不是最小的)。我把它从我的一些代码中挖出来(称为adhoc,基于来自Kernighan& Plauger的“UNIX编程环境”)。这个大纲语法与Yacc编译,没有冲突。
%token NUMBER IF ELSE
%token ELIF END
%token THEN
%start program
%%
program
: stmtlist
;
stmtlist
: /* Nothing */
| stmtlist stmt
;
stmt
: ifstmt
;
ifstmt
: ifcond endif
| ifcond else begin
| ifcond eliflist begin
;
ifcond
: ifstart cond then stmtlist
;
ifstart
: IF
;
cond
: '(' expr ')'
;
then
: /* Nothing */
| THEN
;
endif
: END IF begin
;
else
: ELSE stmtlist END IF
;
eliflist
: elifblock
| elifcond eliflist begin /* RIGHT RECURSION */
;
elifblock
: elifcond else begin
| elifcond endif
;
elifcond
: elif cond then stmtlist end
;
elif
: ELIF
;
begin
: /* Nothing */
;
end
: /* Nothing */
;
expr
: NUMBER
;
%%
我使用'NUMBER'作为虚拟元素,而不是THINGS,我使用ELIF而不是ELSEIF。它包括一个THEN,但这是可选的。 'begin'和'end'操作用于获取生成程序中的程序计数器 - 因此应该可以从中删除而不会影响它。
有一个原因我认为我需要使用正确的递归而不是正常的左递归 - 但我认为这与我使用的代码生成策略有关,而不是其他任何东西。评论中的问号在原文中;我记得不满意。整个计划确实有效 - 这个项目在过去十年左右的时间里一直处于劣势(嗯......我在2004年底和2005年初做了一些工作;在那之前,它是1992年和1993)。
我没有花时间解决为什么这个编译无冲突,而我之前概述的没有。我希望它有所帮助。