我正在继续使用PLY解析。解析IF
和ELSE IF
语句时,我遇到了一个问题。
这是我的代码,我已经删除了不相关的部分,只留下了基础知识(这将在Python 2.7上运行)。
from ply import lex, yacc
tokens = [
'ELSE',
'IF',
'LPAREN',
'RPAREN',
'LCURLY',
'RCURLY'
]
t_IF = r'IF'
t_ELSE = r'ELSE'
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_LCURLY = r'\{'
t_RCURLY = r'\}'
t_ignore = '\r\t '
def t_error(t):
print t
def t_newline(t):
r'\n+'
t.lexer.lineno += len(t.value)
def p_error(p):
print ('error', p)
def p_empty(p):
'empty :'
p[0] = None
def p_if_statement(p):
'if_statement : IF LPAREN RPAREN LCURLY RCURLY elif_statements_or_empty else_statement_or_empty'
p[0] = ('if_statement', (p[6]))
print p[0]
def p_else_statement(p):
'else_statement : ELSE LCURLY RCURLY'
p[0] = ('else_statement')
def p_else_statement_or_empty(p):
'''else_statement_or_empty : else_statement
| empty'''
p[0] = p[1]
def p_elif_statement(p):
'elif_statement : ELSE IF LPAREN RPAREN LCURLY RCURLY'
p[0] = ('elif_statement')
def p_elif_statements_1(p):
'elif_statements : elif_statement'
p[0] = [p[1]]
def p_elif_statements_2(p):
'elif_statements : elif_statements elif_statement'
p[0] = p[1] + [p[2]]
def p_elif_statements_or_empty(p):
'''elif_statements_or_empty : elif_statements
| empty'''
p[0] = p[1]
lexer = lex.lex()
s = 'IF(){}ELSE IF(){}ELSE{}'
parser = yacc.yacc(start='if_statement')
parser.parse(s) # ('error', LexToken(LCURLY,'{',1,21))
基本上,问题在于我正在搜索无限量的elif_statement
s。当遇到ELSE {
时,它认为存在解析错误,因为它期望在IF
之后ELSE
。
这是什么叫“转移/减少错误”?有什么方法可以解决这个问题?
非常感谢任何帮助!
答案 0 :(得分:1)
是的,这是转移/减少冲突的结果。 PLY警告你,你有两个,它们都与else
条款有些相关。
但是,问题与else if
子句的潜在无限序列无关。那不是问题。问题更加微妙,与您使用空制品有关。
从解决问题的角度来看,LR(1)最重要的方面是每个非终端需要减少(即,从右手创建)在紧接着的令牌移位之前。考虑到这一点,让我们来看看if_statement
的语法:
if_statement : IF LPAREN RPAREN LCURLY RCURLY
elif_statements_or_empty
else_statement_or_empty
并且假设我们已经看到IF(){}
,因此下一个标记是ELSE
。此时右侧(此时是解析中唯一的活动项)我们知道下一个标记必须是ELSE
或输入的结尾。在后一种情况下,明确必须做什么:必须减少empty
,然后必须从elif_statements_or_empty
减少empty
;必须减少另一个empty
,并且else_statement_or_empty
必须从empty
减少ELSE
。这一切都有效。
但假设下一个标记为ELSE
。怎么办?由于我们看不到IF
以外的任何内容,因此我们不知道其后是(
还是empty
。但我们必须在两个选项之间做出决定:
减少 elif_statements_or_empty
,然后ELSE
。如果else_statement_or_empty
是ELSE
的开头(在这种情况下不能为空),那将是正确的。
Shift elif_statements_or_empty
,elif_statements
只能是ELSE
的开头,不能为空,因此必须为ELSE IF
。如果ELSE
是IF
条款的一部分,那将是正确的。
这样就可以改变/减少冲突。 Bison总是选择转换/减少冲突的转变,因此它会假设ELSE IF
之后的第一个ELSE
是IF(){}ELSE IF(){}
子句的开头。让我们继续这个假设(在提供的输入的情况下恰好是正确的),并继续直到我们达到以下ELSE
。我们现在看到了ELSE IF(){}
,我们再次盯着else_if_statement
。
此时,绝对明确else_if_statement
必须缩减为else_if_statements
,同样明确ELSE IF
必须缩减为ELSE
。到现在为止还挺好。但现在出现了问题。如果有另一个else_if_statement
,那就足够了,我们应该转移 ELSE
成为以下else_statement
的一部分。但如果else_if_statements
是最终else_if_statements_or_empty
的开头,那么我们需要将我们刚创建的ELSE
缩减为else_statement_or_empty
,因为在我们改变开始else_statement_or_empty
的{{1}}之前需要减少。
再一次,野牛总是为这个转变而努力。但这意味着它无法决定开始elif_statements_or_empty
。
那么,该怎么办?
首先要注意的是empty
生产实际上并没有添加任何价值。 (事实上,elif_statements
制作也没有,我本来只是默默地消除,但我会留下这个括号。)如果我们只是将if_statement
放在elif_statements
中,那么我们就不需要在elif_statements_or_empty
和elif_statements
之间做出决定。当然,我们需要elif_statements: elif_statement | elif_statements elif_statement
来匹配一个空字符串,但这很简单:我们只为递归基本情况提供一个空的右侧。也就是说,而不是
elif_statements: | elif_statements elif_statement
我们将使用
elif_statement
我们现在匹配任何数字,包括零elif_statement
,而不是匹配任何数字,但至少有一个elif_statements
。这正是我们想要的。
现在IF(){}
基本情况为空,我们不再需要在elif_statements
之后决定是否有空集elif_statements
。我们只需减少一个空的elif_statement
,然后看看下一步是else_statement
还是def p_error(p):
print ('error', p)
# Fixed to return both elif and else statements
def p_if_statement(p):
'if_statement : IF LPAREN RPAREN LCURLY RCURLY elif_statements else_statement'
p[0] = ('if_statement', (p[6] + p[7]))
print p[0]
# This needs to return a list because it could be empty
def p_else_statement(p):
'else_statement : ELSE LCURLY RCURLY'
p[0] = ['else_statement']
def p_elif_statement(p):
'elif_statement : ELSE IF LPAREN RPAREN LCURLY RCURLY'
p[0] = 'elif_statement'
def p_elif_statements(p):
'elif_statements : elif_statements elif_statement'
p[0] = p[1] + [p[2]]
# This handles both empty elif_statements and an empty else
# by reducing to an empty list
def p_empty(p):
'''elif_statements :
else_statement :'''
p[0] = []
。因此,通过这种简化,我们实际上消除了两种移位减少冲突。
总之,新的PLY文件:(仅解析器规则,没有其他更改):
{{1}}