pest::process!
[−]
[src]
macro_rules! process { ( @pattern $slf:ident $index:ident ($block:expr) _ : $typ:ident ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) _ : $typ:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) _ ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) _, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) &$head:ident : $typ:ident ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) &$head:ident : $typ:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) &$head:ident ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) &$head:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) $head:ident : $call:ident() ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) $head:ident : $call:ident(), $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) $head:ident : $typ:ident ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) $head:ident : $typ:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) $head:ident ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) $head:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident $index:ident ($block:expr) ) => { ... }; ( @branches $slf:ident $index:ident ( $( $pattern:tt )* ) => $block:expr) => { ... }; ( @branches $slf:ident $index:ident ( $( $pattern:tt )* ) => $block:expr,) => { ... }; ( @branches $slf:ident $index:ident ( $( $pattern:tt )* ) => $block:expr, $( $tail:tt )* ) => { ... }; ( @type (main $typ:ty) $( $_ts:tt )* ) => { ... }; ( @type ($_name:ident $_typ:ty) $( $ts:tt )* ) => { ... }; ( $( $name:ident (&$slf:ident) -> $typ:ty { $( $ts:tt )* } )* ) => { ... }; }
A macro
for pattern-matching queued Token
s generated by a Parser
. It generates a method
process
on &self
that processes the whole queue of Token
s, reducing it to one single
result.
The process
is populated with methods, called matchers, that match patterns and return
results. A pattern is constructed from the following comma-separated items:
Item | What it does |
---|---|
item |
matches any Token |
item: R |
matches a Token of rule R |
&item |
captures a Token |
&item: R |
captures a Token of rule R |
_ |
skips a Token |
_: R |
skips a Token of rule R |
item: fn() |
call matcher fn and store result in item |
process
automatically calls the main
matcher which is mandatory.
Panics
In case all the patterns inside of process!
won't match, the process
method will panic!
.
impl_rdp! { grammar! { a = { ["a"] } b = { ["b"] } } process! { main(&self) -> () { (_: a) => {} } } } let mut parser = Rdp::new(StringInput::new("b")); parser.b(); parser.process();
Examples
Nested letter
Let's consider the following grammar of nested letters:
expression = _{ paren | letter } // we don't need the expression Token paren = { ["("] ~ expression ~ [")"] } letter = { ['a'..'z'] }
Defining the grammar paves way to a simple data-structre (an enum
) that can be either a
Paren
or a Letter
.
#[derive(Debug, PartialEq)] pub enum Expression { Paren(Box<Expression>), Letter(char) }
The processing phase needs to handle two cases: parens and letters. Letters are straightforward captures:
(&letter: letter)
Parens need to recursively process the next item in order to be stored inside of the Paren
.
But before that, it needs to match a paren
Token
that gets ignored.
(_: paren, expression: main())
All together now:
#[derive(Debug, PartialEq)] pub enum Expression { Paren(Box<Expression>), Letter(char) } impl_rdp! { grammar! { expression = _{ paren | letter } paren = { ["("] ~ expression ~ [")"] } letter = { ['a'..'z'] } } process! { main(&self) -> Expression { (&letter: letter) => { Expression::Letter(letter.chars().next().unwrap()) }, (_: paren, expression: main()) => { Expression::Paren(Box::new(expression)) } } } } let mut parser = Rdp::new(StringInput::new("((z))")); assert!(parser.expression()); assert_eq!(parser.process(), Expression::Paren(Box::new(Expression::Paren(Box::new(Expression::Letter('z'))))));
Sentence
To showcase the use of multiple matchers, we'll use a sentence grammar:
sentence = _{ word ~ ([" "] ~ word)* } // we don't need the sentence Token word = { letter* } letter = { ['a'..'z'] }
Let's create a very simple AST that works in this case:
#[derive(Debug, PartialEq)] pub enum Node { Sentence(LinkedList<Node>), // we're using LinkedList because they're more efficient when Word(LinkedList<Node>), // using tail recursion Letter(char) }
To build the Token
processor, let's use a bottom up aproach when writing the matchers. Let's
start by building a word
matcher. We'll call it _word
in order not to clash with the name
of the rule that also gets defined as a method on the Parser
.
_word(&self) -> LinkedList<Node> { // return LinkedList<Node> to build Word with (&head: letter, tail: _word()) => { // usual tail recursion; &head is captured, let mut tail = tail; // tail is recursive tail.push_front(Node::Letter(head.chars().next().unwrap())); tail }, () => { // if rule above doesn't match, there are no more letters to LinkedList::new() // process; return empty list } }
Processing a sentence
is similar, only this time head
will be a _word
call.
_sentence(&self) -> LinkedList<Node> { (_: word, head: _word(), tail: _sentence()) => { // match word Token then call _word let mut tail = tail; tail.push_front(Node::Word(head)); tail }, () => { LinkedList::new() } }
Finally, the main
matcher:
main(&self) -> Node { (list: _sentence()) => { Node::Sentence(list) } }
Putting everything together:
#[derive(Debug, PartialEq)] pub enum Node { Sentence(LinkedList<Node>), Word(LinkedList<Node>), Letter(char) } impl_rdp! { grammar! { sentence = _{ word ~ ([" "] ~ word)* } word = { letter* } letter = { ['a'..'z'] } } process! { main(&self) -> Node { (list: _sentence()) => { Node::Sentence(list) } } _sentence(&self) -> LinkedList<Node> { (_: word, head: _word(), tail: _sentence()) => { let mut tail = tail; tail.push_front(Node::Word(head)); tail }, () => { LinkedList::new() } } _word(&self) -> LinkedList<Node> { (&head: letter, tail: _word()) => { let mut tail = tail; tail.push_front(Node::Letter(head.chars().next().unwrap())); tail }, () => { LinkedList::new() } } } } let mut parser = Rdp::new(StringInput::new("abc def")); assert!(parser.sentence()); parser.process();