Easiest way to get started is through gll-macros
:
[dependencies]
gll = "0.0.2"
gll-macros = "0.0.2"
As an example, this is what you might write for a JSON-like syntax, that uses plain identifiers instead of string literals for field names, and allows values to be parenthesized Rust expressions:
mod json_like {
gll_macros::proc_macro_parser! {
Value =
| Null:"null"
| False:"false"
| True:"true"
| Literal:LITERAL
| Array:{ "[" elems:Value* % "," "]" }
| Object:{ "{" fields:Field* % "," "}" }
| InterpolateRust:{ "(" TOKEN_TREE+ ")" }
;
Field = name:IDENT ":" value:Value;
}
}
You can also use a build script to generate the parser (TODO: document).
To parse a string with that grammar:
let tokens = string.parse::<gll::grammer::proc_macro::TokenStream>().unwrap();
json_like::Value::parse(tokens).unwrap().with(|value| {
// ...
});
All grammars contain a set of named rules, with the syntax Name = rule;
.
(the order between the rules doesn't matter)
Rules are made out of:
{...}
'a'..='d'
is equivalent to "a"|"b"|"c"|"d"
IDENT
, PUNCT
, LITERAL
, TOKEN_TREE
A B
- "A
followed by B
"A | B
- "either A
or B
"A?
- "either A
or nothing"A*
- "zero or more A
s", A+
- "one or more A
s"
A* % ","
- "comma-separated A
s"A* %% ","
- "comma-separated A
s", with an optional trailing commaParts of a rule can be labeled with field names, to allow later access to them:
LetDecl = "let" pat:Pat { "=" init:Expr }? ";";
produces:
// Note: generic parameters omitted for brevity.
struct LetDecl {
pat: Handle<Pat>,
init: Option<Handle<Expr>>,
}
One Rust-specific convention is that alternation fields are enum variants.
Expr = Lit:LITERAL | Add:{ a:Expr "+" b:Expr };
produces:
enum Expr {
Lit(Handle<LITERAL>),
Add {
a: Handle<Expr>,
b: Handle<Expr>,
},
}
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.