转变减少冲突

发布于 2024-07-06 23:13:29 字数 1925 浏览 7 评论 0原文

我在理解我知道没有歧义的语法的移位/归约冲突时遇到了问题。 这种情况是 if else 类型之一,但它不是“悬空 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 解析器。

我很欣赏优雅的解决方案,我设法通过消除 /* 空 */ 规则并复制需要空列表的所有内容来做到这一点,但在我正在研究的更大语法中,它最终就像“意大利面条语法综合症”。

以下是一些链接:

http://nitsan.org/~maratb/cs164/bison.html< /a>

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

GPPG,我正在使用的解析器

Bison 手册

I'm having a problem understanding the shift/reduce confict for a grammar that I know has no ambiguity. The case is one of the if else type but it's not the 'dangling else' problem since I have mandatory END clauses delimiting code blocks.

Here is the grammar for gppg (Its a Bison like compiler compiler ... and that was not an echo):

%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
        ;

Here is the conflict output:

// 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

I already switched arround everything, and I do know how to resolve it, but that solution involves giving up the left recursion on 'elseif' for a right recursion.

Ive been through all the scarse documentation I have found on the internet regarding this issue (I post some links at the end) and still have not found an elegant solution. I know about ANTLR and I don't want to consider it right now. Please limit your solution to Yacc/Bison parsers.

I would appreciate elegant solutions, I managed to do It by eleminating the /* empty */ rules and duplication everything that needed an empty list but in the larger grammar Im working on It just ends up like 'sparghetti grammar syndrome'.

Here are some links:

http://nitsan.org/~maratb/cs164/bison.html

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

GPPG, the parser I'm using

Bison manual

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

心奴独伤 2024-07-13 23:13:29

您修改后的 ELSEIF 规则没有条件标记——名义上应该添加“(”和“)”。

更严重的是,你现在有一个规则,

elsebody : else
         | elseifs else
         ;

并且

elseifs : /* Nothing */
        | elseifs ...something... 
        ;

不需要“什么”; 它由“elsebody”隐式处理,而无需“elseifs”。

我非常倾向于使用规则“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
    ;

我没有通过解析器生成器运行它,但我发现这相对容易理解。

Your revised ELSEIF rule has no markers for a condition -- it should nominally have '(' and ')' added.

More seriously, you now have a rule for

elsebody : else
         | elseifs else
         ;

and

elseifs : /* Nothing */
        | elseifs ...something... 
        ;

The 'nothing' is not needed; it is implicitly taken care of by the 'elsebody' without the 'elseifs'.

I would be very inclined to use rules 'opt_elseifs', 'opt_else', and 'end':

flow : '#' IF '(' ')' statements opt_elseifs opt_else end
     ;

opt_elseifs : /* Nothing */
            | opt_elseifs '#' ELSIF '(' ')' statements 
            ;

opt_else : /* Nothing */
         | '#' ELSE statements
         ;

end : '#' END
    ;

I've not run this through a parser generator, but I find this relatively easy to understand.

奶气 2024-07-13 23:13:29

我认为问题出在 elseifs 子句中。

elseifs : elseifs '#' ELSEIF statements else
        | '#' ELSEIF statements else
        ;

我认为第一个版本不是必需的,因为 else 子句无论如何都会引用 elseifs:

else : '#' END
     | '#' ELSE statements '#' END
     | elseifs
     ;

如果更改 elseifs 会发生什么?:

elseifs : '#' ELSEIF statements else
        ;

I think the problem is in the elseifs clause.

elseifs : elseifs '#' ELSEIF statements else
        | '#' ELSEIF statements else
        ;

I think the first version is not required, since the else clause refers back to elseifs anyway:

else : '#' END
     | '#' ELSE statements '#' END
     | elseifs
     ;

What happens if you change elseifs?:

elseifs : '#' ELSEIF statements else
        ;
岁月无声 2024-07-13 23:13:29

上面乔纳森的答案似乎是最好的,但由于它不适合你,我有一些建议你可以尝试,这将帮助你调试错误。

首先,您是否考虑过将哈希/锐利符号作为标记本身的一部分(即#END、#IF 等)? 这样它们就会被词法分析器取出,这意味着它们不必包含在解析器中。

其次,我敦促您重写规则而不重复任何令牌流。 (“不要重复自己”原则的一部分。)因此,规则“'#'ELSEIF statements else”应该只存在于该文件中的一个位置(而不是像上面那样存在两个位置)。

最后,我建议您研究 IF/ELSEIF/ELSE 标记的优先级和关联性。 我知道您应该能够编写一个不需要这个的解析器,但在这种情况下它可能是您需要的。

The answer from Jonathan above seems like it would be the best, but since its not working for you I have a few suggestions you could try that will help you in debugging the error.

Firstly have you considered making the hash/sharp symbol a part of the tokens themselves (i.e. #END, #IF, etc)? So that they get taken out by the lexer, meaning they don't have to be included in the parser.

Secondly I would urge you to rewrite the rules without duplicating any token streams. (Part of the Don't Repeat Yourself principle.) So the rule " '#' ELSEIF statements else " should only exist in one place in that file (not two as you have above).

Lastly I suggest that you look into precedence and associativity of the IF/ELSEIF/ELSE tokens. I know that you should be able to write a parser that doesn't require this but it might be the thing that you need in this case.

浅笑轻吟梦一曲 2024-07-13 23:13:29

我仍在切换事物,我原来的问题有一些错误,因为 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 子句上使用左递归的解决方案。

I'm still switching thing arround, and my original question had some errors since the elseifs sequence had an else allways at the end which was wrong. Here is another take at the question, this time I get two shift/reduce conflicts:

flow : '#' IF '(' ')' statements elsebody 
     ;

elsebody : else 
         | elseifs else
         ;

else : '#' ELSE statements '#' END
     | '#' END
     ;

elseifs : /* empty */
        | elseifs '#' ELSEIF statements
        ;

The conflicts now are:

// 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

Empty rules just aggravate the gppg i'm affraid. But they seem so natural to use I keep trying them.

I already know right recursion solves the problem as 1800 INFORMATION has said. But I'm looking for a solution with left recursion on the elseifs clause.

春夜浅 2024-07-13 23:13:29
elsebody : elseifs else
         | elseifs
         ;

elseifs : /* empty */
        | elseifs '#' ELSEIF statements
        ;

else : '#' ELSE statements '#' END
     ;

我认为这应该留下递归并始终终止。

elsebody : elseifs else
         | elseifs
         ;

elseifs : /* empty */
        | elseifs '#' ELSEIF statements
        ;

else : '#' ELSE statements '#' END
     ;

I think this should left recurse and always terminate.

三生殊途 2024-07-13 23:13:29

好的 - 这是 if 块的语法(不是最小的)。 我从我拥有的一些代码中挖掘出它(称为 adhoc,基于 Kernighan 和 Plauger 的“UNIX 编程环境”中的 hoc)。 这个大纲语法用 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,但这是可选的。 “开始”和“结束”操作用于获取生成程序中的程序计数器 - 因此应该可以从中删除而不影响它。

我认为我需要使用右递归而不是正常的左递归是有原因的 - 但我认为这与我正在使用的代码生成策略有关,而不是其他任何东西。 评论中的问号是原文的; 我记得我对此并不高兴。 该计划作为一个整体确实有效 - 这是一个在过去十年左右的时间里一直处于次要地位的项目(嗯......我在 2004 年底和 2005 年初做了一些工作;在此之前,是 1992 年和 1993)。

我没有花时间弄清楚为什么它可以无冲突编译,而我之前概述的却不能。 我希望它有帮助。

OK - here is a grammar (not minimal) for if blocks. I dug it out of some code I have (called adhoc, based on hoc from Kernighan & Plauger's "The UNIX Programming Environment"). This outline grammar compiles with Yacc with no conflicts.

%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
    ;

%%

I used 'NUMBER' as the dummy element, instead of THINGS, and I used ELIF instead of ELSEIF. It includes a THEN, but that is optional. The 'begin' and 'end' operations were used to grab the program counter in the generated program - and therefore should be removable from this without affecting it.

There was a reason I thought I needed to use right recursion instead of the normal left recursion - but I think it was to do with the code generation strategy I was using, rather than anything else. The question mark in the comment was in the original; I remember not being happy with it. The program as a whole does work - it is a project that's been on the back burner for the last decade or so (hmmm...I did some work at the end of 2004 and beginning of 2005; prior to that, it was 1992 and 1993).

I've not spent the time working out why this compiles conflict-free and what I outlined earlier does not. I hope it helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文