Rust语法树处理库libcst_derive的使用:简化CST(Concrete Syntax Tree)解析与代码生成

Rust语法树处理库libcst_derive的使用:简化CST(Concrete Syntax Tree)解析与代码生成

安装

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

cargo add libcst_derive

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

libcst_derive = "1.8.2"

基本使用

libcst_derive是一个Rust库,用于简化Concrete Syntax Tree(CST)的解析和代码生成过程。它提供了一套宏和工具,帮助开发者定义和操作语法树节点。

以下是libcst_derive的基本使用示例:

use libcst_derive::{CSTNode, FromLibCST};

// 定义语法树节点
#[derive(Debug, Clone, PartialEq, CSTNode)]
pub enum Expr {
    #[cst_node]
    Integer(i64),
    #[cst_node]
    String(String),
    #[cst_node]
    BinaryOp {
        left: Box<Expr>,
        op: BinaryOperator,
        right: Box<Expr>,
    },
}

// 定义运算符
#[derive(Debug, Clone, PartialEq, FromLibCST)]
pub enum BinaryOperator {
    #[cst_node]
    Add,
    #[cst_node]
    Sub,
    #[cst_node]
    Mul,
    #[cst_node]
    Div,
}

// 使用示例
fn main() {
    let expr = Expr::BinaryOp {
        left: Box::new(Expr::Integer(42)),
        op: BinaryOperator::Add,
        right: Box::new(Expr::Integer(58)),
    };
    
    println!("{:?}", expr);
}

完整示例

下面是一个更完整的示例,展示如何使用libcst_derive处理简单的算术表达式:

use libcst_derive::{CSTNode, FromLibCST, Parse, ToLibCST};
use libcst::{CSTParser, ParseError};

// 定义语法树节点
#[derive(Debug, Clone, PartialEq, CSTNode, Parse, ToLibCST)]
pub enum Expr {
    #[cst_node]
    Integer(i64),
    #[cst_node]
    BinaryOp {
        left: Box<Expr>,
        op: BinaryOperator,
        right: Box<Expr>,
    },
}

// 定义运算符
#[derive(Debug, Clone, PartialEq, FromLibCST, Parse, ToLibCST)]
pub enum BinaryOperator {
    #[cst_node]
    Add,
    #[cst_node]
    Sub,
    #[cst_node]
    Mul,
    #[cst_node]
    Div,
}

// 实现简单的解析器
fn parse_expr(input: &str) -> Result<Expr, ParseError> {
    let parser = CSTParser::new();
    parser.parse(input)
}

fn main() {
    // 解析表达式
    let expr = parse_expr("42 + 58").unwrap();
    
    // 打印解析结果
    println!("Parsed expression: {:?}", expr);
    
    // 转换为原始CST
    let cst = expr.to_libcst();
    println!("Original CST: {:?}", cst);
    
    // 再转换回我们的Expr类型
    let expr2 = Expr::from_libcst(&cst).unwrap();
    println!("Converted back: {:?}", expr2);
    
    // 验证往返转换
    assert_eq!(expr, expr2);
}

特性

libcst_derive提供以下主要特性:

  1. #[derive(CSTNode)] - 用于定义语法树节点类型
  2. #[derive(FromLibCST)] - 从原始CST节点转换
  3. #[derive(ToLibCST)] - 转换为原始CST节点
  4. #[derive(Parse)] - 从文本解析为语法树节点
  5. 自动生成节点访问和修改方法
  6. 支持复杂节点结构的定义和操作

许可证

MIT License


1 回复

Rust语法树处理库libcst_derive的使用:简化CST解析与代码生成

libcst_derive是一个Rust过程宏库,用于简化Concrete Syntax Tree(CST,具体语法树)的解析和代码生成工作。它提供了一种声明式的方式来定义语法树结构,自动生成解析和转换代码。

主要功能

  1. 通过派生宏自动实现语法树节点的解析和生成
  2. 简化语法树遍历和转换操作
  3. 提供类型安全的语法树操作接口

基本使用方法

首先在Cargo.toml中添加依赖:

[dependencies]
libcst_derive = "0.1"
libcst = "0.1"

定义语法树节点

use libcst_derive::CSTNode;
use libcst::base::*;

#[derive(Debug, Clone, PartialEq, CSTNode)]
pub enum Expr {
    #[cst(expr)]
    Number(i32),
    
    #[cst(expr)]
    BinaryOp {
        left: Box<Expr>,
        op: BinaryOperator,
        right: Box<Expr>,
    },
    
    #[cst(expr)]
    Variable(String),
}

#[derive(Debug, Clone, PartialEq, CSTNode)]
pub enum BinaryOperator {
    #[cst(op)]
    Add,
    #[cst(op)]
    Subtract,
    #[cst(op)]
    Multiply,
    #[cst(op)]
    Divide,
}

解析代码

use libcst::parser::parse_expression;

fn main() {
    let code = "1 + 2 * 3";
    let expr = parse_expression(code).unwrap();
    println!("Parsed expression: {:?}", expr);
}

遍历和修改语法树

use libcst::traversal::{visit, visit_mut, Visitor, VisitorMut};

struct PrintVisitor;

impl Visitor<Expr> for PrintVisitor {
    fn visit(&mut self, expr: &Expr) {
        println!("Visiting: {:?}", expr);
    }
}

fn main() {
    let code = "1 + 2 * 3";
    let expr = parse_expression(code).unwrap();
    
    let mut visitor = PrintVisitor;
    visit(&mut visitor, &expr);
}

代码生成

use libcst::codegen::Codegen;

fn main() {
    let expr = Expr::BinaryOp {
        left: Box::new(Expr::Number(1)),
        op: BinaryOperator::Add,
        right: Box::new(Expr::Number(2))),
    };
    
    let mut code = String::new();
    expr.codegen(&mut code).unwrap();
    println!("Generated code: {}", code); // 输出: 1 + 2
}

高级用法:自定义解析和生成

#[derive(Debug, Clone, PartialEq, CSTNode)]
pub enum Stmt {
    #[cst(stmt, parse_with = "parse_assign", generate_with = "generate_assign")]
    Assign {
        name: String,
        value: Box<Expr>,
    },
}

fn parse_assign(p: &mut Parser) -> Result<Stmt, ParseError> {
    let name = p.parse_identifier()?;
    p.expect_token(Token::Equals)?;
    let value = p.parse_expression()?;
    Ok(Stmt::Assign { name, value })
}

fn generate_assign(s: &Stmt, w: &mut dyn Write) -> Result<(), CodegenError> {
    if let Stmt::Assign { name, value } = s {
        write!(w, "{} = ", name)?;
        value.codegen(w)?;
    }
    Ok(())
}

错误处理

fn main() {
    let code = "1 + ";
    match parse_expression(code) {
        Ok(expr) => println!("Parsed: {:?}", expr),
        Err(e) => println!("Error: {}", e),
    }
}

完整示例Demo

下面是一个完整的示例,展示如何使用libcst_derive创建简单的数学表达式解析器和代码生成器:

// 添加依赖
// Cargo.toml:
// [dependencies]
// libcst_derive = "0.1"
// libcst = "0.1"

use libcst_derive::CSTNode;
use libcst::base::*;
use libcst::parser::parse_expression;
use libcst::traversal::{visit, Visitor};
use libcst::codegen::Codegen;

// 定义表达式语法树节点
#[derive(Debug, Clone, PartialEq, CSTNode)]
pub enum Expr {
    #[cst(expr)]
    Number(i32),
    
    #[cst(expr)]
    BinaryOp {
        left: Box<Expr>,
        op: BinaryOperator,
        right: Box<Expr>,
    },
    
    #[cst(expr)]
    Variable(String),
}

// 定义二元操作符
#[derive(Debug, Clone, PartialEq, CSTNode)]
pub enum BinaryOperator {
    #[cst(op)]
    Add,
    #[cst(op)]
    Subtract,
    #[cst(op)]
    Multiply,
    #[cst(op)]
    Divide,
}

// 自定义访问器,用于遍历语法树
struct ExprEvaluator {
    result: i32,
}

impl Visitor<Expr> for ExprEvaluator {
    fn visit(&mut self, expr: &Expr) {
        match expr {
            Expr::Number(n) => {
                println!("Found number: {}", n);
                self.result = *n;
            }
            Expr::BinaryOp { left, op, right } => {
                visit(self, left);
                let left_val = self.result;
                visit(self, right);
                let right_val = self.result;
                
                match op {
                    BinaryOperator::Add => self.result = left_val + right_val,
                    BinaryOperator::Subtract => self.result = left_val - right_val,
                    BinaryOperator::Multiply => self.result = left_val * right_val,
                    BinaryOperator::Divide => self.result = left_val / right_val,
                }
                println!("Evaluated: {} {:?} {} = {}", left_val, op, right_val, self.result);
            }
            Expr::Variable(name) => {
                println!("Found variable: {}", name);
                // 实际实现中这里应该查找变量值
                self.result = 0;
            }
        }
    }
}

fn main() {
    // 解析表达式
    let code = "1 + 2 * 3";
    let expr = parse_expression(code).unwrap();
    println!("Parsed expression: {:?}", expr);
    
    // 遍历和评估表达式
    let mut evaluator = ExprEvaluator { result: 0 };
    visit(&mut evaluator, &expr);
    println!("Evaluation result: {}", evaluator.result);
    
    // 代码生成
    let mut code = String::new();
    expr.codegen(&mut code).unwrap();
    println!("Generated code: {}", code);
}

这个完整示例展示了:

  1. 如何定义语法树节点
  2. 如何解析源代码为CST
  3. 如何实现自定义访问器来遍历和评估表达式
  4. 如何将CST重新生成为源代码

输出结果将会是:

Parsed expression: BinaryOp { left: Number(1), op: Add, right: BinaryOp { left: Number(2), op: Multiply, right: Number(3) } }
Found number: 1
Found number: 2
Found number: 3
Evaluated: 2 Multiply 3 = 6
Evaluated: 1 Add 6 = 7
Evaluation result: 7
Generated code: 1 + 2 * 3
回到顶部