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);
}
}
}
}
代码说明
- 首先我们初始化tree-sitter parser并设置语言为嵌入式模板语言
- 定义一个包含嵌入式模板的示例字符串
- 解析源代码得到一个语法树
- 通过递归遍历语法树节点,根据节点类型生成不同的代码:
- 对于文本内容直接输出
- 对于嵌入的代码片段,根据内容生成相应的代码结构
- 可以扩展此逻辑来实现更复杂的代码生成
参考资料
- 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");
// 使用部分树进行错误恢复
}
}
}
}
实际应用场景
- 模板引擎开发:构建类似 Jinja2、Handlebars 的模板引擎
- 代码生成工具:从模板生成代码
- 静态网站生成器:处理 Markdown 与模板混合的内容
- 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());
}
}
}
}
这个完整示例展示了:
- 如何配置解析器支持HTML模板中的JavaScript和CSS嵌入
- 如何解析包含条件语句和循环的复杂模板
- 如何递归遍历和打印语法树结构
- 如何将模板转换为生成的代码
- 如何处理解析错误并检查部分解析结果
示例输出会显示解析后的语法树结构、生成的代码以及错误处理信息。