Rust语法树解析库tree-sitter-embedded-template的使用,实现嵌入式模板的高效解析与代码生成

Rust语法树解析库tree-sitter-embedded-template的使用,实现嵌入式模板的高效解析与代码生成

简介

tree-sitter-embedded-template是一个用于解析类似ERB和EJS等模板语言的tree-sitter解析器,这些模板语言允许使用<%%>作为分隔符在文本内容中嵌入脚本代码。

安装

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

cargo add tree-sitter-embedded-template

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

tree-sitter-embedded-template = "0.23.2"

完整示例

以下是一个使用tree-sitter-embedded-template解析和生成代码的完整示例:

use tree_sitter::Parser;
use tree_sitter_embedded_template::language;

fn main() {
    // 初始化parser
    let mut parser = Parser::new();
    
    // 设置语言为嵌入式模板语言
    parser.set_language(language()).expect("Error loading embedded template grammar");
    
    // 示例模板代码
    let source_code = r#"
    <html>
      <body>
        <% for user in users %>
          <div class="user">
            <h2><%= user.name %></h2>
            <p><%= user.bio %></p>
          </div>
        <% end %>
      </body>
    </html>
    "#;
    
    // 解析源代码
    let tree = parser.parse(source_code, None).unwrap();
    
    // 获取根节点
    let root_node = tree.root_node();
    
    // 遍历语法树并生成代码
    generate_code(root_node, source_code);
}

fn generate_code(node: tree_sitter::Node, source: &str) {
    // 根据节点类型进行不同的处理
    match node.kind() {
        "template" => {
            // 处理整个模板
            println!("Found template:");
            for child in node.children(&mut node.walk()) {
                generate_code(child, source);
            }
        },
        "text" => {
            // 输出文本内容
            let text = node.utf8_text(source.as_bytes()).unwrap();
            println!("Text content: {}", text.trim());
        },
        "code" => {
            // 处理嵌入的代码
            let code = node.utf8_text(source.as_bytes()).unwrap();
            println!("Embedded code: {}", code);
            
            // 这里可以添加实际的代码生成逻辑
            if code.contains("for") {
                println!("// Generated loop structure");
            } else if code.starts_with("=") {
                println!("// Generated output expression");
            }
        },
        _ => {
            // 处理其他节点类型
            for child in node.children(&mut node.walk()) {
                generate_code(child, source);
            }
        }
    }
}

代码说明

  1. 首先我们初始化tree-sitter parser并设置语言为嵌入式模板语言
  2. 定义一个包含嵌入式模板的示例字符串
  3. 解析源代码得到一个语法树
  4. 通过递归遍历语法树节点,根据节点类型生成不同的代码:
    • 对于文本内容直接输出
    • 对于嵌入的代码片段,根据内容生成相应的代码结构
    • 可以扩展此逻辑来实现更复杂的代码生成

参考资料

  • ERB文档
  • EJS文档

这个示例展示了如何使用tree-sitter-embedded-template来解析模板文件并生成相应的代码结构。您可以根据实际需求扩展此基础框架,例如添加更复杂的代码生成逻辑或支持更多的模板语法特性。


1 回复

tree-sitter-embedded-template: 高效解析与代码生成的Rust库

介绍

tree-sitter-embedded-template 是一个基于 Tree-sitter 的 Rust 库,专门用于解析和处理嵌入式模板语言。它能够高效地解析混合了多种语言的模板文件(如 HTML 中的 JavaScript、Markdown 中的代码块等),并支持代码生成功能。

主要特性

  • 支持多种语言混合解析
  • 高效的增量解析能力
  • 精确的语法树表示
  • 方便的代码转换和生成功能
  • 良好的错误恢复机制

安装

在 Cargo.toml 中添加依赖:

[dependencies]
tree-sitter-embedded-template = "0.1"
tree-sitter = "0.20"

基本使用方法

1. 解析模板

use tree_sitter_embedded_template::Parser;

fn main() {
    // 定义一个包含模板语言的字符串
    let template = r#"
    <html>
        <body>
            {% for item in items %}
                <div>{{ item.name }}</div>
            {% endfor %}
        </body>
    </html>
    "#;

    // 创建解析器并解析模板
    let mut parser = Parser::new();
    let tree = parser.parse(template).unwrap();
    
    // 打印语法树的S表达式表示
    println!("{:?}", tree.root_node().to_sexp());
}

2. 遍历语法树

use tree_sitter::{Node, Tree};
use tree_sitter_embedded_template::Parser;

// 递归遍历语法树的辅助函数
fn walk_tree(node: Node, depth: usize) {
    let indent = "  ".repeat(depth);
    println!("{}{:?}", indent, node.kind());
    
    // 遍历所有子节点
    let mut cursor = node.walk();
    for child in node.children(&mut cursor) {
        walk_tree(child, depth + 1);
    }
}

fn main() {
    // 定义一个简单的条件模板
    let template = "{% if x %}Hello{% else %}World{% endif %}";
    
    // 创建解析器并解析模板
    let mut parser = Parser::new();
    let tree = parser.parse(template).unwrap();
    
    // 从根节点开始遍历语法树
    walk_tree(tree.root_node(), 0);
}

3. 代码生成

use tree_sitter_embedded_template::{Parser, Generator};

fn main() {
    // 定义一个包含循环的模板
    let template = r#"
    {% for user in users %}
    User: {{ user.name }}
    {% endfor %}
    "#;

    // 解析模板
    let mut parser = Parser::new();
    let tree = parser.parse(template).unwrap();
    
    // 创建代码生成器并生成代码
    let mut generator = Generator::new();
    let output = generator.generate(&tree);
    
    // 打印生成的代码
    println!("Generated code:\n{}", output);
}

高级用法

自定义语言嵌入

use tree_sitter_embedded_template::{Parser, LanguageConfig};

fn main() {
    let mut parser = Parser::new();
    
    // 配置自定义语言嵌入规则
    parser.configure_language(LanguageConfig {
        name: "my_template",  // 模板语言名称
        embedded_languages: vec![  // 定义嵌入的语言
            ("javascript", "script"),  // JavaScript代码块
            ("css", "style")          // CSS代码块
        ],
        delimiters: vec![  // 定义模板分隔符
            ("{%", "%}"),  // 控制块分隔符
            ("{{", "}}"),  // 表达式分隔符
        ]
    });
    
    // 定义一个包含CSS和条件逻辑的模板
    let template = r#"
    <style>
        {% if dark_mode %}
        body { background: #333; color: white; }
        {% else %}
        body { background: white; color: black; }
        {% endif %}
    </style>
    "#;
    
    // 解析模板
    let tree = parser.parse(template).unwrap();
    // 这里可以继续处理语法树...
}

增量解析

use tree_sitter_embedded_template::Parser;

fn main() {
    // 创建解析器并解析初始内容
    let mut parser = Parser::new();
    let mut tree = parser.parse("Initial content").unwrap();
    
    // 定义更新后的内容
    let new_text = "Updated content";
    
    // 创建编辑操作,描述内容的变化
    let edit = tree_sitter::InputEdit {
        start_byte: 0,  // 修改开始位置(字节)
        old_end_byte: "Initial".len(),  // 原内容结束位置
        new_end_byte: "Updated".len(),  // 新内容结束位置
        start_position: tree_sitter::Point::new(0, 0),  // 修改开始位置(行列)
        old_end_position: tree_sitter::Point::new(0, 7),  // 原内容结束位置
        new_end_position: tree_sitter::Point::new(0, 7),  // 新内容结束位置
    };
    
    // 应用编辑操作到现有语法树
    tree.edit(&edit);
    
    // 使用增量解析更新语法树
    let new_tree = parser.parse_with_tree(new_text, &tree).unwrap();
}

错误处理

use tree_sitter_embedded_template::Parser;

fn main() {
    // 创建解析器
    let mut parser = Parser::new();
    
    // 尝试解析包含语法错误的模板
    let result = parser.parse("{% invalid syntax %}");
    
    match result {
        Ok(tree) => {
            // 处理成功解析的树
            println!("Parsing succeeded!");
        },
        Err(err) => {
            // 处理解析错误
            eprintln!("Parse error: {}", err);
            
            // 检查是否有部分解析结果
            if let Some(partial_tree) = err.partial_tree() {
                println!("Partial tree available for error recovery");
                // 使用部分树进行错误恢复
            }
        }
    }
}

实际应用场景

  1. 模板引擎开发:构建类似 Jinja2、Handlebars 的模板引擎
  2. 代码生成工具:从模板生成代码
  3. 静态网站生成器:处理 Markdown 与模板混合的内容
  4. IDE/编辑器插件:为混合语言提供语法高亮和代码补全

性能提示

  • 重用 Parser 实例以避免重复初始化开销
  • 对于频繁更新的内容使用增量解析
  • 对大文件考虑分块处理

这个库特别适合需要处理复杂模板场景的Rust应用程序,它结合了Tree-sitter的高效解析能力和Rust的安全性与性能。

完整示例代码

下面是一个完整的示例,展示了如何使用 tree-sitter-embedded-template 解析模板、遍历语法树并进行代码生成:

use tree_sitter_embedded_template::{Parser, Generator, LanguageConfig};
use tree_sitter::{Node, Tree};

fn main() {
    // 1. 配置解析器
    let mut parser = Parser::new();
    parser.configure_language(LanguageConfig {
        name: "html_template",
        embedded_languages: vec![
            ("javascript", "script"),
            ("css", "style")
        ],
        delimiters: vec![
            ("{%", "%}"),
            ("{{", "}}"),
        ]
    });

    // 2. 定义模板内容
    let template = r#"
    <!DOCTYPE html>
    <html>
    <head>
        <style>
            {% if dark_mode %}
            body { background: #333; color: white; }
            {% else %}
            body { background: white; color: black; }
            {% endif %}
        </style>
    </head>
    <body>
        <h1>Welcome, {{ user.name }}!</h1>
        <ul>
            {% for item in items %}
            <li>{{ item }}</li>
            {% endfor %}
        </ul>
    </body>
    </html>
    "#;

    // 3. 解析模板
    let tree = parser.parse(template).unwrap();
    
    // 4. 遍历语法树
    fn print_tree(node: Node, indent: usize) {
        println!("{:indent$}{}: {}",
            "", 
            node.kind(), 
            node.utf8_text(tree.source).unwrap_or_default(),
            indent = indent * 2
        );
        let mut cursor = node.walk();
        for child in node.children(&mut cursor) {
            print_tree(child, indent + 1);
        }
    }
    
    println!("=== Syntax Tree ===");
    print_tree(tree.root_node(), 0);
    
    // 5. 代码生成
    let mut generator = Generator::new();
    let output = generator.generate(&tree);
    
    println!("\n=== Generated Code ===");
    println!("{}", output);
    
    // 6. 错误处理示例
    match parser.parse("{% invalid syntax %}") {
        Ok(_) => println!("Parsing succeeded unexpectedly"),
        Err(e) => {
            println!("\n=== Error Handling ===");
            println!("Error: {}", e);
            if let Some(partial) = e.partial_tree() {
                println!("Partial tree root: {}", partial.root_node().kind());
            }
        }
    }
}

这个完整示例展示了:

  1. 如何配置解析器支持HTML模板中的JavaScript和CSS嵌入
  2. 如何解析包含条件语句和循环的复杂模板
  3. 如何递归遍历和打印语法树结构
  4. 如何将模板转换为生成的代码
  5. 如何处理解析错误并检查部分解析结果

示例输出会显示解析后的语法树结构、生成的代码以及错误处理信息。

回到顶部