使用Forward元素时如何重写infix_notation的operator_rules并保持代码DRY?
解决PyParsing中Forward依赖下复用infix_notation的DRY方案
问题背景
现有简化伪代码如下:
arg = Forward() ... func_call = somestuff + arg term = ... | ... | arg expression = infix_notation(term, operator_rules) ... arg <<= expression | func_call | ... | ... ... statement = var + ASSIGN + expression code_line = Optional(White()) + (statement | func_call | expression) + Optional(dbl_slash_comment).leaveWhitespace()
需求为复用上述解析逻辑,但需给operator_rules配置不同的parse_action。由于Forward定义的arg依赖expression,无法直接封装导入复用;当前复制模块修改的方式违反DRY原则,维护成本高。
核心问题
- 能否重写
infix_notation的operator_rules(包含解析动作和规则列表)?因嵌套表达式增多时解析时间呈指数增长,需根据待解析字符串限制运算符数量。 - 作为
infix_notation的term的Forward元素,阻碍了导入infix_notation元素并重写operator_rules的操作,该如何处理?
优雅解决方案
方案1:工厂函数封装解析器构建逻辑
将解析器的构建过程封装为接受operator_rules参数的函数,每次调用生成独立的解析器实例,既避免重复代码,又能灵活配置规则,同时可导出code_line供外部使用:
from pyparsing import Forward, infix_notation, Optional, White, infixNotation def build_parser(custom_operator_rules): # 每次调用创建全新的Forward实例,避免跨实例依赖冲突 arg = Forward() # 复用固定解析逻辑 func_call = somestuff + arg term = ... | ... | arg expression = infix_notation(term, custom_operator_rules) arg <<= expression | func_call | ... | ... statement = var + ASSIGN + expression code_line = Optional(White()) + (statement | func_call | expression) + Optional(dbl_slash_comment).leaveWhitespace() return code_line # 使用示例:生成不同规则的解析器实例 rules_with_action1 = [ (op1, 2, infixNotation.RIGHT, parse_action1), (op2, 2, infixNotation.LEFT, parse_action2) ] code_line_v1 = build_parser(rules_with_action1) rules_with_action2 = [ (op1, 2, infixNotation.RIGHT, parse_action3), (op3, 1, infixNotation.RIGHT, parse_action4) ] code_line_v2 = build_parser(rules_with_action2)
方案2:动态裁剪运算符规则(针对问题1)
若需根据待解析字符串限制运算符数量,可在工厂函数中先过滤规则再构建解析器,减少解析负担:
def build_parser_for_input(input_str, full_operator_rules): # 提取输入字符串中出现的运算符(需自行实现提取逻辑) present_ops = extract_operators_from_input(input_str) # 过滤出仅包含出现运算符的规则 filtered_rules = [ rule for rule in full_operator_rules if rule[0].parseString(input_str, parseAll=False) != [] ] return build_parser(filtered_rules)
方案3:动态修改infix_notation解析动作(进阶)
若不想重复构建解析器,可直接修改infix_notation生成的表达式内部运算符的解析动作(需熟悉PyParsing内部结构):
# 先构建基础无解析动作的解析器 base_rules = [ (op1, 2, infixNotation.RIGHT, None), (op2, 2, infixNotation.LEFT, None) ] base_code_line = build_parser(base_rules) # 动态替换解析动作的函数 def update_expression_parse_actions(expr, new_rules): # 遍历infix_notation生成的优先级组,替换对应解析动作 for idx, (_, _, _, new_action) in enumerate(new_rules): # 定位对应优先级的运算符解析器(需根据实际结构调整) op_group = expr.exprs[idx] if op_group: op_group.set_parse_action(new_action) # 使用示例 update_expression_parse_actions(base_code_line.exprs[0].exprs[0], rules_with_action1)
内容的提问来源于stack exchange,提问作者Karim Ghibli
相关产品推荐
相关产品推荐

