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

如何使用Python版esprima替换AST指定节点且不影响其余节点

问题原因

核心错误是复用了同一个可变AST节点实例:
Python中esprima生成的AST节点是可变对象,遵循引用传递规则。你提前解析存在accessory_function_expression_statements字典中的表达式节点是全局唯一实例,每次匹配到Lg()调用时,你都直接修改这个实例的left/right属性再返回给AST。
第一次匹配到Lg(GP5["length"], 5)时,你把该实例的左右操作数设为对应参数,插入到if判断条件位置;第二次匹配到Lg(Jj, 21)时,你再次修改同一个实例的左右操作数,此时之前插入到if条件位置的节点因为引用的是同一个对象,值会同步被覆盖,最终所有替换位置都显示最后一次匹配的参数结果。

另外你代码中对参数做重复序列化、反序列化的操作完全没有必要,直接引用原节点的参数即可,还能避免额外的解析开销和潜在格式问题。

修复方法

每次匹配到目标节点时,为替换操作生成独立的新节点实例,不要全局复用同一个节点对象,两种实现方式可选:

  • 对提前准备的模板节点做深拷贝,再修改拷贝后实例的属性
  • 每次替换时直接构造全新的目标AST节点

修正后可运行代码

from __future__ import print_function
import copy
import esprima
from jscodegen_py import jscodegen

generator = jscodegen.CodeGenerator(indent = 2)
def js2ast(js: str):
    return esprima.parseScript(js)
def ast2js(ast: dict):
    return generator.generate(ast)

# 预存替换模板
af = {'Lg': {'RawValue': 'WN5 > AN5', 'operator': '>'}}
accessory_function_templates = {}
for name in af:
    accessory_function_templates[name] = esprima.parse(af[name]['RawValue']).body[0].expression


class MyVisitor(esprima.NodeVisitor):
    def transform_CallExpression(self, node, metadata):
        # 先判断callee是否有name属性,避免非标识符调用的属性报错
        if hasattr(node.callee, 'name') and node.callee.name == 'Lg':
            # 深拷贝模板生成独立新节点,不修改全局模板
            new_node = copy.deepcopy(accessory_function_templates['Lg'])
            # 直接引用原调用的参数节点,无需重复序列化解析
            new_node.left = node.arguments[0]
            new_node.right = node.arguments[1]
            return self.transform_Object(new_node, metadata)
        # 非目标节点保持原有遍历逻辑
        return self.generic_visit(node, metadata)

visitor = MyVisitor()

tree = esprima.parse("""
if (Lg(GP5["length"], 5)) {
    var kP5 = window["parseInt"](GP5[5], 10);
    lP5 = window["isNaN"](kP5) || dK(hA(1), kP5) ? window["Number"]["MAX_VALUE"] : kP5,
        lP5 = kP5;
    var abc = Boolean(Lg(Jj, 21))
}
""", delegate=visitor)

print(ast2js(tree.toDict()))

运行输出(符合预期)

if (GP5["length"] > 5) {
  var kP5 = window["parseInt"](GP5[5], 10);
  lP5 = window["isNaN"](kP5) || dK(hA(1), kP5) ? window["Number"]["MAX_VALUE"] : kP5, lP5 = kP5;
  var abc = Boolean(Jj > 21);
}

内容的提问来源于stack exchange,提问作者JJ Cauton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:18:49