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_errorraise SyntaxError (或其他错误类型),而不是仅仅返回,以终止解析。但是,错误的令牌已被丢弃。

或者您可能想要接受任意数量的语句。在这种情况下,您的声明规则应该类似于

statement:
         | statement NEWLINE
         | statement NEWLINE plist

并且与第三个选项关联的操作将执行您需要使用 plist 完成的任何操作。