2 个版本
使用旧的 Rust 2015
0.1.1 | 2023 年 12 月 1 日 |
---|---|
0.1.0 | 2023 年 12 月 1 日 |
#1664 在 解析器实现
15KB
274 行
Nom Span —
这个库公开了一个名为 Spanned
的结构体,它可以包装你的输入,并允许你跟踪行号、列号和字节偏移量
如何使用它?
以下是一个创建输入和检索所需所有信息的简单示例。
use nom_span::Spanned;
type Span<'a> = Spanned<&'a str>;
fn main() {
let span = Span::new(
r#"{"hello": "world 🙌"}"#,
// Supporting UTF-8
true
);
assert_eq!(span.line(), 1);
assert_eq!(span.col(), 1);
assert_eq!(span.byte_offset(), 0);
}
请注意,支持 UTF-8 是可选的。原因是 UTF-8 字符串需要以不同于纯 ASCII 字符串的方式处理,因此,与 UTF-8 支持相比,可能会存在性能差距(请参阅下面的基准测试)
UTF-8 和 ASCII 比较
一个 UTF-8 字符可能由 1 到 4 个字节组成,因此以 ASCII 方式计数会导致对 UTF-8 字符中的每个字节进行计数,并导致列号意外
use nom_span::Spanned;
type Span<'a> = Spanned<&'a str>;
fn utf8_vs_ascii() {
let utf8 = Span::new("🙌", true);
let ascii = Span::new("🙌", false);
let utf8_after: IResult<Span<'_>, Vec<char>> = many1(anychar)(utf8);
let ascii_after: IResult<Span<'_>, Vec<char>> = many1(anychar)(ascii);
let (utf8_after, _) = utf8_after.unwrap();
let (ascii_after, _) = ascii_after.unwrap();
assert_eq!(utf8_after.col(), 2);
assert_eq!(ascii_after.col(), 5);
}
关于 nom_locate 的看法?
最初我使用 nom_locate,但在构建 json 解析器 时遇到了一些严重的性能问题,所以我决定实现自己的输入。我基本上克隆了 nom_locate 并修改了导致性能问题的计数函数。所以非常感谢这个优秀的 crate,请去给它加一个星标!
与 nom_locate 的区别是什么?
nom_locate 在调用 get_column
时会重新计算你整个输入的所有字符(即使你已经消费了它)。如果你为每个字符调用 get_column
,运行时间将是:O(N^2)
与这个 crate 一样,它在每次消费输入时都会计算行和列。如果你为每个字符调用 col
,运行时间将是:O(2N)
如果您计划只获取列几次,例如,仅在发生错误时,使用nom_locate可能更好,但如果您需要非常频繁地使用,则此crate可能更合适。
依赖项
~1MB
~21K SLoC