#line-column #nom #column #spans #line #parser #line-numbers

nom-span

将 nom 输入包装到 span 中以获取行、列和字节偏移量

2 个版本

使用旧的 Rust 2015

0.1.1 2023 年 12 月 1 日
0.1.0 2023 年 12 月 1 日

#1664解析器实现

MIT 许可证

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