use crate::{InputToken, Parser};
pub fn attempt<P, IT, O>(parser: &P) -> impl Parser<IT, O>
where
P: Parser<IT, O>,
IT: InputToken,
{
|input| {
let handler = input.start_look_ahead();
let output = parser(input);
input.stop_look_ahead(handler, output.is_err());
output
}
}
#[cfg(test)]
mod tests {
use crate::input::Position;
use crate::*;
#[test]
fn empty() {
let mut input = Input::new_from_chars("".chars(), None);
let parser = is('h');
let output = attempt(&parser)(&mut input);
assert_eq!(output, Err(Error::EndOfInput(Some(Box::new('h')))));
}
#[test]
fn empty_shortcut() {
let mut input = Input::new_from_chars("".chars(), None);
let parser = is('h').attempt();
let output = parser(&mut input);
assert_eq!(output, Err(Error::EndOfInput(Some(Box::new('h')))));
}
#[test]
fn success_consumes() {
let mut input = Input::new_from_chars("hel".chars(), None);
let parser = is('h');
let output = attempt(&parser)(&mut input);
assert_eq!(output, Ok('h'));
assert_eq!(is('e')(&mut input), Ok('e'));
assert_eq!(is('l')(&mut input), Ok('l'));
assert!(end_of_input()(&mut input).is_ok());
}
#[test]
fn success_consumes_shortcut() {
let mut input = Input::new_from_chars("hel".chars(), None);
let parser = is('h').attempt();
let output = parser(&mut input);
assert_eq!(output, Ok('h'));
assert_eq!(is('e')(&mut input), Ok('e'));
assert_eq!(is('l')(&mut input), Ok('l'));
assert!(end_of_input()(&mut input).is_ok());
}
#[test]
fn non_consuming_fail_does_not_consume() {
let mut input = Input::new_from_chars("jello".chars(), None);
let parser = is('h');
let output = attempt(&parser)(&mut input);
let mismatch = Mismatch::new('h', 'j');
assert_eq!(
output,
Err(Error::UnexpectedToken(
None,
Position::new(1, 1),
Some(mismatch)
))
);
assert_eq!(any()(&mut input), Ok('j'));
}
#[test]
fn consuming_fail_does_not_consume() {
let mut input = Input::new_from_chars("hello".chars(), None);
let consuming_parser = |input: &mut Input<_>| {
let o1 = is('h')(input)?; let o2 = is('x')(input)?; Ok((o1, o2))
};
let output = attempt(&consuming_parser)(&mut input);
let mismatch = Mismatch::new('x', 'e');
assert_eq!(
output,
Err(Error::UnexpectedToken(
None,
Position::new(1, 2),
Some(mismatch)
))
);
assert_eq!(any()(&mut input), Ok('h'));
assert_eq!(any()(&mut input), Ok('e'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('o'));
assert!(end_of_input()(&mut input).is_ok());
}
#[test]
fn attempt_twice() {
let mut input = Input::new_from_chars("hello".chars(), None);
let parser = is('h');
let first = attempt(&parser)(&mut input);
assert_eq!(first, Ok('h'));
let second = attempt(&parser)(&mut input);
let mismatch = Mismatch::new('h', 'e');
assert_eq!(
second,
Err(Error::UnexpectedToken(
None,
Position::new(1, 2),
Some(mismatch)
))
);
assert_eq!(any()(&mut input), Ok('e'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('o'));
assert!(end_of_input()(&mut input).is_ok());
}
#[test]
fn either_with_attempt_succeeds_consuming() {
let mut input = Input::new_from_chars("hello".chars(), None);
let parser_h = is('h');
let parser_e = is('e');
let parser_attempt_h = attempt(&parser_h);
let parser = either(&parser_attempt_h, &parser_e);
let output = parser(&mut input);
assert_eq!(output, Ok('h'));
assert_eq!(any()(&mut input), Ok('e'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('o'));
assert!(end_of_input()(&mut input).is_ok());
}
#[test]
fn either_with_attempt_fails_not_consuming() {
let mut input = Input::new_from_chars("hello".chars(), None);
let parser_e = is('e');
let parser_l = is('l');
let parser_attempt_e = attempt(&parser_e);
let parser = either(&parser_attempt_e, &parser_l);
let output = parser(&mut input);
let mismatch = Mismatch::new('l', 'h');
assert_eq!(
output,
Err(Error::UnexpectedToken(
None,
Position::new(1, 1),
Some(mismatch)
))
);
assert_eq!(any()(&mut input), Ok('h'));
assert_eq!(any()(&mut input), Ok('e'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('o'));
assert!(end_of_input()(&mut input).is_ok());
}
#[test]
fn either_with_attempt_on_consuming_parser_succeeds_consuming() {
let mut input = Input::new_from_chars("hello".chars(), None);
let parser_h = is('h');
let parser_e = is('e');
let parser_l = is('l');
let parser_hl = |input: &mut Input<_>| {
let o1 = parser_h(input)?;
let o2 = parser_l(input)?;
Ok((o1, o2))
};
let parser_he = |input: &mut Input<_>| {
let o1 = parser_h(input)?;
let o2 = parser_e(input)?;
Ok((o1, o2))
};
let parser_attempt_hl = attempt(&parser_hl);
let parser = either(&parser_attempt_hl, &parser_he);
let output = parser(&mut input);
assert_eq!(output, Ok(('h', 'e')));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('o'));
assert!(end_of_input()(&mut input).is_ok());
}
#[test]
fn either_without_attempt_on_consuming_parser_fails_consuming() {
let mut input = Input::new_from_chars("hello".chars(), None);
let parser_h = is('h');
let parser_e = is('e');
let parser_l = is('l');
let parser_hl = |input: &mut Input<_>| {
let o1 = parser_h(input)?;
let o2 = parser_l(input)?;
Ok((o1, o2))
};
let parser_el = |input: &mut Input<_>| {
let o1 = parser_e(input)?;
let o2 = parser_l(input)?;
Ok((o1, o2))
};
let parser = either(&parser_hl, &parser_el);
let output = parser(&mut input);
let mismatch = Mismatch::new('l', 'e');
assert_eq!(
output,
Err(Error::UnexpectedToken(
None,
Position::new(1, 2),
Some(mismatch)
))
);
assert_eq!(any()(&mut input), Ok('e'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('l'));
assert_eq!(any()(&mut input), Ok('o'));
assert!(end_of_input()(&mut input).is_ok());
}
}