Rust语法树生成库tree-sitter-generate的使用,高效解析与生成编程语言语法树

Tree-sitter Generate

这个辅助crate实现了tree-sitter generate命令的逻辑,可以用于外部工具从语法文件生成解析器。

安装

在项目目录中运行以下Cargo命令:

cargo add tree-sitter-generate

或者在Cargo.toml中添加以下行:

tree-sitter-generate = "0.25.8"

使用示例

下面是一个使用tree-sitter-generate生成语法解析器的完整示例:

use std::path::PathBuf;
use tree_sitter_generate::generate;

fn main() {
    // 定义语法文件路径和输出目录
    let grammar_path = PathBuf::from("path/to/your/grammar.js");
    let output_dir = PathBuf::from("path/to/output/directory");
    
    // 生成解析器
    if let Err(e) = generate(&grammar_path, &output_dir) {
        eprintln!("Failed to generate parser: {}", e);
        std::process::exit(1);
    }
    
    println!("Parser generated successfully!");
}

详细说明

  1. 首先需要准备一个语法定义文件(通常命名为grammar.js)
  2. 指定输出目录,生成的解析器代码将写入该目录
  3. 调用generate函数执行生成过程

生成的解析器包含以下文件:

  • parser.c - 解析器实现
  • tree_sitter/parser.h - 解析器头文件
  • binding.rs - Rust绑定代码

注意事项

  • 需要安装tree-sitter CLI工具
  • 语法文件需要符合tree-sitter的语法规范
  • 生成过程可能需要C编译器和其他构建依赖

完整示例

下面是一个更完整的示例,展示了如何在实际项目中生成和使用tree-sitter解析器:

use std::path::PathBuf;
use std::fs;
use tree_sitter_generate::generate;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. 准备语法文件和输出路径
    let grammar_path = PathBuf::from("examples/my-grammar.js");
    let output_dir = PathBuf::from("src/generated");
    
    // 2. 确保输出目录存在
    fs::create_dir_all(&output_dir)?;
    
    // 3. 生成解析器
    generate(&grammar_path, &output_dir)?;
    
    // 4. 打印成功信息
    println!("解析器生成成功!输出目录: {}", output_dir.display());
    
    // 5. 可以在项目中直接使用生成的解析器
    // 通常需要将生成的binding.rs集成到项目中
    
    Ok(())
}

注意事项

  1. 确保已安装tree-sitter CLI工具
  2. 语法文件需要符合tree-sitter的语法规范
  3. 生成过程需要C编译器和构建工具链
  4. 生成的Rust绑定代码可能需要手动集成到项目中

1 回复

Rust语法树生成库tree-sitter-generate的使用:高效解析与生成编程语言语法树

以下是内容中提供的完整示例:

基本使用方法示例

1. 定义语法规则 (grammar.js)

module.exports = grammar({
  name: 'my_language',
  
  rules: {
    source_file: $ => repeat($.statement),
    
    statement: $ => seq(
      $.identifier,
      '=',
      $.expression,
      ';'
    ),
    
    identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
    
    expression: $ => choice(
      $.number,
      $.identifier,
      $.binary_expression
    ),
    
    binary_expression: $ => prec.left(seq(
      $.expression,
      choice('+', '-', '*', '/'),
      $.expression
    )),
    
    number: $ => /\d+/
  }
});

2. Rust中使用解析器

use tree_sitter::{Parser, Language};

extern "C" { fn tree_sitter_my_language() -> Language; }

fn main() {
    let language = unsafe { tree_sitter_my_language() };
    let mut parser = Parser::new();
    parser.set_language(language).unwrap();
    
    let code = "x = 42 + y;";
    let tree = parser.parse(code, None).unwrap();
    
    let root_node = tree.root_node();
    let mut cursor = root_node.walk();
    
    loop {
        let node = cursor.node();
        println!("{}: {}", node.kind(), node.utf8_text(code.as_bytes()).unwrap());
        
        if !cursor.goto_first_child() {
            break;
        }
    }
}

完整示例Demo:构建一个带变量支持的增强版计算器

use tree_sitter::{Parser, Language, Node};
use std::collections::HashMap;

// 声明外部语言函数
extern "C" { fn tree_sitter_calculator() -> Language; }

fn evaluate(
    node: Node, 
    code: &str, 
    variables: &mut HashMap<String, i64>
) -> Result<i64, String> {
    match node.kind() {
        "number" => {
            node.utf8_text(code.as_bytes())
                .unwrap()
                .parse()
                .map_err(|_| "无效数字".to_string())
        },
        "identifier" => {
            let var_name = node.utf8_text(code.as_bytes()).unwrap();
            variables.get(var_name)
                .copied()
                .ok_or_else(|| format!("未定义变量: {}", var_name))
        },
        "binary_expression" => {
            let left = evaluate(node.named_child(0).unwrap(), code, variables)?;
            let right = evaluate(node.named_child(1).unwrap(), code, variables)?;
            let op = node.child(1).unwrap().utf8_text(code.as_bytes()).unwrap();
            
            match op {
                "+" => Ok(left + right),
                "-" => Ok(left - right),
                "*" => Ok(left * right),
                "/" => if right != 0 {
                    Ok(left / right)
                } else {
                    Err("除以零错误".to_string())
                },
                _ => Err("未知操作符".to_string())
            }
        },
        "assignment" => {
            let var_node = node.named_child(0).unwrap();
            let var_name = var_node.utf8_text(code.as_bytes()).unwrap();
            let value = evaluate(node.named_child(1).unwrap(), code, variables)?;
            variables.insert(var_name.to_string(), value);
            Ok(value)
        },
        "program" => {
            let mut result = 0;
            let mut cursor = node.walk();
            
            for child in node.children(&mut cursor) {
                if child.is_named() {
                    result = evaluate(child, code, variables)?;
                }
            }
            
            Ok(result)
        },
        _ => Err(format!("未知节点类型: {}", node.kind()))
    }
}

fn main() {
    // 获取语言定义
    let language = unsafe { tree_sitter_calculator() };
    let mut parser = Parser::new();
    parser.set_language(language).unwrap();
    
    // 示例代码,支持变量和多个表达式
    let code = r#"
        a = 5;
        b = 10;
        a * b - 3;
    "#;
    
    // 解析代码
    let tree = parser.parse(code, None).unwrap();
    
    // 存储变量的哈希表
    let mut variables = HashMap::new();
    
    // 计算并输出结果
    match evaluate(tree.root_node(), code, &mut variables) {
        Ok(result) => println!("计算结果: {}", result),
        Err(e) => eprintln!("错误: {}", e),
    }
}

对应的语法规则 (grammar.js)

module.exports = grammar({
  name: 'calculator',
  
  rules: {
    program: $ => repeat($._statement),
    
    _statement: $ => choice(
      $.assignment,
      $.expression
    ),
    
    assignment: $ => seq(
      $.identifier,
      '=',
      $.expression,
      ';'
    ),
    
    expression: $ => choice(
      $.number,
      $.identifier,
      $.binary_expression,
      prec.left(seq('(', $.expression, ')'))
    ),
    
    binary_expression: $ => prec.left(seq(
      $.expression,
      choice('+', '-', '*', '/'),
      $.expression
    )),
    
    identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
    number: $ => /\d+/,
    
    comment: $ => token(seq('//', /.*/))
  }
});

这个增强版计算器示例演示了:

  1. 支持变量声明和赋值
  2. 处理多个表达式
  3. 完善的错误处理
  4. 使用哈希表存储变量状态
  5. 支持基本算术运算和括号优先级

要使用这个示例,你需要:

  1. 创建 grammar.js 文件并写入上述语法规则
  2. 运行 tree-sitter generate 生成解析器
  3. 将 Rust 代码保存为 main.rs 并运行
回到顶部