diff --git a/src/combinators.rs b/src/combinators.rs index b3e858e..08d2644 100644 --- a/src/combinators.rs +++ b/src/combinators.rs @@ -1,7 +1,23 @@ -//! Contains combinators that can assemble other mathers or combinators into more complex grammars. +//! Contains combinators that can assemble other matchers or combinators into more complex grammars. /// Turns a matcher into it's inverse, only succeeding if the the matcher returns a Fail. /// Does not consume it's input and only returns (). +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// # use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// # use std::convert::From; +/// # fn main() { +/// # let iter: iter::SliceIter = "foo".into(); +/// let tok = not!(iter, text_token!("bar")); +/// assert!(tok.is_complete()); +/// if let Result::Complete(i, o) = tok { +/// assert_eq!(i.get_offset(), 0); +/// assert_eq!(o, ()); +/// } +/// # } +/// ``` #[macro_export] macro_rules! not { ($i:expr, $f:ident!( $( $args:tt )* ) ) => {{ @@ -24,7 +40,23 @@ macro_rules! not { }; } -/// Matches the provided +/// Checks the given matcher without consuming the input. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// # use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// # use std::convert::From; +/// # fn main() { +/// # let iter: iter::SliceIter = "foo".into(); +/// let tok = peek!(iter, text_token!("foo")); +/// # assert!(tok.is_complete()); +/// # if let Result::Complete(i, o) = tok { +/// # assert_eq!(i.get_offset(), 0); +/// # assert_eq!(o, "foo"); +/// # } +/// # } +/// ``` #[macro_export] macro_rules! peek { ($i:expr, $f:ident!( $( $args:tt )* ) ) => {{ @@ -55,7 +87,25 @@ macro_rules! run { }; } -/// Turns Fails into Aborts. Allows you to turn any parse failure into a hard abort of the parser. +/// Turns Fails into Aborts. Allows you to turn any parse failure into a hard abort of +/// the parser. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// use abortable_parser::iter; +/// # use abortable_parser::Result; +/// # use std::convert::From; +/// # fn main() { +/// +/// let iter: iter::SliceIter = "foo".into(); +/// +/// let tok = must!(iter, text_token!("foo")); +/// # assert!(tok.is_complete()); +/// +/// let fail = must!(iter, text_token!("bar")); +/// # assert!(fail.is_abort()); +/// # } +/// ``` #[macro_export] macro_rules! must { ($i:expr, $f:ident!( $( $args:tt )* ) ) => { @@ -95,7 +145,20 @@ macro_rules! wrap_err { }; } -/// Turns Aborts into fails allowing you to trap and then convert an Abort into a normal Fail. +/// Turns Aborts into fails allowing you to trap and then convert an Abort into a +/// normal Fail. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// # fn main() { +/// let input_str = "foo"; +/// let iter = iter::SliceIter::new(input_str.as_bytes()); +/// let result = trap!(iter, must!(text_token!("bar"))); +/// # assert!(result.is_fail()); +/// # } +/// ``` #[macro_export] macro_rules! trap { ($i:expr, $f:ident!( $( $args:tt )* ) ) => { @@ -112,8 +175,19 @@ macro_rules! trap { }; } -/// Turns Fails and Incompletes into Aborts. It uses an error factory -/// to construct the errors for the Incomplete case. +/// Turns Fails and Incompletes into Aborts. You must specify the error message +/// to use in case the matcher is Incomplete. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// # fn main() { +/// let input_str = "foo"; +/// let iter = iter::SliceIter::new(input_str.as_bytes()); +/// let mut result = must_complete!(iter, "AHHH".to_string(), text_token!("fooooo")); +/// # assert!(result.is_abort()); +/// # } #[macro_export] macro_rules! must_complete { ($i:expr, $e:expr, $f:ident!( $( $args:tt )* ) ) => {{ @@ -132,6 +206,54 @@ macro_rules! must_complete { } /// Captures a sequence of sub parsers output. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// # fn main() { +/// let input_str = "(foobar)"; +/// let iter = iter::SliceIter::new(input_str.as_bytes()); +/// let result = do_each!(iter, +/// _ => text_token!("("), +/// foo => text_token!("foo"), +/// bar => text_token!("bar"), +/// _ => text_token!(")"), +/// (foo, bar) // This expression will be the result of the parse +/// ); +/// # assert!(result.is_complete()); +/// if let Result::Complete(_, o) = result { +/// assert_eq!("foo", o.0); +/// assert_eq!("bar", o.1); +/// } +/// # } +/// ``` +/// +/// Or alternatively rather than a tuple as the output you can return a single +/// expression. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// # use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// # fn main() { +/// # let input_str = "(foobar)"; +/// # let iter = iter::SliceIter::new(input_str.as_bytes()); +/// let result = do_each!(iter, +/// _ => text_token!("("), +/// foo => text_token!("foo"), +/// bar => text_token!("bar"), +/// _ => text_token!(")"), +/// (vec![foo, bar]) // Non tuple expression as a result. +/// ); +/// # assert!(result.is_complete()); +/// if let Result::Complete(_, o) = result { +/// assert_eq!(vec!["foo", "bar"], o); +/// } +/// # } +/// ``` +/// +/// The output from this combinator must be indicated by parentheses. #[macro_export] macro_rules! do_each { ($i:expr, $val:ident => $f:ident) => { @@ -185,6 +307,22 @@ macro_rules! do_each { } /// Returns the output of the first sub parser to succeed. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// # fn main() { +/// let input_str = "foo"; +/// let iter = iter::SliceIter::new(input_str.as_bytes()); +/// let result = either!(iter, text_token!("bar"), text_token!("foo")); +/// # assert!(result.is_complete()); +/// # if let Result::Complete(_, o) = result { +/// # assert_eq!("foo", o); +/// # } else { +/// # assert!(false, "either! did not complete"); +/// # } +/// # } #[macro_export] macro_rules! either { // Initialization case. @@ -271,6 +409,22 @@ macro_rules! either { /// Treats a sub parser as optional. It returns Some(output) for a successful match /// and None for Fails. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// # fn main() { +/// let input_str = "foo"; +/// let iter = iter::SliceIter::new(input_str.as_bytes()); +/// let result = optional!(iter, text_token!("foo")); +/// # assert!(result.is_complete()); +/// # if let Result::Complete(_, o) = result { +/// # assert_eq!("foo", o.unwrap()); +/// # } else { +/// # assert!(false, "optional! did not complete"); +/// # } +/// # } #[macro_export] macro_rules! optional { ($i:expr, $f:ident) => { @@ -301,8 +455,25 @@ macro_rules! optional { }}; } -/// Runs a single parser repeating 0 or mre times and returns a possibly empty +/// Runs a single matcher repeating 0 or mre times and returns a possibly empty /// vector of the parsed results. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// # fn main() { +/// let input_str = "foofoo"; +/// let iter = iter::SliceIter::new(input_str.as_bytes()); +/// let result = repeat!(iter, text_token!("foo")); +/// # assert!(result.is_complete()); +/// if let Result::Complete(_, o) = result { +/// assert_eq!(2, o.len()); +/// assert_eq!("foo", o[0]); +/// assert_eq!("foo", o[1]); +/// } +/// # } +/// ``` #[macro_export] macro_rules! repeat { ($i:expr, $f:ident!( $( $args:tt )* ) ) => {{ diff --git a/src/matchers.rs b/src/matchers.rs index bbe4cce..443e436 100644 --- a/src/matchers.rs +++ b/src/matchers.rs @@ -1,6 +1,25 @@ //! Contains matchers for matching specific patterns or tokens. +use super::{InputIter, Result, Error}; + +use std::fmt::{Debug, Display}; /// Convenience macro for looking for a specific text token in a byte input stream. +/// +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// use std::convert::From; +/// # fn main() { +/// let iter: iter::SliceIter = "foo bar".into(); +/// let tok = text_token!(iter, "foo"); +/// # assert!(tok.is_complete()); +/// if let Result::Complete(i, o) = tok { +/// assert_eq!(i.get_offset(), 3); +/// assert_eq!(o, "foo"); +/// } +/// # } +/// ``` #[macro_export] macro_rules! text_token { ($i:expr, $e:expr) => {{ @@ -28,6 +47,24 @@ macro_rules! text_token { }}; } +/// Consumes an input until it reaches the term combinator matches. +/// +/// If the term never matches then returns incomplete. +/// ``` +/// # #[macro_use] extern crate abortable_parser; +/// use abortable_parser::iter; +/// # use abortable_parser::{Result, Offsetable}; +/// use std::convert::From; +/// # fn main() { +/// let iter: iter::SliceIter = "foo;".into(); +/// let tok = until!(iter, text_token!(";")); +/// # assert!(tok.is_complete()); +/// if let Result::Complete(i, o) = tok { +/// assert_eq!(i.get_offset(), 3); +/// } +/// # } +/// ``` +#[macro_export] macro_rules! until { ($i:expr, $term:ident!( $( $args:tt )* ) ) => {{ use $crate::{Result, Offsetable}; @@ -58,6 +95,7 @@ macro_rules! until { }; } +/// Maps a Result of type Vec<&u8> to a Result of type String. pub fn must_string<'a, I, E>(matched: Result, E>, msg: E) -> Result where I: InputIter, diff --git a/src/test.rs b/src/test.rs index 8508df9..73320e1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -144,7 +144,7 @@ fn test_wrap_err_fail() { assert!(result.is_fail()); if let Result::Fail(e) = result { assert!(e.get_cause().is_some()); - assert_eq!("AAAAHHH!!!", e.get_cause().unwrap().get_err()); + assert_eq!("AAAAHHH!!!", e.get_cause().unwrap().get_msg()); } } @@ -156,7 +156,7 @@ fn test_wrap_err_abort() { assert!(result.is_abort()); if let Result::Abort(e) = result { assert!(e.get_cause().is_some()); - assert_eq!("AAAAHHH!!!", e.get_cause().unwrap().get_err()); + assert_eq!("AAAAHHH!!!", e.get_cause().unwrap().get_msg()); } }