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_list
和 ctor_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
操作的附加值。
我正在尝试解决 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_list
和 ctor_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
操作的附加值。