我正在编写一种编程语言解析器,但陷入了这种Shift / Reduce冲突。
这是通过使用-v运行bison获得的parser.output文件中的冲突状态
State 1
24 ident: TIDENT .
26 call: TIDENT . TLPAREN args TRPAREN
TLPAREN shift, and go to state 24
TLPAREN [reduce using rule 24 (ident)]
$default reduce using rule 24 (ident)
当我尝试实现呼叫规则时,冲突正在发生,它似乎与正常的ident规则冲突。
这是语法的某些部分,(为简单起见,删除了一些动作,但不需要这样做。而且我不确定规则的定义顺序是否重要,如果我错了,请纠正我)
(大写字母是令牌)
身份规则很简单
ident: TIDENT
;
Args,用于通话。
args: /* empty */
|
expr
|
args TCOMMA expr
;
调用调用函数
call:
TIDENT TLPAREN args TRPAREN
;
表达式表达式
expr:
number
|
ternary
|
bool
|
string
|
ident
|
call
|
TLPAREN expr TRPAREN
|
expr TPLUS expr
|
expr TMINUS expr
|
expr TSLASH expr
|
expr TSTAR expr
|
expr TGT expr
|
expr TGE expr
|
expr TLT expr
|
expr TLE expr
;
问题:为什么语法会有移位/减少冲突,您如何解决?我见过类似的样式解析器,它们没有真正奇怪的冲突。
如果您需要查看完整的语法以进行重现,请使用hastebin https://hasteb.in/zozifopi.shell
如果您需要其他详细信息,请在评论中让我知道,我将相应地编辑问题。
答案 0 :(得分:1)
这里的根本问题是语法不明确,因为语句不需要终止(stmts: stmts stmt
),并且语句可以是表达式。因此,两个表达式可以一个接一个地出现而没有标点符号。这意味着f(3)
可以是一个表达式(一个函数调用),也可以是两个表达式(f
和(3)
)。
如果您希望语法分析器始终将其解释为函数调用(这是它的默认行为,因为它倾向于移动),则可以添加几个优先级声明,以便调用具有优先级高于降低优先级:
%precedence TIDENT
//...
%precedence TLPAREN
// ...
%%
expr : ident %prec TIDENT
这只是模糊性的论文,可能会引起令人惊讶的分析。但是,唯一的其他解决方案是使语言明确。
答案 1 :(得分:0)
问题在于,当解析器将TIDENT令牌移至Rails.application.config.before_initialize do
# initialization code goes here
end
令牌之前,该语法允许两种选择:
TLPAREN
减少为TIDENT
,或ident
。野牛通常通过选择换档来解决换档/减少冲突,如果在这种情况下正是您想要的,那么您可以简单地忽略警告。
但是,在这种特殊情况下,您应该能够通过更改TLPAREN
生产的规则来解决冲突:
call
使用该规则,不再先将call:
ident TLPAREN args TRPAREN
;
减小为TLPAREN
即可移动TIDENT
。
或者,您可以考虑完全删除ident
非终结符,而直接在现在使用ident
的任何地方直接使用TIDENT
。也许还有其他选择。最适合您的方法可能取决于您要对语义动作进行的处理。我不能对此发表更具体的评论,因为您已选择将语义动作排除在我们的考虑范围之外。
答案 2 :(得分:0)
默认情况下,Bison会生成一个LR解析器,它是一个自底向上的解析器,它可以决定每种状态是转移令牌还是减少令牌。
冲突真的很简单,并且可以通过输出本身很好地解释(我想知道还不清楚),它告诉您:
如果我找到
IDENTIFIER
,是否应该通过规则24将其减少为ident
的非终止符,还是应该像call
规则中那样将其移位?
这是因为一旦减少,您将无法转移,反之亦然,这确实造成了冲突。
要解决冲突,您需要将选择项移至解析器的相同状态,以便能够根据上下文进行确定。
由于ident
仅具有一条终端IDENT
规则,并且通话规则也是如此,因此您可以轻松地将所有内容移到同一级别以使其始终移动:
expr:
IDENT |
IDENT LPAREN args RPAREN |
...
或对ident
和call
本身使用相同的expr
非终端,以便始终将其减少。