Macro pest::process
[−]
[src]
macro_rules! process { ( @pattern $slf:ident ($block:expr) _ : $typ:ident ) => { ... }; ( @pattern $slf:ident ($block:expr) _ : $typ:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident ($block:expr) _ ) => { ... }; ( @pattern $slf:ident ($block:expr) _, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident ($block:expr) &$head:ident : $typ:ident ) => { ... }; ( @pattern $slf:ident ($block:expr) &$head:ident : $typ:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident ($block:expr) &$head:ident ) => { ... }; ( @pattern $slf:ident ($block:expr) &$head:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident ($block:expr) mut $head:ident : $call:ident() ) => { ... }; ( @pattern $slf:ident ($block:expr) mut $head:ident : $call:ident(), $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident ($block:expr) $head:ident : $call:ident() ) => { ... }; ( @pattern $slf:ident ($block:expr) $head:ident : $call:ident(), $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident ($block:expr) $head:ident : $typ:ident ) => { ... }; ( @pattern $slf:ident ($block:expr) $head:ident : $typ:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident ($block:expr) $head:ident ) => { ... }; ( @pattern $slf:ident ($block:expr) $head:ident, $( $tail:tt )* ) => { ... }; ( @pattern $slf:ident ($block:expr) ) => { ... }; ( @branches $slf:ident $name:ident ( $( $pattern:tt )* ) => $block:expr) => { ... }; ( @branches $slf:ident $name:ident ( $( $pattern:tt )* ) => $block:expr,) => { ... }; ( @branches $slf:ident $name:ident ( $( $pattern:tt )* ) => $block:expr, $( $tail: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 callable 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 |
mut item: fn() |
call matcher fn and store mutable result in item |
Note: Lifetime elision works by using the lifetime of the Parser
instance. To use the
lifetime of the Input
instance, use the explicit 'input
lifetime like in the
test.
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! { ab(&self) -> () { (_: a) => {} } } } let mut parser = Rdp::new(StringInput::new("b")); parser.b(); parser.ab();
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: nested_letter())
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! { nested_letter(&self) -> Expression { (&letter: letter) => { Expression::Letter(letter.chars().next().unwrap()) }, (_: paren, expression: nested_letter()) => { Expression::Paren(Box::new(expression)) } } } } let mut parser = Rdp::new(StringInput::new("((z))")); assert!(parser.expression()); assert_eq!(parser.nested_letter(), 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, mut tail: _word()) => { // usual tail recursion 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(), mut tail: _sentence()) => { // match word Token then call _word 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(), mut tail: _sentence()) => { tail.push_front(Node::Word(head)); tail }, () => { LinkedList::new() } } _word(&self) -> LinkedList<Node> { (&head: letter, mut tail: _word()) => { 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.main();