16个版本 (7个重大变更)
0.8.1 | 2024年6月15日 |
---|---|
0.7.0 | 2024年5月9日 |
0.1.0 | 2024年1月25日 |
#1266 在 解析器实现
每月下载量 1,545次
80KB
1K SLoC
Untwine
具有自动错误恢复的更美观的模式匹配解析器
Untwine 是一个声明式解析库,它允许使用类似于直接模式匹配的自定义宏语法进行解析。这允许创建非常紧凑且性能良好的解析器,并具有高质量的错误信息。这些解析器易于实现,一些精选示例包括
- 12行解析逻辑的几乎完整的JSON解析器
- 支持所有基本的JSON功能,除了特殊的转义序列(除了 \”)
- 6行解析逻辑的尊重pmdas的四运算表达式解析器
- 以及一个操作两个数字的辅助函数
使用 untwine 制作的解析器还具有高质量的错误信息,可以直观地显示错误和相关的语法。
用法
此README主要涵盖示例和功能。有关用法分解,请参阅 https://docs.rs/untwine/latest/untwine/macro.parser.html。
有关更详细的教程,请参阅 https://github.com/boxbeam/untwine/blob/master/TUTORIAL.md。
简单、声明式解析
Untwine 允许您通过描述您想要解析的语法并选择您想要的输出部分来解析。以下是在 Untwine 中实现表达式解析器的代码。
fn operate(left: f64, op: char, right: f64) -> f64 {
match op {
'+' => left + right,
'-' => left - right,
'/' => left / right,
'*' => left * right,
_ => unreachable!(),
}
}
parser! {
sep = #{char::is_ascii_whitespace}*;
num: num=<"-"? '0'-'9'+ ("." '0'-'9'+)?> -> f64 { num.parse().unwrap() }
term = (num | "(" sep expr sep ")") -> f64;
add: first=mul sep ops=(["+-"] sep mul)* -> f64 { ops.into_iter().fold(first, |left, (op, right)| operate(left, op, right)) }
mul: first=term sep ops=(["*/"] sep term)* -> f64 { ops.into_iter().fold(first, |left, (op, right)| operate(left, op, right)) }
pub expr = add -> f64;
}
虽然本指南不会全面解释解析器,但展示了几个关键特性。首先,语法与 Regex 或 EBNF 相似。每个解析器声明它要匹配的模式,并且可以在任何顶级模式前使用 name=
来提取该模式的值到变量中。一旦结构匹配,您就可以在代码块中评估解析器的输出。
这种方法的优点是在利用正式语法的同时,仍然允许与类型系统和 IDE 灵感完全集成。
除了简洁之外,解析器还提供自动从您描述的语法中生成的彩色错误,并有效地说明任何语法错误。
错误
在这里,我将展示由表达式解析器和JSON解析器生成的一些错误。这些错误是通过示例REPLs获得的,可以分别使用以下命令运行:cargo run --example expr
和 cargo run --example json
Untwine支持自动错误恢复,使相同的12行代码在出现错误后继续解析,以发现更多错误。还可以获得包含错误节点的恢复输入。
由于错误表示一个范围而不是单个位置,因此也支持多行错误
要查看这两个解析器的完整实现,请参阅examples/json.rs 和 examples/expr.rs
性能和目标
尽管Untwine目前没有正式的基准测试,但非正式地我发现使用Untwine编写的解析器大约是基本但经过良好优化的手写解析器速度的三分之二。虽然Untwine寻求提供高性能,但它不适合对性能要求极高的解析器。
大多数解析并不那么关键于性能,Untwine非常适合构建编程语言或DSL,在这些语言中,高质量的错误信息更为重要。它试图使解析变得如此简单,以至于即使在没有这样做的情况下也值得编写解析器,这使您能够快速迭代并消除手动解析的大部分痛点。
依赖项
~245–690KB
~16K SLoC