diff --git a/src/ast/basic.rs b/src/ast/basic.rs index b9e5df0..9790f8c 100644 --- a/src/ast/basic.rs +++ b/src/ast/basic.rs @@ -96,3 +96,16 @@ impl HasSpan for Separated { } } } + +#[derive(Debug, Clone)] +pub struct BoundedSeparated { + pub elems: Vec<(Space, E, Space)>, + pub trailing: Option, + pub span: Span, +} + +impl HasSpan for BoundedSeparated { + fn span(&self) -> Span { + self.span + } +} diff --git a/src/parser/basic.rs b/src/parser/basic.rs index 2a65c68..300e953 100644 --- a/src/parser/basic.rs +++ b/src/parser/basic.rs @@ -3,7 +3,7 @@ use chumsky::prelude::*; use chumsky::text::Character; -use crate::ast::{Ident, Line, Separated, Space}; +use crate::ast::{BoundedSeparated, Ident, Line, Separated, Space}; use crate::span::Span; pub type Error = Simple; @@ -82,3 +82,43 @@ pub fn separated_by( }) .boxed() } + +pub fn bounded_separated( + space: impl Parser + Clone + 'static, + start: impl Parser + 'static, + end: impl Parser + 'static, + separator: impl Parser + 'static, + elem: impl Parser + Clone + 'static, +) -> EParser> { + start + .ignore_then(space.clone()) + .then( + elem.clone() + .then(space.clone()) + .then_ignore(separator) + .then(space.clone()) + .repeated(), + ) + .then(elem.then(space).or_not()) + .then_ignore(end) + .map_with_span(|((s0, first_elems), last_elem), span| { + let mut space_before_elem = s0; + let mut elems = vec![]; + for ((elem, s1), s2) in first_elems { + elems.push((space_before_elem, elem, s1)); + space_before_elem = s2; + } + let trailing = if let Some((elem, s1)) = last_elem { + elems.push((space_before_elem, elem, s1)); + None + } else { + Some(space_before_elem) + }; + BoundedSeparated { + elems, + trailing, + span, + } + }) + .boxed() +} diff --git a/src/pretty/basic.rs b/src/pretty/basic.rs index b6812b0..5d4ae8e 100644 --- a/src/pretty/basic.rs +++ b/src/pretty/basic.rs @@ -1,6 +1,8 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; -use crate::ast::{Ident, Separated}; +use crate::ast::{BoundedSeparated, Ident, Separated}; + +use super::NEST_DEPTH; impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Ident { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { @@ -45,3 +47,32 @@ impl Separated { } } } + +impl BoundedSeparated { + pub fn pretty<'a, D, FE>( + self, + allocator: &'a D, + start: DocBuilder<'a, D>, + end: DocBuilder<'a, D>, + separator: DocBuilder<'a, D>, + elem_pretty: FE, + ) -> DocBuilder<'a, D> + where + D: DocAllocator<'a>, + D::Doc: Clone, + FE: Fn(E) -> DocBuilder<'a, D>, + { + allocator + .intersperse( + self.elems + .into_iter() + .map(|(s0, elem, s1)| allocator.line().append(elem_pretty(elem))), + separator.clone(), + ) + .append(self.trailing.map(|s| separator)) + .nest(NEST_DEPTH) + .append(allocator.line()) + .enclose(start, end) + .group() + } +}