问题是您忽略了从解析器生成器获得的移位/减少冲突。虽然 yacc/bison(可能是 PLY)会为您解决错误,但该解决方案可能无法满足您的需求,并且可能导致解析器解析您尝试解析的语言以外的语言。
每当您从 LR 解析器生成器中获得 shift/reduce(或 reduce/reduce)冲突时,您确实需要了解冲突是什么(以及为什么会发生)才能知道是否可以忽略它或是否需要修复它。因此,让我们通过摆脱“hack”(这显然是错误的,而不是您想要解析的东西)以及无用的“empty”规则(只会混淆事物)来修复您的语法:
%token FILE NUMBER
%%
algebraic_notation : piece start_position capture end_position promotion
piece : 'K' | 'Q' | 'B' | 'N' | 'R' | /*pawn*/
start_position : FILE | NUMBER | FILE NUMBER | /*empty*/
end_position : FILE NUMBER
capture : 'x' | /*empty*/
promotion : '=' 'Q' | '=' 'R' | '=' 'N' | '=' 'B' | /*empty*/
现在,当您通过 'bison -v' 运行它时(总是使用 -v 来获取详细的输出文件——我不确定 PLY 的等价物是什么),您会收到有关 shift/reduce 冲突的消息,如果您看在.output
文件中你可以看到它是什么:
state 7
1 algebraic_notation: piece . start_position capture end_position promotion
FILE shift, and go to state 9
NUMBER shift, and go to state 10
FILE [reduce using rule 11 (start_position)]
$default reduce using rule 11 (start_position)
start_position go to state 11
这告诉您,在看到 a 之后piece
,当下一个标记是 时FILE
,它不知道它应该转移(将FILE
视为 (的一部分)start_position
)还是减少(给出一个空的start_position
)。那是因为它需要更多的前瞻性来查看是否有第二个位置可以用作end_position
知道要做什么,所以简单地忽略冲突将导致解析器无法解析许多有效的东西(基本上,任何带有空start_position
和的东西capture
) .
解决涉及像这样的空产生式(或几乎任何涉及空产生式的冲突)的前瞻相关的移位减少冲突的最佳方法是分解语法 - 摆脱空规则并复制任何规则使用和不使用非终端。就您而言,这为您提供了规则:
algebraic_notation : piece capture end_position promotion
algebraic_notation : piece start_position capture end_position promotion
start_position : FILE | NUMBER | FILE NUMBER
(其他规则不变)这样你仍然有一个移位减少冲突:
state 7
1 algebraic_notation: piece . capture end_position promotion
2 | piece . start_position capture end_position promotion
FILE shift, and go to state 9
NUMBER shift, and go to state 10
'x' shift, and go to state 11
FILE [reduce using rule 14 (capture)]
start_position go to state 12
capture go to state 13
基本上,我们只是将冲突移了一步,现在出现了空capture
规则的问题。因此,我们也将其分解:
algebraic_notation : piece end_position promotion
algebraic_notation : piece capture end_position promotion
algebraic_notation : piece start_position end_position promotion
algebraic_notation : piece start_position capture end_position promotion
capture : 'x'
现在野牛报告不再有冲突,所以我们可以有理由相信它会按照我们想要的方式解析。您可以通过摆脱规则并在规则中使用文字来进一步简化capture
它。我个人更喜欢这个,因为我认为避免不必要的间接更清楚:'x'
algebraic_notation
%token FILE NUMBER
%%
algebraic_notation : piece end_position promotion
algebraic_notation : piece 'x' end_position promotion
algebraic_notation : piece start_position end_position promotion
algebraic_notation : piece start_position 'x' end_position promotion
piece : 'K' | 'Q' | 'B' | 'N' | 'R' | /*pawn*/
start_position : FILE | NUMBER | FILE NUMBER
end_position : FILE NUMBER
promotion : '=' 'Q' | '=' 'R' | '=' 'N' | '=' 'B' | /*empty*/