You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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原则,维护成本高。

核心问题

  1. 能否重写infix_notation的operator_rules(包含解析动作和规则列表)?因嵌套表达式增多时解析时间呈指数增长,需根据待解析字符串限制运算符数量。
  2. 作为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 17:23:15