use crate::{
cache::ParsingCache,
parser::{Parsable, Parser, Source},
result::{Error, ParseResult},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Either<A, B> {
first: A,
second: B,
}
impl<A, B> Either<A, B> {
pub fn new(first: A, second: B) -> Self {
Self { first, second }
}
}
#[macro_export]
macro_rules! oneof {
($parser:expr) => {
$parser
};
($first:expr, $($rest:expr),+ $(,)?) => {
$crate::either::Either::new($first, oneof!($($rest),+))
};
}
impl<A, B, Ctx> Parser<Ctx> for Either<A, B>
where
A: Parser<Ctx>,
B: Parser<Ctx>,
{
type Output = (Option<A::Output>, Option<B::Output>);
fn id(&self) -> u64 {
use std::any::TypeId;
use std::hash::{DefaultHasher, Hash, Hasher};
let mut hasher = DefaultHasher::new();
TypeId::of::<Self>().hash(&mut hasher);
self.first.id().hash(&mut hasher);
self.second.id().hash(&mut hasher);
hasher.finish()
}
fn read<S>(
&self,
source: &mut Source<S>,
cache: &mut impl ParsingCache,
context: &mut Ctx,
) -> ParseResult<Self::Output>
where
S: Parsable,
{
match self.first.parse(source, cache, context) {
Ok(result) => Ok((Some(result), None)),
Err(Error::NoMatch) => {
match self.second.parse(source, cache, context) {
Ok(result) => Ok((None, Some(result))),
Err(err) => Err(err),
}
}
Err(err) => Err(err),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{literal::Literal, parser::parse};
use std::io::Cursor;
#[test]
fn test_id_implementation_different_either_parsers() {
let either1 = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
let either2 = Either::new(Literal::from_str("foo"), Literal::from_str("bar"));
assert_ne!(
<Either<_, _> as crate::parser::Parser<()>>::id(&either1),
<Either<_, _> as crate::parser::Parser<()>>::id(&either2),
"Different Either instances should have different IDs to avoid cache collisions"
);
}
#[test]
fn test_id_implementation_same_either_parsers() {
let either1 = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
let either2 = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
assert_eq!(
<Either<_, _> as crate::parser::Parser<()>>::id(&either1),
<Either<_, _> as crate::parser::Parser<()>>::id(&either2),
"Identical Either instances should have the same ID for cache efficiency"
);
}
#[test]
fn test_id_implementation_either_different_order() {
let either1 = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
let either2 = Either::new(Literal::from_str("world"), Literal::from_str("hello"));
assert_ne!(
<Either<_, _> as crate::parser::Parser<()>>::id(&either1),
<Either<_, _> as crate::parser::Parser<()>>::id(&either2),
"Either instances with different order should have different IDs"
);
}
#[test]
fn test_either_first_matches() {
let parser = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
let mut input = Cursor::new(b"hello");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, Some(b"hello".as_slice().into()));
assert_eq!(result.1, None);
}
#[test]
fn test_either_second_matches() {
let parser = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
let mut input = Cursor::new(b"world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, None);
assert_eq!(result.1, Some(b"world".as_slice().into()));
}
#[test]
fn test_either_neither_matches() {
let parser = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
let mut input = Cursor::new(b"foo");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source);
assert!(matches!(result, Err(Error::NoMatch)));
}
#[test]
fn test_either_empty_input() {
let parser = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
let mut input = Cursor::new(b"");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source);
assert!(matches!(result, Err(Error::NoMatch)));
}
#[test]
fn test_either_first_parser_priority() {
let parser = Either::new(Literal::from_str("he"), Literal::from_str("hello"));
let mut input = Cursor::new(b"hello");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, Some(b"he".as_slice().into()));
assert_eq!(result.1, None);
}
#[test]
fn test_either_different_types() {
use crate::class::Class;
use crate::parser::parse;
let parser = Either::new(Literal::from_str("prefix"), Class::digits());
let mut input1 = Cursor::new(b"prefix");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser, &mut source1).unwrap();
assert_eq!(result1.0, Some(b"prefix".as_slice().into()));
assert_eq!(result1.1, None);
let parser2 = Either::new(Literal::from_str("prefix"), Class::digits());
let mut input2 = Cursor::new(b"123");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2).unwrap();
assert_eq!(result2.0, None);
assert_eq!(result2.1, Some(b"123".to_vec()));
}
#[test]
fn test_either_position_tracking() {
use crate::parser::parse;
let parser = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
let mut input = Cursor::new(b"worldXYZ");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, None);
assert_eq!(result.1, Some(b"world".as_slice().into()));
let next_byte = source.peek1().unwrap();
assert_eq!(next_byte, b'X');
}
#[test]
fn test_either_with_complex_parsers() {
use crate::{parser::parse, repeat::Repeat, sequence::Sequence};
let repeat_a = Repeat::new(Literal::from_str("a"));
let hello_world = Sequence::new(Literal::from_str("hello"), Literal::from_str(" world"));
let parser = Either::new(repeat_a, hello_world);
let mut input1 = Cursor::new(b"aaaXYZ");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser, &mut source1).unwrap();
if let Some(repeated_results) = result1.0 {
assert_eq!(repeated_results.len(), 3);
assert_eq!(result1.1, None);
} else {
assert!(result1.1.is_some());
}
let repeat_a2 = Repeat::new(Literal::from_str("a"));
let hello_world2 = Sequence::new(Literal::from_str("hello"), Literal::from_str(" world"));
let parser2 = Either::new(repeat_a2, hello_world2);
let mut input2 = Cursor::new(b"hello worldXYZ");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2).unwrap();
if let Some(repeat_result) = result2.0 {
assert_eq!(repeat_result.len(), 0);
} else if let Some(sequence_result) = result2.1 {
let (hello, world) = sequence_result;
assert_eq!(hello, b"hello".as_slice().into());
assert_eq!(world, b" world".as_slice().into());
} else {
panic!("Either should match one alternative");
}
}
#[test]
fn test_either_nested() {
use crate::parser::parse;
let inner_either = Either::new(Literal::from_str("a"), Literal::from_str("b"));
let outer_either = Either::new(inner_either, Literal::from_str("c"));
let mut input1 = Cursor::new(b"a");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(outer_either, &mut source1).unwrap();
assert!(result1.0.is_some());
let inner_result = result1.0.unwrap();
assert_eq!(inner_result.0, Some(b"a".as_slice().into()));
assert_eq!(inner_result.1, None);
assert_eq!(result1.1, None);
let inner_either2 = Either::new(Literal::from_str("a"), Literal::from_str("b"));
let outer_either2 = Either::new(inner_either2, Literal::from_str("c"));
let mut input2 = Cursor::new(b"b");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(outer_either2, &mut source2).unwrap();
assert!(result2.0.is_some());
let inner_result2 = result2.0.unwrap();
assert_eq!(inner_result2.0, None);
assert_eq!(inner_result2.1, Some(b"b".as_slice().into()));
assert_eq!(result2.1, None);
let inner_either3 = Either::new(Literal::from_str("a"), Literal::from_str("b"));
let outer_either3 = Either::new(inner_either3, Literal::from_str("c"));
let mut input3 = Cursor::new(b"c");
let mut source3 = crate::parser::Source::new(&mut input3);
let result3 = parse(outer_either3, &mut source3).unwrap();
assert_eq!(result3.0, None);
assert_eq!(result3.1, Some(b"c".as_slice().into()));
}
#[test]
fn test_either_chained_alternatives() {
use crate::parser::parse;
let test_cases = vec![
("alpha", "first"),
("beta", "second"),
("gamma", "third"),
("delta", "fourth"),
];
for (input_str, description) in test_cases {
let choice1 = Either::new(Literal::from_str("alpha"), Literal::from_str("beta"));
let choice2 = Either::new(choice1, Literal::from_str("gamma"));
let choice3 = Either::new(choice2, Literal::from_str("delta"));
let mut input = Cursor::new(input_str.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(choice3, &mut source);
assert!(result.is_ok(), "Failed to parse {description} alternative",);
}
}
#[test]
fn test_either_with_bounds() {
use crate::{class::Class, parser::parse};
let two_digits = Class::with_bounds(b"0123456789", 2, 2);
let three_letters = Class::with_bounds(b"abcdefghijklmnopqrstuvwxyz", 3, 3);
let parser = Either::new(two_digits, three_letters);
let mut input1 = Cursor::new(b"12X");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser, &mut source1).unwrap();
assert_eq!(result1.0, Some(b"12".to_vec()));
assert_eq!(result1.1, None);
let two_digits2 = Class::with_bounds(b"0123456789", 2, 2);
let three_letters2 = Class::with_bounds(b"abcdefghijklmnopqrstuvwxyz", 3, 3);
let parser2 = Either::new(two_digits2, three_letters2);
let mut input2 = Cursor::new(b"abc1");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2).unwrap();
assert_eq!(result2.0, None);
assert_eq!(result2.1, Some(b"abc".to_vec()));
}
#[test]
fn test_either_failure_modes() {
use crate::parser::parse;
let parser = Either::new(Literal::from_str("hello"), Literal::from_str("world"));
let mut input = Cursor::new(b"goodbye");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source);
assert!(matches!(result, Err(Error::NoMatch)));
let first_byte = source.peek1().unwrap();
assert_eq!(first_byte, b'g');
}
#[test]
fn test_either_backtracking_behavior() {
use crate::{class::Class, parser::parse};
let digits_then_alpha = crate::sequence::Sequence::new(Class::digits(), Class::alpha());
let just_alpha = Class::alpha();
let parser = Either::new(digits_then_alpha, just_alpha);
let mut input = Cursor::new(b"abcdef");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, None);
assert_eq!(result.1, Some(b"abcdef".to_vec()));
}
#[test]
fn test_either_cache_interaction() {
use crate::parser::parse;
let parser1 = Either::new(Literal::from_str("test"), Literal::from_str("demo"));
let parser2 = Either::new(Literal::from_str("test"), Literal::from_str("demo"));
let mut input1 = Cursor::new(b"testXYZ");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser1, &mut source1).unwrap();
assert_eq!(result1.0, Some(b"test".as_slice().into()));
let mut input2 = Cursor::new(b"demoXYZ");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2).unwrap();
assert!(result2.0.is_some() || result2.1.is_some());
}
#[test]
fn test_oneof_macro_single() {
use crate::{oneof, parser::parse};
let parser = oneof![Literal::from_str("hello")];
let mut input = Cursor::new(b"hello");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result, b"hello".as_slice().into());
}
#[test]
fn test_oneof_macro_two() {
use crate::{oneof, parser::parse};
let parser = oneof![Literal::from_str("hello"), Literal::from_str("world")];
let mut input1 = Cursor::new(b"hello");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser, &mut source1).unwrap();
assert_eq!(result1.0, Some(b"hello".as_slice().into()));
assert_eq!(result1.1, None);
let parser2 = oneof![Literal::from_str("hello"), Literal::from_str("world")];
let mut input2 = Cursor::new(b"world");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2).unwrap();
assert_eq!(result2.0, None);
assert_eq!(result2.1, Some(b"world".as_slice().into()));
}
#[test]
fn test_oneof_macro_three() {
use crate::{oneof, parser::parse};
let parser = oneof![
Literal::from_str("alpha"),
Literal::from_str("beta"),
Literal::from_str("gamma")
];
let mut input1 = Cursor::new(b"alpha");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser, &mut source1).unwrap();
assert_eq!(result1.0, Some(b"alpha".as_slice().into()));
assert_eq!(result1.1, None);
let parser2 = oneof![
Literal::from_str("alpha"),
Literal::from_str("beta"),
Literal::from_str("gamma")
];
let mut input2 = Cursor::new(b"beta");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2).unwrap();
assert_eq!(result2.0, None);
assert!(result2.1.is_some());
if let Some(inner_result) = result2.1 {
assert_eq!(inner_result.0, Some(b"beta".as_slice().into()));
assert_eq!(inner_result.1, None);
}
let parser3 = oneof![
Literal::from_str("alpha"),
Literal::from_str("beta"),
Literal::from_str("gamma")
];
let mut input3 = Cursor::new(b"gamma");
let mut source3 = crate::parser::Source::new(&mut input3);
let result3 = parse(parser3, &mut source3).unwrap();
assert_eq!(result3.0, None);
assert!(result3.1.is_some());
if let Some(inner_result) = result3.1 {
assert_eq!(inner_result.0, None);
assert_eq!(inner_result.1, Some(b"gamma".as_slice().into()));
}
}
#[test]
fn test_oneof_macro_four() {
use crate::{oneof, parser::parse};
let test_cases = vec![
("alpha", "first"),
("beta", "second"),
("gamma", "third"),
("delta", "fourth"),
];
for (input_str, _description) in test_cases {
let parser = oneof![
Literal::from_str("alpha"),
Literal::from_str("beta"),
Literal::from_str("gamma"),
Literal::from_str("delta")
];
let mut input = Cursor::new(input_str.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source);
assert!(result.is_ok());
}
}
#[test]
fn test_oneof_macro_with_trailing_comma() {
use crate::{oneof, parser::parse};
let parser = oneof![Literal::from_str("hello"), Literal::from_str("world"),];
let mut input = Cursor::new(b"hello");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, Some(b"hello".as_slice().into()));
assert_eq!(result.1, None);
}
#[test]
fn test_oneof_macro_different_types() {
use crate::{class::Class, oneof, parser::parse};
let parser = oneof![Literal::from_str("prefix"), Class::digits(), Class::alpha()];
let mut input1 = Cursor::new(b"prefix");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(parser, &mut source1).unwrap();
assert_eq!(result1.0, Some(b"prefix".as_slice().into()));
assert_eq!(result1.1, None);
let parser2 = oneof![Literal::from_str("prefix"), Class::digits(), Class::alpha()];
let mut input2 = Cursor::new(b"123");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(parser2, &mut source2).unwrap();
assert_eq!(result2.0, None);
assert!(result2.1.is_some());
if let Some(inner_result) = result2.1 {
assert_eq!(inner_result.0, Some(b"123".to_vec()));
assert_eq!(inner_result.1, None);
}
let parser3 = oneof![Literal::from_str("prefix"), Class::digits(), Class::alpha()];
let mut input3 = Cursor::new(b"abc");
let mut source3 = crate::parser::Source::new(&mut input3);
let result3 = parse(parser3, &mut source3).unwrap();
assert_eq!(result3.0, None);
assert!(result3.1.is_some());
if let Some(inner_result) = result3.1 {
assert_eq!(inner_result.0, None);
assert_eq!(inner_result.1, Some(b"abc".to_vec()));
}
}
#[test]
fn test_oneof_macro_failure() {
use crate::{oneof, parser::parse};
let parser = oneof![
Literal::from_str("hello"),
Literal::from_str("world"),
Literal::from_str("test")
];
let mut input = Cursor::new(b"goodbye");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source);
assert!(matches!(result, Err(crate::result::Error::NoMatch)));
}
}