Repository: Marwes/combine (language: rust)

Issue:
Code organisation for larger parser
I have a [400 lines `nom` parser](https://github.com/Yamakaky/dcpu/blob/master/src/assembler/parser/nom_parser.rs) I consider to convert to `combine` (mostly for the better error messages). How would you organize it? I like being able to cleanly separate each small parser. How can I do that with `combine`?



Discussion / hints:
You can look at the [json parser](https://github.com/Marwes/combine/blob/master/benches/json.rs) and [gluon](https://github.com/gluon-lang/gluon/blob/8a3e9f5efd8016c77506530ebf4c8b510100d957/parser/src/lib.rs) parser. Though if you do not need to share state between the parsers you can omit the structure passed as `&self`.

```
fn parse_ident(&self, input: I) -> ParseResult<Id, I> {
    self.parser(ParserEnv::<I, F>::parse_ident2)
        .map(|x| x.0)
        .parse_stream(input)
}
```

Basically just wrap the parser in a function with the signature for [parser](https://docs.rs/combine/2.0.0/combine/fn.parser.html) and within that function just call `parse_stream`. With some extra work do it like [integer](https://github.com/Marwes/combine/blob/master/benches/json.rs#L45-L55) in the json parser. By calling parse_lazy you tell the parser to delay emitting error messages so the `integer` wraps the `parser(integer_)` (which does not emit an error) into `parser(integer_).expected("integer")` to ensure that the error message actual displays what was expected. I can try to explain `parse_lazy` in a better way if you want but most of the time you don't need to worry about it. It should just be relevant in hot loops.

This is something `impl Trait` will make much nicer eventually (and with maximum performance as well). It might be possible to add some macros to make this nicer as well.

I see. Will there be performance difference with using local variables like in the example?

``` rust
    let integer = spaces()
        //parse a string of digits into an i32
        .with(many1(digit()).map(|string: String| string.parse::<i32>().unwrap()));

    //Parse integers separated by commas, skipping whitespace
    let mut integer_list = sep_by(integer, spaces().skip(char(',')));
```

I didn't need shared state for the nom parser, so free functions should be fine.

Nope, there should be no difference at all. 

Cool!

I started the implementation: https://github.com/Yamakaky/dcpu/blob/combine/src/assembler/parser/combine_parser.rs
It doesn't compile and PN and SN are dummy type declarations. Could you quickly review it?

Since you take a `&str` as input you may want to consider using range (zero-copy) parsers https://docs.rs/combine/2.0.0/combine/range/index.html (which I now realize I have forgotten to add doc comments on, they have examples at least) . Also, since you use boxed parsers you may want to be careful so you don't recreate the parser to often (harming performance). But of course always profile before optimizing. Otherwise it looks as I would expect! 👍 

I tried boxed parser since I didn't really know how to return one otherwise...

Ah, sorry. The way to "return" a parser without boxing right now is to write roughly

``` rust
fn pos_number(input: &str) -> ParseResult<&str, int> {
    raw_number().and_then(|(digits, base)| u16::from_str_radix(digits,
                                                                    base)).parse_stream(input)
}

// Use pos_number
parser(pos_number).parse("abc");
```

That way the type is hidden by the function.

Does your `pos_number` work with `Or`?

Yep, [parser](https://docs.rs/combine/2.0.0/combine/fn.parser.html) is just a way to make a `Parser` from a function.

Alternatively, with a bit more boilerplate you could write this.

```
fn pos_number() -> Expected<FnParser<fn (&str) -> ParseResult<&str, int>>> {
    parser(pos_number_).expected("positive number")
}
fn pos_number_(input: &str) -> ParseResult<&str, int> {
    raw_number().and_then(|(digits, base)| u16::from_str_radix(digits,
                                                                    base)).parse_lazy(input)
}

pos_number().parse("abc");
```

By calling `parse_lazy` over `parse_stream` it becomes slightly more efficient if `pos_number` are used in `or`. This is because the parser holds off on generating error information for as long as possible until an entire chain of `or` parsers has failed.

_Making a note to properly explain how this works somewhere in the documentation_

Make an other note: generate `pos_number` using a macro ^^

Perhaps. That would work in this case but it would not be possible to define a parser that was generic if it were a macro. Most of my own parsers are generic which is why I haven't added it.

``` rust
fn pos_number<I>() -> Expected<FnParser<fn (I) -> ParseResult<I, int>>> where I : Stream<Item = char> {
    parser(pos_number_).expected("positive number")
}
fn pos_number_<I>(input: I) -> ParseResult<I, int> where I : Stream<Item = char> {
    raw_number().and_then(|(digits, base)| u16::from_str_radix(digits,
                                                                    base)).parse_lazy(input)
}
```

Had a thought, as long as the parsers are not incredibly complex or mutually recursive an easy way to get re-usability could be to use macros which produce parsers.

``` rust
macro_rules! pos_number {
    () => { raw_number().and_then(|(digits, base)| u16::from_str_radix(digits, base)) }
}

pos_number!().parse_*(input)
```

why not ^^


Fix patch:
diff --git a/benches/json.rs b/benches/json.rs
index de01e286..befa777e 100644
--- a/benches/json.rs
+++ b/benches/json.rs
@@ -10,11 +10,11 @@ use std::path::Path;
 
 use bencher::{black_box, Bencher};
 
-use pc::primitives::{BufferedStream, Consumed, ParseError, ParseResult, Parser, State, Stream};
+use pc::primitives::{BufferedStream, Consumed, IteratorStream, ParseError, ParseResult, Parser,
+                     State, Stream};
 use pc::combinator::{any, between, choice, many, optional, parser, satisfy, sep_by, Expected,
                      FnParser, Skip, many1};
 use pc::char::{char, digit, spaces, string, Spaces};
-use pc::from_iter;
 
 #[derive(PartialEq, Debug)]
 enum Value {
@@ -238,7 +238,7 @@ fn bench_buffered_json(bencher: &mut Bencher) {
         .and_then(|mut file| file.read_to_string(&mut data))
         .unwrap();
     bencher.iter(|| {
-        let buffer = BufferedStream::new(from_iter(data.chars()), 1);
+        let buffer = BufferedStream::new(IteratorStream::new(data.chars()), 1);
         let mut parser = Json::value();
         match parser.parse(State::new(buffer.as_stream())) {
             Ok((Value::Array(v), _)) => {
diff --git a/src/char.rs b/src/char.rs
index 55aff327..70cf6b0f 100644
--- a/src/char.rs
+++ b/src/char.rs
@@ -2,6 +2,7 @@ use primitives::{ConsumedResult, ParseError, Parser, Stream};
 use combinator::{satisfy, skip_many, token, tokens, Expected, Satisfy, SkipMany, Token, With};
 use std::marker::PhantomData;
 
+
 /// Parses a character and succeeds if the character is equal to `c`.
 ///
 /// ```
@@ -18,25 +19,24 @@ where
     token(c)
 }
 
-impl_token_parser! { Digit(), char, Expected<Satisfy<I, fn (char) -> bool>> }
-
-/// Parses a base-10 digit.
-///
-/// ```
-/// use combine::Parser;
-/// use combine::char::digit;
-/// assert_eq!(digit().parse("9"), Ok(('9', "")));
-/// assert!(digit().parse("A").is_err());
-/// ```
-#[inline(always)]
-pub fn digit<I>() -> Digit<I>
-where
-    I: Stream<Item = char>,
-{
-    Digit(
-        satisfy(static_fn!((c, char) -> bool { c.is_digit(10) })).expected("digit"),
-        PhantomData,
-    )
+pub use self::digit::Digit;
+parser!{
+    #[derive(Clone)]
+    pub struct Digit;
+    /// Parses a base-10 digit.
+    ///
+    /// ```
+    /// use combine::Parser;
+    /// use combine::char::digit;
+    /// assert_eq!(digit().parse("9"), Ok(('9', "")));
+    /// assert!(digit().parse("A").is_err());
+    /// ```
+    pub fn digit[I]()(I) -> char
+    where
+        [I: Stream<Item = char>,]
+    {
+        satisfy(|c: char| c.is_digit(10)).expected("digit")
+    }
 }
 
 impl_token_parser! { Space(), char, Expected<Satisfy<I, fn (char) -> bool>> }
diff --git a/src/combinator.rs b/src/combinator.rs
index df8dd562..e6adbb39 100644
--- a/src/combinator.rs
+++ b/src/combinator.rs
@@ -1317,6 +1317,7 @@ impl<'a, I: Stream, O> Parser for FnMut(I) -> ParseResult<O, I> + 'a {
         self(input)
     }
 }
+
 #[derive(Clone)]
 pub struct FnParser<I, F>(F, PhantomData<fn(I) -> I>);
 
diff --git a/src/lib.rs b/src/lib.rs
index eb9f93cc..f47801bc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -38,7 +38,7 @@
 //! This library currently contains five modules:
 //!
 //! * [`combinator`] contains the before mentioned parser combinators and thus contains the main
-//! building blocks for creating any sort of complex parsers. It consists of free functions such
+//! building exprs for creating any sort of complex parsers. It consists of free functions such
 //! as [`many`] and [`satisfy`] as well as a few methods on the [`Parser`] trait which provides a few
 //! functions such as [`or`] which are more natural to use method calls.
 //!
@@ -86,35 +86,39 @@
 //! could define `expr` as `fn expr(input: &str) -> ParseResult<Expr, &str>`
 //!
 //! ```
+//! #[macro_use]
 //! extern crate combine;
 //! use combine::char::{char, letter, spaces};
 //! use combine::{between, many1, parser, sep_by, Parser};
 //! use combine::primitives::{State, Stream, ParseResult};
 //!
 //! #[derive(Debug, PartialEq)]
-//! enum Expr {
+//! pub enum Expr {
 //!     Id(String),
 //!     Array(Vec<Expr>),
 //!     Pair(Box<Expr>, Box<Expr>)
 //! }
 //!
-//! fn expr<I>(input: I) -> ParseResult<Expr, I>
-//!     where I: Stream<Item=char>
+//! // The `parser!` macro can be used to define parser producing functions in most cases
+//! // (for more advanced uses standalone functions can be defined to handle parsing)
+//! parser!{
+//!    fn expr[I]()(I) -> Expr
+//!     where [I: Stream<Item=char>]
 //! {
 //!     let word = many1(letter());
 //!
 //!     //Creates a parser which parses a char and skips any trailing whitespace
 //!     let lex_char = |c| char(c).skip(spaces());
 //!
-//!     let comma_list = sep_by(parser(expr::<I>), lex_char(','));
+//!     let comma_list = sep_by(expr(), lex_char(','));
 //!     let array = between(lex_char('['), lex_char(']'), comma_list);
 //!
 //!     //We can use tuples to run several parsers in sequence
 //!     //The resulting type is a tuple containing each parsers output
 //!     let pair = (lex_char('('),
-//!                 parser(expr::<I>),
+//!                 expr(),
 //!                 lex_char(','),
-//!                 parser(expr::<I>),
+//!                 expr(),
 //!                 lex_char(')'))
 //!                    .map(|t| Expr::Pair(Box::new(t.1), Box::new(t.3)));
 //!
@@ -122,11 +126,11 @@
 //!         .or(array.map(Expr::Array))
 //!         .or(pair)
 //!         .skip(spaces())
-//!         .parse_stream(input)
+//! }
 //! }
 //!
 //! fn main() {
-//!     let result = parser(expr)
+//!     let result = expr()
 //!         .parse("[[], (hello, world), [rust]]");
 //!     let expr = Expr::Array(vec![
 //!           Expr::Array(Vec::new())
@@ -197,6 +201,279 @@ macro_rules! impl_token_parser {
 }
 }
 
+/// 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.
+///
+/// ```
+/// #[macro_use]
+/// extern crate combine;
+/// use combine::char::digit;
+/// use combine::{any, choice, many1, Parser, Stream};
+///
+/// parser!{
+///     fn integer[I]()(I) -> i32
+///         where [I: Stream<Item = char>]
+///     {
+///         // The body must be a block body ( `{ <block body> }`) which ends with an expression
+///         // which evaluates to a parser
+///         let digits = many1(digit());
+///         digits.and_then(|s: String| s.parse())
+///     }
+/// }
+///
+/// #[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[I]()(I) -> IntOrString
+///         where [I: Stream<Item = char>]
+///     {
+///         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[F, P](f: F)(P::Input) -> (P::Output, P::Output)
+///         where [P: Parser,
+///                F: FnMut() -> P]
+///     {
+///         (f(), f())
+///     }
+/// }
+///
+/// fn main() {
+///     assert_eq!(integer().parse("123"), Ok((123, "")));
+///     assert!(integer().parse("!").is_err());
+///
+///     assert_eq!(integer_or_string().parse("123"), Ok((IntOrString::Int(123), "")));
+///     assert_eq!(
+///         integer_or_string().parse("abc"),
+///         Ok((IntOrString::String("abc".to_string()), ""))
+///     );
+///     assert_eq!(twice(|| digit()).parse("123"), Ok((('1', '2'), "3")));
+/// }
+/// ```
+#[macro_export]
+macro_rules! parser {
+    (
+        $(#[$attr:meta])*
+        pub fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),* )
+            ($input_type: ty) -> $output_type: ty
+            { $($parser: tt)* }
+    ) => {
+        parser!{
+            $(#[$attr])*
+            pub fn $name [$($type_params)*]( $($arg : $arg_type),* )($input_type) -> $output_type
+                where []
+            { $($parser)* }
+        }
+    };
+    (
+        $(#[$attr:meta])*
+        fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),* )
+            ($input_type: ty) -> $output_type: ty
+            { $($parser: tt)* }
+    ) => {
+        parser!{
+            $(#[$attr])*
+            fn $name [$($type_params)*]( $($arg : $arg_type),* )($input_type) -> $output_type
+                where []
+            { $($parser)* }
+        }
+    };
+    (
+        $(#[$attr:meta])*
+        pub fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),* )
+            ($input_type: ty) -> $output_type: ty
+            where [$($where_clause: tt)*]
+        { $($parser: tt)* }
+    ) => {
+        parser!{
+            pub struct __Parser;
+            $(#[$attr])*
+            pub fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
+                where [$($where_clause)*]
+            { $($parser)* }
+        }
+    };
+    (
+        $(#[$attr:meta])*
+        fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),*) ($input_type: ty) -> $output_type: ty
+            where [$($where_clause: tt)*]
+        { $($parser: tt)* }
+    ) => {
+        parser!{
+            struct __Parser;
+            $(#[$attr])*
+            fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
+                where [$($where_clause)*]
+            { $($parser)* }
+        }
+    };
+    (
+        $(#[$derive:meta])*
+        pub struct $type_name: ident;
+        $(#[$attr:meta])*
+        pub fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),* )
+            ($input_type: ty) -> $output_type: ty
+            where [$($where_clause: tt)*]
+        { $($parser: tt)* }
+    ) => {
+        combine_parser_impl!{
+            (pub)
+            $(#[$derive])*
+            struct $type_name;
+            $(#[$attr])*
+            fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
+                where [$($where_clause)*]
+            { $($parser)* }
+        }
+    };
+    (
+        $(#[$derive:meta])*
+        struct $type_name: ident;
+        $(#[$attr:meta])*
+        fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),*) ($input_type: ty) -> $output_type: ty
+            where [$($where_clause: tt)*]
+        { $($parser: tt)* }
+    ) => {
+        combine_parser_impl!{
+            ()
+            $(#[$derive])*
+            struct $type_name;
+            $(#[$attr:meta])*
+            fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
+                where [$($where_clause)*]
+            { $($parser)* }
+        }
+    };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! combine_parse_lazy {
+    ($input: ident
+        $stmt: stmt; $($parser: tt)*
+    ) => { {
+        $stmt;
+        combine_parse_lazy!{$input $($parser)*}
+    } };
+    ($input: ident
+        $parser: expr
+    ) => {
+        $parser.parse_lazy($input)
+    };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! combine_add_error {
+    ($errors: ident ($input_type : ty, $output_type : ty)
+        $stmt: stmt; $($parser: tt)*
+    ) => { {
+        $stmt;
+        combine_add_error!{$errors ($input_type, $output_type) $($parser)*}
+    } };
+    ($errors: ident ($input_type : ty, $output_type : ty)
+        $parser: expr
+    ) => { {
+        let mut parser = $parser;
+        {
+            let _: &mut Parser<Input = $input_type, Output = $output_type> = &mut parser;
+        }
+        parser.add_error($errors)
+    } }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! combine_parser_impl {
+    (
+        ( $($pub_: tt)* )
+        $(#[$derive:meta])*
+        struct $type_name: ident;
+        $(#[$attr:meta])*
+        fn $name: ident [$($type_params: tt)*]( $($arg: ident :  $arg_type: ty),*) ($input_type: ty) -> $output_type: ty
+            where [$($where_clause: tt)*]
+        { $($parser: tt)* }
+    ) => {
+        mod $name {
+            use super::*;
+
+            $(#[$derive])*
+            pub struct $type_name<$($type_params)*>
+                where $($where_clause)*
+            {
+                $(pub $arg : $arg_type,)*
+                __marker: ::std::marker::PhantomData<fn ($input_type) -> $output_type>
+            }
+
+            // We want this to work on older compilers, at least for a while
+            #[allow(non_shorthand_field_patterns)]
+            impl<$($type_params)*> $crate::Parser for $type_name<$($type_params)*>
+                where $($where_clause)*
+            {
+                type Input = $input_type;
+                type Output = $output_type;
+
+                #[inline]
+                fn parse_lazy(
+                    &mut self,
+                    input: $input_type
+                    ) -> $crate::primitives::ConsumedResult<$output_type, $input_type>
+                {
+                    let $type_name { $( $arg: ref mut $arg,)* __marker: _ } = *self;
+                    combine_parse_lazy!(input $($parser)*)
+                }
+
+                #[inline]
+                fn add_error(&mut self, errors: &mut $crate::ParseError<$input_type>) {
+                    let $type_name { $( $arg : ref mut $arg,)*  __marker: _ } = *self;
+                    combine_add_error!(errors ($input_type, $output_type) $($parser)*)
+                }
+            }
+            #[inline(always)]
+            pub fn parse< $($type_params)* >(
+                    $($arg : $arg_type),*
+                ) -> self::$type_name<$($type_params)*>
+                where $($where_clause)*
+            {
+                $type_name {
+                    $($arg : $arg,)*
+                    __marker: ::std::marker::PhantomData
+                }
+            }
+        }
+
+        $(#[$attr])*
+        #[inline(always)]
+        $($pub_)* fn $name< $($type_params)* >(
+                $($arg : $arg_type),*
+            ) -> self::$name::$type_name<$($type_params)*>
+            where $($where_clause)*
+        {
+            self::$name::parse(
+                $($arg,)*
+            )
+        }
+    };
+}
+
+
 pub extern crate byteorder;
 
 /// Module containing the primitive types which is used to create and compose more advanced
@@ -281,7 +558,7 @@ mod tests {
     }
 
     #[derive(Debug, PartialEq)]
-    enum Expr {
+    pub enum Expr {
         Id(String),
         Int(i64),
         Array(Vec<Expr>),
@@ -289,31 +566,31 @@ mod tests {
         Times(Box<Expr>, Box<Expr>),
     }
 
-    #[allow(unconditional_recursion)]
-    fn expr<I>(input: I) -> ParseResult<Expr, I>
-    where
-        I: Stream<Item = char>,
-    {
-        let word = many1(letter()).expected("identifier");
-        let integer = parser(integer);
-        let array = between(char('['), char(']'), sep_by(parser(expr), char(','))).expected("[");
-        let paren_expr = between(char('('), char(')'), parser(term)).expected("(");
-        let spaces = spaces();
-        spaces
-            .clone()
-            .with(
-                word.map(Expr::Id)
-                    .or(integer.map(Expr::Int))
-                    .or(array.map(Expr::Array))
-                    .or(paren_expr),
-            )
-            .skip(spaces)
-            .parse_stream(input)
+    parser!{
+        fn expr[I]()(I) -> Expr
+        where
+            [I: Stream<Item = char>,]
+        {
+            let word = many1(letter()).expected("identifier");
+            let integer = parser(integer);
+            let array = between(char('['), char(']'), sep_by(expr(), char(','))).expected("[");
+            let paren_expr = between(char('('), char(')'), parser(term)).expected("(");
+            let spaces = spaces();
+            spaces
+                .clone()
+                .with(
+                    word.map(Expr::Id)
+                        .or(integer.map(Expr::Int))
+                        .or(array.map(Expr::Array))
+                        .or(paren_expr),
+                )
+                .skip(spaces)
+        }
     }
 
     #[test]
     fn expression() {
-        let result = sep_by(parser(expr), char(',')).parse("int, 100, [[], 123]");
+        let result = sep_by(expr(), char(',')).parse("int, 100, [[], 123]");
         let exprs = vec![
             Expr::Id("int".to_string()),
             Expr::Int(100),
@@ -327,7 +604,7 @@ mod tests {
         let input = r"
 ,123
 ";
-        let result = parser(expr).parse(State::new(input));
+        let result = expr().parse(State::new(input));
         let err = ParseError {
             position: SourcePosition { line: 2, column: 1 },
             errors: vec![
@@ -353,7 +630,7 @@ mod tests {
         }
         let mul = char('*').map(|_| times);
         let add = char('+').map(|_| plus);
-        let factor = chainl1(parser(expr), mul);
+        let factor = chainl1(expr(), mul);
         chainl1(factor, add).parse_stream(input)
     }
 


Test patch:
diff --git a/tests/date.rs b/tests/date.rs
index 1f4963d2..6b94e304 100644
--- a/tests/date.rs
+++ b/tests/date.rs
@@ -1,13 +1,11 @@
 //! Parser example for ISO8601 dates. This does not handle the entire specification but it should
 //! show the gist of it and be easy to extend to parse additional forms.
 
+#[macro_use]
 extern crate combine;
 
-use combine::combinator::FnParser;
 use combine::char::{char, digit};
-use combine::{choice, many, optional, parser, ParseResult, Parser, Stream};
-
-type FnPtrParser<O, I> = FnParser<I, fn(I) -> ParseResult<O, I>>;
+use combine::{choice, many, optional, Parser, Stream};
 
 #[derive(PartialEq, Debug)]
 pub struct Date {
@@ -30,28 +28,18 @@ pub struct DateTime {
     pub time: Time,
 }
 
-fn two_digits_to_int((x, y): (char, char)) -> i32 {
-    let x = x.to_digit(10).expect("digit");
-    let y = y.to_digit(10).expect("digit");
-    (x * 10 + y) as i32
-}
-
-
-// Parsers which are used frequntly can be wrapped like this to avoid writing parser(fn_name) in
-// several places.
-fn two_digits<I>() -> FnPtrParser<i32, I>
-where
-    I: Stream<Item = char>,
-{
-    fn two_digits_<I>(input: I) -> ParseResult<i32, I>
+parser!{
+    fn two_digits[I]()(I) -> i32
     where
-        I: Stream<Item = char>,
+        [I: Stream<Item = char>,]
     {
         (digit(), digit())
-            .map(two_digits_to_int)
-            .parse_stream(input)
+            .map(|(x, y): (char, char)| {
+                let x = x.to_digit(10).expect("digit");
+                let y = y.to_digit(10).expect("digit");
+                (x * 10 + y) as i32
+            })
     }
-    parser(two_digits_)
 }
 
 /// Parses a time zone
@@ -59,95 +47,100 @@ where
 /// -06:30
 /// -01
 /// Z
-fn time_zone<I>(input: I) -> ParseResult<i32, I>
-where
-    I: Stream<Item = char>,
-{
-    let utc = char('Z').map(|_| 0);
-    let offset = (
-        choice([char('-'), char('+')]),
-        two_digits(),
-        optional(optional(char(':')).with(two_digits())),
-    ).map(|(sign, hour, minute)| {
-            let offset = hour * 60 + minute.unwrap_or(0);
-            if sign == '-' {
-                -offset
-            } else {
-                offset
-            }
-        });
-
-    utc.or(offset).parse_stream(input)
+parser!{
+    fn time_zone[I]()(I) -> i32
+    where
+        [I: Stream<Item = char>,]
+    {
+        let utc = char('Z').map(|_| 0);
+        let offset = (
+            choice([char('-'), char('+')]),
+            two_digits(),
+            optional(optional(char(':')).with(two_digits())),
+        ).map(|(sign, hour, minute)| {
+                let offset = hour * 60 + minute.unwrap_or(0);
+                if sign == '-' {
+                    -offset
+                } else {
+                    offset
+                }
+            });
+
+        utc.or(offset)
+    }
 }
 
 /// Parses a date
 /// 2010-01-30
-fn date<I>(input: I) -> ParseResult<Date, I>
-where
-    I: Stream<Item = char>,
-{
-    (
-        many::<String, _>(digit()),
-        char('-'),
-        two_digits(),
-        char('-'),
-        two_digits(),
-    ).map(|(year, _, month, _, day)| {
-            // Its ok to just unwrap since we only parsed digits
-            Date {
-                year: year.parse().unwrap(),
-                month: month,
-                day: day,
-            }
-        })
-        .parse_stream(input)
+parser!{
+    fn date[I]()(I) -> Date
+    where
+        [I: Stream<Item = char>,]
+    {
+        (
+            many::<String, _>(digit()),
+            char('-'),
+            two_digits(),
+            char('-'),
+            two_digits(),
+        ).map(|(year, _, month, _, day)| {
+                // Its ok to just unwrap since we only parsed digits
+                Date {
+                    year: year.parse().unwrap(),
+                    month: month,
+                    day: day,
+                }
+            })
+    }
 }
 
 /// Parses a time
 /// 12:30:02
-fn time<I>(input: I) -> ParseResult<Time, I>
-where
-    I: Stream<Item = char>,
-{
-    (
-        two_digits(),
-        char(':'),
-        two_digits(),
-        char(':'),
-        two_digits(),
-        parser(time_zone),
-    ).map(|(hour, _, minute, _, second, time_zone)| {
-            // Its ok to just unwrap since we only parsed digits
-            Time {
-                hour: hour,
-                minute: minute,
-                second: second,
-                time_zone: time_zone,
-            }
-        })
-        .parse_stream(input)
+parser!{
+    fn time[I]()(I) -> Time
+    where
+        [I: Stream<Item = char>,]
+    {
+        (
+            two_digits(),
+            char(':'),
+            two_digits(),
+            char(':'),
+            two_digits(),
+            time_zone(),
+        ).map(|(hour, _, minute, _, second, time_zone)| {
+                // Its ok to just unwrap since we only parsed digits
+                Time {
+                    hour: hour,
+                    minute: minute,
+                    second: second,
+                    time_zone: time_zone,
+                }
+            })
+    }
 }
 
 /// Parses a date time according to ISO8601
 /// 2015-08-02T18:54:42+02
-fn date_time<I>(input: I) -> ParseResult<DateTime, I>
-where
-    I: Stream<Item = char>,
-{
-    (parser(date), char('T'), parser(time))
-        .map(|(date, _, time)| {
-            DateTime {
-                date: date,
-                time: time,
-            }
-        })
-        .parse_stream(input)
+parser!{
+    fn date_time[I]()(I) -> DateTime
+    where
+        [I: Stream<Item = char>,]
+    {
+        (date(), char('T'), time())
+            .map(|(date, _, time)| {
+                DateTime {
+                    date: date,
+                    time: time,
+                }
+            })
+    }
 }
 
 #[test]
 fn test() {
     // A parser for
-    let result = parser(date_time).parse("2015-08-02T18:54:42+02");
+    let result = date_time().parse("2015-08-02T18:54:42+02");
     let d = DateTime {
         date: Date {
             year: 2015,
@@ -163,7 +156,7 @@ fn test() {
     };
     assert_eq!(result, Ok((d, "")));
 
-    let result = parser(date_time).parse("50015-12-30T08:54:42Z");
+    let result = date_time().parse("50015-12-30T08:54:42Z");
     let d = DateTime {
         date: Date {
             year: 50015,
diff --git a/tests/ini.rs b/tests/ini.rs
index 712d3c7a..c9057cb6 100644
--- a/tests/ini.rs
+++ b/tests/ini.rs
@@ -1,4 +1,5 @@
 //! Parser example for INI files.
+#[macro_use]
 extern crate combine;
 
 use std::collections::HashMap;
@@ -13,65 +14,70 @@ pub struct Ini {
     pub sections: HashMap<String, HashMap<String, String>>,
 }
 
-fn property<I>(input: I) -> ParseResult<(String, String), I>
-where
-    I: Stream<Item = char>,
-{
-    (
-        many1(satisfy(|c| c != '=' && c != '[' && c != ';')),
-        token('='),
-        many1(satisfy(|c| c != '\n' && c != ';')),
-    ).map(|(key, _, value)| (key, value))
-        .message("while parsing property")
-        .parse_stream(input)
+parser!{
+    fn property[I]()(I) -> (String, String)
+        where [I: Stream<Item = char>]
+    {
+        (
+            many1(satisfy(|c| c != '=' && c != '[' && c != ';')),
+            token('='),
+            many1(satisfy(|c| c != '\n' && c != ';')),
+        ).map(|(key, _, value)| (key, value))
+            .message("while parsing property")
+    }
 }
-
-fn whitespace<I>(input: I) -> ParseResult<(), I>
-where
-    I: Stream<Item = char>,
-{
-    let comment = (token(';'), skip_many(satisfy(|c| c != '\n'))).map(|_| ());
-    // Wrap the `spaces().or(comment)` in `skip_many` so that it skips alternating whitespace and
-    // comments
-    skip_many(skip_many1(space()).or(comment)).parse_stream(input)
+parser!{
+    fn whitespace[I]()(I) -> ()
+    where
+        [I: Stream<Item = char>,]
+    {
+        let comment = (token(';'), skip_many(satisfy(|c| c != '\n'))).map(|_| ());
+        // Wrap the `spaces().or(comment)` in `skip_many` so that it skips alternating whitespace and
+        // comments
+        skip_many(skip_many1(space()).or(comment))
+    }
 }
 
-fn properties<I>(input: I) -> ParseResult<HashMap<String, String>, I>
-where
-    I: Stream<Item = char>,
-{
-    // After each property we skip any whitespace that followed it
-    many(parser(property).skip(parser(whitespace))).parse_stream(input)
+parser!{
+    fn properties[I]()(I) -> HashMap<String, String>
+    where
+        [I: Stream<Item = char>,]
+    {
+        // After each property we skip any whitespace that followed it
+        many(property().skip(whitespace()))
+    }
 }
 
-fn section<I>(input: I) -> ParseResult<(String, HashMap<String, String>), I>
-where
-    I: Stream<Item = char>,
-{
-    (
-        between(token('['), token(']'), many(satisfy(|c| c != ']'))),
-        parser(whitespace),
-        parser(properties),
-    ).map(|(name, _, properties)| (name, properties))
-        .message("while parsing section")
-        .parse_stream(input)
+parser!{
+    fn section[I]()(I) -> (String, HashMap<String, String>)
+    where
+        [I: Stream<Item = char>,]
+    {
+        (
+            between(token('['), token(']'), many(satisfy(|c| c != ']'))),
+            whitespace(),
+            properties(),
+        ).map(|(name, _, properties)| (name, properties))
+            .message("while parsing section")
+    }
 }
 
-fn ini<I>(input: I) -> ParseResult<Ini, I>
-where
-    I: Stream<Item = char>,
-{
-    (
-        parser(whitespace),
-        parser(properties),
-        many(parser(section)),
-    ).map(|(_, global, sections)| {
-            Ini {
-                global: global,
-                sections: sections,
-            }
-        })
-        .parse_stream(input)
+parser!{
+    fn ini[I]()(I) -> Ini
+    where
+        [I: Stream<Item = char>,]
+    {
+        (
+            whitespace(),
+            properties(),
+            many(section()),
+        ).map(|(_, global, sections)| {
+                Ini {
+                    global: global,
+                    sections: sections,
+                }
+            })
+    }
 }
 
 #[test]
@@ -97,14 +103,14 @@ type=LL(1)
     section.insert(String::from("type"), String::from("LL(1)"));
     expected.sections.insert(String::from("section"), section);
 
-    let result = parser(ini).parse(text).map(|t| t.0);
+    let result = ini().parse(text).map(|t| t.0);
     assert_eq!(result, Ok(expected));
 }
 
 #[test]
 fn ini_error() {
     let text = "[error";
-    let result = parser(ini).parse(State::new(text)).map(|t| t.0);
+    let result = ini().parse(State::new(text)).map(|t| t.0);
     assert_eq!(
         result,
         Err(ParseError {