我正在为国际象棋代数符号开发一个BNF并遇到一个有趣的案例,输入到了错误的非终端。
我的开始BNF规则如下(请注意,这故意不包括铸造或备注):
algebraic_notation : piece start_position capture end_position promotion
piece
,start_position
,capture
和promotion
可以为空,从而允许像'd4'这样的移动。问题在于,当输入这样的移动时,输入('d4')由start_position
获取,因此导致错误b / c不再有end_position
的输入,这不能是空。
明显的黑客/解决方法是允许end_position
为空,然后检查我们是否获得了任何输入并采取相应措施。
这确实有效,但我想知道是否有办法解决这个问题。输入是否可能不会转到第一个匹配的符号,如果它导致整个表达式不匹配?
另一个问题是这是否是BNF的标准行为,或者是我正在使用的yaccer的问题:PLY v 3.3。
尝试使用flex / bison并且做了同样的事情。所以它似乎不是PLY特有的。
以下是完整性的所有相关规则:
algebraic_notation : piece start_position capture end_position promotion
piece : KING
| QUEEN
| BISHOP
| KNIGHT
| ROOK
| pawn
pawn : empty
start_position : FILE
| NUMBER
| FILE NUMBER
| empty
end_position : FILE NUMBER
| empty // this line is the hack/workaround
capture : CAPTURE
| empty
promotion : EQUAL QUEEN
| EQUAL ROOK
| EQUAL KNIGHT
| EQUAL BISHOP
| empty
empty :
答案 0 :(得分:1)
问题是你忽略了从解析器生成器获得的转移/减少冲突。虽然yacc / bison(可能是PLY)会为你解决错误,但是这个解决方案可能没有做你想要的,并且可能会导致解析器解析你想要解析的语言之外的语言。
每当你从LR解析器生成器得到转移/减少(或减少/减少)冲突时,你真的需要了解冲突是什么(及其发生的原因),以了解是否可以忽略它或是否需要修理它。因此,让我们通过摆脱'hack'(这显然是错误的而不是你要解析的东西)以及无用的“空”规则(这只会让事情混淆)来修复你的语法:
%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的等价物是什么),你得到关于转移/减少冲突的信息,并且如果查看.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
这告诉您,在看到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'
现在bison报告没有更多的冲突,所以我们可以合理地相信它会解析我们想要的方式。您可以通过删除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*/
答案 1 :(得分:0)
我没有使用PLY,并且没有看到你尝试过的完整的flex / bison文件我可能会选择一个非问题,但在我看来,你并没有给解析器一个关于不再有了它的想法对于当前的algebraic_notation
规则。你没有说你怎么知道输入'd4'与start_position匹配,但如果解析器知道它有规则的所有标记,并且唯一的非空标记是end_position,它必须匹配'd4'到这一点。
如何引入标记行结束的令牌,如EOL。所以你的第一条规则就是:
algebraic_notation : piece start_position capture end_position promotion EOL
并且解析器现在看到令牌'd4'后跟EOL - 这会改变行为吗?
答案 2 :(得分:0)
如果将start_position capture end_position
包装到中间块中,并从start_pos中删除FILE NUMBER
,会发生什么情况:
middle: start_pos capture end_pos
| end_pos capture end_pos
| capture end_pos
start_pos : FILE
| NUMBER
| empty
end_pos : FILE NUMBER
capture : CAPTURE
| empty
答案 3 :(得分:0)
这个问题很好地说明了一个问题,即计算机科学理论,从语法中删除epsilon(或空)的作品。通过简化语法来移除空制作,可以解决国际象棋符号模糊性的问题(对于yacc或PLY)。在SO / SE和其他网站上有很多这方面的材料。我为感兴趣的读者附上了一份参考书目。
通过对规则进行无意识的转换以消除盲/空/ epsilon制作,我们得到以下CFG:
algebraic_notation : piece start_position capture end_position promotion
| piece start_position capture end_position
| piece start_position capture promotion
| piece start_position end_position promotion
| piece capture end_position promotion
| piece start_position capture
| piece start_position end_position
| piece capture end_position
| piece start_position promotion
| piece capture promotion
| piece end_position promotion
| piece promotion
| piece end_position
| piece capture
| piece start_position
| piece
| start_position capture end_position promotion
| start_position capture end_position
| start_position capture promotion
| start_position end_position promotion
| capture end_position promotion
| start_position capture
| start_position end_position
| capture end_position
| end_position promotion
| start_position
| capture
| end_position
| promotion
piece : KING
| QUEEN
| BISHOP
| KNIGHT
| ROOK
start_position : FILE
| NUMBER
| FILE NUMBER
end_position : FILE NUMBER
capture : CAPTURE
promotion : EQUAL QUEEN
| EQUAL ROOK
| EQUAL KNIGHT
| EQUAL BISHOP
(这可以通过删除国际象棋符号中不会出现的那些组合来简化,但这对读者来说是一种练习。)
参考书目
最好的书籍可能是:
Hopcroft & Ullman Introduction to Automata Theory, Languages, and Computation
Aho& Ullman 解析,翻译和编译理论
或者只是去看看Jeff Ullman班级的幻灯片:
或者关于SO / SE的一系列相关问题: