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

如何用ANTLR4与Python提取多语言文件中的函数、类及文档注释

用ANTLR4 + Python实现跨语言函数/类及注释提取

完全可以实现,既然已经有主流编程语言的ANTLR4解析器和词法分析器,只需要基于这些语法文件,结合Python的ANTLR4库,通过语法树遍历提取目标内容,最终整理成你需要的格式。具体步骤如下:

核心思路

ANTLR4通过语法文件生成词法分析器和解析器,将源代码转换成抽象语法树(AST);我们通过自定义**监听器(Listener)或访问器(Visitor)**遍历AST,定位到类、函数的节点,同时关联提取对应的文档注释,最后按要求格式输出。

具体实现步骤

1. 按文件后缀匹配对应语法

不同后缀的文件对应不同编程语言,先建立后缀与ANTLR4语法组件的映射:

  • .js → JavaScriptLexer/JavaScriptParser
  • .java → JavaLexer/JavaParser
  • .rs → RustLexer/RustParser

2. 编写自定义监听器/访问器

针对每种语言,编写对应的监听器,重写节点进入/退出的方法,完成以下操作:

  • 定位类/结构体、函数/方法的语法节点(比如Java的classDeclaration、Rust的fn_item)
  • 从原代码中截取节点对应的代码片段(通过Token的起止索引)
  • 提取关联的文档注释:ANTLR会把注释放入隐藏通道(HIDDEN Channel),需要从目标节点的前置Token中筛选出注释内容,拼接到代码前

以JavaScript为例,监听器核心逻辑示例:

import antlr4
from JavaScriptLexer import JavaScriptLexer
from JavaScriptParser import JavaScriptParser
from JavaScriptListener import JavaScriptListener

class JSCodeExtractor(JavaScriptListener):
    def __init__(self, raw_code):
        self.raw_code = raw_code
        self.output = []
        self.current_class = None  # 记录当前遍历的类

    # 进入函数声明节点时触发
    def enterFunctionDeclaration(self, ctx):
        # 获取函数代码的起止位置
        start_idx = ctx.start.start
        end_idx = ctx.stop.stop
        # 提取函数上方关联的注释
        comments = self._fetch_associated_comments(ctx.start)
        # 拼接注释与函数代码
        full_func_code = comments + self.raw_code[start_idx:end_idx+1]
        
        if self.current_class:
            # 如果是类的方法,加入当前类的方法列表
            self.current_class["methods"].append(f'"{full_func_code}"')
        else:
            # 独立函数直接加入输出列表
            self.output.append(f'"{full_func_code}"')

    # 进入类声明节点时触发
    def enterClassDeclaration(self, ctx):
        class_name = ctx.Identifier().getText()
        self.current_class = {"name": class_name, "methods": []}

    # 退出类声明节点时触发,将类整理成要求的格式
    def exitClassDeclaration(self, ctx):
        class_entry = f'"class {self.current_class["name"]}": [{", ".join(self.current_class["methods"])}]'
        self.output.append(class_entry)
        self.current_class = None

    # 从隐藏通道提取目标节点的关联注释
    def _fetch_associated_comments(self, start_token):
        comments = []
        prev_token = start_token.previousToken()
        # 往前遍历隐藏通道的Token,收集注释
        while prev_token and prev_token.channel == antlr4.Lexer.HIDDEN:
            if prev_token.type in [JavaScriptLexer.LINE_COMMENT, JavaScriptLexer.BLOCK_COMMENT]:
                comments.insert(0, prev_token.text)
            prev_token = prev_token.previousToken()
        return "\n".join(comments) + "\n" if comments else ""

# 处理单个JS文件的函数
def process_js_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        raw_code = f.read()
    input_stream = antlr4.InputStream(raw_code)
    lexer = JavaScriptLexer(input_stream)
    token_stream = antlr4.CommonTokenStream(lexer)
    parser = JavaScriptParser(token_stream)
    ast = parser.program()  # 生成AST
    
    extractor = JSCodeExtractor(raw_code)
    walker = antlr4.ParseTreeWalker()
    walker.walk(extractor, ast)  # 遍历AST
    
    return extractor.output

3. 多语言适配与格式整合

针对Java、Rust等语言,只需要替换对应的Lexer/Parser,调整监听的节点类型(比如Java的methodDeclaration、Rust的struct_item),并适配注释提取逻辑(比如Rust的///注释、Java的/** */文档注释)。

最后将所有文件的提取结果整合,加上外层的output: [和],用逗号分隔各个元素,即可得到你要求的格式。

注意事项

  • 不同语言的语法细节差异:比如Rust的函数可能有不同的修饰符、Java的内部类嵌套,需要对应调整节点监听逻辑
  • 注释关联的准确性:部分语言注释可能与代码间隔空行,需要处理Token的位置判断,确保注释属于目标函数/类
  • 代码截取的准确性:注意Token的起止索引是否包含完整的代码块(比如函数的大括号)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 09:35:22