13个版本 (7个破坏性更新)
0.8.1 | 2024年6月15日 |
---|---|
0.7.0 | 2024年5月9日 |
#2131 in 过程宏
每月 1,311 次下载
在 untwine 中使用
56KB
1.5K SLoC
Untwine
具有自动错误恢复功能的更漂亮的模式匹配解析器
Untwine是一个声明式解析库,它允许使用自定义宏语法进行类似于直接模式匹配的解析风格。这允许创建非常紧凑的解析器,具有合理的性能特征和高质量的错误消息。这些解析器易于实现,一些精选示例包括
- 12行解析逻辑的近乎完整的JSON解析器
- 支持所有基本的JSON功能,除特殊转义序列(非\)外
- 6行解析逻辑的遵循pmdas的四运算表达式解析器
- 以及一个操作两个数字的辅助函数
使用untwine制作的解析器还具有高质量的错误消息,可以直观地显示错误和相关的语法。
用法
本说明将主要涵盖示例和功能。有关使用分解,请参阅 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解析器生成的部分错误。这些是通过示例REPL获得的,可以使用以下命令分别运行:cargo run --example expr
和 cargo run --example json
。
Untwine支持自动错误恢复,即使在发生错误后也能继续解析12行代码以发现更多错误。还可以获取包含错误节点的恢复输入。
由于错误表示的是范围而不是单个位置,因此也支持多行错误
要查看这两个解析器的完整实现,请参阅 examples/json.rs 和 examples/expr.rs
性能和目标
尽管Untwine目前没有正式的基准测试,但非正式地我发现使用Untwine编写的解析器大约有基本但优化良好的手写解析器三分之二的速度。虽然Untwine寻求提供高性能,但它不适用于高度性能关键的解析器。
大多数解析并不是那么关键,Untwine非常适合构建编程语言或DSL,其中高质量的错误消息更为重要。它试图让解析变得如此简单,以至于在以前可能不会编写解析器的情况下也值得编写解析器,让您能够极快地进行迭代并消除手动解析的大部分痛点。
依赖关系
~260–700KB
~17K SLoC