我正在努力解决Bison的转移/减少冲突。我有遵循语法规则
new_expr:
T_NEW class_name_reference optional_generics_list ctor_arguments
{ $$ = zend_ast_create(ZEND_AST_NEW, $2, $4, $3); }
| T_NEW anonymous_class
{ $$ = $2; }
optional_generics_list:
/* empty */ { $$ = NULL; }
| generics_list { $$ = $1; }
ctor_arguments:
/* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_ARG_LIST); }
| argument_list { $$ = $1; }
这里的问题在于,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)
答案 0 :(得分:0)
bison输出文件指出的问题是,new_expr
可能后跟&lt; ,而&lt; 不属于generics_list
。例如,如果语法也包含如下产品,那将是这种情况:
term: new_expr
comparison: term '<' term
(当然,人们会期望真正的语法有更多的可能性,但那些是必不可少的。)
换句话说,如果您的语法允许您比较两个新构造的对象,那么解析器无法判断它看到的&lt; 是否是generics_list
的开头,或new_expr
之后的简单比较运算符,其中generics_list
和ctor_arguments
都已被省略:
if new Foo < oldFoo then...
myFoo = new Foo<int>(42)
最简单的解决方法是坚持new_expr
括号如果要在表达式中使用它。
对于它的价值,C ++通过知道名称是否是模板化来处理这个问题。如果名称可以采用模板参数,则名称后面的&lt; 将被解释为启动模板参数列表;否则它是一个小于运营商。因此,如果v
是模板化的,那么您必须编写(new v) < ...
,但如果v
只是一个简单的类型名称,则可以省略括号:new int < ...
。实施这是棘手的;您需要某种词汇反馈,并且需要对可以放置模板声明的位置施加一些限制。 C ++具有一些其他独特的解析挑战,具有相似的分辨率。例如,new int * i
是一个错误,因为*
被解析为指针类型修饰符,使用的规则表明new
表达式中的类型是可解析的最长令牌序列作为一种类型。
当new
用作任何运算符的左参数时,我会选择强制括号,因为它对读者来说不那么容易混淆。它还简化了语法,这是一件好事,因为语法不仅仅用于解析;它们是语言文档的重要组成部分,不必要的复杂语法使语言难以学习和理解。
有趣的是,在编写上述关于C ++的注释的过程中,我发现了一个主要C ++编译器的解析器中的一个错误。 (至少,我想我知道哪个编译器是错误的;更准确的说我发现两个流行的编译器具有不一致的行为,因此其中一个必定是错误的。)一个更简单的规则对任何一个都没有影响非人为的程序,并且会使错误的可能性降低(并且更容易验证)。因此,对于我来说,在表达式中间允许无表达式new
操作的附加值并不是很清楚。