Tree-sitter Patterns 技能
通用的 tree-sitter 代码解析模式。涵盖 AST 访问者、查询模式和语言插件开发。框架无关。
设计原则
这项技能是 框架通用 的。它提供了通用的 tree-sitter 模式:
- 不针对 Code-Index-MCP、treesitter-chunker 或任何特定项目
- 涵盖所有 tree-sitter 项目通用的模式
- 项目特定的查询放在项目特定的技能中
变量
| 变量 | 默认 | 描述 |
|---|---|---|
| TREE_SITTER_DIR | tree_sitter | 语言解析器目录 |
| QUERY_DIR | queries | .scm 查询文件目录 |
| LANGUAGES | auto | 自动检测或语言列表 |
指令
MANDATORY - 按顺序遵循以下工作流程。
- 确定要解析的语言
- 安装适当的语言解析器
- 编写查询以提取需求
- 处理边缘情况和错误
红旗 - 停止并重新考虑
如果你即将:
- 没有错误处理地解析(语法错误很常见)
- 假设所有文件都成功解析
- 不在样本代码上测试就编写查询
- 忽略大文件的性能
停止 -> 添加错误处理 -> 在边缘情况上测试 -> 然后继续
食谱书
语言插件开发
- 如果:创建新的语言解析器
- 那么:阅读并执行
./食谱书/语言插件.md
AST 访问者模式
- 如果:遍历 AST 以提取信息
- 那么:阅读并执行
./食谱书/ast-访问者.md
查询模式
- 如果:编写 tree-sitter 查询
- 那么:阅读并执行
./食谱书/查询模式.md
快速参考
Python 设置
import tree_sitter_python as tspython
from tree_sitter import Language, Parser
# 创建解析器
parser = Parser(Language(tspython.language()))
# 解析代码
source = b"def hello(): pass"
tree = parser.parse(source)
# 访问根节点
root = tree.root_node
print(root.sexp())
节点导航
# 获取子节点
for child in node.children:
print(child.type, child.text)
# 仅命名子节点(跳过标点符号)
for child in node.named_children:
print(child.type)
# 按类型查找
def find_all(node, type_name):
results = []
if node.type == type_name:
results.append(node)
for child in node.children:
results.extend(find_all(child, type_name))
return results
functions = find_all(root, "function_definition")
查询语言
; 匹配函数定义
(function_definition
name: (identifier) @function.name
parameters: (parameters) @function.params
body: (block) @function.body)
; 匹配类定义
(class_definition
name: (identifier) @class.name
body: (block) @class.body)
; 匹配导入
(import_statement
(dotted_name) @import.module)
; 匹配装饰函数
(decorated_definition
(decorator) @decorator
definition: (function_definition
name: (identifier) @function.name))
运行查询
from tree_sitter import Query
query = Query(Language(tspython.language()), """
(function_definition
name: (identifier) @name
body: (block) @body)
""")
captures = query.captures(root)
for node, name in captures:
print(f"{name}: {node.text.decode()}")
常见节点类型
| 语言 | 函数 | 类 | 导入 |
|---|---|---|---|
| Python | function_definition |
class_definition |
import_statement |
| JavaScript | function_declaration |
class_declaration |
import_statement |
| TypeScript | function_declaration |
class_declaration |
import_statement |
| Go | function_declaration |
type_declaration |
import_declaration |
| Rust | function_item |
impl_item |
use_declaration |
错误处理
def safe_parse(source: bytes) -> tuple[Tree | None, list[str]]:
"""带错误收集的解析。"""
tree = parser.parse(source)
errors = []
def collect_errors(node):
if node.type == "ERROR" or node.is_missing:
errors.append(f"Error at {node.start_point}: {node.text[:50]}")
for child in node.children:
collect_errors(child)
collect_errors(tree.root_node)
return tree, errors
tree, errors = safe_parse(source)
if errors:
print(f"Parse errors: {errors}")
访问者模式
from abc import ABC, abstractmethod
class ASTVisitor(ABC):
"""tree-sitter AST的基础访问者。"""
def visit(self, node):
method_name = f"visit_{node.type}"
visitor = getattr(self, method_name, self.generic_visit)
return visitor(node)
def generic_visit(self, node):
for child in node.named_children:
self.visit(child)
@abstractmethod
def visit_function_definition(self, node):
pass
class FunctionExtractor(ASTVisitor):
def __init__(self):
self.functions = []
def visit_function_definition(self, node):
name_node = node.child_by_field_name("name")
if name_node:
self.functions.append(name_node.text.decode())
self.generic_visit(node)
extractor = FunctionExtractor()
extractor.visit(tree.root_node)
print(extractor.functions)
性能提示
- 增量解析:对于编辑,使用
parser.parse(new_source, old_tree) - 延迟评估:如果只需要特定节点,不要遍历整个树
- 查询优化:使用更具体的查询以减少匹配
- 内存管理:大文件可以使用大量内存
- 批量处理:并行处理多个文件
集成
与代码分析集成
def analyze_file(path: Path) -> CodeAnalysis:
source = path.read_bytes()
tree = parser.parse(source)
return CodeAnalysis(
functions=extract_functions(tree),
classes=extract_classes(tree),
imports=extract_imports(tree),
complexity=calculate_complexity(tree)
)
与 BAML 集成
class CodeStructure {
functions FunctionInfo[]
classes ClassInfo[]
imports string[]
}
class FunctionInfo {
name string
parameters string[]
return_type string?
line_start int
line_end int
}
最佳实践
- 错误容忍:始终优雅地处理解析错误
- 使用查询:优先使用查询而不是手动遍历
- 在真实代码上测试:用实际代码库测试,不仅仅是样本
- 记录节点类型:参考语言语法以获取节点类型
- 版本解析器:固定 tree-sitter 语言版本