You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用Python实现代码预处理器并解决AST递归遍历代码节点的问题

如何用Python实现代码预处理器并解决AST递归遍历代码节点的问题

看起来你已经找对方向了——用AST做代码分析确实是处理这类问题的最优解,不过你遇到的递归遍历问题,大概率是没搞懂NodeVisitor的核心工作逻辑。

简单说,你提到的If、函数定义、For循环这些嵌套节点没被遍历到,几乎肯定是因为你在自定义的generic_visit方法里,没有正确调用父类的generic_visit来触发子节点的递归遍历。默认情况下,NodeVisitorgeneric_visit会自动遍历当前节点的所有子属性,但如果你重写这个方法时只处理了当前节点,却忘了调用父类方法,那嵌套在里面的代码就会被忽略。

下面给你一个修正后的完整示例,不仅能解决递归遍历问题,还能帮你提取函数名、调用、参数这些你需要的信息:

import ast

class MyVisitor(ast.NodeVisitor):
    def __init__(self):
        self.processed_nodes = []  # 用来存所有处理后的节点信息

    def generic_visit(self, node):
        # 先记录当前节点的基础信息(可以根据你的需求调整字段)
        basic_info = {
            "node_type": type(node).__name__,
            "line_number": node.lineno,
            "column": node.col_offset
        }
        self.processed_nodes.append(basic_info)
        
        # 关键!必须调用父类的generic_visit,才能递归遍历子节点
        super().generic_visit(node)

    # 专门处理函数定义节点,提取函数名和参数
    def visit_FunctionDef(self, node):
        func_detail = {
            "node_type": "FunctionDefinition",
            "function_name": node.name,
            "parameters": [arg.arg for arg in node.args.args],
            "start_line": node.lineno
        }
        self.processed_nodes.append(func_detail)
        
        # 别忘了递归处理函数体里的所有语句
        self.generic_visit(node)

    # 处理函数调用节点,提取调用的函数名和参数
    def visit_Call(self, node):
        # 处理函数名的情况:如果是直接调用变量名,就取id;否则用dump看结构
        func_name = node.func.id if isinstance(node.func, ast.Name) else ast.dump(node.func)
        call_detail = {
            "node_type": "FunctionCall",
            "function_name": func_name,
            "arguments": [ast.dump(arg) for arg in node.args],
            "line_number": node.lineno
        }
        self.processed_nodes.append(call_detail)
        
        # 递归处理参数里的子节点(比如参数是表达式的情况)
        self.generic_visit(node)

    # 处理赋值语句,提取赋值目标和值
    def visit_Assign(self, node):
        assign_detail = {
            "node_type": "Assignment",
            "targets": [ast.dump(target) for target in node.targets],
            "value": ast.dump(node.value),
            "line_number": node.lineno
        }
        self.processed_nodes.append(assign_detail)
        
        self.generic_visit(node)

# 测试用的示例代码
test_code = """
def UART(ONE):                              
    ONE = UUUU001 + 4   
    print(ONE)   

if ONE > 5:
    for i in range(3):
        print(i)
"""

# 把代码解析成AST语法树
code_ast = ast.parse(test_code)
# 创建Visitor实例并遍历AST
visitor = MyVisitor()
visitor.visit(code_ast)

# 打印处理后的结果
for item in visitor.processed_nodes:
    print(item)

再给你几个实用的小技巧:

  • 如果你不确定某个节点的结构,可以先用print(ast.dump(code_ast, indent=2))把整个AST树打印出来,直观看到各个节点的属性和嵌套关系。
  • 如果你只需要处理特定类型的节点,不用写全所有visit_XXX方法,父类的generic_visit会自动跳过未自定义处理的节点,但依然会递归遍历它们的子节点。
  • 要是你之后需要修改代码(比如自动补全、重构),可以用ast.NodeTransformer代替NodeVisitor,它支持修改或替换节点。

备注:内容来源于stack exchange,提问作者Coliban

火山引擎 最新活动