summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Turner <jturner.usa@gmail.com>2025-10-27 01:37:29 -0400
committerJohn Turner <jturner.usa@gmail.com>2025-10-27 01:37:29 -0400
commitb8250a0f3975b63f54eea370d9d0907e92252984 (patch)
treeaa0fb7a46e89e18d7230e84923dfc11ea78a4a86
parent4d28f32d78c68558bb838060af515074af9c2c5e (diff)
downloadmon-b8250a0f3975b63f54eea370d9d0907e92252984.tar.gz
bring back separated_list but with a range parameter
-rw-r--r--src/lib.rs79
-rw-r--r--tests/sexpr.rs3
2 files changed, 80 insertions, 2 deletions
diff --git a/src/lib.rs b/src/lib.rs
index cdc5d0f..02ab717 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,6 +7,8 @@ use core::{
ops::{Bound, Range, RangeBounds},
};
+use regex::Replacer;
+
use crate::{
input::{Character, Input, InputIter},
mode::{Check, Emit, Mode},
@@ -192,6 +194,22 @@ pub trait Parser<I: Input>: Sized {
fn not(self) -> impl Parser<I, Output = ()> {
Not { parser: self }
}
+
+ fn separated_list<P, R>(
+ self,
+ delimiter: P,
+ range: R,
+ ) -> impl Parser<I, Output = Vec<Self::Output>>
+ where
+ P: Parser<I>,
+ R: RangeBounds<usize>,
+ {
+ SeparatedList {
+ parser: self,
+ delimiter,
+ range,
+ }
+ }
}
impl<I, O, F> Parser<I> for F
@@ -411,6 +429,56 @@ where
}
}
+struct SeparatedList<P1, P2, R> {
+ parser: P1,
+ delimiter: P2,
+ range: R,
+}
+
+impl<I, P1, P2, R> Parser<I> for SeparatedList<P1, P2, R>
+where
+ I: Input,
+ P1: Parser<I>,
+ P2: Parser<I>,
+ R: RangeBounds<usize>,
+{
+ type Output = Vec<P1::Output>;
+
+ fn run<OM: Mode, EM: Mode, Tracer: Trace<I>>(
+ &self,
+ mut it: InputIter<I>,
+ ) -> ParserResult<I, Self::Output, OM, EM> {
+ Tracer::trace("separated list", it.clone());
+
+ let mut outputs = OM::bind(|| Vec::new());
+ let mut i = 0usize;
+
+ while (Bound::Unbounded, self.range.end_bound()).contains(&i) {
+ 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
+ }
+ _ if self.range.contains(&i) => break,
+ _ => return Err(EM::bind(|| it)),
+ };
+
+ it = match self.delimiter.check(it.clone()) {
+ Ok((rest, _)) => rest,
+ _ if self.range.contains(&i) => break,
+ _ => return Err(EM::bind(|| it)),
+ };
+
+ i += 1;
+ }
+
+ Ok((it, outputs))
+ }
+}
+
pub struct OneOf<It> {
it: It,
}
@@ -889,6 +957,17 @@ mod test {
use super::*;
#[test]
+ fn test_separated_list() {
+ let input = "a b c";
+ let it = InputIter::new(input);
+
+ alpha1()
+ .separated_list(whitespace(), 1..)
+ .check_finished(it)
+ .unwrap();
+ }
+
+ #[test]
fn test_regex_parser() {
let it = InputIter::new("abc 123");
diff --git a/tests/sexpr.rs b/tests/sexpr.rs
index 4434670..328d868 100644
--- a/tests/sexpr.rs
+++ b/tests/sexpr.rs
@@ -32,8 +32,7 @@ fn int<'a>() -> impl Parser<&'a str, Output = Sexpr> {
fn sexpr<'a>(it: InputIter<&'a str>) -> ParserResult<&'a str, Sexpr> {
sexpr
- .followed_by(whitespace())
- .list(0..)
+ .separated_list(whitespace(), 0..)
.delimited_by(tag("("), tag(")"))
.map(|output| Sexpr::List(output))
.or(atom())