mod concat;
pub use concat::*;
use crate::{
action::{Action, Input, Output},
combinator::{bytes, Combinator, Contextual, Eat},
digest::Digest,
instant::Instant,
};
use std::{
ops::{self, RangeFrom},
slice::SliceIndex,
};
#[derive(Debug, Clone, Copy)]
pub struct Add<Lhs, Rhs> {
lhs: Lhs,
rhs: Rhs,
}
impl<Lhs, Rhs> Add<Lhs, Rhs> {
#[inline]
const fn new(lhs: Lhs, rhs: Rhs) -> Self {
Self { lhs, rhs }
}
}
impl<Lhs, Rhs> ops::Add<Combinator<Rhs>> for Combinator<Lhs> {
type Output = Combinator<Add<Lhs, Rhs>>;
#[inline]
fn add(self, rhs: Combinator<Rhs>) -> Self::Output {
Self::Output::new(Add::new(self.action, rhs.action))
}
}
unsafe impl<
Lhs: Action<Text: Digest, Value: Concat<Rhs::Value>>,
Rhs: Action<Text = Lhs::Text, State = Lhs::State, Heap = Lhs::Heap>,
> Action for Add<Lhs, Rhs>
where
RangeFrom<usize>: SliceIndex<Lhs::Text, Output = Lhs::Text>,
{
type Text = Lhs::Text;
type State = Lhs::State;
type Heap = Lhs::Heap;
type Value = <Lhs::Value as Concat<Rhs::Value>>::Output;
#[inline]
fn exec(
&self,
mut input: Input<&Instant<&Self::Text>, &mut Self::State, &mut Self::Heap>,
) -> Option<Output<Self::Value>> {
self.lhs.exec(input.reborrow()).and_then(|output| {
self
.rhs
.exec(input.reborrow_with(&unsafe { input.instant.to_digested_unchecked(output.digested) }))
.map(|rhs_output| Output {
value: output.value.concat(rhs_output.value),
digested: unsafe { output.digested.unchecked_add(rhs_output.digested) },
})
})
}
}
impl<Lhs: Action<Text = str>> ops::Add<char> for Combinator<Lhs> {
type Output = Combinator<Add<Lhs, Contextual<Eat<char>, Lhs::State, Lhs::Heap>>>;
#[inline]
fn add(self, rhs: char) -> Self::Output {
Self::Output::new(Add::new(self.action, Contextual::new(Eat::new(rhs))))
}
}
impl<Lhs: Action<Text = str>> ops::Add<String> for Combinator<Lhs> {
type Output = Combinator<Add<Lhs, Contextual<Eat<String>, Lhs::State, Lhs::Heap>>>;
#[inline]
fn add(self, rhs: String) -> Self::Output {
Self::Output::new(Add::new(self.action, Contextual::new(Eat::new(rhs))))
}
}
impl<'a, Lhs: Action<Text = str>> ops::Add<&'a str> for Combinator<Lhs> {
type Output = Combinator<Add<Lhs, Contextual<Eat<&'a str>, Lhs::State, Lhs::Heap>>>;
#[inline]
fn add(self, rhs: &'a str) -> Self::Output {
Self::Output::new(Add::new(self.action, Contextual::new(Eat::new(rhs))))
}
}
impl<Lhs: Action<Text = [u8]>> ops::Add<u8> for Combinator<Lhs> {
type Output = Combinator<Add<Lhs, Contextual<bytes::Eat<u8>, Lhs::State, Lhs::Heap>>>;
#[inline]
fn add(self, rhs: u8) -> Self::Output {
Self::Output::new(Add::new(self.action, Contextual::new(bytes::Eat::new(rhs))))
}
}
impl<Lhs: Action<Text = [u8]>> ops::Add<Vec<u8>> for Combinator<Lhs> {
type Output = Combinator<Add<Lhs, Contextual<bytes::Eat<Vec<u8>>, Lhs::State, Lhs::Heap>>>;
#[inline]
fn add(self, rhs: Vec<u8>) -> Self::Output {
Self::Output::new(Add::new(self.action, Contextual::new(bytes::Eat::new(rhs))))
}
}
impl<'a, Lhs: Action<Text = [u8]>> ops::Add<&'a [u8]> for Combinator<Lhs> {
type Output = Combinator<Add<Lhs, Contextual<bytes::Eat<&'a [u8]>, Lhs::State, Lhs::Heap>>>;
#[inline]
fn add(self, rhs: &'a [u8]) -> Self::Output {
Self::Output::new(Add::new(self.action, Contextual::new(bytes::Eat::new(rhs))))
}
}
impl<'a, const N: usize, Lhs: Action<Text = [u8]>> ops::Add<&'a [u8; N]> for Combinator<Lhs> {
type Output = Combinator<Add<Lhs, Contextual<bytes::Eat<&'a [u8; N]>, Lhs::State, Lhs::Heap>>>;
#[inline]
fn add(self, rhs: &'a [u8; N]) -> Self::Output {
Self::Output::new(Add::new(self.action, Contextual::new(bytes::Eat::new(rhs))))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
combinator::{bytes, take},
contextual,
instant::Instant,
};
use std::fmt::Debug;
fn helper<Text: ?Sized + Digest, Value: PartialEq + Debug>(
action: impl Action<Text = Text, State = (), Heap = (), Value = Value>,
input: &Text,
output: Option<Output<Value>>,
) where
RangeFrom<usize>: SliceIndex<Text, Output = Text>,
{
assert_eq!(
action.exec(Input {
instant: &Instant::new(input),
state: &mut (),
heap: &mut ()
}),
output
)
}
#[test]
fn combinator_add() {
let rejecter = || take(0).reject(|_| true);
let accepter_unit = || take(1);
let accepter_int = || take(1).bind((123,));
helper(rejecter() + accepter_unit(), "123", None);
helper(accepter_unit() + rejecter(), "123", None);
helper(
accepter_unit() + accepter_int(),
"123",
Some(Output {
value: (123,),
digested: 2,
}),
);
helper(
accepter_int() + accepter_unit(),
"123",
Some(Output {
value: (123,),
digested: 2,
}),
);
helper(
accepter_int() + accepter_int(),
"123",
Some(Output {
value: (123, 123),
digested: 2,
}),
);
}
#[test]
fn combinator_add_char() {
helper(
take(1) + '2',
"12",
Some(Output {
digested: 2,
value: (),
}),
);
}
#[test]
fn combinator_add_str() {
helper(
take(1) + "23",
"123",
Some(Output {
digested: 3,
value: (),
}),
);
}
#[test]
fn combinator_add_string() {
helper(
take(1) + "23".to_string(),
"123",
Some(Output {
digested: 3,
value: (),
}),
);
}
#[test]
fn combinator_add_u8() {
helper(
bytes::take(1) + b'2',
b"123",
Some(Output {
digested: 2,
value: (),
}),
);
}
#[test]
fn combinator_add_u8_slice() {
helper(
bytes::take(1) + "2".as_bytes(),
b"123",
Some(Output {
digested: 2,
value: (),
}),
);
}
#[test]
fn combinator_add_u8_const_slice() {
helper(
bytes::take(1) + b"2",
b"123",
Some(Output {
digested: 2,
value: (),
}),
);
}
#[test]
fn combinator_add_vec_u8() {
helper(
bytes::take(1) + vec![b'2'],
b"123",
Some(Output {
digested: 2,
value: (),
}),
);
}
fn _with_contextual() {
contextual!(i32, i32);
fn validate(_: impl Action<State = i32, Heap = i32>) {}
validate(take(1) + 'a'); validate(take(1) + "a"); validate(take(1) + "a".to_string()); validate(bytes::take(1) + b'a'); validate(bytes::take(1) + b"a"); validate(bytes::take(1) + b"a".as_bytes()); validate(bytes::take(1) + b"a".to_vec()); }
}