use crate::{
cache::ParsingCache,
parser::{Parsable, Parser, Source},
result::ParseResult,
};
#[macro_export]
macro_rules! seq {
($parser:expr) => {
$crate::sequence::Sequence::new($parser, ())
};
($first:expr, $($rest:expr),+ $(,)?) => {
$crate::sequence::Sequence::new($first, seq!($($rest),+))
};
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Sequence<A, B> {
first: A,
second: B,
}
pub trait Push<P> {
type Output;
fn push(self, parser: P) -> Self::Output;
}
impl<A, B> Sequence<A, B> {
pub fn new(first: A, second: B) -> Self {
Self { first, second }
}
}
impl<A, B> Sequence<A, B> {
pub fn push<P>(self, parser: P) -> Sequence<A, B::Output>
where
B: Push<P>,
{
Sequence::new(self.first, self.second.push(parser))
}
}
impl<P> Push<P> for () {
type Output = Sequence<P, ()>;
fn push(self, parser: P) -> Self::Output {
Sequence::new(parser, ())
}
}
impl<A, B, P> Push<P> for Sequence<A, B>
where
B: Push<P>,
{
type Output = Sequence<A, B::Output>;
fn push(self, parser: P) -> Self::Output {
Sequence::new(self.first, self.second.push(parser))
}
}
impl<A, B, Ctx> Parser<Ctx> for Sequence<A, B>
where
A: Parser<Ctx>,
B: Parser<Ctx>,
{
type Output = (A::Output, 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,
{
let first_result = self.first.parse(source, cache, context)?;
let second_result = self.second.parse(source, cache, context)?;
Ok((first_result, second_result))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{literal::Literal, parser::parse, seq};
use std::io::Cursor;
#[test]
fn test_sequence_both_match() {
let parser = Sequence::new(Literal::from_str("hello"), Literal::from_str("world"));
let mut input = Cursor::new(b"helloworld");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"hello".as_slice().into());
assert_eq!(result.1, b"world".as_slice().into());
}
#[test]
fn test_sequence_first_fails() {
let parser = Sequence::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!(result.is_err());
}
#[test]
fn test_sequence_second_fails() {
let parser = Sequence::new(Literal::from_str("hello"), Literal::from_str("world"));
let mut input = Cursor::new(b"hellogoodbye");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source);
assert!(result.is_err());
}
#[test]
fn test_sequence_empty_input() {
let parser = Sequence::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!(result.is_err());
}
#[test]
fn test_seq_macro_single() {
let parser = seq![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, b"hello".as_slice().into());
}
#[test]
fn test_seq_macro_two() {
let parser = seq![Literal::from_str("hello"), Literal::from_str("world")];
let mut input = Cursor::new(b"helloworld");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"hello".as_slice().into());
assert_eq!(result.1.0, b"world".as_slice().into());
}
#[test]
fn test_seq_macro_three() {
let parser = seq![
Literal::from_str("hello"),
Literal::from_str(" "),
Literal::from_str("world")
];
let mut input = Cursor::new(b"hello world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"hello".as_slice().into());
assert_eq!(result.1.0, b" ".as_slice().into());
assert_eq!(result.1.1.0, b"world".as_slice().into());
}
#[test]
fn test_seq_macro_four() {
let parser = seq![
Literal::from_str("hello"),
Literal::from_str(" "),
Literal::from_str("beautiful"),
Literal::from_str(" world")
];
let mut input = Cursor::new(b"hello beautiful world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"hello".as_slice().into());
assert_eq!(result.1.0, b" ".as_slice().into());
assert_eq!(result.1.1.0, b"beautiful".as_slice().into());
assert_eq!(result.1.1.1.0, b" world".as_slice().into());
}
#[test]
fn test_seq_macro_five() {
let parser = seq![
Literal::from_str("a"),
Literal::from_str("b"),
Literal::from_str("c"),
Literal::from_str("d"),
Literal::from_str("e")
];
let mut input = Cursor::new(b"abcde");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"a".as_slice().into());
assert_eq!(result.1.0, b"b".as_slice().into());
assert_eq!(result.1.1.0, b"c".as_slice().into());
assert_eq!(result.1.1.1.0, b"d".as_slice().into());
assert_eq!(result.1.1.1.1.0, b"e".as_slice().into());
}
#[test]
fn test_seq_macro_with_trailing_comma() {
let parser = seq![
Literal::from_str("hello"),
Literal::from_str(" "),
Literal::from_str("world"),
];
let mut input = Cursor::new(b"hello world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"hello".as_slice().into());
assert_eq!(result.1.0, b" ".as_slice().into());
assert_eq!(result.1.1.0, b"world".as_slice().into());
}
#[test]
fn test_sequence_different_types() {
use crate::class::Class;
let parser = Sequence::new(Literal::from_str("prefix"), Class::digits());
let mut input = Cursor::new(b"prefix123");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"prefix".as_slice().into());
assert_eq!(result.1, b"123".to_vec());
}
#[test]
fn test_seq_macro_different_types() {
use crate::class::Class;
let parser = seq![
Literal::from_str("prefix"),
Class::digits(),
Literal::from_str("suffix")
];
let mut input = Cursor::new(b"prefix123suffix");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"prefix".as_slice().into());
assert_eq!(result.1.0, b"123".to_vec());
assert_eq!(result.1.1.0, b"suffix".as_slice().into());
}
#[test]
fn test_sequence_position_tracking() {
let parser = Sequence::new(Literal::from_str("hello"), Literal::from_str("world"));
let mut input = Cursor::new(b"helloworld123");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"hello".as_slice().into());
assert_eq!(result.1, b"world".as_slice().into());
let remaining = source.peek1().unwrap();
assert_eq!(remaining, b'1');
}
#[test]
fn test_seq_macro_failure() {
let parser = seq![
Literal::from_str("hello"),
Literal::from_str(" "),
Literal::from_str("world")
];
let mut input = Cursor::new(b"hello goodbye");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source);
assert!(result.is_err());
}
#[test]
fn test_push_to_single_element() {
let base = seq![Literal::from_str("hello")];
let extended = base.push(Literal::from_str(" world"));
let mut input = Cursor::new(b"hello world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(extended, &mut source).unwrap();
assert_eq!(result.0, b"hello".as_slice().into());
assert_eq!(result.1.0, b" world".as_slice().into());
}
#[test]
fn test_push_to_two_elements() {
let base = seq![Literal::from_str("hello"), Literal::from_str(" ")];
let extended = base.push(Literal::from_str("world"));
let mut input = Cursor::new(b"hello world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(extended, &mut source).unwrap();
assert_eq!(result.0, b"hello".as_slice().into());
assert_eq!(result.1.0, b" ".as_slice().into());
assert_eq!(result.1.1.0, b"world".as_slice().into());
}
#[test]
fn test_push_to_three_elements() {
let base = seq![
Literal::from_str("hello"),
Literal::from_str(" "),
Literal::from_str("beautiful")
];
let extended = base.push(Literal::from_str(" world"));
let mut input = Cursor::new(b"hello beautiful world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(extended, &mut source).unwrap();
assert_eq!(result.0, b"hello".as_slice().into());
assert_eq!(result.1.0, b" ".as_slice().into());
assert_eq!(result.1.1.0, b"beautiful".as_slice().into());
assert_eq!(result.1.1.1.0, b" world".as_slice().into());
}
#[test]
fn test_push_multiple_times() {
let base = seq![Literal::from_str("a")];
let step1 = base.push(Literal::from_str("b"));
let step2 = step1.push(Literal::from_str("c"));
let final_parser = step2.push(Literal::from_str("d"));
let mut input = Cursor::new(b"abcd");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(final_parser, &mut source).unwrap();
assert_eq!(result.0, b"a".as_slice().into());
assert_eq!(result.1.0, b"b".as_slice().into());
assert_eq!(result.1.1.0, b"c".as_slice().into());
assert_eq!(result.1.1.1.0, b"d".as_slice().into());
}
#[test]
fn test_push_chaining() {
let parser = seq![Literal::from_str("a")]
.push(Literal::from_str("b"))
.push(Literal::from_str("c"))
.push(Literal::from_str("d"));
let mut input = Cursor::new(b"abcd");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"a".as_slice().into());
assert_eq!(result.1.0, b"b".as_slice().into());
assert_eq!(result.1.1.0, b"c".as_slice().into());
assert_eq!(result.1.1.1.0, b"d".as_slice().into());
}
#[test]
fn test_push_different_types() {
use crate::class::Class;
let base = seq![Literal::from_str("prefix")];
let extended = base.push(Class::digits());
let mut input = Cursor::new(b"prefix123");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(extended, &mut source).unwrap();
assert_eq!(result.0, b"prefix".as_slice().into());
assert_eq!(result.1.0, b"123".to_vec());
}
#[test]
fn test_push_mixed_types_chain() {
use crate::class::Class;
let parser = seq![Literal::from_str("start")]
.push(Class::digits())
.push(Literal::from_str("_"))
.push(Class::digits()) .push(Literal::from_str("end"));
let mut input = Cursor::new(b"start123_456end");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"start".as_slice().into());
assert_eq!(result.1.0, b"123".to_vec());
assert_eq!(result.1.1.0, b"_".as_slice().into());
assert_eq!(result.1.1.1.0, b"456".to_vec());
assert_eq!(result.1.1.1.1.0, b"end".as_slice().into());
}
#[test]
fn test_push_failure() {
let base = seq![Literal::from_str("hello")];
let extended = base.push(Literal::from_str(" world"));
let mut input = Cursor::new(b"hello goodbye");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(extended, &mut source);
assert!(result.is_err());
}
#[test]
fn test_push_with_class_alpha() {
use crate::class::Class;
let parser = seq![Literal::from_str("start")]
.push(Class::digits())
.push(Literal::from_str("_"))
.push(Class::alpha())
.push(Literal::from_str("_end"));
let mut input = Cursor::new(b"start123_abc_end");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"start".as_slice().into());
assert_eq!(result.1.0, b"123".to_vec());
assert_eq!(result.1.1.0, b"_".as_slice().into());
assert_eq!(result.1.1.1.0, b"abc".to_vec());
assert_eq!(result.1.1.1.1.0, b"_end".as_slice().into());
}
#[test]
fn test_push_with_until_parser() {
use crate::{class::Class, until::Until};
let parser = seq![Literal::from_str("start")]
.push(Class::digits())
.push(Literal::from_str("_"))
.push(Until::new(Literal::from_str("end")))
.push(Literal::from_str("end"));
let mut input = Cursor::new(b"start123_abcend");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(parser, &mut source).unwrap();
assert_eq!(result.0, b"start".as_slice().into());
assert_eq!(result.1.0, b"123".to_vec());
assert_eq!(result.1.1.0, b"_".as_slice().into());
assert_eq!(result.1.1.1.0, b"abc".to_vec()); assert_eq!(result.1.1.1.1.0, b"end".as_slice().into());
}
#[test]
fn test_id_implementation_different_sequences() {
let seq1 = Sequence::new(Literal::from_str("hello"), Literal::from_str("world"));
let seq2 = Sequence::new(Literal::from_str("foo"), Literal::from_str("bar"));
let id1 = <Sequence<_, _> as crate::parser::Parser<()>>::id(&seq1);
let id2 = <Sequence<_, _> as crate::parser::Parser<()>>::id(&seq2);
assert_ne!(
id1, id2,
"Different Sequence instances should have different IDs to avoid cache collisions"
);
}
#[test]
fn test_id_implementation_same_sequences() {
let seq1 = Sequence::new(Literal::from_str("hello"), Literal::from_str("world"));
let seq2 = Sequence::new(Literal::from_str("hello"), Literal::from_str("world"));
assert_eq!(
<Sequence<_, _> as crate::parser::Parser<()>>::id(&seq1),
<Sequence<_, _> as crate::parser::Parser<()>>::id(&seq2),
"Identical Sequence instances should have the same ID for cache efficiency"
);
}
#[test]
fn test_id_implementation_sequence_cache_correctness() {
let seq1 = Sequence::new(Literal::from_str("hello"), Literal::from_str("world"));
let seq2 = Sequence::new(Literal::from_str("foo"), Literal::from_str("bar"));
let mut input1 = Cursor::new(b"helloworld");
let mut source1 = crate::parser::Source::new(&mut input1);
let result1 = parse(seq1, &mut source1);
assert!(result1.is_ok(), "First parse should succeed");
let mut input2 = Cursor::new(b"foobar");
let mut source2 = crate::parser::Source::new(&mut input2);
let result2 = parse(seq2, &mut source2);
assert!(
result2.is_ok(),
"Second parse should succeed without cache collision"
);
if let (Ok((first1, second1)), Ok((first2, second2))) = (result1, result2) {
assert_eq!(first1, b"hello".as_slice().into());
assert_eq!(second1, b"world".as_slice().into());
assert_eq!(first2, b"foo".as_slice().into());
assert_eq!(second2, b"bar".as_slice().into());
} else {
panic!("Both parses should succeed");
}
}
#[test]
fn test_id_implementation_nested_sequences() {
let seq1 = Sequence::new(
Literal::from_str("outer1"),
Sequence::new(Literal::from_str("inner1"), Literal::from_str("end1")),
);
let seq2 = Sequence::new(
Literal::from_str("outer2"),
Sequence::new(Literal::from_str("inner2"), Literal::from_str("end2")),
);
let id1 = <Sequence<_, _> as crate::parser::Parser<()>>::id(&seq1);
let id2 = <Sequence<_, _> as crate::parser::Parser<()>>::id(&seq2);
assert_ne!(
id1, id2,
"Different nested Sequence instances should have different IDs to avoid cache collisions"
);
}
}