#nom #parser #proc-macro #parser-generator #macro #generator #pest

nom-rule

一个用于在简单DSL中定义nom组合器的过程宏

8个版本

0.3.1 2024年7月9日
0.3.0 2022年5月8日
0.2.2 2022年4月27日
0.2.1 2022年2月6日
0.1.1 2022年1月31日

#340 in 解析工具

Download history 1963/week @ 2024-04-24 985/week @ 2024-05-01 3061/week @ 2024-05-08 2222/week @ 2024-05-15 2178/week @ 2024-05-22 1890/week @ 2024-05-29 1904/week @ 2024-06-05 1691/week @ 2024-06-12 1918/week @ 2024-06-19 2254/week @ 2024-06-26 2314/week @ 2024-07-03 2744/week @ 2024-07-10 2035/week @ 2024-07-17 1915/week @ 2024-07-24 1895/week @ 2024-07-31 1646/week @ 2024-08-07

7,861 每月下载量
2 个crate中使用(通过 databend-common-ast

MIT 许可证

28KB
557

nom-rule

Documentation Crates.io LICENSE

一个用于在简单DSL中定义nom组合器的过程宏。需要nom v5.0+和nightly Rust工具链。

依赖项

[dependencies]
nom = "7"
nom-rule = "0.2"

语法

此crate提供的过程宏 rule! 旨在简化语法规范的编写并提高可维护性,它遵循以下简单规则

  1. TOKEN:按token类型匹配token。如果匹配到token类型,应提供解析器来消耗下一个token。它将扩展为 match_token(TOKEN)
  2. ";":按token文本匹配token。如果匹配到token文本,应提供解析器来消耗下一个token。在这个示例中,它将扩展为 match_text(";")
  3. #fn_name:外部nom解析器函数。在上面的示例中,ident 是一个预定义的标识符解析器。
  4. a ~ b ~ c:按顺序取一个解析器的解析器序列。它将扩展为 nom::sequence::tuple
  5. (...)+:一个或多个重复的模式。它将被展开为nom::multi::many1
  6. (...)*:零个或多个重复的模式。它将被展开为nom::multi::many0
  7. (...)?:可选解析器。它将被展开为nom::combinator::opt
  8. a | b | c:在a、b和c之间进行选择。它将被展开为nom::branch::alt
  9. &a:窥视。它将被展开为nom::combinator::peek(a)。注意,它不会消耗输入。
  10. !a:否定谓词。它将被展开为nom::combinator::not。注意,它不会消耗输入。
  11. ^a:Cut解析器。它将被展开为nom::combinator::cut
  12. ... : "description":错误报告的上下文描述。它将被展开为nom::error::context

示例

定义match_text解析器和match_token解析器用于您的自定义令牌类型。如果您解析器使用&str&[u8]作为输入,因为您不会匹配令牌类型,所以可以使用nom::combinator::fail作为match_token

#[derive(Clone, Debug, PartialEq)]
struct Token<'a> {
    kind: TokenKind,
    text: &'a str,
    span: Span,
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum TokenKind {
    Whitespace,

    // Keywords
    CREATE,
    TABLE,

    // Symbols
    LParen,
    RParen,
    Semicolon,
    Comma,

    Ident,
}

fn match_text<'a, Error: ParseError<Input<'a>>>(
    text: &'a str,
) -> impl FnMut(Input<'a>) -> IResult<Input<'a>, &'a Token<'a>, Error> {
    move |i| satisfy(|token: &Token<'a>| token.text == text)(i)
}

fn match_token<'a, Error: ParseError<Input<'a>>>(
    kind: TokenKind,
) -> impl FnMut(Input<'a>) -> IResult<Input<'a>, &'a Token<'a>, Error> {
    move |i| satisfy(|token: &Token<'a>| token.kind == kind)(i)
}

然后通过自定义宏将其包装,将其两个解析器提供给nom_rule::rule!

macro_rules! rule {
    ($($tt:tt)*) => { 
        nom_rule::rule!($crate::match_text, $crate::match_token, $($tt)*)
    }
}

定义创建表SQL的解析器

let mut rule = rule!(
    CREATE ~ TABLE ~ #ident ~ ^"(" ~ (#ident ~ #ident ~ ","?)* ~ ")" ~ ";" : "CREATE TABLE statement"
);

它将被展开为

let mut rule = 
    nom::error::context(
        "CREATE TABLE statement",
        nom::sequence::tuple((
            (crate::match_token)(CREATE),
            (crate::match_token)(TABLE),
            ident,
            (nom::combinator::cut(crate::match_text)("(")),
            nom::multi::many0(nom::sequence::tuple((
                ident,
                ident,
                nom::combinator::opt((crate::match_text)(",")),
            ))),
            (crate::match_text)(")"),
            (crate::match_text)(";"),
        ))
    );

更多示例请参考 tests/lib.rs 和主要依赖项 databend

自动序列

nom-rule可以在必要时自动在规则中插入~,以便使上面的示例与以下示例具有相同的工作方式

let mut rule = rule!(
    CREATE TABLE #ident "(" (#ident #ident ","?)* ")" ";" : "CREATE TABLE statement"
);

要启用此功能,您需要将以下内容添加到Cargo.toml

nom-rule = { version = "0.2", features = ["auto-sequence"] }

依赖项

~2.5MB
~55K SLoC