| use combine::{ |
| parser::{ |
| char::{digit, letter}, |
| choice::choice, |
| combinator::not_followed_by, |
| range::range, |
| token::{any, eof, token, Token}, |
| }, |
| Parser, |
| }; |
| |
| #[test] |
| fn choice_empty() { |
| let mut parser = choice::<_, &mut [Token<&str>]>(&mut []); |
| let result_err = parser.parse("a"); |
| assert!(result_err.is_err()); |
| } |
| |
| #[test] |
| fn tuple() { |
| let mut parser = (digit(), token(','), digit(), token(','), letter()); |
| assert_eq!(parser.parse("1,2,z"), Ok((('1', ',', '2', ',', 'z'), ""))); |
| } |
| |
| #[test] |
| fn issue_99() { |
| let result = any().map(|_| ()).or(eof()).parse(""); |
| assert!(result.is_ok(), "{:?}", result); |
| } |
| |
| #[test] |
| fn not_followed_by_does_not_consume_any_input() { |
| let mut parser = not_followed_by(range("a")).map(|_| "").or(range("a")); |
| |
| assert_eq!(parser.parse("a"), Ok(("a", ""))); |
| |
| let mut parser = range("a").skip(not_followed_by(range("aa"))); |
| |
| assert_eq!(parser.parse("aa"), Ok(("a", "a"))); |
| assert!(parser.parse("aaa").is_err()); |
| } |
| |
| #[cfg(feature = "std")] |
| mod tests_std { |
| use super::*; |
| |
| use combine::easy::{Error, Errors}; |
| use combine::parser::byte::alpha_num; |
| use combine::parser::byte::bytes; |
| use combine::parser::byte::bytes_cmp; |
| use combine::parser::byte::num::be_u32; |
| use combine::parser::char::char; |
| use combine::parser::char::{string, string_cmp}; |
| use combine::parser::combinator::no_partial; |
| use combine::parser::range; |
| use combine::parser::repeat::{skip_until, take_until}; |
| use combine::stream::position; |
| use combine::stream::position::SourcePosition; |
| use combine::{ |
| attempt, count, count_min_max, easy, many, optional, position, sep_by, sep_end_by1, |
| unexpected, value, EasyParser, |
| }; |
| |
| #[derive(Clone, PartialEq, Debug)] |
| struct CloneOnly { |
| s: String, |
| } |
| |
| #[test] |
| fn token_clone_but_not_copy() { |
| // Verify we can use token() with a StreamSlice with an token type that is Clone but not |
| // Copy. |
| let input = &[ |
| CloneOnly { s: "x".to_string() }, |
| CloneOnly { s: "y".to_string() }, |
| ][..]; |
| let result = token(CloneOnly { s: "x".to_string() }).easy_parse(input); |
| assert_eq!( |
| result, |
| Ok(( |
| CloneOnly { s: "x".to_string() }, |
| &[CloneOnly { s: "y".to_string() }][..] |
| )) |
| ); |
| } |
| |
| #[test] |
| fn sep_by_committed_error() { |
| type TwoLettersList = Vec<(char, char)>; |
| |
| let mut parser2 = sep_by((letter(), letter()), token(',')); |
| let result_err: Result<(TwoLettersList, &str), easy::ParseError<&str>> = |
| parser2.easy_parse("a,bc"); |
| assert!(result_err.is_err()); |
| } |
| |
| /// The expected combinator should retain only errors that are not `Expected` |
| #[test] |
| fn expected_retain_errors() { |
| let mut parser = digit() |
| .message("message") |
| .expected("N/A") |
| .expected("my expected digit"); |
| assert_eq!( |
| parser.easy_parse(position::Stream::new("a")), |
| Err(Errors { |
| position: SourcePosition::default(), |
| errors: vec![ |
| Error::Unexpected('a'.into()), |
| Error::Message("message".into()), |
| Error::Expected("my expected digit".into()), |
| ], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn tuple_parse_error() { |
| let mut parser = (digit(), digit()); |
| let result = parser.easy_parse(position::Stream::new("a")); |
| assert_eq!( |
| result, |
| Err(Errors { |
| position: SourcePosition::default(), |
| errors: vec![ |
| Error::Unexpected('a'.into()), |
| Error::Expected("digit".into()), |
| ], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn message_tests() { |
| // Ensure message adds to both committed and empty errors, interacting with parse_lazy and |
| // parse_stream correctly on either side |
| let input = "hi"; |
| |
| let mut ok = char('h').message("not expected"); |
| let mut empty0 = char('o').message("expected message"); |
| let mut empty1 = char('o').message("expected message").map(|x| x); |
| let mut empty2 = char('o').map(|x| x).message("expected message"); |
| let mut committed0 = char('h').with(char('o')).message("expected message"); |
| let mut committed1 = char('h') |
| .with(char('o')) |
| .message("expected message") |
| .map(|x| x); |
| let mut committed2 = char('h') |
| .with(char('o')) |
| .map(|x| x) |
| .message("expected message"); |
| |
| assert!(ok.easy_parse(position::Stream::new(input)).is_ok()); |
| |
| let empty_expected = Err(Errors { |
| position: SourcePosition { line: 1, column: 1 }, |
| errors: vec![ |
| Error::Unexpected('h'.into()), |
| Error::Expected('o'.into()), |
| Error::Message("expected message".into()), |
| ], |
| }); |
| |
| let committed_expected = Err(Errors { |
| position: SourcePosition { line: 1, column: 2 }, |
| errors: vec![ |
| Error::Unexpected('i'.into()), |
| Error::Expected('o'.into()), |
| Error::Message("expected message".into()), |
| ], |
| }); |
| |
| assert_eq!( |
| empty0.easy_parse(position::Stream::new(input)), |
| empty_expected |
| ); |
| assert_eq!( |
| empty1.easy_parse(position::Stream::new(input)), |
| empty_expected |
| ); |
| assert_eq!( |
| empty2.easy_parse(position::Stream::new(input)), |
| empty_expected |
| ); |
| |
| assert_eq!( |
| committed0.easy_parse(position::Stream::new(input)), |
| committed_expected |
| ); |
| assert_eq!( |
| committed1.easy_parse(position::Stream::new(input)), |
| committed_expected |
| ); |
| assert_eq!( |
| committed2.easy_parse(position::Stream::new(input)), |
| committed_expected |
| ); |
| } |
| |
| #[test] |
| fn expected_tests() { |
| // Ensure `expected` replaces only empty errors, interacting with parse_lazy and |
| // parse_stream correctly on either side |
| let input = "hi"; |
| |
| let mut ok = char('h').expected("not expected"); |
| let mut empty0 = char('o').expected("expected message"); |
| let mut empty1 = char('o').expected("expected message").map(|x| x); |
| let mut empty2 = char('o').map(|x| x).expected("expected message"); |
| let mut committed0 = char('h').with(char('o')).expected("expected message"); |
| let mut committed1 = char('h') |
| .with(char('o')) |
| .expected("expected message") |
| .map(|x| x); |
| let mut committed2 = char('h') |
| .with(char('o')) |
| .map(|x| x) |
| .expected("expected message"); |
| |
| assert!(ok.easy_parse(position::Stream::new(input)).is_ok()); |
| |
| let empty_expected = Err(Errors { |
| position: SourcePosition { line: 1, column: 1 }, |
| errors: vec![ |
| Error::Unexpected('h'.into()), |
| Error::Expected("expected message".into()), |
| ], |
| }); |
| |
| let committed_expected = Err(Errors { |
| position: SourcePosition { line: 1, column: 2 }, |
| errors: vec![Error::Unexpected('i'.into()), Error::Expected('o'.into())], |
| }); |
| |
| assert_eq!( |
| empty0.easy_parse(position::Stream::new(input)), |
| empty_expected |
| ); |
| assert_eq!( |
| empty1.easy_parse(position::Stream::new(input)), |
| empty_expected |
| ); |
| assert_eq!( |
| empty2.easy_parse(position::Stream::new(input)), |
| empty_expected |
| ); |
| |
| assert_eq!( |
| committed0.easy_parse(position::Stream::new(input)), |
| committed_expected |
| ); |
| assert_eq!( |
| committed1.easy_parse(position::Stream::new(input)), |
| committed_expected |
| ); |
| assert_eq!( |
| committed2.easy_parse(position::Stream::new(input)), |
| committed_expected |
| ); |
| } |
| |
| #[test] |
| fn try_tests() { |
| // Ensure attempt adds error messages exactly once |
| assert_eq!( |
| attempt(unexpected("test")).easy_parse(position::Stream::new("hi")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 1 }, |
| errors: vec![ |
| Error::Unexpected('h'.into()), |
| Error::Unexpected("test".into()), |
| ], |
| }) |
| ); |
| assert_eq!( |
| attempt(char('h').with(unexpected("test"))).easy_parse(position::Stream::new("hi")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 2 }, |
| errors: vec![ |
| Error::Unexpected('i'.into()), |
| Error::Unexpected("test".into()), |
| ], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn sequence_error() { |
| let mut parser = (char('a'), char('b'), char('c')); |
| |
| assert_eq!( |
| parser.easy_parse(position::Stream::new("c")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 1 }, |
| errors: vec![Error::Unexpected('c'.into()), Error::Expected('a'.into())], |
| }) |
| ); |
| |
| assert_eq!( |
| parser.easy_parse(position::Stream::new("ac")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 2 }, |
| errors: vec![Error::Unexpected('c'.into()), Error::Expected('b'.into())], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn optional_empty_ok_then_error() { |
| let mut parser = (optional(char('a')), char('b')); |
| |
| assert_eq!( |
| parser.easy_parse(position::Stream::new("c")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 1 }, |
| errors: vec![ |
| Error::Unexpected('c'.into()), |
| Error::Expected('a'.into()), |
| Error::Expected('b'.into()), |
| ], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn nested_optional_empty_ok_then_error() { |
| let mut parser = ((optional(char('a')), char('b')), char('c')); |
| |
| assert_eq!( |
| parser.easy_parse(position::Stream::new("c")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 1 }, |
| errors: vec![ |
| Error::Unexpected('c'.into()), |
| Error::Expected('a'.into()), |
| Error::Expected('b'.into()), |
| ], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn committed_then_optional_empty_ok_then_error() { |
| let mut parser = (char('b'), optional(char('a')), char('b')); |
| |
| assert_eq!( |
| parser.easy_parse(position::Stream::new("bc")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 2 }, |
| errors: vec![ |
| Error::Unexpected('c'.into()), |
| Error::Expected('a'.into()), |
| Error::Expected('b'.into()), |
| ], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn sequence_in_choice_parser_empty_err() { |
| let mut parser = choice(( |
| (optional(char('a')), char('1')), |
| (optional(char('b')), char('2')).skip(char('d')), |
| )); |
| |
| assert_eq!( |
| parser.easy_parse(position::Stream::new("c")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 1 }, |
| errors: vec![ |
| Error::Expected('a'.into()), |
| Error::Expected('1'.into()), |
| Error::Expected('b'.into()), |
| Error::Expected('2'.into()), |
| Error::Unexpected('c'.into()), |
| ], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn sequence_in_choice_array_parser_empty_err() { |
| let mut parser = choice([ |
| (optional(char('a')), char('1')), |
| (optional(char('b')), char('2')), |
| ]); |
| |
| assert_eq!( |
| parser.easy_parse(position::Stream::new("c")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 1 }, |
| errors: vec![ |
| Error::Expected('a'.into()), |
| Error::Expected('1'.into()), |
| Error::Expected('b'.into()), |
| Error::Expected('2'.into()), |
| Error::Unexpected('c'.into()), |
| ], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn sequence_in_choice_array_parser_empty_err_where_first_parser_delay_errors() { |
| let mut p1 = char('1'); |
| let mut p2 = no_partial((optional(char('b')), char('2')).map(|t| t.1)); |
| let mut parser = |
| choice::<_, [&mut dyn Parser<_, Output = _, PartialState = _>; 2]>([&mut p1, &mut p2]); |
| |
| assert_eq!( |
| parser.easy_parse(position::Stream::new("c")), |
| Err(Errors { |
| position: SourcePosition { line: 1, column: 1 }, |
| errors: vec![ |
| Error::Expected('1'.into()), |
| Error::Expected('b'.into()), |
| Error::Expected('2'.into()), |
| Error::Unexpected('c'.into()), |
| ], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn sep_end_by1_dont_eat_separator_twice() { |
| let mut parser = sep_end_by1(digit(), token(';')); |
| assert_eq!(parser.parse("1;;"), Ok((vec!['1'], ";"))); |
| } |
| |
| #[test] |
| fn count_min_max_empty_error() { |
| assert_eq!( |
| count_min_max(1, 1, char('a')).or(value(vec![])).parse("b"), |
| Ok((vec![], "b")) |
| ); |
| } |
| |
| #[test] |
| fn sequence_parser_resets_partial_state_issue_168() { |
| assert_eq!( |
| take_until::<String, _, _>(attempt((char('a'), char('b')))).parse("aaab"), |
| Ok((String::from("aa"), "ab")) |
| ); |
| } |
| |
| #[test] |
| fn parser_macro_must_impl_parse_mode_issue_168() { |
| assert_eq!( |
| skip_until(attempt((char('a'), char('b')))).parse("aaab"), |
| Ok(((), "ab")) |
| ); |
| } |
| |
| #[test] |
| fn recognize_parser_issue_168() { |
| assert_eq!( |
| range::recognize(skip_until(attempt((char('a'), char('b'))))).parse("aaab"), |
| Ok(("aa", "ab")) |
| ); |
| } |
| |
| #[test] |
| fn sequence_in_optional_report_delayed_error() { |
| assert_eq!( |
| optional(position().with(char('a'))) |
| .skip(char('}')) |
| .easy_parse("b") |
| .map_err(|e| e.errors), |
| Err(vec![ |
| Error::Unexpected('b'.into()), |
| Error::Expected('a'.into()), |
| Error::Expected('}'.into()), |
| ]), |
| ); |
| } |
| |
| #[test] |
| fn sequence_in_optional_nested_report_delayed_error() { |
| assert_eq!( |
| optional(position().with(char('a'))) |
| .skip(optional(position().with(char('c')))) |
| .skip(char('}')) |
| .easy_parse("b") |
| .map_err(|e| e.errors), |
| Err(vec![ |
| Error::Unexpected('b'.into()), |
| Error::Expected('a'.into()), |
| Error::Expected('c'.into()), |
| Error::Expected('}'.into()), |
| ]), |
| ); |
| } |
| |
| #[test] |
| fn sequence_in_optional_nested_2_report_delayed_error() { |
| assert_eq!( |
| ( |
| char('{'), |
| optional(position().with(char('a'))) |
| .skip(optional(position().with(char('c')))) |
| .skip(char('}')) |
| ) |
| .easy_parse("{b") |
| .map_err(|e| e.errors), |
| Err(vec![ |
| Error::Unexpected('b'.into()), |
| Error::Expected('a'.into()), |
| Error::Expected('c'.into()), |
| Error::Expected('}'.into()), |
| ]), |
| ); |
| } |
| |
| macro_rules! sequence_many_test { |
| ($many:expr, $seq:expr) => { |
| let mut parser = $seq($many(position().with(char('a'))), char('}')); |
| let expected_error = Err(vec![ |
| Error::Unexpected('b'.into()), |
| Error::Expected('a'.into()), |
| Error::Expected('}'.into()), |
| ]); |
| assert_eq!( |
| parser.easy_parse("ab").map_err(|e| e.errors), |
| expected_error, |
| ); |
| }; |
| } |
| |
| #[test] |
| fn sequence_in_many_report_delayed_error() { |
| use combine::parser::{repeat, sequence}; |
| |
| sequence_many_test!(repeat::many::<Vec<_>, _, _>, sequence::skip); |
| sequence_many_test!(repeat::many1::<Vec<_>, _, _>, sequence::skip); |
| sequence_many_test!(repeat::many::<Vec<_>, _, _>, sequence::with); |
| sequence_many_test!(repeat::many1::<Vec<_>, _, _>, sequence::with); |
| sequence_many_test!(repeat::many::<Vec<_>, _, _>, |l, x| sequence::between( |
| l, |
| char('|'), |
| x, |
| )); |
| sequence_many_test!(repeat::many1::<Vec<_>, _, _>, |l, x| sequence::between( |
| l, |
| char('|'), |
| x, |
| )); |
| } |
| |
| macro_rules! sequence_sep_by_test { |
| ($many:expr, $seq:expr) => { |
| let mut parser = $seq($many(position().with(char('a')), char(',')), char('}')); |
| let expected_error = Err(vec![ |
| Error::Unexpected('b'.into()), |
| Error::Expected(','.into()), |
| Error::Expected('}'.into()), |
| ]); |
| assert_eq!( |
| parser.easy_parse("a,ab").map_err(|e| e.errors), |
| expected_error, |
| ); |
| }; |
| } |
| |
| #[test] |
| fn sequence_in_sep_by_report_delayed_error() { |
| use combine::parser::{repeat, sequence}; |
| |
| sequence_sep_by_test!(repeat::sep_by::<Vec<_>, _, _, _>, sequence::skip); |
| sequence_sep_by_test!(repeat::sep_by1::<Vec<_>, _, _, _>, sequence::skip); |
| sequence_sep_by_test!(repeat::sep_by::<Vec<_>, _, _, _>, sequence::with); |
| sequence_sep_by_test!(repeat::sep_by1::<Vec<_>, _, _, _>, sequence::with); |
| } |
| |
| #[test] |
| fn choice_compose_on_error() { |
| let ident = |s| attempt(string(s)); |
| let mut parser = choice((ident("aa").skip(string(";")), choice((ident("cc"),)))); |
| |
| assert_eq!( |
| parser.easy_parse("c").map_err(|err| err.errors), |
| Err(vec![ |
| Error::Unexpected('c'.into()), |
| Error::Expected("aa".into()), |
| Error::Unexpected("end of input".into()), |
| Error::Expected("cc".into()), |
| ]), |
| ); |
| } |
| |
| #[test] |
| fn choice_compose_issue_175() { |
| let ident = |s| attempt(string(s)); |
| let mut parser = many::<Vec<_>, _, _>(position().and(choice(( |
| ident("aa").skip(string(";")), |
| choice((ident("bb"), ident("cc"))), |
| )))) |
| .skip(string(".")); |
| |
| assert_eq!( |
| parser.easy_parse("c").map_err(|err| err.errors), |
| Err(vec![ |
| Error::Unexpected('c'.into()), |
| Error::Expected("aa".into()), |
| Error::Expected("bb".into()), |
| Error::Expected("cc".into()), |
| ]), |
| ); |
| } |
| |
| #[test] |
| fn test() { |
| let mut parser = (digit(), letter()); |
| |
| assert_eq!( |
| parser.easy_parse("11").map_err(|err| err.errors), |
| Err(vec![ |
| Error::Unexpected('1'.into()), |
| Error::Expected("letter".into()), |
| ]), |
| ); |
| } |
| |
| #[test] |
| fn lifetime_inference() { |
| fn _string(source: &str) { |
| range::take(1).or(string("a")).parse(source).ok(); |
| range::take(1) |
| .or(string_cmp("a", |x, y| x == y)) |
| .parse(source) |
| .ok(); |
| let _: &'static str = string("a").parse(source).unwrap().0; |
| let _: &'static str = string_cmp("a", |x, y| x == y).parse(source).unwrap().0; |
| } |
| fn _bytes(source: &[u8]) { |
| range::take(1).or(bytes(&[0u8])).parse(source).ok(); |
| range::take(1) |
| .or(bytes_cmp(&[0u8], |x, y| x == y)) |
| .parse(source) |
| .ok(); |
| let _: &'static [u8] = bytes(&[0u8]).parse(source).unwrap().0; |
| let _: &'static [u8] = bytes_cmp(&[0u8], |x, y| x == y).parse(source).unwrap().0; |
| } |
| } |
| |
| #[test] |
| fn test_nested_count_overflow() { |
| let key = || count::<Vec<_>, _, _>(64, alpha_num()); |
| let value_bytes = |
| || be_u32().then_partial(|&mut size| count::<Vec<_>, _, _>(size as usize, any())); |
| let value_messages = |
| (be_u32(), be_u32()).then_partial(|&mut (_body_size, message_count)| { |
| count::<Vec<_>, _, _>(message_count as usize, value_bytes()) |
| }); |
| let put = (bytes(b"PUT"), key()) |
| .map(|(_, key)| key) |
| .and(value_messages); |
| |
| let parser = || put.map(|(_, messages)| messages); |
| |
| let command = &b"PUTkey\x00\x00\x00\x12\x00\x00\x00\x02\x00\x00\x00\x04\xDE\xAD\xBE\xEF\x00\x00\x00\x02\xBE\xEF"[..]; |
| let result = parser().parse(command).unwrap(); |
| assert_eq!(2, result.0.len()); |
| } |
| |
| #[test] |
| fn not_followed_by_empty_error_issue_220() { |
| let mut parser = string("let").skip(not_followed_by(eof().map(|_| "EOF"))); |
| assert_eq!( |
| parser.easy_parse("let").map_err(|err| err.errors), |
| Err(vec![]), |
| ); |
| } |
| } |