syntacks 0.1.0-alpha0.2

An opinionated parser library for Rust
Documentation
use std::{
	iter::repeat,
	marker::PhantomData,
	ops::{Bound, RangeBounds},
};

use crate::{Error, Input, Output, Parser, SourceSpan};

pub fn fold<'src, I, O1, O2, P, R, FInit, FAccumulate>(
	parser: P,
	range: R,
	init: FInit,
	accumulate: FAccumulate,
) -> Fold<'src, I, O1, O2, P, R, FInit, FAccumulate>
where
	I: Input<'src>,
	P: Parser<'src, I, O1>,
	R: RangeBounds<usize>,
	FInit: FnMut() -> O2,
	FAccumulate: FnMut(&mut O2, O1),
{
	Fold::<'src, I, O1, O2, P, R, FInit, FAccumulate> {
		parser,
		range,
		init,
		accumulate,
		_phantom: PhantomData,
	}
}

pub struct Fold<'src, I, O1, O2, P, R, FInit, FAccumulate>
where
	I: Input<'src>,
	R: RangeBounds<usize>,
	P: Parser<'src, I, O1>,
	FInit: FnMut() -> O2,
	FAccumulate: FnMut(&mut O2, O1),
{
	parser: P,
	range: R,
	init: FInit,
	accumulate: FAccumulate,
	_phantom: PhantomData<&'src (I, O1)>,
}

struct State<'src, 'a, I, O1, O2, P, F>
where
	I: Input<'src>,
	P: Parser<'src, I, O1>,
	F: FnMut(&mut O2, O1),
{
	parser: &'a mut P,
	accumulate: &'a mut F,
	source_span: SourceSpan<'src, I::Source>,
	value: O2,
	remaining: I,
	_phantom: PhantomData<&'src O1>,
}

impl<'src, 'a, I, O1, O2, P, F> State<'src, 'a, I, O1, O2, P, F>
where
	I: Input<'src>,
	P: Parser<'src, I, O1>,
	F: FnMut(&mut O2, O1),
{
	fn parse(&mut self) -> Result<(), Error<'src, I::Source>> {
		let Output { source_span, value, remaining } = self.parser.parse(self.remaining.clone())?;

		(self.accumulate)(&mut self.value, value);

		self.remaining = remaining;
		self.source_span += source_span;

		Ok::<_, Error<'src, I::Source>>(())
	}
}

impl<'src, I, O1, O2, P, R, FInit, FAccumulate> Parser<'src, I, O2> for Fold<'src, I, O1, O2, P, R, FInit, FAccumulate>
where
	I: Input<'src>,
	P: Parser<'src, I, O1>,
	R: RangeBounds<usize>,
	FInit: FnMut() -> O2,
	FAccumulate: FnMut(&mut O2, O1),
{
	fn parse(&mut self, input: I) -> crate::ParserResult<'src, I, O2> {
		let mut state: State<'src, '_, I, _, O2, P, FAccumulate> = State {
			parser: &mut self.parser,
			accumulate: &mut self.accumulate,
			source_span: input.source_span().start(),
			value: (self.init)(),
			remaining: input,
			_phantom: PhantomData::<&'src O1>,
		};

		repeat(()).take(range_to_minimum(&self.range)).try_for_each(|_| state.parse())?;
		repeat(()).take(range_to_maximum(&self.range)).try_for_each(|_| state.parse())?;

		Ok(Output {
			source_span: state.source_span,
			value: state.value,
			remaining: state.remaining,
		})
	}

	async fn parse_async(&mut self, input: I) -> crate::ParserResult<'src, I, O2> {
		let mut count = 0;
		let mut value = (self.init)();
		let mut source_span = input.source_span().start();
		let mut remaining = input;

		let minimum = range_to_minimum(&self.range);

		for _ in 0..minimum {
			let Output {
				source_span: inner_source_span,
				value: inner_value,
				remaining: inner_remaining,
			} = self.parser.parse_async(remaining).await?;

			(self.accumulate)(&mut value, inner_value);

			remaining = inner_remaining;
			source_span += inner_source_span;

			count += 1;
		}

		let maximum = range_to_maximum(&self.range);

		for _ in count..=maximum {
			if let Ok(Output {
				source_span: inner_source_span,
				value: inner_value,
				remaining: inner_remaining,
			}) = self.parser.parse_async(remaining.clone()).await
			{
				(self.accumulate)(&mut value, inner_value);

				remaining = inner_remaining;
				source_span += inner_source_span;
			} else {
				break;
			}
		}

		Ok(Output { source_span, value, remaining })
	}
}

fn range_to_minimum(range: &impl RangeBounds<usize>) -> usize {
	match range.start_bound() {
		Bound::Included(value) => *value,
		Bound::Excluded(value) => value + 1,
		Bound::Unbounded => 0,
	}
}

fn range_to_maximum(range: &impl RangeBounds<usize>) -> usize {
	match range.start_bound() {
		Bound::Included(value) => value + 1,
		Bound::Excluded(value) => *value,
		Bound::Unbounded => 0,
	}
}