shape_ast/parser/expressions/control_flow/
pattern_matching.rs1use crate::ast::{Expr, MatchArm, MatchExpr, Pattern};
8use crate::error::{Result, ShapeError};
9use crate::parser::Rule;
10use pest::iterators::Pair;
11
12use super::super::super::pair_span;
13use crate::parser::pair_location;
14
15pub fn parse_match_expr(pair: Pair<Rule>) -> Result<Expr> {
17 let span = pair_span(&pair);
18 let pair_loc = pair_location(&pair);
19 let mut inner = pair.into_inner();
20
21 let scrutinee_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
22 message: "expected expression to match against".to_string(),
23 location: Some(pair_loc),
24 })?;
25 let scrutinee = parse_match_scrutinee(scrutinee_pair)?;
26 let mut arms = Vec::new();
27
28 for arm_pair in inner {
29 if arm_pair.as_rule() == Rule::match_arm {
30 let arm_inner_pairs: Vec<_> = arm_pair.into_inner().collect();
31 let pattern_span = Some(pair_span(&arm_inner_pairs[0]));
32 let pattern = parse_pattern(arm_inner_pairs[0].clone())?;
33
34 let mut guard = None;
35 let mut body = None;
36
37 for i in 1..arm_inner_pairs.len() {
39 let next = &arm_inner_pairs[i];
40 if next.as_rule() == Rule::expression {
41 if body.is_none() && guard.is_some() {
42 body = Some(super::super::parse_expression(next.clone())?);
43 } else if guard.is_none() {
44 if i < arm_inner_pairs.len() - 1 {
46 guard = Some(Box::new(super::super::parse_expression(next.clone())?));
47 } else {
48 body = Some(super::super::parse_expression(next.clone())?);
49 }
50 }
51 }
52 }
53
54 let body = body.ok_or_else(|| ShapeError::ParseError {
55 message: "Match arm missing body".to_string(),
56 location: None,
57 })?;
58
59 arms.push(MatchArm {
60 pattern,
61 guard,
62 body: Box::new(body),
63 pattern_span,
64 });
65 }
66 }
67
68 Ok(Expr::Match(
69 Box::new(MatchExpr {
70 scrutinee: Box::new(scrutinee),
71 arms,
72 }),
73 span,
74 ))
75}
76
77fn parse_match_scrutinee(pair: Pair<Rule>) -> Result<Expr> {
78 let pair_loc = pair_location(&pair);
79 match pair.as_rule() {
80 Rule::match_scrutinee => {
81 let inner = pair
82 .into_inner()
83 .next()
84 .ok_or_else(|| ShapeError::ParseError {
85 message: "expected match scrutinee".to_string(),
86 location: Some(pair_loc),
87 })?;
88 parse_match_scrutinee(inner)
89 }
90 Rule::match_scrutinee_ident => {
91 let ident_pair = pair
92 .into_inner()
93 .next()
94 .ok_or_else(|| ShapeError::ParseError {
95 message: "expected identifier in match scrutinee".to_string(),
96 location: Some(pair_loc),
97 })?;
98 Ok(Expr::Identifier(
99 ident_pair.as_str().to_string(),
100 pair_span(&ident_pair),
101 ))
102 }
103 Rule::ident => Ok(Expr::Identifier(
104 pair.as_str().to_string(),
105 pair_span(&pair),
106 )),
107 Rule::match_scrutinee_struct => {
113 let inner = pair
114 .into_inner()
115 .next()
116 .ok_or_else(|| ShapeError::ParseError {
117 message: "expected struct literal in match scrutinee".to_string(),
118 location: Some(pair_loc),
119 })?;
120 super::super::parse_primary_expr(inner)
121 }
122 Rule::struct_literal => super::super::parse_primary_expr(pair),
123 Rule::expression => super::super::parse_expression(pair),
124 _ => super::super::parse_expression(pair),
125 }
126}
127
128pub fn parse_pattern(pair: Pair<Rule>) -> Result<Pattern> {
130 let pair_loc = pair_location(&pair);
131 match pair.as_rule() {
132 Rule::pattern => {
133 let inner = pair
134 .into_inner()
135 .next()
136 .ok_or_else(|| ShapeError::ParseError {
137 message: "expected pattern content".to_string(),
138 location: Some(pair_loc),
139 })?;
140 parse_pattern(inner)
141 }
142 Rule::pattern_wildcard => Ok(Pattern::Wildcard),
143 Rule::pattern_typed => {
144 let mut inner = pair.into_inner();
145 let name = inner
146 .next()
147 .ok_or_else(|| ShapeError::ParseError {
148 message: "expected identifier in typed pattern".to_string(),
149 location: Some(pair_loc.clone()),
150 })?
151 .as_str()
152 .to_string();
153 let type_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
154 message: "expected type annotation in typed pattern".to_string(),
155 location: Some(pair_loc),
156 })?;
157 let type_annotation = crate::parser::parse_type_annotation(type_pair)?;
158 Ok(Pattern::Typed {
159 name,
160 type_annotation,
161 })
162 }
163 Rule::pattern_identifier => Ok(Pattern::Identifier(pair.as_str().to_string())),
164 Rule::pattern_literal => {
165 let literal_pair = pair
166 .into_inner()
167 .next()
168 .ok_or_else(|| ShapeError::ParseError {
169 message: "expected literal in pattern".to_string(),
170 location: Some(pair_loc.clone()),
171 })?;
172 let literal = super::super::literals::parse_literal(literal_pair)?;
173 match literal {
174 Expr::Literal(lit, _) => Ok(Pattern::Literal(lit)),
175 _ => Err(ShapeError::ParseError {
176 message: "expected literal in pattern".to_string(),
177 location: Some(pair_loc),
178 }),
179 }
180 }
181 Rule::pattern_array => {
182 let mut patterns = Vec::new();
183 for inner in pair.into_inner() {
184 patterns.push(parse_pattern(inner)?);
185 }
186 Ok(Pattern::Array(patterns))
187 }
188 Rule::pattern_object => {
189 let mut fields = Vec::new();
190 for field in pair.into_inner() {
191 if field.as_rule() == Rule::pattern_field {
192 let mut field_inner = field.into_inner();
193 let name = field_inner
194 .next()
195 .ok_or_else(|| ShapeError::ParseError {
196 message: "expected field name in object pattern".to_string(),
197 location: Some(pair_loc.clone()),
198 })?
199 .as_str()
200 .to_string();
201 let pattern = if let Some(pattern_pair) = field_inner.next() {
203 parse_pattern(pattern_pair)?
204 } else {
205 Pattern::Identifier(name.clone())
206 };
207 fields.push((name, pattern));
208 }
209 }
210 Ok(Pattern::Object(fields))
211 }
212 Rule::pattern_constructor => {
213 let mut inner = pair.into_inner();
214 let ctor_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
215 message: "expected constructor pattern".to_string(),
216 location: Some(pair_loc.clone()),
217 })?;
218 parse_constructor_pattern(ctor_pair)
219 }
220 _ => Err(ShapeError::ParseError {
221 message: format!("unexpected pattern rule: {:?}", pair.as_rule()),
222 location: Some(pair_loc),
223 }),
224 }
225}
226
227fn parse_constructor_pattern(pair: Pair<Rule>) -> Result<Pattern> {
228 let pair_loc = pair_location(&pair);
229 match pair.as_rule() {
230 Rule::pattern_qualified_constructor => {
231 let inner = pair.into_inner();
232 let mut ident_segments = Vec::new();
233 let mut payload_pair = None;
234 for child in inner {
235 match child.as_rule() {
236 Rule::ident | Rule::variant_ident => {
237 ident_segments.push(child.as_str().to_string())
238 }
239 Rule::pattern_constructor_payload => payload_pair = Some(child),
240 _ => {}
241 }
242 }
243 if ident_segments.len() < 2 {
244 return Err(ShapeError::ParseError {
245 message: "expected Enum::Variant in constructor pattern".to_string(),
246 location: Some(pair_loc),
247 });
248 }
249 let variant = ident_segments.pop().unwrap();
250 let enum_path = if ident_segments.len() == 1 {
251 crate::ast::TypePath::simple(ident_segments.remove(0))
252 } else {
253 crate::ast::TypePath::from_segments(ident_segments)
254 };
255 let fields = if let Some(payload) = payload_pair {
256 parse_constructor_payload(payload)?
257 } else {
258 crate::ast::PatternConstructorFields::Unit
259 };
260 Ok(Pattern::Constructor {
261 enum_name: Some(enum_path),
262 variant,
263 fields,
264 })
265 }
266 Rule::pattern_unqualified_constructor => {
267 let mut inner = pair.into_inner();
268 let name_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
269 message: "expected constructor name in pattern".to_string(),
270 location: Some(pair_loc.clone()),
271 })?;
272 let variant = match name_pair.as_rule() {
273 Rule::pattern_constructor_name => name_pair
274 .clone()
275 .into_inner()
276 .next()
277 .map(|p| p.as_str().to_string())
278 .unwrap_or_else(|| name_pair.as_str().to_string()),
279 Rule::pattern_constructor_keyword => name_pair.as_str().to_string(),
280 _ => name_pair.as_str().to_string(),
281 };
282 let fields = if let Some(payload_pair) = inner.next() {
283 parse_constructor_payload(payload_pair)?
284 } else {
285 crate::ast::PatternConstructorFields::Unit
286 };
287 Ok(Pattern::Constructor {
288 enum_name: None,
289 variant,
290 fields,
291 })
292 }
293 _ => Err(ShapeError::ParseError {
294 message: format!("unexpected constructor pattern rule: {:?}", pair.as_rule()),
295 location: Some(pair_loc),
296 }),
297 }
298}
299
300fn parse_constructor_payload(pair: Pair<Rule>) -> Result<crate::ast::PatternConstructorFields> {
301 let pair_loc = pair_location(&pair);
302 match pair.as_rule() {
303 Rule::pattern_constructor_payload => {
304 let inner = pair
305 .into_inner()
306 .next()
307 .ok_or_else(|| ShapeError::ParseError {
308 message: "expected constructor payload".to_string(),
309 location: Some(pair_loc),
310 })?;
311 parse_constructor_payload(inner)
312 }
313 Rule::pattern_constructor_tuple => {
314 let mut patterns = Vec::new();
315 for inner in pair.into_inner() {
316 patterns.push(parse_pattern(inner)?);
317 }
318 Ok(crate::ast::PatternConstructorFields::Tuple(patterns))
319 }
320 Rule::pattern_constructor_struct => {
321 let mut fields = Vec::new();
322 for field in pair.into_inner() {
323 if field.as_rule() == Rule::pattern_field {
324 let field_loc = pair_location(&field);
325 let mut field_inner = field.into_inner();
326 let name = field_inner
327 .next()
328 .ok_or_else(|| ShapeError::ParseError {
329 message: "expected field name in constructor pattern".to_string(),
330 location: Some(field_loc.clone()),
331 })?
332 .as_str()
333 .to_string();
334 let pattern = if let Some(pattern_pair) = field_inner.next() {
335 parse_pattern(pattern_pair)?
336 } else {
337 crate::ast::Pattern::Identifier(name.clone())
339 };
340 fields.push((name, pattern));
341 }
342 }
343 Ok(crate::ast::PatternConstructorFields::Struct(fields))
344 }
345 _ => Err(ShapeError::ParseError {
346 message: format!("unexpected constructor payload rule: {:?}", pair.as_rule()),
347 location: Some(pair_loc),
348 }),
349 }
350}