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) 会匹配 infixrecordprojectionconstructor 等节点。

语法中的超类型包括以下集合:

  • expression - 适用于任何表达式位置的规则
  • pattern - 适用于任何模式位置的规则
  • type - 类型规则
  • quantified_type - 带有 forall 的类型
  • constraint - 类似于 type,但用于上下文
  • constraints - 类似于 quantified_type,用于约束
  • type_param - 类型和类头中的原子节点
  • declaration - 所有顶级声明
  • decl - 适用于局部绑定的声明
  • class_declinstance_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
追踪

testparse 命令提供两种获取详细解析信息的模式:

$ 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());
            }
        }
    }
}

这个更完整的示例展示了:

  1. 解析带有语言扩展的 Haskell 代码
  2. 打印完整的语法树
  3. 遍历 AST 查找特定类型的节点(函数定义和数据类型定义)
  4. 提取节点内容

要运行此代码,同样需要在 Cargo.toml 中添加 tree-sitter 和 tree-sitter-haskell 依赖。


1 回复

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);
}

注意事项

  1. tree-sitter-haskell需要与tree-sitter运行时库配合使用
  2. 解析大型文件时,考虑使用增量解析以提高性能
  3. 语法树的结构可能随着Tree-sitter版本的更新而变化
  4. 某些边缘情况的Haskell语法可能不被完全支持

总结

tree-sitter-haskell为Rust开发者提供了强大的Haskell代码解析能力,可以用于构建代码编辑器、静态分析工具、代码格式化工具等。通过Tree-sitter的查询系统,可以方便地提取代码中的特定结构,而增量解析功能则使其适合用于实时应用。

回到顶部