我尝试通过PLY编写一个简单的解析器,但是下面的解析器将在每个NEWLINE之后丢失第一个字符串。
输入为“a b c \ nb d e \ nc f”。
我的解析器将第一行语句解析为状态(0,((''a','b'),'c'),0)),但下一个标记'b'丢失。第二行语句是state(0,(('d','e'),0))。我该如何解决这个问题?
import ply.lex as lex
import ply.yacc as yacc
tokens = ('STRING', 'NEWLINE')
t_STRING = r'[^ \n]+'
t_ignore = r' '
def t_NEWLINE(t):
r'\n'
t.lexer.lineno += 1
return t
def t_error(t):
print("Illegal character %s" % t.value[0])
t.lexer.skip(1)
def p_statement_interactive(p):
'''statement : plist
| plist NEWLINE'''
p[0] = (0, (p[1],0))
print "state", p[0]
def p_item_string_expr(p):
'''plist : plist pitem
| pitem'''
if len(p) > 2:
p[0] = (p[1],p[2])
else:
p[0] = p[1]
print "str2", p[0]
def p_item_string(p):
'''pitem : STRING'''
p[0] = p[1]
print "str1", p[0]
def p_error(p):
if not p:
print("SYNTAX ERROR AT EOF")
def main():
data = """a b c
b d e
c f"""
lexer = lex.lex(debug=0)
lexer.input(data)
while True:
tok = lexer.token()
if not tok:
break # No more input
print(tok)
parser = yacc.yacc()
parser.parse(data)
if __name__ == '__main__':
main()
结果是:
LexToken(STRING,'a',1,0)
LexToken(STRING,'b',1,2)
LexToken(STRING,'c',1,4)
LexToken(NEWLINE,'\n',1,5)
LexToken(STRING,'b',2,10)
LexToken(STRING,'d',2,12)
LexToken(STRING,'e',2,14)
LexToken(NEWLINE,'\n',2,15)
LexToken(STRING,'c',3,20)
LexToken(STRING,'f',3,22)
str1 a
str2 a
str1 b
str2 ('a', 'b')
str1 c
str2 (('a', 'b'), 'c')
state (0, ((('a', 'b'), 'c'), 0))
str1 d
str2 d
str1 e
str2 ('d', 'e')
state (0, (('d', 'e'), 0))
str1 f
str2 f
state (0, ('f', 0))
答案 0 :(得分:1)
您的p_error
功能:
def p_error(p):
if not p:
print("SYNTAX ERROR AT EOF")
除了在输入结束时,默默地忽略错误。默默地忽略错误几乎总是错误的,并且几乎总是令人困惑,就像在这种情况下一样。
您的statement
作品只接受一行,可能以换行符结尾。除文件结束指示符之外的任何令牌都不能跟随换行符。因此第二个令牌b
- 即第二行开头的令牌 - 会导致语法错误。
由于语法错误被静默忽略,因此没有迹象表明存在此错误。由于PLY将进入错误恢复模式,解析器将有效地重新启动。但是,违规令牌b
已被“处理”,因此重新启动会从下一个令牌d
开始。
这将在第二个换行符后再次发生。同样,第三行开头的c
将导致语法错误,该语法错误被静默忽略,然后被丢弃,解析器将在输入f
处重新启动。
我不清楚你的期望是什么。一种可能性是raise SyntaxError
中的p_error
(或其他一些错误类型),而不仅仅是返回,以终止解析。但是,错误的令牌已经被丢弃了。
或者您可能希望接受任意数量的陈述。在这种情况下,您的声明规则应该类似于
statement:
| statement NEWLINE
| statement NEWLINE plist
与第三个选项相关联的操作可以执行plist
所需的任何操作。