# nom-rule
[](https://docs.rs/nom-rule/)
[](https://crates.io/crates/nom-rule)
[](https://github.com/andylokandy/nom-rule/blob/master/LICENSE)
A procedural macro for defining [nom](https://crates.io/crates/nom) combinators in simple DSL. Requires `nom` v5.0+.
## Dependencies
```toml
[dependencies]
nom = "7"
nom-rule = "0.1"
```
## Syntax
The procedural macro `rule!` provided by this crate is designed for the ease of writing grammar spec as well as to improve maintainability, it follows these simple rules:
1. `TOKEN`: match the token by token kind. You should provide a parser to eat the next token if the token kind matched. it will get expanded into `match_token(TOKEN)`.
2. `";"`: match the token by token text. You should provide a parser to eat the next token if the token text matched. it will get expanded into `match_text(";")` in this example.
3. `#fn_name`: an external nom parser function. In the example above, `ident` is a predefined parser for identifiers.
4. `a ~ b ~ c`: a sequence of parsers to take one by one. It'll get expanded into `nom::sequence::tuple`.
5. `(...)+`: one or more repeated patterns. It'll get expanded into `nom::multi::many1`.
6. `(...)*`: zero or more repeated patterns. It'll get expanded into `nom::multi::many0`.
7. `(...)?`: Optional parser. It'll get expanded into `nom::combinator::opt`.
8. `a | b | c`: Choices between a, b, and c. It'll get expanded into `nom::branch::alt`.
## Example
Define `match_text` parser and `match_token` parser for your custom token type. You can use `nom::combinator::fail` as `match_token` if your parser use `&str` or `&[u8]` as input because you won't match on token kinds.
```rust
#[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)
}
```
Then give the two parser to `nom_rule::rule!` by wrapping it into a custom macro:
```rust
macro_rules! rule {
($($tt:tt)*) => { nom_rule::rule!(($crate::match_text), ($crate::match_token), $($tt)*) }
}
```
To define a parser for the SQL of creating table:
```rust
let mut rule = rule!(
CREATE ~ TABLE ~ #ident ~ "(" ~ (#ident ~ #ident ~ ","?)* ~ ")" ~ ";"
);
```
It will get translated into:
```rust
let mut rule = nom::sequence::tuple((
(match_token)(CREATE),
(match_token)(TABLE),
ident,
(match_text)("("),
nom::multi::many0(nom::sequence::tuple((
ident,
ident,
nom::combinator::opt((match_text)(",")),
))),
(match_text)(")"),
(match_text)(";"),
));
```