基于PLY解析DNET文件:如何用BNF保证列表令牌类型一致?
问题描述
我正在使用PLY解析DNET文件,DNET规范建议的部分语法如下:
<value> -> NUMBER | STRING | ID <values> -> <value> | <values> COMMA <value> <list> -> LPAREN <values> RPAREN | LPAREN RPAREN
该语法在不关心列表内令牌类型混合时可正常工作,但我希望确保解析器仅生成有效列表,即保证列表内令牌类型一致。我的思路是为每个合法令牌类型组合定义列表产生式,例如要保证是STRING令牌列表:
<strings> -> STRING | <strings> COMMA STRING <string_list> -> LPAREN <strings> RPAREN | LPAREN RPAREN
但这需要定义大量不同的列表产生式,请问在解析器层面有没有更优的实现方式,还是应该在构建AST后执行此类验证?
解决方案
有两种可行的优化方向,具体选择取决于需求复杂度:
1. 解析器层面的动态验证(推荐)
不用定义大量重复产生式,直接在解析列表的语义动作里做类型一致性检查:
- 保留原有
<list>和<values>语法,在p_values相关函数中跟踪当前列表的元素类型:def p_values_single(p): 'values : value' p[0] = [p[1]] # 记录第一个元素的类型 p.parser.list_type = type(p[1]) def p_values_multiple(p): 'values : values COMMA value' # 检查当前元素类型与列表已有类型是否一致 if type(p[3]) != p.parser.list_type: raise SyntaxError(f"列表元素类型不一致:期望{p.parser.list_type.__name__},但得到{type(p[3]).__name__}") p[0] = p[1] + [p[3]] - 空列表场景下,可在
p_list_empty中将list_type设为None,后续根据上下文灵活处理。
这种方式无需修改语法结构,仅通过语义动作加检查,代码简洁且能在解析阶段直接抛出错误,便于快速定位问题。
2. AST后验证
如果DNET语法存在更复杂的上下文相关类型约束,或希望解析与验证逻辑解耦,可在构建完AST后遍历节点做检查:
- 遍历AST中的列表节点,提取所有元素的类型进行比对;
- 发现混合类型时,抛出带具体位置信息的验证错误。
这种方式逻辑清晰,验证规则可集中维护,适合类型约束多变的场景,但错误提示的精准度略低于解析阶段的即时检查。
总结
如果仅要求列表内部元素类型一致,优先选择解析器层面的语义动作验证,代码量小且错误反馈及时;若存在复杂的上下文类型约束,AST后验证会更灵活。
内容的提问来源于stack exchange,提问作者Mackie Messer
相关产品推荐
相关产品推荐

