Actually just three programming languages in a trenchcoat
1use super::*;
2use crate::{Parser, Spanned};
3use source_span::Span;
4use trilogy_scanner::{Token, TokenType::*};
5
6/// An array pattern.
7///
8/// ```trilogy
9/// [1, 2, 3, ..rest, 5, 6, 7]
10/// ```
11#[derive(Clone, Debug)]
12pub struct ArrayPattern {
13 pub open_bracket: Token,
14 /// The head elements, which come before the optional `rest` element. Will be all elements if `rest` is `None`.
15 pub head: Vec<Pattern>,
16 /// A single optional spread elements, containing the rest of the elements.
17 pub rest: Option<RestPattern>,
18 /// The tail elements, which follow the spread. Will be empty if `rest` is `None`.
19 pub tail: Vec<Pattern>,
20 pub close_bracket: Token,
21}
22
23impl ArrayPattern {
24 pub(crate) fn parse_elements(
25 parser: &mut Parser,
26 open_bracket: Token,
27 mut head: Vec<Pattern>,
28 ) -> SyntaxResult<Self> {
29 let rest = loop {
30 if parser.check(CBrack).is_ok() {
31 break None;
32 };
33 if let Ok(spread) = parser.expect(OpDotDot) {
34 if let Ok(dot) = parser.expect(OpDot) {
35 parser.error(ErrorKind::TripleDot { dot: dot.span }.at(spread.span));
36 }
37 break Some(RestPattern::parse(parser, spread)?);
38 }
39 head.push(Pattern::parse(parser)?);
40 if parser.check(CBrack).is_ok() {
41 break None;
42 };
43 parser.expect(OpComma).map_err(|token| {
44 parser.expected(
45 token,
46 "expected `]` to end or `,` to continue array pattern",
47 )
48 })?;
49 };
50
51 if let Some(rest) = rest {
52 return Self::parse_rest(parser, open_bracket, head, rest, vec![]);
53 }
54
55 let close_bracket = parser
56 .expect(CBrack)
57 .map_err(|token| parser.expected(token, "expected `]` to end array pattern"))?;
58
59 Ok(Self {
60 open_bracket,
61 head,
62 rest: None,
63 tail: vec![],
64 close_bracket,
65 })
66 }
67
68 pub(crate) fn parse_rest(
69 parser: &mut Parser,
70 start: Token,
71 head: Vec<Pattern>,
72 rest: RestPattern,
73 mut tail: Vec<Pattern>,
74 ) -> SyntaxResult<Self> {
75 // at this point, either we:
76 // * saw the `]`, so there will be no rest; or
77 // * parsed a rest pattern, so there must be a comma next before allowing
78 // more elements, or there is no comma so we must be at end of array.
79 if parser.expect(OpComma).is_ok() {
80 loop {
81 if parser.check(CBrack).is_ok() {
82 break;
83 };
84 if let Ok(token) = parser.expect(OpDotDot) {
85 // Avoid an error cascade here by parsing the rest pattern as a regular
86 // pattern, discarding the "restness".
87 parser.error(SyntaxError::new(
88 token.span,
89 "array patterns may contain at most one rest (`..`) segment",
90 ));
91 }
92 tail.push(Pattern::parse(parser)?);
93 if parser.check(CBrack).is_ok() {
94 break;
95 };
96 parser.expect(OpComma).map_err(|token| {
97 parser.expected(
98 token,
99 "expected `]` to end or `,` to continue array pattern",
100 )
101 })?;
102 }
103 }
104
105 let end = parser
106 .expect(CBrack)
107 .map_err(|token| parser.expected(token, "expected `]` to end array pattern"))?;
108
109 Ok(Self {
110 open_bracket: start,
111 head,
112 rest: Some(rest),
113 tail,
114 close_bracket: end,
115 })
116 }
117
118 pub(crate) fn parse(parser: &mut Parser) -> SyntaxResult<Self> {
119 let start = parser
120 .expect(OBrack)
121 .expect("Caller should have found this");
122 Self::parse_elements(parser, start, vec![])
123 }
124
125 pub(crate) fn parse_from_expression(
126 parser: &mut Parser,
127 start: Token,
128 elements: Punctuated<ArrayElement>,
129 (spread, next): (Option<Token>, Pattern),
130 ) -> SyntaxResult<Self> {
131 let (head, rest, tail) = elements.into_iter().try_fold(
132 (vec![], None, vec![]),
133 |(mut head, mut spread, mut tail), element| {
134 match element {
135 ArrayElement::Element(expr) if spread.is_none() => {
136 head.push(expr.try_into()?);
137 }
138 ArrayElement::Element(expr) => tail.push(expr.try_into()?),
139 ArrayElement::Spread(sp, expr) if spread.is_none() => {
140 spread = Some(RestPattern::try_from((sp, expr))?);
141 }
142 ArrayElement::Spread(token, element) => return Err(SyntaxError::new(
143 token.span.union(element.span()),
144 "an array pattern may contain only one rest element, or you might have meant this to be an array expression",
145 )),
146 }
147 Ok((head, spread, tail))
148 },
149 ).inspect_err(|error| {
150 parser.error(error.clone());
151 })?;
152 match (spread, rest) {
153 (None, None) => Self::parse_elements(parser, start, head),
154 (None, Some(rest)) => Self::parse_rest(parser, start, head, rest, tail),
155 (Some(token), None) => {
156 Self::parse_rest(parser, start, head, RestPattern::new(token, next), tail)
157 }
158 (Some(token), Some(..)) => {
159 let error = SyntaxError::new(
160 token.span.union(next.span()),
161 "an array pattern may only contain a single spread element, or you might have meant this to be an array expression",
162 );
163 parser.error(error.clone());
164 Err(error)
165 }
166 }
167 }
168}
169
170impl TryFrom<ArrayLiteral> for ArrayPattern {
171 type Error = SyntaxError;
172
173 fn try_from(value: ArrayLiteral) -> Result<Self, Self::Error> {
174 let mut head = vec![];
175 let mut tail = vec![];
176 let mut rest = None;
177
178 for element in value.elements {
179 match element {
180 ArrayElement::Element(val) if rest.is_none() => head.push(val.try_into()?),
181 ArrayElement::Element(val) => tail.push(val.try_into()?),
182 ArrayElement::Spread(spread, val) if rest.is_none() => {
183 rest = Some(RestPattern::try_from((spread, val))?)
184 }
185 ArrayElement::Spread(.., val) => {
186 return Err(SyntaxError::new(
187 val.span(),
188 "an array pattern may contain only a single spread element",
189 ));
190 }
191 }
192 }
193
194 Ok(Self {
195 open_bracket: value.open_bracket,
196 head,
197 rest,
198 tail,
199 close_bracket: value.close_bracket,
200 })
201 }
202}
203
204impl Spanned for ArrayPattern {
205 fn span(&self) -> Span {
206 self.open_bracket.span.union(self.close_bracket.span)
207 }
208}
209
210#[cfg(test)]
211mod test {
212 use super::*;
213
214 test_parse!(arraypat_empty: "[]" => Pattern::parse => Pattern::Array(..));
215 test_parse!(arraypat_one: "[1]" => Pattern::parse => Pattern::Array(..));
216 test_parse!(arraypat_one_tc: "[1, ]" => Pattern::parse => Pattern::Array(..));
217 test_parse!(arraypat_many: "[1, 2, 3]" => Pattern::parse => Pattern::Array(..));
218 test_parse!(arraypat_many_tc: "[1, 2, 3, ]" => Pattern::parse => Pattern::Array(..));
219 test_parse!(arraypat_spread_middle: "[1, 2, ..a, 4, 5]" => Pattern::parse => Pattern::Array(..));
220 test_parse!(arraypat_spread_end: "[1, 2, ..a]" => Pattern::parse => Pattern::Array(..));
221 test_parse!(arraypat_spread_start: "[..a, 1, 2]" => Pattern::parse => Pattern::Array(..));
222
223 test_parse_error!(arraypat_spread_multi: "[..a, 1, ..b]" => Pattern::parse => "array patterns may contain at most one rest (`..`) segment");
224 test_parse_error!(arraypat_expression: "[f 2]" => Pattern::parse);
225 test_parse_error!(arraypat_empty_tc: "[,]" => Pattern::parse);
226 test_parse_error!(arraypat_missing_item: "[1,,]" => Pattern::parse);
227 test_parse_error!(arraypat_missing_end: "[1,2," => Pattern::parse);
228 test_parse_error!(arraypat_incomplete: "[1, 2" => Pattern::parse => "expected `]` to end or `,` to continue array pattern");
229 test_parse_error!(arraypat_mismatched: "[1, 2)" => Pattern::parse => "expected `]` to end or `,` to continue array pattern");
230}