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 Tokens generated by a Parser. It generates a method process on &self that processes the whole queue of Tokens, 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();