1use crate::{
2 Cursor, CursorSink, KindSet, Parse, Parser, Peek, Result as ParserResult, SemanticEq, Span, ToCursors, ToSpan,
3 token_macros::Comma,
4};
5use bumpalo::{
6 Bump,
7 collections::{Vec, vec::IntoIter},
8};
9use std::{
10 ops::{Index, IndexMut},
11 slice::{Iter, IterMut},
12};
13
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
35pub struct CommaSeparated<'a, T, const MIN: usize = 1> {
36 items: Vec<'a, (T, Option<Comma>)>,
37}
38
39impl<'a, T, const MIN: usize> CommaSeparated<'a, T, MIN> {
40 pub fn new_in(bump: &'a Bump) -> Self {
41 Self { items: Vec::new_in(bump) }
42 }
43
44 pub fn is_empty(&self) -> bool {
45 self.items.is_empty()
46 }
47
48 pub fn len(&self) -> usize {
49 self.items.len()
50 }
51}
52
53impl<'a, T: Peek<'a>, const MIN: usize> Peek<'a> for CommaSeparated<'a, T, MIN> {
54 const PEEK_KINDSET: KindSet = T::PEEK_KINDSET;
55
56 #[inline(always)]
57 fn peek<Iter>(p: &Parser<'a, Iter>, c: Cursor) -> bool
58 where
59 Iter: Iterator<Item = crate::Cursor> + Clone,
60 {
61 T::peek(p, c)
62 }
63}
64
65impl<'a, T: Parse<'a> + Peek<'a>, const MIN: usize> Parse<'a> for CommaSeparated<'a, T, MIN> {
66 fn parse<Iter>(p: &mut Parser<'a, Iter>) -> ParserResult<Self>
67 where
68 Iter: Iterator<Item = crate::Cursor> + Clone,
69 {
70 let mut items = Self::new_in(p.bump());
71 if MIN == 0 && !<T>::peek(p, p.peek_n(1)) {
72 return Ok(items);
73 }
74 loop {
75 let item = p.parse::<T>()?;
76 if p.peek::<Comma>() {
77 let checkpoint = p.checkpoint();
78 let comma = p.parse::<Comma>()?;
79 if !<T>::peek(p, p.peek_n(1)) {
80 p.rewind(checkpoint);
81 items.items.push((item, None));
82 break;
83 }
84 items.items.push((item, Some(comma)));
85 } else {
86 items.items.push((item, None));
87 break;
88 }
89 }
90 if MIN > items.len() {
91 p.parse::<Comma>()?;
92 }
93 Ok(items)
94 }
95}
96
97impl<'a, T: ToCursors, const MIN: usize> ToCursors for CommaSeparated<'a, T, MIN> {
98 fn to_cursors(&self, s: &mut impl CursorSink) {
99 ToCursors::to_cursors(&self.items, s);
100 }
101}
102
103impl<'a, T: ToSpan, const MIN: usize> ToSpan for CommaSeparated<'a, T, MIN> {
104 fn to_span(&self) -> Span {
105 let first = self.items[0].to_span();
106 first + self.items.last().map(|t| t.to_span()).unwrap_or(first)
107 }
108}
109
110impl<'a, T: SemanticEq, const MIN: usize> SemanticEq for CommaSeparated<'a, T, MIN> {
111 fn semantic_eq(&self, other: &Self) -> bool {
112 self.items.semantic_eq(&other.items)
113 }
114}
115
116impl<'a, T, const MIN: usize> IntoIterator for CommaSeparated<'a, T, MIN> {
117 type Item = (T, Option<Comma>);
118 type IntoIter = IntoIter<'a, Self::Item>;
119
120 fn into_iter(self) -> Self::IntoIter {
121 self.items.into_iter()
122 }
123}
124
125impl<'a, 'b, T, const MIN: usize> IntoIterator for &'b CommaSeparated<'a, T, MIN> {
126 type Item = &'b (T, Option<Comma>);
127 type IntoIter = Iter<'b, (T, Option<Comma>)>;
128
129 fn into_iter(self) -> Self::IntoIter {
130 self.items.iter()
131 }
132}
133
134impl<'a, 'b, T, const MIN: usize> IntoIterator for &'b mut CommaSeparated<'a, T, MIN> {
135 type Item = &'b mut (T, Option<Comma>);
136 type IntoIter = IterMut<'b, (T, Option<Comma>)>;
137
138 fn into_iter(self) -> Self::IntoIter {
139 self.items.iter_mut()
140 }
141}
142
143impl<'a, T, I, const MIN: usize> Index<I> for CommaSeparated<'a, T, MIN>
144where
145 I: ::core::slice::SliceIndex<[(T, Option<Comma>)]>,
146{
147 type Output = I::Output;
148
149 #[inline]
150 fn index(&self, index: I) -> &Self::Output {
151 Index::index(&self.items, index)
152 }
153}
154
155impl<'a, T, I, const MIN: usize> IndexMut<I> for CommaSeparated<'a, T, MIN>
156where
157 I: ::core::slice::SliceIndex<[(T, Option<Comma>)]>,
158{
159 #[inline]
160 fn index_mut(&mut self, index: I) -> &mut Self::Output {
161 IndexMut::index_mut(&mut self.items, index)
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::{EmptyAtomSet, T, test_helpers::*};
169
170 #[test]
171 fn size_test() {
172 assert_eq!(std::mem::size_of::<CommaSeparated<T![Ident]>>(), 32);
173 }
174
175 #[test]
176 fn test_writes() {
177 assert_parse!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "foo");
178 assert_parse!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "one,two");
179 assert_parse!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "one,two,three");
180 assert_parse!(EmptyAtomSet::ATOMS, CommaSeparated<(T![Number], CommaSeparated<T![Ident]>)>, "1 foo, 2 bar");
181 }
182
183 #[test]
184 fn test_spans() {
185 assert_parse_span!(
186 EmptyAtomSet::ATOMS,
187 CommaSeparated<T![Ident]>,
188 r#"
189 foo bar
190 ^^^
191 "#
192 );
193 assert_parse_span!(
194 EmptyAtomSet::ATOMS,
195 CommaSeparated<T![Ident]>,
196 r#"
197 foo, bar, baz 1
198 ^^^^^^^^^^^^^
199 "#
200 );
201 }
202
203 #[test]
204 fn test_peek() {
205 assert_peek_false!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "");
206 assert_peek_false!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, ",");
207 }
208
209 #[test]
210 fn test_errors() {
211 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "one,two,three,");
212 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident]>, "one two");
213 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident], 2>, "one");
214 assert_parse_error!(EmptyAtomSet::ATOMS, CommaSeparated<T![Ident], 3>, "one, two");
215 }
216}