#[allow(clippy::wildcard_imports)]
use crate::base::*;
use crate::base::lang::let_expr;
#[cfg(feature = "alloc")]
use crate::{Parse, Parser};
#[cfg(feature = "alloc")]
use alloc::{format, string::String, vec , vec::Vec};
#[test]
fn a_to_b() {
let parser = {
maybe_space() >> anything()
<< maybe_space()
<< exact(&b'-')
<< exact(&b'>')
<< maybe_space()
& anything() << maybe_space() << end()
};
assert_eq!(parser.parse(b"A -> B"), Ok((&b'A', &b'B')));
assert_eq!(parser.parse(b"A->B"), Ok((&b'A', &b'B')));
assert_eq!(parser.parse(b" A -> B "), Ok((&b'A', &b'B')));
}
#[cfg(feature = "alloc")]
#[test]
fn comma_separated_alphabet() {
let parser = comma_separated(anything(), true);
assert_eq!(parser.partial(b"a"), Ok((vec![&b'a'], &[][..])));
assert_eq!(
parser.partial(b" a, b, c,d,e,f, g , h , i , j , k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z , ").map(|(v, etc)| (v.into_iter().map(|x| char::from(*x)).collect(), etc)),
Ok((('a'..='z').collect::<Vec<_>>(), &*vec![])),
);
}
#[cfg(feature = "alloc")]
#[derive(Debug, PartialEq)]
enum Literal {
Character(u8),
Digit(u8),
}
#[cfg(feature = "alloc")]
fn literal<'input>() -> Parser<'input, impl Parse<'input, Input = u8, Output = Literal>> {
#![allow(clippy::arithmetic_side_effects)]
((exact(&b'\'') >> (lowercase() | uppercase()) << exact(&b'\''))
.pipe(|x| Ok(Literal::Character(*x))))
| (digit().pipe(|x| Ok(Literal::Digit(x))))
}
#[cfg(feature = "alloc")]
#[test]
fn literals() {
let parser = exact(&b'(') >> comma_separated(literal(), true) << exact(&b')');
assert_eq!(
parser.parse(
b"('a', 0, 'b', 1, 'c', 2, 'd', 3, 'e', 4, 'f', 5, 'g', 6, 'h', 7, 'i', 8, 'j', 9,)"
),
Ok(vec![
Literal::Character(b'a'),
Literal::Digit(0),
Literal::Character(b'b'),
Literal::Digit(1),
Literal::Character(b'c'),
Literal::Digit(2),
Literal::Character(b'd'),
Literal::Digit(3),
Literal::Character(b'e'),
Literal::Digit(4),
Literal::Character(b'f'),
Literal::Digit(5),
Literal::Character(b'g'),
Literal::Digit(6),
Literal::Character(b'h'),
Literal::Digit(7),
Literal::Character(b'i'),
Literal::Digit(8),
Literal::Character(b'j'),
Literal::Digit(9),
])
);
}
#[test]
fn optional_zero() {
#![allow(clippy::assertions_on_result_states)]
let parser = optional(exact(&b'0')) >> exact(&b'1') & exact(&b'2') & exact(&b'3');
assert_eq!(parser.parse(b"123"), Ok(((&b'1', &b'2'), &b'3')));
assert_eq!(parser.parse(b"0123"), Ok(((&b'1', &b'2'), &b'3')));
assert!(parser.parse(b"00123").is_err());
}
mod failures {
#![allow(
clippy::let_underscore_untyped,
clippy::wildcard_imports,
clippy::let_underscore_must_use
)]
use super::*;
#[should_panic]
#[test]
fn fail() {
let _ = (anything() << exact(&b'!')).parse_or_panic(b"???");
}
#[should_panic]
#[test]
fn not_everything() {
let _ = (exact(&b'?') >> exact(&b'?')).parse_or_panic(b"???");
}
#[should_panic]
#[test]
fn oob() {
let _ = (anything() & anything() & anything() & anything()).parse_or_panic(b"???");
}
#[should_panic]
#[test]
fn not_expecting_a_newline() {
let _ = (anything() << exact(&b'!')).parse_or_panic(b"?\n?\n?");
}
#[should_panic]
#[test]
fn multiline_fail() {
let _ = (anything() << maybe_space() << exact(&b'!')).parse_or_panic(b"?\n?\n?");
}
#[should_panic]
#[test]
fn multiline_not_everything() {
let _ = (exact(&b'?') << maybe_space() >> exact(&b'?')).parse_or_panic(b"?\n?\n?");
}
#[should_panic]
#[test]
fn multiline_oob() {
let _ = (anything() << maybe_space()
& anything() << maybe_space()
& anything() << maybe_space()
& anything())
.parse_or_panic(b"?\n?\n?");
}
#[cfg(feature = "alloc")]
mod msg {
use super::*;
#[test]
fn fail_msg() {
assert_eq!(
(anything() << exact(&b'!')).parse(b"???"),
Err(String::from(concat!(
"\r\n",
" | \u{1b}[0;1;31mError while parsing:\u{1b}[0m\r\n",
" 1 | ?\u{1b}[0;1;41m?\u{1b}[0m?\r\n",
" | \u{1b}[0;1;31m^ Expected 33 but found 63\u{1b}[0m\r\n",
)))
);
}
#[test]
fn not_everything_msg() {
assert_eq!(
(exact(&b'?') << exact(&b'?')).parse(b"???"),
Err(String::from(concat!(
"\r\n",
" | \u{1b}[0;1;31mError while parsing:\u{1b}[0m\r\n",
" 1 | ??\u{1b}[0;1;41m?\u{1b}[0m\r\n",
" | \u{1b}[0;1;31m^ Unparsed input remains after parsing what should have been everything\u{1b}[0m\r\n",
)))
);
}
#[test]
fn oob_msg() {
assert_eq!(
(anything() & anything() & anything() & anything()).parse(b"???"),
Err(String::from(concat!(
"\r\n",
" | \u{1b}[0;1;31mError while parsing:\u{1b}[0m\r\n",
" 1 | ???\u{1b}[0;1;41m \u{1b}[0m\r\n",
" | \u{1b}[0;1;31m^ Reached end of input but expected an item\u{1b}[0m\r\n",
)))
);
}
#[test]
fn not_expecting_a_newline_msg() {
assert_eq!(
(anything() << exact(&b'!')).parse(b"?\n?\n?"),
Err(String::from(concat!(
"\r\n",
" | \u{1b}[0;1;31mError while parsing:\u{1b}[0m\r\n",
" 1 | ?\u{1b}[0;1;41m \u{1b}[0m\r\n",
" | \u{1b}[0;1;31m^ Expected 33 but found 10\u{1b}[0m\r\n",
" 2 | ?\r\n",
)))
);
}
#[test]
fn multiline_fail_msg() {
assert_eq!(
(anything() << maybe_space() << exact(&b'!')).parse(b"?\n?\n?"),
Err(String::from(concat!(
"\r\n",
" | \u{1b}[0;1;31mError while parsing:\u{1b}[0m\r\n",
" 1 | ?\r\n",
" 2 | \u{1b}[0;1;41m?\u{1b}[0m\r\n",
" | \u{1b}[0;1;31m^ Expected 33 but found 63\u{1b}[0m\r\n",
" 3 | ?\r\n",
)))
);
}
#[test]
fn multiline_not_everything_msg() {
assert_eq!(
(exact(&b'?') << maybe_space() >> exact(&b'?')).parse(b"?\n?\n?"),
Err(String::from(concat!(
"\r\n",
" | \u{1b}[0;1;31mError while parsing:\u{1b}[0m\r\n",
" 1 | ?\r\n",
" 2 | ?\u{1b}[0;1;41m \u{1b}[0m\r\n",
" | \u{1b}[0;1;31m^ Unparsed input remains after parsing what should have been everything\u{1b}[0m\r\n",
" 3 | ?\r\n",
)))
);
}
#[test]
fn multiline_oob_msg() {
assert_eq!(
(anything() << maybe_space()
& anything() << maybe_space()
& anything() << maybe_space()
& anything())
.parse(b"?\n?\n?"),
Err(String::from(concat!(
"\r\n",
" | \u{1b}[0;1;31mError while parsing:\u{1b}[0m\r\n",
" 2 | ?\r\n",
" 3 | ?\u{1b}[0;1;41m \u{1b}[0m\r\n",
" | \u{1b}[0;1;31m^ Reached end of input but expected an item\u{1b}[0m\r\n",
)))
);
}
}
}
#[cfg(feature = "alloc")]
#[test]
fn binops_without_precedence() {
assert_eq!(
precedence::raw_binary_ops(
lowercase(),
::alloc::collections::BTreeSet::from_iter([&b"+"[..], b"-", b"*", b"/"])
)
.partial(b"a + b - c * d / e"),
Ok((
(
&b'a',
vec![
(&b"+"[..], &b'b'),
(b"-", &b'c'),
(b"*", &b'd'),
(b"/", &b'e')
]
),
&[][..]
))
);
}
#[cfg(feature = "alloc")]
proptest::proptest! {
#[test]
fn prop_unsigned_int(i: usize) {
let fmt = format!("{i:}");
let p = unsigned_integer().parse_or_panic(fmt.as_bytes());
assert_eq!(p, i);
}
#[test]
fn prop_signed_int(i: isize) {
let fmt = format!("{i:}");
let p = signed_integer().parse_or_panic(fmt.as_bytes());
assert_eq!(p, i);
}
}
#[cfg(feature = "alloc")]
#[test]
fn parse_huge_ints() {
#![allow(clippy::as_conversions, clippy::assertions_on_result_states)]
const SMALLER: usize = usize::MAX;
const LARGER: u128 = (SMALLER as u128).overflowing_add(1).0;
let fmt_smaller = format!("{SMALLER:}");
let fmt_larger = format!("{LARGER:}");
assert_eq!(
unsigned_integer().parse(fmt_smaller.as_bytes()),
Ok(SMALLER)
);
if core::mem::size_of::<usize>() >= core::mem::size_of::<u128>() {
return; }
assert!(unsigned_integer().parse(fmt_larger.as_bytes()).is_err());
}
#[test]
fn let_exprs() {
#![allow(
clippy::let_underscore_untyped,
clippy::unwrap_used,
let_underscore_drop
)]
let parser = let_expr(digit());
assert_eq!(parser.parse(b"let a = 0"), Ok((&b"a"[..], 0)));
assert_eq!(parser.parse(b"let a=0"), Ok((&b"a"[..], 0)));
assert_eq!(parser.parse(b"let a = 0"), Ok((&b"a"[..], 0)));
let _ = parser.parse(b"leta = 0").unwrap_err();
}