Rust语法分析库tree-sitter-haskell的使用,高效解析Haskell代码的Tree-sitter绑定
tree-sitter-haskell
Haskell 的 tree-sitter 语法解析器
参考资料
- Haskell 2010 语言报告 - 语法参考
- GHC 语言扩展
支持的语言扩展
以下扩展支持情况:✅ 表示支持,❌ 表示不支持,➖️ 表示不涉及解析:
- AllowAmbiguousTypes ➖️
- ApplicativeDo ➖️
- Arrows ❌
- BangPatterns ✅
- BinaryLiterals ✅
- BlockArguments ✅
- CApiFFI ✅
- ConstrainedClassMethods ✅
- ConstraintKinds ✅
- CPP ✅
- CUSKs ✅
- DataKinds ✅
- DatatypeContexts ✅
- DefaultSignatures ✅
- DeriveAnyClass ➖️
- DeriveDataTypeable ➖️
- DeriveFoldable ➖️
- DeriveFunctor ➖️
- DeriveGeneric ➖️
- DeriveLift ➖️
- DeriveTraversable ➖️
- DerivingStrategies ✅
- DerivingVia ✅
- DisambiguateRecordFields ➖️
- DuplicateRecordFields ➖️
- EmptyCase ✅
- EmptyDataDecls ✅
- EmptyDataDeriving ✅
- ExistentialQuantification ✅
- ExplicitForAll ✅
- ExplicitNamespaces ✅
- ExtendedDefaultRules ➖️
- FlexibleContexts ✅
- FlexibleInstances ✅
- ForeignFunctionInterface ✅
- FunctionalDependencies ✅
- GADTs ✅
- GADTSyntax ✅
- GeneralisedNewtypeDeriving ➖️
- GHCForeignImportPrim ✅
- Haskell2010 ➖️
- Haskell98 ➖️
- HexFloatLiterals ✅
- ImplicitParams ✅
- ImplicitPrelude ➖️
- ImportQualifiedPost ✅
- ImpredicativeTypes ➖️
- IncoherentInstances ➖️
- InstanceSigs ✅
- InterruptibleFFI ✅
- KindSignatures ✅
- LambdaCase ✅
- LexicalNegation ❌
- LiberalTypeSynonyms ✅
- LinearTypes ✅
- ListTuplePuns ✅
- MagicHash ✅
- Modifiers ❌
- MonadComprehensions ➖️
- MonadFailDesugaring ➖️
- MonoLocalBinds ➖️
- MonomorphismRestriction ➖️
- MultiParamTypeClasses ✅
- MultiWayIf ✅
- NamedFieldPuns ✅
- NamedWildCards ✅
- NegativeLiterals ➖️
- NondecreasingIndentation ✅
- NPlusKPatterns ➖️
- NullaryTypeClasses ✅
- NumDecimals ➖️
- NumericUnderscores ✅
- OverlappingInstances ➖️
- OverloadedLabels ✅
- OverloadedLists ➖️
- OverloadedRecordDot ✅
- OverloadedRecordUpdate ✅
- OverloadedStrings ➖️
- PackageImports ✅
- ParallelListComp ✅
- PartialTypeSignatures ✅
- PatternGuards ✅
- PatternSynonyms ✅
- PolyKinds ➖️
- PostfixOperators ➖️
- QualifiedDo ✅
- QuantifiedConstraints ✅
- QuasiQuotes ✅
- Rank2Types ✅
- RankNTypes ✅
- RebindableSyntax ➖️
- RecordWildCards ➖️
- RecursiveDo ✅
- RequiredTypeArguments ✅
- RoleAnnotations ✅
- Safe ➖️
- ScopedTypeVariables ✅
- StandaloneDeriving ✅
- StandaloneKindSignatures ✅
- StarIsType ✅
- StaticPointers ❌
- Strict ➖️
- StrictData ✅
- TemplateHaskell ✅
- TemplateHaskellQuotes ✅
- TraditionalRecordSyntax ➖️
- TransformListComp ✅
- Trustworthy ➖️
- TupleSections ✅
- TypeAbstractions ✅
- TypeApplications ✅
- TypeData ✅
- TypeFamilies ✅
- TypeFamilyDependencies ✅
- TypeInType ✅
- TypeOperators ✅
- TypeSynonymInstances ➖️
- UnboxedSums ✅
- UnboxedTuples ✅
- UndecidableInstances ➖️
- UndecidableSuperClasses ➖️
- UnicodeSyntax ✅
- UnliftedFFITypes ➖️
- UnliftedNewtypes ✅
- Unsafe ➖️
- ViewPatterns ✅
已知问题
CPP
预处理指令 #elif
和 #else
无法正确处理,因为需要手动将解析器状态重置为 #if
时的状态。作为变通方案,替代分支中的代码块会被作为指令的一部分进行解析。
查询语法
语法包含多种超类型,将多个其他节点类型分组到一个名称下。
超类型名称不会作为额外节点出现在解析树中,但可以在查询中以特殊方式使用:
- 作为别名,匹配任何子类型
- 作为子类型的前缀,仅当子类型作为超类型的产物出现时才匹配其符号
例如,查询 (expression)
会匹配 infix
、record
、projection
、constructor
等节点。
语法中的超类型包括以下集合:
expression
- 适用于任何表达式位置的规则pattern
- 适用于任何模式位置的规则type
- 类型规则quantified_type
- 带有forall
的类型constraint
- 类似于type
,但用于上下文constraints
- 类似于quantified_type
,用于约束type_param
- 类型和类头中的原子节点declaration
- 所有顶级声明decl
- 适用于局部绑定的声明class_decl
和instance_decl
- 适用于类和实例的声明statement
-do
表达式的不同形式qualifier
- 列表推导式的不同形式guard
- 函数方程和 case 替代项中的不同形式
开发
主要驱动工具是 tree-sitter CLI。其他组件需要额外工具。
输出路径
CLI 将解析器共享库写入 $TREE_SITTER_LIBDIR
目录,默认为 $HOME/.cache/tree-sitter/lib
。
可以设置环境变量到本地路径:
export TREE_SITTER_LIBDIR=$PWD/.lib
语法规则
grammar.js
文件包含语法规则的入口点。解析从 rules
字段的第一个项目开始:
{
rules: {
haskell: $ => seq(
optional($.header),
optional($._body),
),
}
}
生成解析器
将 JavaScript 规则定义转换为 C 代码:
$ tree-sitter generate
编译解析器
编译 C 代码:
$ tree-sitter generate --build
WebAssembly
编译为 WebAssembly:
$ tree-sitter build --wasm
测试解析器
基本测试基础设施包括一组代码片段和相关参考 AST:
$ tree-sitter test
调试
调试符号包含在共享库中,可以使用 coredumpctl debug
检查回溯和内存。
禁用优化:
$ tree-sitter test --debug-build
追踪
test
和 parse
命令提供两种获取详细解析信息的模式:
$ tree-sitter test --debug
$ tree-sitter test --debug-graph
以下是使用 tree-sitter-haskell 解析 Haskell 代码的完整 Rust 示例:
use tree_sitter::Parser;
use tree_sitter_haskell;
fn main() {
// 创建解析器
let mut parser = Parser::new();
// 设置 Haskell 语言
parser.set_language(tree_sitter_haskell::language()).expect("Error loading Haskell grammar");
// 要解析的 Haskell 代码
let code = r#"
module Main where
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
main :: IO ()
main = print (factorial 5)
"#;
// 解析代码
let tree = parser.parse(code, None).unwrap();
// 获取根节点
let root_node = tree.root_node();
// 打印语法树
println!("{}", root_node.to_sexp());
// 遍历 AST
let mut cursor = root_node.walk();
for child in root_node.children(&mut cursor) {
println!("Child: {:?}", child);
}
}
要运行此代码,需要在 Cargo.toml 中添加以下依赖:
[dependencies]
tree-sitter = "0.20"
tree-sitter-haskell = "0.23.1"
以下是另一个更完整的示例,展示如何使用 tree-sitter-haskell 进行更复杂的解析和查询:
use tree_sitter::{Parser, Tree, Node};
use tree_sitter_haskell;
fn main() {
// 初始化解析器
let mut parser = Parser::new();
parser.set_language(tree_sitter_haskell::language()).unwrap();
// 解析 Haskell 代码
let code = r#"
{-# LANGUAGE GADTs #-}
module Example where
data Expr a where
Lit :: Int -> Expr Int
Add :: Expr Int -> Expr Int -> Expr Int
Eq :: Expr Int -> Expr Int -> Expr Bool
eval :: Expr a -> a
eval (Lit i) = i
eval (Add x y) = eval x + eval y
eval (Eq x y) = eval x == eval y
"#;
let tree = parser.parse(code, None).unwrap();
// 打印语法树
println!("Syntax tree:\n{}", tree.root_node().to_sexp());
// 查找所有函数定义
println!("\nFunction definitions:");
find_function_definitions(&tree);
// 查找所有数据类型定义
println!("\nData type definitions:");
find_data_type_definitions(&tree);
}
// 查找函数定义
fn find_function_definitions(tree: &Tree) {
let root_node = tree.root_node();
let mut cursor = root_node.walk();
// 查询函数定义节点
for node in root_node.children(&mut cursor) {
if node.kind() == "function" {
// 获取函数名
if let Some(name_node) = node.child_by_field_name("name") {
println!("Function: {}", name_node.utf8_text(tree.root_node().start_byte()).unwrap());
}
}
}
}
// 查找数据类型定义
fn find_data_type_definitions(tree: &Tree) {
let root_node = tree.root_node();
let mut cursor = root_node.walk();
// 查询数据类型定义节点
for node in root_node.children(&mut cursor) {
if node.kind() == "data_declaration" {
// 获取类型名
if let Some(name_node) = node.child_by_field_name("name") {
println!("Data type: {}", name_node.utf8_text(tree.root_node().start_byte()).unwrap());
}
}
}
}
这个更完整的示例展示了:
- 解析带有语言扩展的 Haskell 代码
- 打印完整的语法树
- 遍历 AST 查找特定类型的节点(函数定义和数据类型定义)
- 提取节点内容
要运行此代码,同样需要在 Cargo.toml 中添加 tree-sitter 和 tree-sitter-haskell 依赖。
Rust语法分析库tree-sitter-haskell的使用:高效解析Haskell代码的Tree-sitter绑定
介绍
tree-sitter-haskell是一个Rust语言的Tree-sitter绑定库,专门用于解析Haskell代码。它基于Tree-sitter的语法分析系统,提供了高效、准确的Haskell代码解析能力。
Tree-sitter是一个增量解析系统,能够:
- 快速解析代码
- 生成详细的语法树
- 支持增量更新(当源代码变化时,只需重新解析变化的部分)
- 提供丰富的查询功能
安装方法
在Cargo.toml中添加依赖:
[dependencies]
tree-sitter = "0.20"
tree-sitter-haskell = "0.19"
基本使用方法
1. 解析Haskell代码
use tree_sitter::Parser;
use tree_sitter_haskell::language;
fn main() {
// 创建新的解析器
let mut parser = Parser::new();
// 设置Haskell语言解析器
parser.set_language(language()).expect("Error loading Haskell grammar");
// Haskell示例代码
let code = r#"
module Main where
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
"#;
// 解析代码并获取语法树
let tree = parser.parse(code, None).unwrap();
let root_node = tree.root_node();
// 打印语法树的S表达式表示
println!("Syntax tree: {}", root_node.to_sexp());
}
2. 遍历语法树
// 递归打印语法树节点
fn print_nodes(node: tree_sitter::Node, code: &str, indent: usize) {
let indent_str = " ".repeat(indent);
// 打印节点类型
println!("{}{:?}", indent_str, node.kind());
// 如果有子节点,递归打印
if node.child_count() > 0 {
for child in node.children(&mut node.walk()) {
print_nodes(child, code, indent + 2);
}
} else {
// 打印叶子节点的文本内容
println!("{} Text: {:?}", indent_str, node.utf8_text(code.as_bytes()).unwrap());
}
}
// 在上面的main函数中,解析后添加:
print_nodes(root_node, code, 0);
3. 查询特定语法结构
use tree_sitter::Query;
// 查找所有函数定义
fn find_functions(tree: &tree_sitter::Tree, code: &str) {
// 定义查询字符串,匹配所有function节点
let query_str = "(function) @func";
let query = Query::new(language(), query_str).unwrap();
let mut cursor = tree_sitter::QueryCursor::new();
// 遍历所有匹配项
for match_ in cursor.matches(&query, tree.root_node(), code.as_bytes()) {
for capture in match_.captures {
let node = capture.node;
// 打印函数位置和内容
println!("Found function at {}:{}",
node.start_position().row,
node.start_position().column);
println!("Function text: {}", node.utf8_text(code.as_bytes()).unwrap());
}
}
}
高级用法
1. 增量解析
let mut parser = Parser::new();
parser.set_language(language()).unwrap();
// 第一次完整解析
let mut tree = parser.parse(code, None).unwrap();
// 定义代码编辑的范围
let edit = tree_sitter::InputEdit {
start_byte: 10,
old_end_byte: 15,
new_end_byte: 20,
start_position: tree_sitter::Point::new(1, 0),
old_end_position: tree_sitter::Point::new(1, 5),
new_end_position: tree_sitter::Point::new(1, 10),
};
// 应用编辑并增量解析
tree.edit(&edit);
let new_tree = parser.parse(&new_code, Some(&tree)).unwrap();
2. 使用查询系统提取特定信息
// 查找类型签名
fn find_type_signatures(tree: &tree_sitter::Tree, code: &str) {
// 查询匹配类型签名,捕获变量名和类型
let query_str = "
(type_signature (variable) @name
(type) @type)
";
let query = Query::new(language(), query_str).unwrap();
let mut cursor = tree_sitter::QueryCursor::new();
// 处理每个匹配项
for match_ in cursor.matches(&query, tree.root_node(), code.as_bytes()) {
let mut name = None;
let mut type_ = None;
// 提取捕获的变量名和类型
for capture in match_.captures {
match query.capture_names()[capture.index as usize].as_str() {
"name" => name = Some(capture.node),
"type" => type_ = Some(capture.node),
_ => (),
}
}
// 打印找到的类型签名
if let (Some(name), Some(type_)) = (name, type_) {
println!("Found type signature:");
println!(" Name: {}", name.utf8_text(code.as_bytes()).unwrap());
println!(" Type: {}", type_.utf8_text(code.as_bytes()).unwrap());
}
}
}
实际应用示例
构建完整的Haskell代码分析工具
use tree_sitter::{Parser, Tree, Query, QueryCursor};
use tree_sitter_haskell::language;
// Haskell分析器结构体
struct HaskellAnalyzer {
parser: Parser,
}
impl HaskellAnalyzer {
// 创建新的分析器实例
fn new() -> Self {
let mut parser = Parser::new();
parser.set_language(language()).unwrap();
HaskellAnalyzer { parser }
}
// 分析Haskell代码
fn analyze(&mut self, code: &str) {
println!("Analyzing Haskell code...");
let tree = self.parser.parse(code, None).unwrap();
self.count_functions(&tree, code);
self.list_modules(&tree, code);
self.find_imports(&tree, code);
self.find_type_signatures(&tree, code);
}
// 统计函数数量
fn count_functions(&self, tree: &Tree, code: &str) {
let query_str = "(function) @func";
let query = Query::new(language(), query_str).unwrap();
let cursor = QueryCursor::new();
let count = cursor.matches(&query, tree.root_node(), code.as_bytes()).count();
println!("Total functions found: {}", count);
}
// 列出所有模块
fn list_modules(&self, tree: &Tree, code: &str) {
let query_str = "(module (module_name) @name)";
let query = Query::new(language(), query_str).unwrap();
let mut cursor = QueryCursor::new();
println!("Modules found:");
for match_ in cursor.matches(&query, tree.root_node(), code.as_bytes()) {
for capture in match_.captures {
let name = capture.node.utf8_text(code.as_bytes()).unwrap();
println!("- {}", name);
}
}
}
// 查找所有导入语句
fn find_imports(&self, tree: &Tree, code: &str) {
let query_str = "(import) @import";
let query = Query::new(language(), query_str).unwrap();
let mut cursor = QueryCursor::new();
println!("Imports found:");
for match_ in cursor.matches(&query, tree.root_node(), code.as_bytes()) {
for capture in match_.captures {
let import = capture.node.utf8_text(code.as_bytes()).unwrap();
println!("- {}", import);
}
}
}
// 查找类型签名
fn find_type_signatures(&self, tree: &Tree, code: &str) {
let query_str = "(type_signature (variable) @name (type) @type)";
let query = Query::new(language(), query_str).unwrap();
let mut cursor = QueryCursor::new();
println!("Type signatures found:");
for match_ in cursor.matches(&query, tree.root_node(), code.as_bytes()) {
let mut name = None;
let mut type_ = None;
for capture in match_.captures {
match query.capture_names()[capture.index as usize].as_str() {
"name" => name = Some(capture.node),
"type" => type_ = Some(capture.node),
_ => (),
}
}
if let (Some(n), Some(t)) = (name, type_) {
println!("- {} :: {}",
n.utf8_text(code.as_bytes()).unwrap(),
t.utf8_text(code.as_bytes()).unwrap());
}
}
}
}
fn main() {
// 创建分析器实例
let mut analyzer = HaskellAnalyzer::new();
// 示例Haskell代码
let code = r#"
module Math where
import Data.List
import System.IO
-- 计算平方
square :: Int -> Int
square x = x * x
-- 计算立方
cube :: Int -> Int
cube x = x * x * x
-- 阶乘函数
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
"#;
// 分析代码
analyzer.analyze(code);
}
注意事项
- tree-sitter-haskell需要与tree-sitter运行时库配合使用
- 解析大型文件时,考虑使用增量解析以提高性能
- 语法树的结构可能随着Tree-sitter版本的更新而变化
- 某些边缘情况的Haskell语法可能不被完全支持
总结
tree-sitter-haskell为Rust开发者提供了强大的Haskell代码解析能力,可以用于构建代码编辑器、静态分析工具、代码格式化工具等。通过Tree-sitter的查询系统,可以方便地提取代码中的特定结构,而增量解析功能则使其适合用于实时应用。