Macro combine::parser

source ·
macro_rules! parser {
    (
        type PartialState = $partial_state: ty;
        $(#[$attr:meta])*
        $fn_vis: vis fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),*)
            ($input_type: ty) -> $output_type: ty
            where [$($where_clause: tt)*]
        $parser: block
    ) => { ... };
    (
        $(#[$derive:meta])*
        $struct_vis: vis struct $type_name: ident;
        type PartialState = $partial_state: ty;
        $(#[$attr:meta])*
        $fn_vis: vis fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),* )
            ($input_type: ty) -> $output_type: ty
            where [$($where_clause: tt)*]
        $parser: block
    ) => { ... };
    (
        $(#[$attr:meta])*
        $fn_vis: vis fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),*)
            ($input_type: ty) -> $output_type: ty
            where [$($where_clause: tt)*]
        $parser: block
    ) => { ... };
    (
        $(#[$derive:meta])*
        $struct_vis: vis struct $type_name: ident;
        $(#[$attr:meta])*
        $fn_vis: vis fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),* )
            ($input_type: ty) -> $output_type: ty
            where [$($where_clause: tt)*]
        $parser: block
    ) => { ... };
}
Expand description

Declares a named parser which can easily be reused.

The expression which creates the parser should have no side effects as it may be called multiple times even during a single parse attempt.

NOTE: You can use impl Trait in the return position instead. See the json parser for an example.

#[macro_use]
extern crate combine;
use combine::parser::char::digit;
use combine::{any, choice, from_str, many1, Parser, EasyParser, Stream};
use combine::error::ParseError;

parser!{
    /// `[Input]` represents a normal type parameters and lifetime declaration for the function
    /// It gets expanded to `<Input>`
    fn integer[Input]()(Input) -> i32
    where [
        Input: Stream<Token = char>,
        <Input::Error as ParseError<Input::Token, Input::Range, Input::Position>>::StreamError:
            From<::std::num::ParseIntError>,
    ]
    {
        // The body must be a block body ( `{ <block body> }`) which ends with an expression
        // which evaluates to a parser
        from_str(many1::<String, _, _>(digit()))
    }
}

#[derive(Debug, PartialEq)]
pub enum IntOrString {
    Int(i32),
    String(String),
}
// prefix with `pub` to declare a public parser
parser!{
    // Documentation comments works as well

    /// Parses an integer or a string (any characters)
    pub fn integer_or_string[Input]()(Input) -> IntOrString
    where [
        Input: Stream<Token = char>,
        <Input::Error as ParseError<Input::Token, Input::Range, Input::Position>>::StreamError:
            From<::std::num::ParseIntError>,
    ]
    {
        choice!(
            integer().map(IntOrString::Int),
            many1(any()).map(IntOrString::String)
        )
    }
}

parser!{
    // Give the created type a unique name
    #[derive(Clone)]
    pub struct Twice;
    pub fn twice[Input, F, P](f: F)(Input) -> (P::Output, P::Output)
        where [P: Parser<Input>,
               F: FnMut() -> P]
    {
        (f(), f())
    }
}

fn main() {
    assert_eq!(integer().easy_parse("123"), Ok((123, "")));
    assert!(integer().easy_parse("!").is_err());

    assert_eq!(
        integer_or_string().easy_parse("123"),
        Ok((IntOrString::Int(123), ""))
    );
    assert_eq!(
        integer_or_string().easy_parse("abc"),
        Ok((IntOrString::String("abc".to_string()), ""))
    );
    assert_eq!(twice(|| digit()).parse("123"), Ok((('1', '2'), "3")));
}