PLY yacc解析器在每个换行符后丢失第一个术语

时间:2016-06-15 16:40:38

标签: python parsing yacc ply

我尝试通过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))

1 个答案:

答案 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所需的任何操作。