我一直在使用ply编写LALR解析器,并且在尝试解析乘法时遇到了不一致。
由于完整的解析器link有几千行,我不会在这里包含它,但我创建了一个简单的演示:
import ply.lex as lex
import ply.yacc as yacc
tokens = (
'int',
'times',
'plus',
)
precedence = (
('left', 'plus'),
('left', 'times'),
)
t_ignore = ' \t\n '
t_int = r' \d+ '
t_plus = r' \+ '
t_times = ' \* '
def p_int(args):
'expr : int'
args[0] = int(args[1])
def p_times(args):
'''expr : expr times expr
| expr expr %prec times'''
if len(args) == 3:
args[0] = args[1] * args[2]
elif len(args) == 4:
args[0] = args[1] * args[3]
def p_plus(args):
'expr : expr plus expr'
args[0] = args[1] + args[3]
lex.lex()
parser = yacc.yacc()
while True:
s = raw_input('>> ')
print " = ", parser.parse(s)
没有转移/减少冲突或减少/减少PLY报告的冲突但我得到以下不一致:
>> 1 + 2 3
= 9
>> 1 + 2 * 3
= 7
这对我来说很奇怪,因为显式和隐式时间规则具有相同的优先级。 但我认为这可能是因为PLY为'times'标记赋予优先权,从而将其转移到堆栈上,以支持使用p_plus规则减少表达式。我该如何解决这个问题?
编辑:更简单的演示。
答案 0 :(得分:1)
快速入侵:将int
标记添加到优先规范(优先级为时间)。然后int
标记将被适当地移动到符号堆栈上。那是(根据原始问题),
precedence = (
('left', 'plus'),
('left', 'times', 'int'),
)
这可行但在处理潜在的大量令牌时很麻烦(开放式括号, 符号,花车等。)。
>> 1 + 2 3
= 7
我仍然想知道是否有更优雅的解决方案。
答案 1 :(得分:0)
还有一个更合适的解决方案:放弃运算符优先级,并以通常的方式定义非终结符expr
,term
和factor
。原因?隐式乘法没有优先权决策所依据的特征性令牌,您也不想手动恢复非终结符的FIRST集。此外,随着语法的增长,第一组也会随之增长。
在BNF中,请考虑以下类似内容:
expr -> term | expr + term | expr - term
term -> factor | term * factor | term factor | term / factor
factor -> number | variable | ( expr )
顺便说一句,您可能想制作一个额外的非终端层来表示这样一个事实,即隐含的邻接乘法通常被认为比除法具有更高的优先级,而显式乘法通常被解释为具有相同的优先级。
这并非特定于任何特定的解析器生成器框架。