我使用PLY来解析自定义定义文件的命令。命令每行定义一个,每个命令应以保留关键字开头,后跟多个字符串。我已成功设法为语法编写词法分析器和解析器,但我在生产中提出SyntaxError
时遇到问题。
根据PLY's documentation,只需在解析器规则的主体内抛出SyntaxError
即可:
如有必要,生产规则可以手动强制解析器进入错误恢复。这是通过像这样引发SyntaxError异常来完成的:
def p_production(p): 'production : some production ...' raise SyntaxError
我的代码在遇到无效语法时会在生产中引发SyntaxError
,但是当我运行程序时,不会引发此错误。这是一个最小的工作示例:
from ply import lex, yacc
class Parser(object):
# reserved keyword tokens
reserved = {
"r": "R"
}
# top level tokens
tokens = [
'CHUNK',
'NEWLINE'
]
# add reserved tokens
tokens += reserved.values()
# ignore spaces and tabs
t_ignore = ' \t'
def __init__(self):
# lexer and parser handlers
self.lexer = lex.lex(module=self)
self.parser = yacc.yacc(module=self)
def parse(self, text):
# pass text to yacc
self.parser.parse(text, lexer=self.lexer)
# detect new lines
def t_newline(self, t):
r'\n+'
# generate newline token
t.type = "NEWLINE"
return t
def t_CHUNK(self, t):
r'[a-zA-Z0-9_=.:]+'
# check if chunk is a keyword
t.type = self.reserved.get(t.value.lower(), 'CHUNK')
return t
def t_error(self, t):
raise SyntaxError("token error")
def p_instruction_list(self, p):
'''instruction_list : instruction
| instruction_list instruction'''
pass
# match instruction on their own lines
def p_instruction(self, p):
'''instruction : command NEWLINE
| NEWLINE'''
pass
def p_command(self, p):
'''command : R CHUNK CHUNK CHUNK CHUNK'''
# parse command
if p[2] not in ["a", "b"]:
raise SyntaxError("invalid thing")
def p_error(self, p):
raise SyntaxError("parsing error")
if __name__ == "__main__":
parser = Parser()
parser.parse("""
r a text text text
r c text text text
r b text text text
""")
上面的示例在没有输出任何内容的情况下运行,这意味着它已成功解析了文本,即使p_command
由于行r c text text text
(第二个令牌{{1}而应该引发语法错误}}无效;只有c
或a
才有效。
我做错了什么?
答案 0 :(得分:1)
您负责打印错误消息,但您没有:
手动设置错误的一个重要方面是在这种情况下
p_error()
函数将 NOT 被调用。如果您需要发出错误消息,请确保在提升SyntaxError
的制作中执行此操作。
我不相信p_error()
应该提出SyntaxError
。它应该只打印一个适当的消息(或以其他方式记录发生错误的事实)并让错误恢复继续进行。但无论如何,在这种情况下不会被调用,如上面的引用所示。
我也不是100%相信lexer加注SyntaxError
。我首选的词汇错误策略是将它们传递给解析器,从而将错误处理集中在一个地方。
如果您不关心错误恢复,请不要在任何规则中使用error
令牌。该令牌仅用于错误恢复。如果您只是想在遇到错误时立即抛出异常,请在p_error
中执行此操作,并在不会自动调用它的位置显式调用p_error
(例如检测到令牌错误和错误)在语义行为中)。您可以抛出ValueError
或从中派生的东西;我会远离SyntaxError
,这对Ply和Python来说具有特殊的意义。