use core::{
fmt::Debug,
ops::{
Range,
RangeFrom,
RangeFull,
RangeInclusive,
RangeTo,
RangeToInclusive,
},
};
use binator_core::{
Contexting,
Parse,
Parsed,
Streaming,
};
use crate::UtilsAtom;
#[derive(Clone)]
pub struct FoldBounds<Parser, Bounds, Init, F> {
parser: Parser,
bounds: Bounds,
init: Init,
f: F,
}
pub fn fold_bounds<Bounds, Stream, Context, Parser, Acc, Init, F>(
parser: Parser, bounds: Bounds, init: Init, fold: F,
) -> FoldBounds<Parser, Bounds, Init, F>
where
Stream: Streaming,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc,
Bounds: FoldBoundsParse,
Acc: Debug,
{
FoldBounds {
bounds,
parser,
init,
f: fold,
}
}
pub trait FoldBoundsParse {
fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
&self, parser: &mut Parser, init: &mut Init, f: &mut F, stream: Stream,
) -> Parsed<Acc, Stream, Context>
where
Stream: Streaming,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc;
}
impl<Bounds, Stream, Context, Parser, Acc, Init, F> Parse<Stream, Context>
for FoldBounds<Parser, Bounds, Init, F>
where
Stream: Streaming,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc,
Bounds: FoldBoundsParse,
Acc: Debug,
{
type Token = Acc;
fn parse(&mut self, stream: Stream) -> Parsed<Acc, Stream, Context> {
self
.bounds
.fold_bounds(&mut self.parser, &mut self.init, &mut self.f, stream)
}
}
macro_rules! allow_failure {
($stream:expr, $parser:expr, $acc:expr, $fold:expr) => {{
match $parser.parse($stream.clone()) {
Parsed::Success { token, stream } => {
$acc = $fold($acc, token);
$stream = stream;
}
Parsed::Failure(_context) => {
return Parsed::Success {
token: $acc,
stream: $stream,
};
}
Parsed::Error(context) => {
return Parsed::Error(context);
}
};
}};
}
macro_rules! deny_failure {
($stream:expr, $parser:expr, $acc:expr, $fold:expr, $min:expr, $i:expr) => {{
match $parser.parse($stream) {
Parsed::Success { token, stream } => {
$acc = $fold($acc, token);
$stream = stream;
}
Parsed::Failure(context) => {
return Parsed::Failure(context.add(UtilsAtom::MinNotReach { min: $min, i: $i }));
}
Parsed::Error(context) => {
return Parsed::Error(context);
}
};
}};
}
impl FoldBoundsParse for RangeFull {
fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
&self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
) -> Parsed<Acc, Stream, Context>
where
Stream: Streaming,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc,
{
let mut acc = init();
loop {
allow_failure!(stream, parser, acc, f)
}
}
}
impl FoldBoundsParse for RangeFrom<usize> {
fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
&self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
) -> Parsed<Acc, Stream, Context>
where
Stream: Streaming,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc,
{
let mut acc = init();
for i in 0..self.start {
deny_failure!(stream, parser, acc, f, self.start, i)
}
loop {
allow_failure!(stream, parser, acc, f)
}
}
}
impl FoldBoundsParse for Range<usize> {
fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
&self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
) -> Parsed<Acc, Stream, Context>
where
Stream: Streaming,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc,
{
let mut acc = init();
for i in 0..self.start {
deny_failure!(stream, parser, acc, f, self.start, i)
}
for _ in self.start..self.end {
allow_failure!(stream, parser, acc, f)
}
Parsed::Success { token: acc, stream }
}
}
impl FoldBoundsParse for RangeInclusive<usize> {
fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
&self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
) -> Parsed<Acc, Stream, Context>
where
Stream: Streaming,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc,
{
let mut acc = init();
for i in 0..*self.start() {
deny_failure!(stream, parser, acc, f, *self.start(), i)
}
for _ in *self.start()..=*self.end() {
allow_failure!(stream, parser, acc, f)
}
Parsed::Success { token: acc, stream }
}
}
impl FoldBoundsParse for RangeTo<usize> {
fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
&self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
) -> Parsed<Acc, Stream, Context>
where
Stream: Streaming,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc,
{
let mut acc = init();
for _ in 0..self.end {
allow_failure!(stream, parser, acc, f)
}
Parsed::Success { token: acc, stream }
}
}
impl FoldBoundsParse for RangeToInclusive<usize> {
fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
&self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
) -> Parsed<Acc, Stream, Context>
where
Stream: Streaming,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc,
{
let mut acc = init();
for _ in 0..=self.end {
allow_failure!(stream, parser, acc, f)
}
Parsed::Success { token: acc, stream }
}
}
macro_rules! impl_primitive {
($primitive:ident) => {
impl FoldBoundsParse for $primitive {
fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
&self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
) -> Parsed<Acc, Stream, Context>
where
Stream: Clone,
Stream: Eq,
Context: Contexting<UtilsAtom<Stream>>,
Parser: Parse<Stream, Context>,
Init: FnMut() -> Acc,
F: FnMut(Acc, Parser::Token) -> Acc,
{
let mut acc = init();
let min = usize::from(*self);
for i in 0..min {
deny_failure!(stream, parser, acc, f, min, i)
}
Parsed::Success { token: acc, stream }
}
}
};
}
macro_rules! impl_primitives {
($($primitives:ident,)*) => {
$(impl_primitive!{$primitives})*
};
}
impl_primitives!(usize,);
#[cfg(test)]
mod tests {
use core::convert::Infallible;
use binator_base::{
is,
BaseAtom,
};
use binator_context::{
Keep,
Last,
};
use binator_core::{
Contexting,
CoreAtom,
Parse,
Parsed,
Streaming,
};
use derive_more::{
Display,
From,
};
use crate::{
Utils,
UtilsAtom,
};
#[derive(Display, Debug, Clone, From, PartialEq)]
enum FromAtom<Stream: Streaming> {
Fold(UtilsAtom<Stream>),
Is(BaseAtom<u8>),
Any(CoreAtom<Stream, Infallible>),
Stream,
}
type HandleAtom<Stream> = Keep<Last, FromAtom<Stream>>;
#[test]
fn fold_bounds_full() {
let stream = b"[".as_ref();
let result: Parsed<_, _, HandleAtom<_>> = is(b'[')
.or(is(b']'))
.fold_bounds(.., || (), |_, _| ())
.parse(stream);
let expected = Parsed::Success {
token: (),
stream: &stream[1..],
};
assert_eq!(result, expected);
}
#[test]
fn fold_bounds() {
let stream = b"[".as_ref();
let result: Parsed<_, _, HandleAtom<_>> = is(b'[')
.or(is(b']'))
.fold_bounds(1..5, || (), |_, _| ())
.parse(stream);
let expected = Parsed::Success {
token: (),
stream: &stream[1..],
};
assert_eq!(result, expected);
}
#[test]
fn fold_bounds_failure() {
let stream = b"abcd".as_ref();
let result: Parsed<_, _, HandleAtom<_>> = is(b'[')
.or(is(b']'))
.fold_bounds(1..5, || (), |_, _| ())
.parse(stream);
let context = Keep::new(UtilsAtom::MinNotReach { i: 0, min: 1 });
assert_eq!(result, Parsed::Failure(context));
}
}