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