From b8534bc7179cb3fc23ef8158f96d9bf86e0e1caf Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Wed, 5 Sep 2018 22:36:09 -0500 Subject: [PATCH] FEATURE: Add peek and not combinators. --- src/macros.rs | 77 +++++++++++++++++++++++++++++++++------------------ src/test.rs | 50 +++++++++++++++++++++++++++++---- 2 files changed, 95 insertions(+), 32 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index e3c2d1b..c3b6f59 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -25,17 +25,62 @@ macro_rules! text_token { }}; } -// FIXME(jwall): We need until!, not! and peek!. + +// FIXME(jwall): We need peek!. + +/// 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_export] +macro_rules! not { + ($i:expr, $f:ident!( $( $args:tt )* ) ) => {{ + use $crate::{Result, Error}; + let _i = $i.clone(); + match trap!(_i, $f!($($args)*)) { + Result::Complete(i, _) => Result::Fail(Error::new("Matched on input when we shouldn't have.".to_string(), &i)), + Result::Abort(e) => Result::Abort(e), + Result::Incomplete(offset) => Result::Incomplete(offset), + Result::Fail(_) => Result::Complete($i, ()), + } + }}; + + ($i:expr, $f:ident( $( $args:tt )* ) ) => { + not!($i, run!($f($($args)*))) + }; + + ($i:expr, $f:ident) => { + not!($i, run!($f)) + }; +} + +/// Matches the provided +#[macro_export] +macro_rules! peek { + ($i:expr, $f:ident!( $( $args:tt )* ) ) => {{ + use $crate::Result; + let _i = $i.clone(); + match $f!(_i, $($args)*) { + Result::Complete(_, o) => Result::Complete($i, o), + Result::Incomplete(offset) => Result::Incomplete(offset), + Result::Abort(e) => Result::Abort(e), + Result::Fail(e) => Result::Fail(e), + } + }}; + + ($i:expr, $f:ident( $( $args:tt )* ) ) => { + peek!($i, run!($f($($args)*))) + }; + + ($i:expr, $f:ident) => { + peek!($i, run!($f)) + }; +} + /// Converts a function indentifier into a macro call. Useful when writing your own macro combinator. #[macro_export] macro_rules! run { ($i:expr, $f:ident) => { $f($i) }; - - ($i:expr, $f:ident( $( $args:tt )* ) ) => { - $f($i, $($args)*) - }; } /// Turns Fails into Aborts. Allows you to turn any parse failure into a hard abort of the parser. @@ -50,10 +95,6 @@ macro_rules! must { } }; - ($i:expr, $f:ident( $( $args:tt )* ) ) => { - must!($i, run!($f($($args)*))) - }; - ($i:expr, $f:ident) => { must!($i, run!($f)) }; @@ -94,10 +135,6 @@ macro_rules! trap { } }; - ($i:expr, $f:ident( $( $args:tt )* ) ) => { - trap!($i, run!($f($($args)*))) - }; - ($i:expr, $f:ident) => { trap!($i, run!($f)) }; @@ -117,10 +154,6 @@ macro_rules! must_complete { } }}; - ($i:expr, $efn:expr, $f:ident( $( $args:tt )* ) ) => { - must_complete!($i, $efn, run!($f($($args)*))) - }; - ($i:expr, $efn:expr, $f:ident) => { must_complete!($i, $efn, run!($f)) }; @@ -173,16 +206,6 @@ macro_rules! do_each { do_each!($i, _ => run!($f), $( $rest )* ) }; - ($i:expr, $val:ident => $f:ident( $(args:tt)* ), $($rest:tt)* ) => { - // If any single one of these matchers fails then all of them are failures. - do_each!($i, $val => run!($f($($args)*)), $( $rest )* ) - }; - - ($i:expr, _ => $f:ident( $(args:tt)* ), $($rest:tt)* ) => { - // If any single one of these matchers fails then all of them are failures. - do_each!($i, _ => run!($f($($args)*)), $( $rest )* ) - }; - // Our Terminal condition ($i:expr, ( $($rest:tt)* ) ) => { Result::Complete($i, ($($rest)*)) diff --git a/src/test.rs b/src/test.rs index 2b77333..b27270a 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,4 +1,6 @@ -use super::{Offsetable, Result}; +use std::fmt::{Display, Debug}; + +use super::{InputIter, Offsetable, Result}; use iter::SliceIter; #[test] @@ -31,22 +33,26 @@ fn test_slice_iter() { assert_eq!('o' as u8, out[2]); } -fn will_fail(i: SliceIter) -> Result, String, String> { +fn will_fail(i: I) -> Result +where I: InputIter, C: Debug + Display { Result::Fail(super::Error::new("AAAAHHH!!!".to_string(), &i)) } -fn parse_byte(mut i: SliceIter) -> Result, u8, String> { +fn parse_byte<'a, I>(mut i: I) -> Result +where I: InputIter { match i.next() { Some(b) => Result::Complete(i, *b), None => Result::Incomplete(i.get_offset()), } } -fn will_not_complete(_: SliceIter) -> Result, String, String> { +fn will_not_complete<'a, I>(_: I) -> Result +where I: InputIter { Result::Incomplete(0) } -fn parse_three(i: SliceIter) -> Result, String, String> { +fn parse_three<'a, I>(i: I) -> Result +where I: InputIter { let mut _i = i.clone(); let mut out = String::new(); loop { @@ -66,6 +72,40 @@ fn parse_three(i: SliceIter) -> Result, String, String> { } } +#[test] +fn test_peek() { + let input_str = "foo bar"; + let iter = SliceIter::new(input_str.as_bytes()); + let pristine = iter.clone(); + let result = peek!(iter, text_token!("foo")); + assert!(result.is_complete()); + if let Result::Complete(i, o) = result { + assert_eq!(pristine.get_offset(), i.get_offset()); + assert_eq!("foo", o); + } +} + +#[test] +fn test_not_success() { + let input_str = "foo bar"; + let iter = SliceIter::new(input_str.as_bytes()); + let pristine = iter.clone(); + let result = not!(iter, will_fail); + assert!(result.is_complete()); + if let Result::Complete(i, o) = result { + assert_eq!(pristine.get_offset(), i.get_offset()); + assert_eq!((), o); + } +} + +#[test] +fn test_not_fail() { + let input_str = "foo bar"; + let iter = SliceIter::new(input_str.as_bytes()); + let result = not!(iter, text_token!("foo")); + assert!(result.is_fail()); +} + #[test] fn test_text_token() { let input_str = "foo bar";