PLY yacc 解析器在每个换行符后都丢失了第一个术语
PLY yacc parser lost first term after every newline
我尝试通过 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))
您的 p_error
函数:
def p_error(p):
if not p:
print("SYNTAX ERROR AT EOF")
静默忽略错误,输入结束时除外。默默地忽略错误几乎总是错误的,而且几乎总是令人困惑,就像在这种情况下一样。
您的 statement
产品只接受单行,可能以换行符终止。除了文件结尾指示符之外,没有其他标记可以跟在换行符之后。所以第二个标记 b
—— 即第二行开头的标记 —— 导致语法错误。
由于语法错误被静默忽略,因此没有指示此错误。由于 PLY 随后将进入错误恢复模式,解析器将有效地重新启动。但是,有问题的令牌 b
已经是 "handled",因此从下一个令牌 d
.
开始重新启动
这将在第二个换行符之后再次发生。同样,第三行开头的 c
会导致语法错误,它会被静默忽略,然后被丢弃,解析器会在输入 f
.
处重新启动
我不清楚你的期望是什么。一种可能性是在 p_error
中 raise SyntaxError
(或其他错误类型),而不是仅仅返回,以终止解析。但是,错误的令牌已被丢弃。
或者您可能想要接受任意数量的语句。在这种情况下,您的声明规则应该类似于
statement:
| statement NEWLINE
| statement NEWLINE plist
并且与第三个选项关联的操作将执行您需要使用 plist
完成的任何操作。
我尝试通过 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))
您的 p_error
函数:
def p_error(p):
if not p:
print("SYNTAX ERROR AT EOF")
静默忽略错误,输入结束时除外。默默地忽略错误几乎总是错误的,而且几乎总是令人困惑,就像在这种情况下一样。
您的 statement
产品只接受单行,可能以换行符终止。除了文件结尾指示符之外,没有其他标记可以跟在换行符之后。所以第二个标记 b
—— 即第二行开头的标记 —— 导致语法错误。
由于语法错误被静默忽略,因此没有指示此错误。由于 PLY 随后将进入错误恢复模式,解析器将有效地重新启动。但是,有问题的令牌 b
已经是 "handled",因此从下一个令牌 d
.
这将在第二个换行符之后再次发生。同样,第三行开头的 c
会导致语法错误,它会被静默忽略,然后被丢弃,解析器会在输入 f
.
我不清楚你的期望是什么。一种可能性是在 p_error
中 raise SyntaxError
(或其他错误类型),而不是仅仅返回,以终止解析。但是,错误的令牌已被丢弃。
或者您可能想要接受任意数量的语句。在这种情况下,您的声明规则应该类似于
statement:
| statement NEWLINE
| statement NEWLINE plist
并且与第三个选项关联的操作将执行您需要使用 plist
完成的任何操作。