【问题标题】:Bison can't solve conflicts shift-reduce and reduce-reduceBison 无法解决冲突 shift-reduce 和 reduce-reduce
【发布时间】:2016-09-30 21:59:36
【问题描述】:

我正在使用 Bison 编写解析器,但似乎无法正确语法。

有两个冲突:

以下是围绕冲突一使用的一些规则:

program                 :           function END_OF_FILE {return 0;}
formal_parameters       :           OPEN_PAREN formal_parameter list_E_fparameter CLOSE_PAREN | OPEN_PAREN CLOSE_PAREN
formal_parameter        :           expression_parameter | function_parameter
function                :           return_options IDENTIFIER formal_parameters block
function_parameter      :           return_options IDENTIFIER formal_parameters
expression_parameter    :           VAR identifier_list IDENTIFIER | identifier_list IDENTIFIER
variable_creation       :           identifier_list COLON type SEMI_COLON
labels                  :           LABELS identifier_list SEMI_COLON
list_E_identifiers      :           list_E_identifiers COMMA IDENTIFIER |  
identifier_list         :           IDENTIFIER list_E_identifiers
return_options          :           VOID | IDENTIFIER

状态 12 冲突:1 个减少/减少

state 12

   56 identifier_list: IDENTIFIER . list_E_identifiers
   60 return_options: IDENTIFIER .
  102 list_E_identifiers: . list_E_identifiers COMMA IDENTIFIER
  103                   | .

    COMMA       reduce using rule 103 (list_E_identifiers)
    IDENTIFIER  reduce using rule 60 (return_options)
    IDENTIFIER  [reduce using rule 103 (list_E_identifiers)]
    $default    reduce using rule 60 (return_options)

    list_E_identifiers  go to state 23

状态 64 冲突:1 次移位/减少

state 64

    8 body: OPEN_BRACE list_E_statement . CLOSE_BRACE
   17 statement: . opt_declaration unlabeled_statement
   18          | . compound
   31 compound: . OPEN_BRACE list_NE_unlstatement CLOSE_BRACE
   73 opt_declaration: . IDENTIFIER COLON
   74                | .
   94 list_E_statement: list_E_statement . statement

    CLOSE_BRACE  shift, and go to state 68
    IDENTIFIER   shift, and go to state 69
    OPEN_BRACE   shift, and go to state 70

    IDENTIFIER  [reduce using rule 74 (opt_declaration)]
    $default    reduce using rule 74 (opt_declaration)

    statement        go to state 71
    compound         go to state 72
    opt_declaration  go to state 73

谁能帮帮我?我看过http://www.gnu.org/software/bison/manual/html_node/Understanding.html,但不明白这是什么意思。

如果有帮助,我可以发布完整的语法。

谢谢!

【问题讨论】:

  • 问题是“什么是 shift/reduce 或 reduce/reduce 冲突?”还是“为什么我的语法中有这些?”是的,您可能应该在此处发布语法,以便我们提供帮助。
  • 冲突并不意味着您的语法不“正确”。 awk 实用程序是在 Yacc 的发源地开发的,但它的 Yacc 语法有九十多个冲突。冲突自动解决;例如,通过偏爱转移而不是减少,可以为您解决转移/减少问题。 Yacc 甚至有一个 %expect N 指令,告诉它预期一定数量的冲突而不是抱怨。
  • @templatetypedef 如果问题不正确,我很抱歉,我希望有人帮助我找到冲突,或者至少帮助我理解这个调试输出的含义,以便我可以找到冲突。
  • @luisforque:没有必要拆分列表的“其余部分”。只需写identifier_list: identifier | identifier_list ',' identifierparameter_list: formal_parameter | parameter_list ',' formal_parameter 等。你不需要到处使用空的产品,有时它们会给你带来麻烦。 (偶尔,您确实需要指定前缀而不减少。但这种情况非常罕见。)
  • @rici 你写的 parameter_list 和 identifier_list 的情况,这些情况不能为0,对吧?例如,在我的规则中,我需要一个包含 0 个或多个元素的标识符列表。这就是我这样做的原因。在您的解决方案中,元素必须为 1 或更多,对吗?

标签: parsing bison yacc


【解决方案1】:

第二个冲突是“可选”元素的经典问题。为可选标签编写规则非常诱人,但optional_label 无法产生任何结果这一事实迫使解析器在获得足够信息之前尝试做出决定。

LR 解析器必须在吸收任何进一步的标记之前“减少”(识别)非终结符。他们可以提前查看下一个 k 标记(LR(1) 解析器的下一个 1 标记,这是 bison 生成的),但他们不能暂时使用该标记,然后再返回执行减少。

因此,当解析器处于下一个标记(即标识符)应该开始语句的位置时,它可能正在查看以标识符开头的语句,或者它可能正在查看以一个标识符。它可以通过查看标识符后面的冒号(如果有的话)来区分,但它看不到前面那么远。

现在,如果不是因为需要减少空optional_declaration 或包含标签的事实,就不会有问题。如果你写了这样的东西:

statement: basic_statement | compound
basic_statement: unlabeled_statement | declaration unlabeled_statement
declaration: IDENTIFIER COLON

那么解析器在看到标识符时就不必做出决定。它只需要在生产结束时做出决定;当有两个可能的生产要完成时,它完全有能力向前推进。但是当你强制它识别一个可选标签时,它必须知道标签是否不存在以减少(识别)空产生。

对于第一个冲突,我们可以从输出中看到,在某些上下文中,前瞻符号是IDENTIFIER,您可以有return_optionsidentifier_list。由于这两个产生式都可以产生一个IDENTIFIER,因此解析器将不知道要减少哪一个。

有了可用的实际语法,很容易找到return_options IDENTIFIERidentifier_list IDENTIFIER 都可能出现的上下文:

formal_parameter    : expression_parameter | function_parameter
expression_parameter: identifier_list IDENTIFIER
function_parameter  : return_options  IDENTIFIER …

这个语法没有歧义。如果IDENTIFIER IDENTIFIERfunction_parameter的开头,那么后面必须跟(;如果是expression_parameter,那么后面必须跟,之一> 或 )。但这是第二个下一个标记,这意味着您需要一个 LR(2) 解析器。

所以我将就处理 LR(2) 语法给出我通常的建议。可以将 LR(k) 语法重写为 LR(1) 语法,不管 k 的值如何,但结果通常是臃肿和丑陋的。因此,如果您使用 bison 并且愿意忍受动作评估可能会稍微延迟的可能性,那么您最简单的解决方案是让 bison 生成 GLR 解析器。通常,只需将%glr-parser 添加到选项部分就足够了。

值得注意的是,您的语法似乎是 C 和类 Pascal 语法之间的一种不安混合。在 C 中,参数中的第一个标记始终是类型;函数的返回类型或以下标识符的类型。在 Pascal 中,参数中的最后一个标记是类型。但是在您的语法中,有时第一个标记是类型,有时是最后一个标记。从某种意义上说,正是这种不一致导致了语法上的尴尬。

(Pascal 有更多的标点符号:类型总是以冒号开头,函数参数以单词 function 开头。这些额外的标记不需要使语法正常工作,但可以说它们使人类更容易阅读语法。)

【讨论】:

  • 关于第二个冲突,你说的很有道理。我一直忘记 Bison 一次只能查看一个元素这一事实。关于第一个冲突,我不明白为什么 Bison 会达到需要在 return_options 和 identifier_list 之间做出决定的状态。我将更新有关这些规则的更多详细信息。
  • 查看冲突一,我发现存在某种循环依赖:formal_parameters ->formal_parameter -> function_parameter ->formal_parameters。这是允许的吗?会不会引发冲突?
  • @luisforque:你需要找到LR解析的介绍。 Wikipedia page 可能是一个好的开始;比读龙书要短一点:)
  • 我正在为一个类创建解析器。该语言是教授创建的,但是是的,它是 Pascal 和 C 的混合体。我认为解析器应该是 LR(1),所以也许我做错了什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多