diff options
| author | John Turner <jturner.usa@gmail.com> | 2025-10-21 20:48:00 -0400 |
|---|---|---|
| committer | John Turner <jturner.usa@gmail.com> | 2025-10-21 20:48:21 -0400 |
| commit | 0e23b9fa82d95ca365523afac554a4fb6d461a23 (patch) | |
| tree | 296fc7fc3e8208198b7e87adf4a016a9ea93aedf | |
| parent | 876855e826bf3c7bd47b822fb41fd91ab46ad7e5 (diff) | |
| download | mon-0e23b9fa82d95ca365523afac554a4fb6d461a23.tar.gz | |
impl some basic parsers and combinators
| -rw-r--r-- | src/input.rs | 127 | ||||
| -rw-r--r-- | src/lib.rs | 821 | ||||
| -rw-r--r-- | src/mode.rs | 66 | ||||
| -rw-r--r-- | tests/sexpr.rs | 66 |
4 files changed, 1080 insertions, 0 deletions
diff --git a/src/input.rs b/src/input.rs new file mode 100644 index 0000000..0857c8c --- /dev/null +++ b/src/input.rs @@ -0,0 +1,127 @@ +use core::{fmt, iter, slice, str::CharIndices}; + +use crate::Span; + +pub trait Input: Clone { + type Item; + type Items: Clone + Iterator<Item = (usize, Self::Item)>; + + fn items(&self) -> Self::Items; + + fn slice(&self, span: Span) -> Self; + + fn len(&self) -> usize; +} + +impl<'a> Input for &'a str { + type Item = char; + type Items = CharIndices<'a>; + + fn items(&self) -> Self::Items { + self.char_indices() + } + + fn slice(&self, span: Span) -> Self { + &self[span] + } + + fn len(&self) -> usize { + (*self).len() + } +} + +impl<'a> Input for &'a [u8] { + type Item = u8; + type Items = iter::Enumerate<iter::Copied<slice::Iter<'a, u8>>>; + + fn items(&self) -> Self::Items { + self.iter().copied().enumerate() + } + + fn slice(&self, span: Span) -> Self { + &self[span] + } + + fn len(&self) -> usize { + (*self).len() + } +} + +pub trait Character { + fn is_alphabetic(&self) -> bool; + + fn is_numeric(&self) -> bool; + + fn is_whitespace(&self) -> bool; + + fn is_alphanumeric(&self) -> bool { + self.is_alphabetic() || self.is_numeric() + } +} + +impl Character for char { + fn is_alphabetic(&self) -> bool { + (*self).is_ascii_alphabetic() + } + + fn is_numeric(&self) -> bool { + (*self).is_numeric() + } + + fn is_whitespace(&self) -> bool { + (*self).is_whitespace() + } +} + +#[derive(Clone)] +pub struct InputIter<I: Input> { + pub it: I::Items, + pub input: I, +} + +impl<I> InputIter<I> +where + I: Input, +{ + pub fn new(input: I) -> Self { + Self { + it: input.items(), + input, + } + } + + pub fn position(&self) -> usize { + match self.it.clone().next() { + Some((i, _)) => i, + None => self.input.len(), + } + } + + pub fn is_finished(&self) -> bool { + self.clone().next().is_none() + } + + pub fn rest(&self) -> I { + self.input.slice(self.position()..self.input.len()) + } +} + +impl<I> Iterator for InputIter<I> +where + I: Input, +{ + type Item = (usize, I::Item); + + fn next(&mut self) -> Option<Self::Item> { + self.it.next() + } +} + +impl<I> fmt::Debug for InputIter<I> +where + I: Input + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.rest()) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..fc91cb1 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,821 @@ +use core::{cmp::PartialEq, fmt, iter::Iterator, marker::Sized, ops::Range}; + +use crate::{ + input::{Character, Input, InputIter}, + mode::{Check, Emit, Mode}, +}; + +pub mod input; +pub mod mode; + +pub type Span = Range<usize>; + +pub type ParserOk<I, O> = (InputIter<I>, O); + +pub type ParserResult<I, O, OM = Emit, EM = Emit> = + Result<ParserOk<I, <OM as Mode>::Output<O>>, <EM as Mode>::Output<InputIter<I>>>; + +pub trait Trace<I: Input> { + fn trace(parser: &str, input: InputIter<I>); +} + +pub struct DebugTracer; + +impl<I> Trace<I> for DebugTracer +where + I: Input + fmt::Debug, +{ + fn trace(parser: &str, input: InputIter<I>) { + eprintln!("{}:{:?}", parser, input) + } +} + +impl<I> Trace<I> for () +where + I: Input, +{ + fn trace(_: &str, _: InputIter<I>) {} +} + +#[derive(Debug)] +pub enum ParserFinishedError<I: Input> { + Err(InputIter<I>), + Unfinished(InputIter<I>), +} + +pub trait Parser<I: Input>: Sized { + type Output; + + fn run<OM: Mode, EM: Mode, T: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM>; + + fn parse(&mut self, it: InputIter<I>) -> ParserResult<I, Self::Output> { + self.run::<Emit, Emit, ()>(it) + } + + fn check(&mut self, it: InputIter<I>) -> ParserResult<I, Self::Output, Check, Emit> { + self.run::<Check, Emit, ()>(it) + } + + fn trace<T: Trace<I>>(&mut self, it: InputIter<I>) -> ParserResult<I, Self::Output> { + self.run::<Emit, Emit, T>(it) + } + + fn parse_finished(&mut self, it: InputIter<I>) -> Result<Self::Output, ParserFinishedError<I>> { + match self.parse(it) { + Ok((rest, output)) if rest.is_finished() => Ok(output), + Ok((rest, _)) => Err(ParserFinishedError::Unfinished(rest)), + Err(rest) => Err(ParserFinishedError::Err(rest)), + } + } + + fn check_finished(&mut self, it: InputIter<I>) -> Result<(), ParserFinishedError<I>> { + match self.parse(it) { + Ok((rest, _)) if rest.is_finished() => Ok(()), + Ok((rest, _)) => Err(ParserFinishedError::Unfinished(rest)), + Err(rest) => Err(ParserFinishedError::Err(rest)), + } + } + + fn map<F, O>(self, f: F) -> impl Parser<I, Output = O> + where + F: Fn(Self::Output) -> O, + { + Map { parser: self, f } + } + + fn and<P>(self, right: P) -> impl Parser<I, Output = (Self::Output, P::Output)> + where + P: Parser<I>, + { + And { left: self, right } + } + + fn or<P, O>(self, right: P) -> impl Parser<I, Output = O> + where + Self: Parser<I, Output = O>, + P: Parser<I, Output = O>, + { + Or { left: self, right } + } + + fn and_not<P>(self, not: P) -> impl Parser<I, Output = Self::Output> + where + P: Parser<I>, + { + AndNot { parser: self, not } + } + + fn preceded_by<P>(self, preceded: P) -> impl Parser<I, Output = Self::Output> + where + P: Parser<I>, + { + PrecededBy { + parser: self, + preceded, + } + } + + fn followed_by<P>(self, followed: P) -> impl Parser<I, Output = Self::Output> + where + P: Parser<I>, + { + FollowedBy { + parser: self, + followed, + } + } + + fn delimited_by<P1, P2>(self, left: P1, right: P2) -> impl Parser<I, Output = Self::Output> + where + P1: Parser<I>, + P2: Parser<I>, + { + DelimitedBy { + parser: self, + left, + right, + } + } + + fn recognize(self) -> impl Parser<I, Output = I> { + Recognize { parser: self } + } + + fn separated_list<P>(self, delimiter: P) -> impl Parser<I, Output = Vec<Self::Output>> + where + P: Parser<I>, + { + SeparatedList { + parser: self, + delimiter, + } + } + + fn separated_list1<P>(self, delimiter: P) -> impl Parser<I, Output = Vec<Self::Output>> + where + P: Parser<I>, + { + SeparatedList1 { + parser: self, + delimiter, + } + } +} + +impl<I, O, F> Parser<I> for F +where + I: Input, + F: Fn(InputIter<I>) -> Result<ParserOk<I, O>, InputIter<I>>, +{ + type Output = O; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("fn", it.clone()); + + match self(it) { + Ok((rest, output)) => Ok((rest, OM::bind(|| output))), + Err(rest) => Err(EM::bind(|| rest)), + } + } +} + +struct Map<P, F> { + parser: P, + f: F, +} + +impl<I, P, F, O> Parser<I> for Map<P, F> +where + I: Input, + P: Parser<I>, + F: Fn(P::Output) -> O, +{ + type Output = O; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + match self.parser.run::<OM, EM, Tracer>(it) { + Ok((rest, output)) => Ok((rest, OM::map(output, |o| (self.f)(o)))), + Err(rest) => Err(rest), + } + } +} + +struct And<P1, P2> { + left: P1, + right: P2, +} + +impl<I, P1, P2> Parser<I> for And<P1, P2> +where + I: Input, + P1: Parser<I>, + P2: Parser<I>, +{ + type Output = (P1::Output, P2::Output); + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("and", it.clone()); + + let (rest, o1) = match self.left.run::<OM, EM, Tracer>(it) { + Ok((rest, output)) => (rest, output), + Err(rest) => return Err(rest), + }; + + let (rest, o2) = match self.right.run::<OM, EM, Tracer>(rest) { + Ok((rest, output)) => (rest, output), + Err(rest) => return Err(rest), + }; + + Ok((rest, OM::combine(o1, o2, |o1, o2| (o1, o2)))) + } +} + +struct AndNot<P1, P2> { + parser: P1, + not: P2, +} + +impl<I, P1, P2> Parser<I> for AndNot<P1, P2> +where + I: Input, + P1: Parser<I>, + P2: Parser<I>, +{ + type Output = P1::Output; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("andnot", it.clone()); + + match self.not.run::<Check, Check, Tracer>(it.clone()) { + Ok(_) => return Err(EM::bind(|| it.clone())), + _ => (), + }; + + self.parser.run::<OM, EM, Tracer>(it) + } +} + +struct Or<P1, P2> { + left: P1, + right: P2, +} + +impl<I, P1, P2, O> Parser<I> for Or<P1, P2> +where + I: Input, + P1: Parser<I, Output = O>, + P2: Parser<I, Output = O>, +{ + type Output = O; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("or", it.clone()); + + match ( + self.left.run::<OM, EM, Tracer>(it.clone()), + self.right.run::<OM, EM, Tracer>(it.clone()), + ) { + (Ok((rest, output)), _) => Ok((rest, output)), + (_, Ok((rest, output))) => Ok((rest, output)), + (_, Err(rest)) => Err(rest), + } + } +} + +pub struct Take { + amt: usize, +} + +impl<I> Parser<I> for Take +where + I: Input, +{ + type Output = I; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + mut it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("take", it.clone()); + + let start = it.clone(); + + for _ in 0..self.amt { + match it.next() { + Some(_) => continue, + None => return Err(EM::bind(|| it)), + } + } + + Ok(( + it.clone(), + OM::bind(|| it.input.slice(start.position()..it.position())), + )) + } +} + +pub fn take<I>(amt: usize) -> impl Parser<I, Output = I> +where + I: Input, +{ + Take { amt } +} + +pub struct TakeWhile<P> { + parser: P, +} + +impl<I, P> Parser<I> for TakeWhile<P> +where + I: Input, + P: Parser<I>, +{ + type Output = I; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + mut it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("takewhile", it.clone()); + + let start = it.clone(); + + while let Ok((rest, _)) = self.parser.run::<Check, Check, Tracer>(it.clone()) { + it = rest; + } + + Ok(( + it.clone(), + OM::bind(|| it.input.slice(start.position()..it.position())), + )) + } +} + +pub fn take_while<I, P>(parser: P) -> impl Parser<I, Output = I> +where + I: Input, + P: Parser<I>, +{ + TakeWhile { parser } +} + +pub struct TakeWhile1<P> { + parser: P, +} + +impl<I, P> Parser<I> for TakeWhile1<P> +where + I: Input, + P: Parser<I>, +{ + type Output = I; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + mut it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("takewhile1", it.clone()); + + let start = it.clone(); + + while let Ok((rest, _)) = self.parser.run::<Check, Check, Tracer>(it.clone()) { + it = rest; + } + + if it.position() > start.position() { + Ok(( + it.clone(), + OM::bind(|| it.input.slice(start.position()..it.position())), + )) + } else { + Err(EM::bind(|| it)) + } + } +} + +pub fn take_while1<I, P>(parser: P) -> impl Parser<I, Output = I> +where + I: Input, + P: Parser<I>, +{ + TakeWhile1 { parser } +} + +pub struct OneOf<It> { + it: It, +} + +impl<I, It> Parser<I> for OneOf<It> +where + I: Input, + I::Item: PartialEq<It::Item>, + It: Iterator, +{ + type Output = I::Item; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + mut it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("oneof", it.clone()); + + match it.next() { + Some((_, item)) if self.it.any(|i| item == i) => Ok((it, OM::bind(|| item))), + _ => Err(EM::bind(|| it)), + } + } +} + +pub fn one_of<I, It>(it: It) -> impl Parser<I, Output = I::Item> +where + I: Input, + I::Item: PartialEq<It::Item>, + It: Iterator, +{ + OneOf { it } +} + +pub struct If<F> { + f: F, +} + +impl<I, F> Parser<I> for If<F> +where + I: Input, + F: Fn(&I::Item) -> bool, +{ + type Output = I::Item; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + mut it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("if", it.clone()); + + match it.next() { + Some((_, output)) if (self.f)(&output) => Ok((it, OM::bind(|| output))), + _ => Err(EM::bind(|| it)), + } + } +} + +pub fn r#if<I, F>(f: F) -> impl Parser<I, Output = I::Item> +where + I: Input, + F: Fn(&I::Item) -> bool, +{ + If { f } +} + +struct PrecededBy<P1, P2> { + parser: P1, + preceded: P2, +} + +impl<I, P1, P2> Parser<I> for PrecededBy<P1, P2> +where + I: Input, + P1: Parser<I>, + P2: Parser<I>, +{ + type Output = P1::Output; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("preceded by", it.clone()); + + let rest = match self.preceded.check(it.clone()) { + Ok((rest, _)) => rest, + Err(_) => return Err(EM::bind(|| it)), + }; + + self.parser.run::<OM, EM, Tracer>(rest) + } +} + +pub struct FollowedBy<P1, P2> { + parser: P1, + followed: P2, +} + +impl<I, P1, P2> Parser<I> for FollowedBy<P1, P2> +where + I: Input, + P1: Parser<I>, + P2: Parser<I>, +{ + type Output = P1::Output; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("followed by", it.clone()); + + let (rest, output) = match self.parser.run::<OM, EM, Tracer>(it) { + Ok((rest, output)) => (rest, output), + Err(rest) => return Err(rest), + }; + + let Ok((rest, _)) = self.followed.check(rest.clone()) else { + return Err(EM::bind(|| rest.clone())); + }; + + Ok((rest, output)) + } +} + +struct DelimitedBy<P1, P2, P3> { + parser: P1, + left: P2, + right: P3, +} + +impl<I, P1, P2, P3> Parser<I> for DelimitedBy<P1, P2, P3> +where + I: Input, + P1: Parser<I>, + P2: Parser<I>, + P3: Parser<I>, +{ + type Output = P1::Output; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("delimited by", it.clone()); + + let rest = match self.left.check(it.clone()) { + Ok((rest, _)) => rest, + Err(_) => return Err(EM::bind(|| it)), + }; + + let (rest, output) = match self.parser.run::<OM, EM, Tracer>(rest.clone()) { + Ok((rest, output)) => (rest, output), + Err(rest) => return Err(rest), + }; + + let Ok((rest, _)) = self.right.check(rest.clone()) else { + return Err(EM::bind(|| rest)); + }; + + Ok((rest, output)) + } +} + +struct Recognize<P> { + parser: P, +} + +impl<I, P> Parser<I> for Recognize<P> +where + I: Input, + P: Parser<I>, +{ + type Output = I; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("recognize", it.clone()); + + let start = it.clone(); + + let rest = match self.parser.check(it.clone()) { + Ok((rest, _)) => rest, + Err(_) => return Err(EM::bind(|| it)), + }; + + Ok(( + rest.clone(), + OM::bind(|| it.input.slice(start.position()..rest.position())), + )) + } +} + +struct Tag<T>(T); + +impl<I, T> Parser<I> for Tag<T> +where + I: Input + PartialEq<T>, + T: Input, +{ + type Output = I; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("tag", it.clone()); + + match take(self.0.len()).parse(it.clone()) { + Ok((rest, output)) if output == self.0 => Ok((rest, OM::bind(|| output))), + Ok(_) => return Err(EM::bind(|| it)), + Err(rest) => return Err(EM::bind(|| rest)), + } + } +} + +pub fn tag<I, T>(tag: T) -> impl Parser<I, Output = I> +where + I: Input + PartialEq<T>, + T: Input, +{ + Tag(tag) +} + +struct SeparatedList<P1, P2> { + parser: P1, + delimiter: P2, +} + +impl<I, P1, P2> Parser<I> for SeparatedList<P1, P2> +where + I: Input, + P1: Parser<I>, + P2: Parser<I>, +{ + type Output = Vec<P1::Output>; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + mut it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("separated list", it.clone()); + + let mut outputs = OM::bind(|| Vec::new()); + + loop { + it = match self.parser.run::<OM, EM, Tracer>(it.clone()) { + Ok((rest, output)) => { + outputs = OM::combine(outputs, output, |mut acc, e| { + acc.push(e); + acc + }); + rest + } + _ => break, + }; + + it = match self.delimiter.check(it.clone()) { + Ok((rest, _)) => rest, + _ => break, + }; + } + + Ok((it, outputs)) + } +} + +pub struct SeparatedList1<P1, P2> { + parser: P1, + delimiter: P2, +} + +impl<I, P1, P2> Parser<I> for SeparatedList1<P1, P2> +where + I: Input, + P1: Parser<I>, + P2: Parser<I>, +{ + type Output = Vec<P1::Output>; + + fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>( + &mut self, + mut it: InputIter<I>, + ) -> ParserResult<I, Self::Output, OM, EM> { + Tracer::trace("separated list 1", it.clone()); + + it = match self.delimiter.check(it.clone()) { + Ok((rest, _)) => rest, + Err(_) => it, + }; + + let mut outputs = OM::bind(|| Vec::new()); + let mut len = 0; + + loop { + it = match self.parser.run::<OM, EM, Tracer>(it.clone()) { + Ok((rest, output)) => { + outputs = OM::combine(outputs, output, |mut acc, e| { + acc.push(e); + acc + }); + len += 1; + rest + } + _ => break, + }; + + it = match self.delimiter.check(it.clone()) { + Ok((rest, _)) => rest, + _ if len > 0 => break, + _ => return Err(EM::bind(|| it)), + }; + } + + Ok((it, outputs)) + } +} + +pub fn alpha<I>() -> impl Parser<I, Output = I> +where + I: Input, + I::Item: Character, +{ + take_while(r#if(|c: &I::Item| c.is_alphabetic())) +} + +pub fn alpha1<I>() -> impl Parser<I, Output = I> +where + I: Input, + I::Item: Character, +{ + take_while1(r#if(|c: &I::Item| c.is_alphabetic())) +} + +pub fn numeric<I>() -> impl Parser<I, Output = I> +where + I: Input, + I::Item: Character, +{ + take_while(r#if(|c: &I::Item| c.is_numeric())) +} + +pub fn numeric1<I>() -> impl Parser<I, Output = I> +where + I: Input, + I::Item: Character, +{ + take_while1(r#if(|c: &I::Item| c.is_numeric())) +} + +pub fn alphanumeric<I>() -> impl Parser<I, Output = I> +where + I: Input, + I::Item: Character, +{ + take_while(r#if(|c: &I::Item| c.is_alphanumeric())) +} + +pub fn alphanumeric1<I>() -> impl Parser<I, Output = I> +where + I: Input, + I::Item: Character, +{ + take_while1(r#if(|c: &I::Item| c.is_alphanumeric())) +} + +pub fn whitespace<I>() -> impl Parser<I, Output = I> +where + I: Input, + I::Item: Character, +{ + take_while(r#if(|c: &I::Item| c.is_whitespace())) +} + +pub fn whitespace1<I>() -> impl Parser<I, Output = I> +where + I: Input, + I::Item: Character, +{ + take_while1(r#if(|c: &I::Item| c.is_whitespace())) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_separated_list() { + let input = "a b c"; + let it = InputIter::new(input); + + alpha1() + .separated_list(whitespace()) + .check_finished(it) + .unwrap(); + } +} diff --git a/src/mode.rs b/src/mode.rs new file mode 100644 index 0000000..3825e31 --- /dev/null +++ b/src/mode.rs @@ -0,0 +1,66 @@ +pub trait Mode { + type Output<T>; + + fn bind<T, F>(f: F) -> Self::Output<T> + where + F: FnOnce() -> T; + + fn map<T, U, F>(t: Self::Output<T>, f: F) -> Self::Output<U> + where + F: FnOnce(T) -> U; + + fn combine<T, U, V, F>(t: Self::Output<T>, u: Self::Output<U>, f: F) -> Self::Output<V> + where + F: FnOnce(T, U) -> V; +} + +pub struct Emit; + +impl Mode for Emit { + type Output<T> = T; + + fn bind<T, F>(f: F) -> Self::Output<T> + where + F: FnOnce() -> T, + { + f() + } + + fn map<T, U, F>(t: Self::Output<T>, f: F) -> Self::Output<U> + where + F: FnOnce(T) -> U, + { + f(t) + } + + fn combine<T, U, V, F>(t: Self::Output<T>, u: Self::Output<U>, f: F) -> Self::Output<V> + where + F: FnOnce(T, U) -> V, + { + f(t, u) + } +} + +pub struct Check; + +impl Mode for Check { + type Output<T> = (); + + fn bind<T, F>(_: F) -> Self::Output<T> + where + F: FnOnce() -> T, + { + } + + fn map<T, U, F>(_: Self::Output<T>, _: F) -> Self::Output<U> + where + F: FnOnce(T) -> U, + { + } + + fn combine<T, U, V, F>(_: Self::Output<T>, _: Self::Output<U>, _: F) -> Self::Output<V> + where + F: FnOnce(T, U) -> V, + { + } +} diff --git a/tests/sexpr.rs b/tests/sexpr.rs new file mode 100644 index 0000000..651d534 --- /dev/null +++ b/tests/sexpr.rs @@ -0,0 +1,66 @@ +#![allow(dead_code)] + +use mon::{ + alpha1, alphanumeric, alphanumeric1, input::InputIter, numeric1, tag, whitespace, Parser, + ParserResult, +}; + +#[derive(Debug)] +enum Sexpr { + List(Vec<Sexpr>), + Atom(String), + String(String), + Int(i64), +} + +fn atom<'a>() -> impl Parser<&'a str, Output = Sexpr> { + alpha1() + .and(alphanumeric()) + .recognize() + .map(|output: &str| Sexpr::Atom(output.to_string())) +} + +fn string<'a>() -> impl Parser<&'a str, Output = Sexpr> { + alphanumeric1() + .delimited_by(tag("\""), tag("\"")) + .map(|output: &str| Sexpr::String(output.to_string())) +} + +fn int<'a>() -> impl Parser<&'a str, Output = Sexpr> { + numeric1().map(|output: &str| Sexpr::Int(output.parse().unwrap())) +} + +fn sexpr<'a>(it: InputIter<&'a str>) -> ParserResult<&'a str, Sexpr> { + sexpr + .separated_list(whitespace()) + .delimited_by(tag("("), tag(")")) + .map(|output| Sexpr::List(output)) + .or(atom()) + .or(string()) + .or(int()) + .parse(it) +} + +#[test] +fn test_atom() { + let input = "atom"; + let it = InputIter::new(input); + + atom().check_finished(it).unwrap(); +} + +#[test] +fn test_string() { + let input = r#""string""#; + let it = InputIter::new(input); + + string().check_finished(it).unwrap() +} + +#[test] +fn test_sexpr() { + let input = r#"(let ((a "hello") (b 2)))"#; + let it = InputIter::new(input); + + dbg!(sexpr(it).unwrap()); +} |
