【问题标题】:Bison: Bad if structure野牛:如果结构不好
【发布时间】:2012-05-13 18:24:05
【问题描述】:

我已经在 bison 中创建了一个完整的解析器(当然还有在 flex 中的完整词法分析器),我昨天才注意到我的解析器有问题。实际上在 If 结构中。

这是我在解析器中的规则:http://pastebin.com/TneESwUx

这里无法识别单个 IF,如果我将“%prec IFX”替换为“END”,通过添加一个新令牌“END”(flex 中的"end" return END;),它就可以工作。但是我不想有一个新的“end”关键字,所以我不使用这个解决方案。

请帮帮我。

【问题讨论】:

    标签: c++ parsing bison yacc lex


    【解决方案1】:

    '处理这种规则的正确方法不是优先,它是重构使用可选的else-part,以便解析器可以使用token lookahead来决定如何解析。我会设计成这样:

    stmt      : IF '(' expression ')' stmts else_part
              | /* other statement productions here */
    
    else_part : /* optional */
              | ELSE stmts
    
    stmts     : stmt
              | '{' stmt_list '}'
              | '{' '}'
    
    stmt_list : stmt
              | stmt_list ';' stmt
    

    (这种特殊的stmts 方法而不是允许stmt 包含一个块在产生方面可能不是最佳的,并且可能会在您的语言中引入奇怪的东西,但是如果没有更多细节,很难确定.bison 可以生成一份报告,向您展示它生成的解析器是如何工作的;您可能想研究它。还要注意意外的移位/归约冲突,尤其是任何归约/归约冲突。)

    请注意,在这种语法中,移位/归约冲突是完全正常的; LALR(1) 解析器的重点是将这些冲突用作一个特征,通过单个标记向前看以解决冲突。它们是专门报告的,以便您可以更轻松地检测出您通过错误地考虑语法而引入的那些。

    您的IfExpression 也需要重构以匹配;诀窍是else_part 应该为$$ 生成某种条件表达式,并且在IF 的生产中您测试$6(对应于else_part)并调用适当的IfExpression 构造函数。

    【讨论】:

    • 这是我真正的 stmts 规则:stmts: stmt | '{' stmt_list '}' | '{' '}' ; 我想保持这样的规则。那么这里 optional else 的解决方案是什么?
    • 你的stmt_list是我的stmts;然后使用你的stmts,我在制作中使用了`stmt。我会编辑以匹配。
    • 我真正的 stmt 规则是这样的:stmt: IF '(' exp ')' stmts %prec IFX { $$ = new IfExpression($3, $5); } | IF '(' exp ')' stmts ELSE stmts { $$ = new IfExpression($3, $5, $7); } 所以一个规则有一个不同的动作。用你的解决方案是不可能的,对吧?
    • 我之前没有正确复制您的stmts;现在应该是正确的。希望我有用地解释了另一个;关键是您想要的那种 if/else 必然是模棱两可的,并且需要某种前瞻来解决它。这种歧义被报告为移位/减少冲突。
    • 是的,但我不想要移位/减少冲突。 John Levine 的书中推荐了优先解决方案..
    【解决方案2】:

    您的语法模棱两可,因此您必须忍受 shift/reduce 冲突。 END 标记通过确保 IF 语句始终正确关闭(就像一对括号)来消除歧义。

    括号在这里做了一个很好的类比。假设你有这样的语法:

    maybe_closed_parens : '(' stuff
                        | '(' stuff ')'
                        ;
    

    stuff本身会产生一些语法符号,maybe_closed_parens就是其中之一。

    所以如果你有((((((whatever 这样的输入,那就是正确的。括号不必闭合。但是如果你添加)呢?哪个(正在关闭?

    这很像无法分辨哪个IFELSE 匹配。

    如果您将END 添加到IF 的语法中(无论是否有ELSE),那么这就像有一个右括号。 IFEND 类似于 ()

    当然,在你的语言中不想要END 这个词在风格上是正确的,因为你已经有了花括号块,它们基本上是Pascal 的BEGINEND 的替代拼写。您的 } 已经是 END 关键字。

    所以你可以做的是强加IF 只接受复合语句(即完全大括号)的规则:

    if_statement : IF condition compound_statement
                 | IF condition compound_statement ELSE compound_statement
    

    现在不可能有​​像 if x if y w else z 这样的歧义,因为必须存在大括号:if x { if y { w } else { z } } or if x { if y { w } } else { z }

    我似乎记得 Perl 是做出这种选择的语言的一个例子。这不是一个坏主意,因为它不仅消除了您的歧义,更重要的是它消除了程序中的歧义。

    我发现您的语法中没有compound_statement 短语结构规则,因为您的语句会直接生成一个包含在{} 中的短语。如果你采用这种方法,你将不得不破解它。

    【讨论】:

    • 那么,您的解决方案是始终强制使用大括号?如果我不想那样呢?如果我想要 C 中的 if/else 语句?
    • 如果你想要像 C 中那样的 if/else 语句,那么你就可以保留带有 shift/reduce 冲突的模棱两可的语法。 Yacc 通过选择 shift 操作而不是 reduce 来解决它,因此解析器会将 else 与最接近的 if 匹配。 %expect 声明可用于指示语法中预期存在一定数量的冲突。
    • 好吧,你还没有理解我的问题...如果我使用歧义语法,Bison 无法识别简单的IF 语句。 (没有else
    • 在该代码中,例如:pastebin.com/WVHj6v2S Bison 将无法识别第一个 if 的结尾。但是如果我添加 else { dootherthing; } 没关系...
    • 如果没有 else 会发生什么?语法错误? (另外,我没有在您的规则中看到终止分号)。
    猜你喜欢
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多