Shift/Reduce 尝试添加可选规则时,Bison 发生冲突

Shift/Reduce conflict in Bison, when trying to add optional rule

我正在尝试解决 Bison 中的 shift/reduce 冲突。我有以下语法规则

new_expr:
    T_NEW class_name_reference optional_generics_list ctor_arguments
        { $$ = zend_ast_create(ZEND_AST_NEW, , , ); }
|   T_NEW anonymous_class
        { $$ = ; }

optional_generics_list:
    /* empty */     { $$ = NULL; }
|   generics_list   { $$ = ; }

ctor_arguments:
    /* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_ARG_LIST); }
|   argument_list { $$ = ; }

这里的问题在于,optional_generics_list 和 ctor_arguments 都可以为空。我如何指定(如果可以的话)如果 optional_generics_list 和 ctor_arguments 都为空,那么 ctor_arguments 应该具有更高的优先级。或者也许我的问题不正确,而如何解决这个冲突呢。

一些更新信息: 也许生成的 .output 文件的输出会有帮助:

    State 156 conflicts: 1 shift/reduce

State 156:

  303 new_expr: "new (T_NEW)" class_name_reference . optional_generics_list ctor_arguments

    '<'  shift, and go to state 304

    '<'       [reduce using rule 168 (optional_generics_list)]
    $default  reduce using rule 168 (optional_generics_list)

    optional_generics_list  go to state 305
    generics_list           go to state 306


State 305

  303 new_expr: "new (T_NEW)" class_name_reference optional_generics_list . ctor_arguments

    '('  shift, and go to state 229

    $default  reduce using rule 405 (ctor_arguments)

    argument_list   go to state 546
    ctor_arguments  go to state 552


State 306

  169 optional_generics_list: generics_list .

    $default  reduce using rule 169 (optional_generics_list)

bison 输出文件指出的问题是 new_expr 后面可能跟不属于 [=13 的 < =].例如,如果语法还包含像这样的产生式,那就是这种情况:

term: new_expr
comparison: term '<' term

(当然,人们会期望真正的语法有比这更多的可能性,但这些是必不可少的。)

换句话说,如果您的语法允许您比较两个 newly-constructed 对象,那么解析器无法判断它看到的 < 是否是一个对象的开始generics_list,或者在 new_expr 之后的简单比较运算符,其中 generics_listctor_arguments 都被省略了:

if new Foo < oldFoo then...

myFoo = new Foo<int>(42)

最简单的解决方法是,如果要在表达式中使用 new_expr,则坚持将其括在括号中。

对于它的价值,C++ 通过知道名称是否被模板化来处理这个问题。如果名称可以采用模板参数,则名称后面的 < 被解释为开始模板参数列表;否则它是 less-than 运算符。所以如果 v 是模板化的,那么你必须写 (new v) < ...,但如果 v 只是一个简单的类型名,你可以省略括号:new int < ...。实施起来很棘手;你需要某种词汇反馈,你需要对模板声明的放置位置施加一些限制。 C++ 有一些其他具有类似分辨率的独特解析挑战。例如,new int * i 是一个错误,因为 * 被解析为指针类型修饰符,使用的规则表明 new 表达式中的类型是最长的可解析标记序列作为一种类型。

new 用作任何运算符的左参数时,我会选择强制括号,因为它对 reader 来说不那么令人困惑。它还简化了语法,这是一件好事,因为语法不仅用于解析;它们是语言文档的重要组成部分,不必要的复杂语法使语言不必要地难以学习和理解。

有趣的是,在编写上述关于C++ 的笔记的过程中,我发现了一个主要C++ 编译器的解析器中的错误。 (至少,我想我知道哪个编译器有问题;更准确地说,我发现两个流行的编译器有不一致的行为,所以其中一个肯定是错误的。)一个更简单的规则不会对任何一个产生影响non-contrived 程序,并且会大大降低错误的可能性(并且更容易验证)。因此,我完全不清楚在表达式中间允许未加括号的 new 操作的附加值。