57 个版本
0.6.2 | 2024年7月26日 |
---|---|
0.6.0 | 2024年5月25日 |
0.5.1 | 2023年12月2日 |
0.5.0 | 2023年10月13日 |
0.1.1 | 2021年11月25日 |
#34 在 编程语言 中
563 每月下载次数
在 lambdascript 中使用
1MB
12K SLoC
rustlr
LR-风格解析生成器
包含多个示例的教程可在此处找到:教程
新版本 0.6
对关键组件的重写主要由内部更改组成,除了解析器与词法分析器的交互方式。现在,语法中的语义动作可以直接调整词法分析器。通过给 rustlr
应用程序提供 -zc
选项,保留了 0.5 版本的风格。
除了传统的 LR 和 LALR 解析生成外,Rustlr 还支持以下选项
- 一个实验性功能,可以生成用于 选择性 Marcus-Leermakers 语法 的解析器。这是一个比传统 LR 更大的无歧义语法的类。例如,以下语法
S --> a B c | A B d
B --> b | B b
A --> a
是无歧义的,但它 不是 LR(k),因为它不能决定是否使用固定数量的前瞻将 a
还原为 A
。然而,rustlr 仍然可以为这个语法生成一个确定性的解析器(无回溯)。请参阅教程的 附录。Rustlr 是唯一一个支持此功能的实用解析生成器。
-
从语法中创建抽象语法数据类型和语义动作的选项。Rustlr 语法包含一个子语言,用于控制如何生成 AST。
-
支持选择 bumpalo 创建递归 AST,使用引用而不是智能指针:这使 深层模式匹配 成为可能。
-
识别正则表达式风格的运算符
*
、+
和?
,这简化了语法的编写,并允许创建更好的 AST。 -
从语法中自动生成词法扫描器。
-
运算符优先级和结合性声明进一步允许编写更接近 EBNF 语法的语法。
-
能够通过交互或从脚本中训练解析器,以获得更好的错误报告。
-
为Rust和F#生成解析器。Rustlr旨在促进在编译器和语言分析工具的创建中使用强类型函数式编程语言。其他此类语言的解析器生成将逐步可用。
Rustlr旨在简化精确高效的解析器的创建,尽管可能的话将保持向后兼容性,但将继续发展和纳入新功能。
快速示例:算术表达式及其抽象语法
以下是一个Rustlr语法的示例内容,simplecalc.grammar
auto
terminals + * - / ; ( ) # verbatim terminal symbols
valterminal Int i32 # terminal symbol with value
nonterminal E
nonterminal T : E # specifies that AST for T should merge into E
nonterminal F : E
nonterminal ExpList
startsymbol ExpList
variant-group-for E BinaryOp + - * / # controls AST generation
# production rules:
E --> E + T | E - T | T
T --> T * F | T / F | F
F:Neg --> - F # 'Neg' names enum variant in AST
F --> Int | ( E )
ExpList --> E<;+> ;? # ;-separated list with optional trailing ;
!mod simplecalc_ast; // !-lines are injected verbatim into the parser
!fn main() {
! let mut scanner1 = simplecalclexer::from_str("10+-2*4; 9-(4-1);");
! let mut parser1 = make_parser(scanner1);
! let parseresult = parse_with(&mut parser1);
! let ast =
! parseresult.
! unwrap_or_else(|x| {
! println!("Parsing errors encountered; results not guaranteed..");
! x
! });
! println!("\nAST: {:?}\n",&ast);
!}//main
该语法识别由分号分隔的一个或多个算术表达式。除了解析器外,该语法还从终端符号的声明中生成词法扫描器。它还创建了以下抽象语法类型以及产生这些类型实例的语义动作。
#[derive(Debug)]
pub enum E {
BinaryOp(&'static str,LBox<E>,LBox<E>),
Int(i32),
Neg(LBox<E>),
E_Nothing,
}
impl Default for E { fn default()->Self { E::E_Nothing } }
#[derive(Default,Debug)]
pub struct ExpList(pub Vec<LC<E>>,);
LBox和LC是包含AST构造在原始源文件中起始行和列位置的结构的结构。这些信息由解析器自动插入到结构中。LBox封装了一个Box,并作为自定义智能指针,而LC在一个暴露的元组中包含额外的信息。两者都实现了LBox<T>
和LC<T>
,从而以非侵入的方式携带额外的信息。
Rustlr根据语法生成AST类型,但特殊的声明可以控制这些类型的精确结构。对于只有一个产生式的非终端符号,通常生成一个结构,而对于有多个产生式的非终端符号,生成一个枚举,每个产生式对应一个变体。然而,由T
和F
的产生式生成的枚举变体被nonterminal T : E
和nonterminal F : E
的声明合并到E
的类型中。variant-group-for
声明将原本四个变体合并为一个。一元减法规则上的Neg
标签将这种情况与“二元操作”变体组区分开来。
Rustlr的AST类型实现了Default trait,因此即使在遇到解析错误时也会返回部分结果。
可以手动覆盖自动生成的AST类型和语义动作。
还支持指定运算符的优先级和结合性,而不是使用T
和F
类别。
生成的解析器和词法分析器通常形成单独的模块。然而,在这个快速示例中,我们直接将main
注入解析器,以演示如何调用它。要运行此示例,
-
将rustlr作为命令行应用程序安装:
cargo install rustlr
-
为示例创建一个Cargo包,并在包中
cargo add rustlr --no-default-features
。关闭默认功能将仅包含运行时解析例程。 -
将语法保存到crate中,命名为
simplecalc.grammar
。文件名决定了创建的模块名称,并且必须以.grammar
后缀。 -
使用以下命令在crate中运行rustlr应用程序:
rustlr simplecalc.grammar -o src/main.rs
cargo运行
预期的输出是
AST: ExpList([BinaryOp("+", Int(10), BinaryOp("*", Neg(Int(2)), Int(4))), BinaryOp("-", Int(9), BinaryOp("-", Int(4), Int(1)))])
Rustlr也可以通过调用rustlr::generate函数在Rust内部调用。
新增于版本0.6.1,0.6.2
-table
选项将解析表存储到二进制文件中,而不是内联静态数组中。在0.6.2中修复了Windows路径名兼容性问题。
新增于版本0.6.0
语义动作现在可以控制词法分析器的行为。
新增于版本0.5.0
现在可以选择只安装运行时解析器,而不包括解析器生成例程。
新增于版本0.4.13
在自动生成过程中,如[x]
这样的boxed标签现在用LC表示,而不是LBox。
新增于版本0.4.11和0.4.12
默认情况下,通配符_
令牌携带令牌的原始文本作为其语义值。现在,variant-group
指令已被variant-group-for
弃用(尽管仍然可用)。
新增于版本0.4.10
如果给rustlr::generate函数提供-trace 0
选项,则rustlr可以完全静默。所有报告都由函数记录和返回。
新增于版本0.4.9:错误日志选项
给定解析器实例parser
,现在可以调用parser1.set_err_report(true)
,这将内部记录解析错误而不是将其打印到stderr。可以通过调用parser1.get_err_report()
检索错误报告。
新增于版本0.4.8:从Yacc/Bison语法转换。
如果给rustlr可执行文件提供以".y"结尾的文件路径,它将尝试将yacc/bison风格的语法转换为rustlr自己的语法语法,移除所有语义动作和其他语言特定的内容。忽略所有其他命令行选项。
有关更多文档,请参阅教程。
依赖项
~2.4–3.5MB
~56K SLoC