我正在尝试在解析过程中实现用户友好的语法错误处理。根据我在官方PLY documentation中观察到的情况。一种方法是在第一个SyntaxError
发生时引发异常并终止解析。但是,正如文档所建议的,我想做类似的事情来使用解析器重新同步技术。
文档说:
处理语法错误的最佳方式是编写包含错误标记的语法规则。例如,假设您的语言对这样的打印语句具有语法规则:
def p_statement_print(p): 'statement : PRINT expr SEMI' ...
要考虑表达错误的可能性,您可以编写其他语法规则,如下所示:
def p_statement_print_error(p): 'statement : PRINT error SEMI' print("Syntax error in print statement. Bad expression")
我有这样一个语法摘录:
def p_operation(self, p) -> None:
'''
operation : unaryOperation
| binaryOperation
'''
def p_unaryOperation(self, p) -> None:
'''
unaryOperation : unaryOperation L_SQUARE_BRACKET projection R_SQUARE_BRACKET
| RELATION_NAME
'''
def p_projection(self, p) -> None:
'''
projection : multipleAttributes
| attribute
'''
def p_multipleAttributes(self, p) -> None:
'''
multipleAttributes : projection COMMA attribute
'''
def p_attribute(self, p) -> None:
'''
attribute : ATTRIBUTE
'''
我不确定如何定义这样的新规则,包括error
令牌。我应该用error
令牌替换每个非终结符吗?
期待您的答复!非常感谢您的帮助
答案 0 :(得分:1)
您绝对不应该为每个非终端添加错误信息。
当有一些令牌通常可以将解析上下文重置为已知状态时,重新同步工作。在具有清晰的语句结尾标记的语言中(在您引用的示例中为分号),该标记可以很好地用作重新同步点。丢弃直到下一个分号的文本,然后再从那里进行解析不会在100%的时间内起作用,但是在许多情况下确实可以。
括号和括号也可以用作重新同步点,但是启发式方法不那么可靠,因为许多语法错误是括号或括号不匹配的结果。例如,扫描缺少的右方括号可能会丢弃整个输入。
在没有明确的语句定界符的语言中,重新同步更加复杂,包括Python之类的语言,其中的换行符仅在语句未嵌套在括号内时才终止语句。丢弃最多换行符可能会起作用,但是您可能必须处理扫描程序与解析器之间的反馈,该反馈确定何时将换行符作为令牌传输以及何时将其跳过为空白。
不一致的缩进可能是有用的重新同步触发器,但有几点警告。首先,您一定不能拒绝带有“误导”缩进的有效输入,因此触发器在重新同步过程中比在正常解析过程中需要更加敏感。其次,跟踪不一致的缩进肯定需要解析器->扫描器反向通道。因此,它比简单的紧急恢复要多得多,但可以有效。
最重要的是,对于良好的错误报告和恢复,几乎没有通用算法(如果有)。您需要根据语言的句法本质来制定策略。
理想情况下,您将希望通过检查解析器对常见错误的响应来优化代码,但这只有在进行实际部署并了解常见错误之后才能真正做到。因此,我能提供的最佳建议是从一个简单的恢复策略开始,看看它如何处理不同的语法错误,尤其是您偶然创建的语法错误(或您的朋友和合作者的语法错误)。保留遇到的各种语法错误的存档,可用于测试对诊断和恢复代码的改进。别指望它是完美的,因为这是一个棘手的问题,但是请尽一切可能使它更准确。