转移减少冲突

时间:2008-10-12 21:58:10

标签: grammar conflict gppg

我在理解移位/缩小语法的问题时遇到了问题,我知道这种语法没有含糊之处。这个案例是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

http://compilers.iecc.com/comparch/article/98-01-079

GPPG, the parser I'm using

Bison manual

6 个答案:

答案 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)。

我没有花时间解决为什么这个编译无冲突,而我之前概述的没有。我希望它有所帮助。