01

Tree-sitter 代码解析引擎

从源码字节流到结构化 AST——CGB 如何"读懂"12种语言

什么是 Tree-sitter,为什么选它

Tree-sitter 不是一个语言解析器,而是一个解析器生成框架。每种语言对应一个独立的 grammar 包(如 `tree-sitter-python`),CGB 在运行时动态加载。 增量解析的核心价值在于:只有文件发生改动时才重新解析受影响的子树,对大型仓库的增量更新极为重要。

CGB 目前支持12种语言,每种语言对应一个 LanguageSpec 实例,在 ParserLoader 初始化时统一注册。

🐍
Python
JS / TS
🦀
Rust
🔵
Go
Java
⚙️
C / C++
🎯
C# / Scala
🐘
PHP / Lua

S 表达式查询:精确捕获 AST 节点

Tree-sitter 使用 S 表达式查询语法在整棵 AST 上做模式匹配。每个 LanguageSpec 持有5类查询: function_query(函数定义)、class_query(类/接口)、call_query(调用表达式)、import_query(导入语句)、func_ptr_assign_query(函数指针赋值,主要用于 C/C++)。

FunctionMatch 与 FQN 构建

每次查询命中后,解析器将原始 Node 对象包装成 FunctionMatch。 其中最关键的字段是 qualified_name(FQN,完全限定名),它是图数据库中函数节点的主键。

解析流水线:从字节到图节点

完整的解析过程分为5个阶段,每个阶段都有明确的输入输出约定。

📦
ParserLoader
🌳
Parser.parse
🔍
S-expr Query
📋
FunctionMatch
🗄️
图数据库
点击"下一步"开始 Step 0 / 5

调用解析:call_query + FunctionRegistryTrie

Pass 1 把所有函数定义写入图之后,Pass 2 才执行调用解析。call_query 从每个函数体内提取所有 call_expression,得到被调函数的简名(如 parse)。 但简名无法直接定位到 FQN,需要在 FunctionRegistryTrie 中做模糊匹配。

Pass 2 调用解析 — 组件对话
0 / 4 messages
💡 关键洞察

调用解析的歧义是静态分析的核心难题。CGB 的策略是:优先用导入信息消歧,有唯一匹配则直接连边,有多匹配则按调用方与被调用方的模块距离打分取最近者,无法消歧时留存为 UnresolvedCall 节点(不丢数据)。这比全丢或随机猜都要好得多。