Actually just three programming languages in a trenchcoat
1use super::{record_literal::RecordPatternElement, *};
2use crate::{Parser, Spanned};
3use source_span::Span;
4use trilogy_scanner::{Token, TokenType::*};
5
6#[derive(Clone, Debug)]
7pub struct RecordPattern {
8 pub open_brace_pipe: Token,
9 pub elements: Vec<(Pattern, Pattern)>,
10 pub rest: Option<RestPattern>,
11 pub close_brace_pipe: Token,
12 pub span: Span,
13}
14
15impl RecordPattern {
16 pub(crate) fn parse(parser: &mut Parser) -> SyntaxResult<Self> {
17 let open_brace_pipe = parser.expect(OBracePipe).unwrap();
18 Self::parse_elements(parser, open_brace_pipe, vec![])
19 }
20
21 pub(crate) fn parse_elements(
22 parser: &mut Parser,
23 open_brace_pipe: Token,
24 mut elements: Vec<(Pattern, Pattern)>,
25 ) -> SyntaxResult<Self> {
26 let rest = loop {
27 if parser.check(CBracePipe).is_ok() {
28 break None;
29 };
30 if let Ok(spread) = parser.expect(OpDotDot) {
31 if let Ok(dot) = parser.expect(OpDot) {
32 parser.error(ErrorKind::TripleDot { dot: dot.span }.at(spread.span));
33 }
34 break Some(RestPattern::parse(parser, spread)?);
35 }
36 let key = Pattern::parse(parser)?;
37 parser.expect(OpFatArrow).map_err(|token| {
38 parser.expected(
39 token,
40 "expected `=>` to separate key and value of record element pattern",
41 )
42 })?;
43 let value = Pattern::parse(parser)?;
44 elements.push((key, value));
45 if parser.check(CBracePipe).is_ok() {
46 break None;
47 };
48 parser.expect(OpComma).map_err(|token| {
49 parser.expected(token, "expected `,` between record pattern elements")
50 })?;
51 };
52
53 if let Some(rest) = rest {
54 return Self::parse_rest(parser, open_brace_pipe, elements, rest);
55 }
56
57 let close_brace_pipe = parser
58 .expect(CBracePipe)
59 .map_err(|token| parser.expected(token, "expected `|}` to end record pattern"))?;
60
61 Ok(Self {
62 span: open_brace_pipe.span.union(close_brace_pipe.span),
63 open_brace_pipe,
64 elements,
65 rest,
66 close_brace_pipe,
67 })
68 }
69
70 pub(crate) fn parse_rest(
71 parser: &mut Parser,
72 open_brace_pipe: Token,
73 elements: Vec<(Pattern, Pattern)>,
74 rest: RestPattern,
75 ) -> SyntaxResult<Self> {
76 // We'll consume this trailing comma anyway as if it was going to work,
77 // and report an appropriate error. One of few attempts at smart error
78 // handling in this parser so far!
79 if let Ok(comma) = parser.expect(OpComma) {
80 let Ok(end) = parser.expect(CBracePipe) else {
81 let error = SyntaxError::new(
82 comma.span,
83 "a rest (`..`) element must end a record pattern",
84 );
85 parser.error(error.clone());
86 return Err(error);
87 };
88 parser.error(SyntaxError::new(
89 comma.span,
90 "no trailing comma is permitted after the rest (`..`) element in a record pattern",
91 ));
92 return Ok(Self {
93 span: open_brace_pipe.span.union(end.span),
94 open_brace_pipe,
95 elements,
96 rest: Some(rest),
97 close_brace_pipe: end,
98 });
99 }
100
101 let end = parser
102 .expect(CBracePipe)
103 .map_err(|token| parser.expected(token, "expected `|}` to end record pattern"))?;
104
105 Ok(Self {
106 span: open_brace_pipe.span.union(end.span),
107 open_brace_pipe,
108 elements,
109 rest: Some(rest),
110 close_brace_pipe: end,
111 })
112 }
113
114 pub(super) fn parse_from_expression(
115 parser: &mut Parser,
116 open_brace_pipe: Token,
117 elements: Vec<RecordElement>,
118 head_element: RecordPatternElement,
119 ) -> SyntaxResult<Self> {
120 let (mut elements, rest) = elements
121 .into_iter()
122 .try_fold((vec![], None::<Pattern>), |(mut elements, mut rest), element| {
123 match element {
124 RecordElement::Element{ key, value, .. } if rest.is_none() => {
125 elements.push((key.try_into()?, value.try_into()?));
126 },
127 RecordElement::Element{ key, value, .. } => {
128 return Err(SyntaxError::new(
129 key.span().union(value.span()),
130 "no elements may follow the rest element of a record pattern, you might have meant this to be an expression",
131 ));
132 },
133 RecordElement::Spread{ value, .. } if rest.is_none() => {
134 rest = Some(value.try_into()?);
135 },
136 RecordElement::Spread{ span, ..} => {
137 return Err(SyntaxError::new(
138 span,
139 "a record pattern may contain only one rest element, you might have meant this to be an expression",
140 ));
141 },
142 }
143 Ok((elements, rest))
144 })
145 .inspect_err(|error| {
146 parser.error(error.clone());
147 })?;
148 match (rest, head_element) {
149 (None, RecordPatternElement::Element { key, value, .. }) => {
150 elements.push((key, value));
151 Self::parse_elements(parser, open_brace_pipe, elements)
152 }
153 (None, RecordPatternElement::Spread { spread, value, .. }) => Self::parse_rest(
154 parser,
155 open_brace_pipe,
156 elements,
157 RestPattern::new(spread, value),
158 ),
159 (Some(..), element @ RecordPatternElement::Element { .. }) => Err(SyntaxError::new(
160 element.span(),
161 "no elements may follow the rest element in a record pattern, you might have meant this to be an expression",
162 )),
163 (Some(..), element @ RecordPatternElement::Spread { .. }) => Err(SyntaxError::new(
164 element.span(),
165 "a record pattern may contain only one rest element, you might have meant this to be an expression",
166 )),
167 }
168 }
169
170 pub fn start_token(&self) -> &Token {
171 &self.open_brace_pipe
172 }
173
174 pub fn end_token(&self) -> &Token {
175 &self.close_brace_pipe
176 }
177}
178
179impl Spanned for RecordPattern {
180 fn span(&self) -> Span {
181 self.span
182 }
183}
184
185impl TryFrom<RecordLiteral> for RecordPattern {
186 type Error = SyntaxError;
187
188 fn try_from(value: RecordLiteral) -> Result<Self, Self::Error> {
189 let mut head = vec![];
190 let mut rest = None;
191
192 for element in value.elements {
193 match element {
194 RecordElement::Element { key, value, .. } if rest.is_none() => {
195 head.push((key.try_into()?, value.try_into()?))
196 }
197 RecordElement::Spread { spread, value, .. } if rest.is_none() => {
198 rest = Some(RestPattern::try_from((spread, value))?)
199 }
200 RecordElement::Element { .. } | RecordElement::Spread { .. } => {
201 return Err(SyntaxError::new(
202 element.span(),
203 "no elements may follow the rest (`..`) element in a set pattern",
204 ));
205 }
206 }
207 }
208
209 Ok(Self {
210 span: value
211 .open_brace_pipe
212 .span
213 .union(value.close_brace_pipe.span),
214 open_brace_pipe: value.open_brace_pipe,
215 elements: head,
216 rest,
217 close_brace_pipe: value.close_brace_pipe,
218 })
219 }
220}