如何用syntax构建Rust解析器:Rust插件完整实战(含AST递归结构) 如何用syntax构建Rust解析器Rust插件完整实战含AST递归结构【免费下载链接】syntaxSyntactic analysis toolkit, language-agnostic parser generator.项目地址: https://gitcode.com/gh_mirrors/sy/syntaxsyntax 是一款语言无关的解析器生成工具Syntactic analysis toolkit / parser generator内置 LR 与 LL 解析算法一条命令即可为 JavaScript、Python、Rust 等 8 种语言生成解析器。本文以 Rust 为目标语言带你从零用 syntax-cli 生成一个计算器解析器并构建带BoxNodeAST 递归结构——全程无需手写任何解析状态机代码。认识 syntax写一次语法生成 8 种语言的解析器 syntax 把「解析表计算」和「目标语言代码生成」解耦LR/LL 解析算法与 LALR(1)、SLR(1) 等解析表的计算已内置各目标语言由插件实现且目标语言由输出文件扩展名自动决定.rs即 Rust。Rust 插件的核心源码都在 src/plugins/rust/ 目录下文件作用rust-parser-generator-trait.jsRust 特有的代码生成逻辑类型标注解析、解析表转 Rust 向量等lr-parser-generator-rust.jsLR 模式生成入口lr.template.rs生成的 Rust 解析器骨架tokenizer.template.rs生成的 Rust 词法分析器基于正则插件的完整说明可参考 src/plugins/rust/README.md。 需要参与工具开发时可执行git clone https://gitcode.com/gh_mirrors/sy/syntax再运行npm install npm run build。快速安装 syntax-cli 并搭建 Cargo 项目第 1 步全局安装命令行工具npm install -g syntax-cli syntax-cli --help第 2 步新建 Cargo 工程cargo new calc-parser --bin # 主程序 cd calc-parser cargo new syntax # 本地库 crate存放生成的解析器在主程序Cargo.toml中依赖该 crate[dependencies] syntax { path syntax }第 3 步为生成的解析器添加外部依赖syntax 生成的 Rust 解析器依赖onig正则与lazy_static两个 crate在syntax/Cargo.toml中声明[dependencies] onig 4 lazy_static 1⚠️onig要求 Rust ≥ 1.26.0可用rustup update升级工具链。Rust 解析器语法文件编写要点Rust 是强类型语言与动态语言插件最大的区别在于每个使用了参数的产生式都必须声明参数类型与返回类型语法如下|$1: i32, $3: i32| - i32; // $1、$3 为 i32结果为 i32下面是一份完整的计算器语法Yacc/Bison 风格可直接保存为syntax/grammar.g%lex %% \s return ; \d return NUMBER; return ; * return *; ( return (; ) return ); /lex %left %left * %{ type TResult i32; // 必须定义解析结果的最终类型 %} %% Expr : Expr Expr { |$1: i32, $3: i32| - i32; $$ $1 $3 } | Expr * Expr { |$1: i32, $3: i32| - i32; $$ $1 * $3 } | NUMBER { || - i32; $$ yytext.parse::i32().unwrap() } | ( Expr ) { $$ $2 };三个易踩坑的细节%{ ... %}模块包含代码会原样注入生成文件Rust 插件要求其中必须定义type TResult ...;否则生成直接报错纯传递参数可省略类型标注( Expr )里$$ $2未做运算无需声明类型%left顺序决定优先级*写在之后2 2 * 2才能正确解析为2 (2 * 2)同时自动消除 shift-reduce 冲突。LALR(1) 模式一键生成 Rust 解析器syntax-cli -g syntax/grammar.g -m LALR1 -o syntax/src/lib.rs ✓ Successfully generated: syntax/src/lib.rs输出文件是 Rust 文件目标语言自动识别为 Rust-m LALR1是最实用的模式默认算法为 LALR(1)-by-SLR(1)。生成后在calc-parser/src/main.rs中直接使用use syntax::Parser; fn main() { let mut parser Parser::new(); println!({:?}, parser.parse(2 2 * 2)); // 6 println!({:?}, parser.parse((2 2) * 2)); // 8 }cargo run即可看到输出6和8。进阶用递归 enum Box 构建 AST 的完整套路 直接求值太简单官方示例 examples/calc-ast.rs.g 展示了如何生成一棵表达式树。核心是递归枚举完整源码见 calc-ast.rs.g#L37-L52#[derive(Debug)] pub enum Node { Literal(i32), Binary { op: static str, left: BoxNode, right: BoxNode, }, } pub type TResult Node;关键点Rust 结构体大小必须确定而 AST 天然递归所以用BoxNode堆分配打断循环引用。产生式里只需把类型标注换成NodeExpr : Expr Expr { |$1: Node; $3: Node| - Node; $$ Node::Binary { op: , left: Box::new($1), right: Box::new($3), } } | NUMBER { || - Node; $$ Node::Literal(yytext.parse::i32().unwrap()) };此时解析2 2 * 2得到的不再是数字而是Binary { op: , left: Literal(2), right: Binary { op: *, left: Literal(2), right: Literal(2) } }你还可以在%{ %}中定义on_parse_begin/on_parse_end钩子在解析开始与结束时打印日志、统计耗时实现轻量级中间件。解析冲突排查与 --debug 调试技巧--validate检查语法是否存在冲突并给出具体的冲突产生式与可选解决方案%left/%right/%nonassoc声明算子的结合性与优先级可自动消解 shift-reduce / reduce-reduce 冲突--debug输出构建规范集合、生成解析表、解析各阶段的耗时方便定位慢在哪一步--table打印解析表直观查看状态与动作。syntax-cli -g examples/calculator-assoc.g -m lalr1 -w -p id * id idRust 插件关键文件导航 资料路径Rust 插件使用指南本文步骤出处src/plugins/rust/README.md简单求值版示例语法examples/calc.rs.g递归 AST 版示例语法examples/calc-ast.rs.gRust 代码生成特性类型/SV 枚举/解析表src/plugins/rust/rust-parser-generator-trait.js新语言插件开发指南src/plugins/README.md工具主文档语法格式、冲突、LL/LR 模式README.md同一份语法文件换个输出扩展名如-o calcparser.py就能生成对应语言的解析器——这正是 syntax「语言无关」的魅力所在。【免费下载链接】syntaxSyntactic analysis toolkit, language-agnostic parser generator.项目地址: https://gitcode.com/gh_mirrors/sy/syntax创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考