Tree-sitter解析代码

Tree-sitter 语法树到底是什么、长什么样、包含什么东西。

一、通俗解释

Tree-sitter 会把一行行代码,解析成一棵结构化的「语法树」(像家族树一样有层级、有分类),让机器能看懂代码的结构(哪个是方法、哪个是方法名、哪个是代码体),而不是把代码当成纯文字。


二、直观看:语法树长什么样?

最简单的Java方法举例:

原始Java代码

public void test() { System.out.println("Hi"); }

Tree-sitter 解析后的语法树结构

┌── method_declaration (方法定义:代码里判断的核心节点) │ ├── modifiers (修饰符:public) │ ├── void (返回值类型) │ ├── identifier (方法名:test ← 代码提取的内容) │ ├── parameters (参数列表) │ │ └── () │ └── body (方法体) │ ├── { │ ├── expression_statement (执行语句) │ └── }

代码遍历「语法树」

def extract_java_methods_tree_sitter(code: str, file_path: str) -> List[Document]: """ 使用Tree-sitter解析Java文件,提取所有方法定义 :param code: Java代码文本内容 :param file_path: 代码文件路径(用于元数据) :return: 包含每个方法的Document列表(元数据含方法名、语言等) """ tree = java_parser.parse(bytes(code, "utf8")) root = tree.root_node docs = [] # 递归遍历语法树,提取方法节点 def visit(node): if node.type == "method_declaration": # 提取方法名 name_node = next( (child for child in node.children if child.type == "identifier"), None ) method_name = name_node.text.decode() if name_node else "unknown" # 提取方法完整代码 method_text = code[node.start_byte : node.end_byte] docs.append( Document( page_content=method_text, metadata={ "source": file_path, "type": "code", "language": "java", "method_name": method_name, }, ) ) else: for child in node.children: visit(child) visit(root) return docs
  • 整棵树的是整个代码

  • 树枝是类、方法、变量

  • 树叶是方法名、关键字、符号


三、语法树包含哪些核心东西?(对应代码里的用法)

Tree-sitter 的每一个节点(Node)都自带 5 个核心属性,visit函数全靠这些属性工作

属性名

作用

代码里的用法

node.type

节点类型(标记这是什么语法)

if node.type == "method_declaration"判断是不是方法

node.children

子节点列表(树枝/叶子)

遍历子节点找方法名child for child in node.children

node.start_byte

代码起始字节位置

截取完整方法code[start_byte:end_byte]

node.end_byte

代码结束字节位置

同上

node.text

节点对应的原始代码文本

提取方法名name_node.text.decode()


四、完整的语法树节点类型(Java常用)

Tree-sitter 给Java代码定义了标准化的节点类型,解析代码全靠这些:

  1. method_declaration→ 方法定义(核心用的)

  2. class_declaration→ 类定义

  3. identifier→ 标识符(方法名、变量名)

  4. modifiers→ 修饰符(public/private/static)

  5. block→ 代码块(大括号里的内容)

  6. import_declaration→ 导入语句

  7. field_declaration→ 成员变量


五、核心代码

基于语法树的结构

if node.type == "method_declaration": # 找到「方法」节点 name_node = next(child for child in node.children if child.type == "identifier") # 在方法的子节点里,找到「方法名」节点 method_text = code[node.start_byte : node.end_byte] # 用字节位置,截取整个方法代码

本质就是:

在语法树上,精准找到「方法」树枝 → 摘下「方法名」叶子 → 剪下整根树枝(完整代码)


六、总结

  1. Tree-sitter 语法树 = 代码的结构化家谱

  2. 每个节点都有类型、位置、子节点

  3. 机器靠这棵树「读懂代码」,而不是瞎拆文字

  4. 解析函数就是在这棵树上「找到方法子节点」

这就是Tree-sitter 解析代码比普通文本分割强10倍的原因。

七、不同语言的区别

对比项JavaPythonJavaScriptC++
函数节点method_declarationfunction_definitionfunction_declarationfunction_definition
代码块block无(缩进)blockblock
函数名位置第 3 个子节点第 2 个子节点第 2 个子节点第 2 个子节点
顶层根节点compilation_unitmoduleprogramtranslation_unit
独有节点annotation、enumdecorator、async_defarrow_functiontemplate、class