Skip to main content

shape_ast/parser/expressions/control_flow/
pattern_matching.rs

1//! Pattern matching expression parsing
2//!
3//! This module handles parsing of pattern matching expressions:
4//! - Match expressions
5//! - Pattern parsing (wildcard, identifier, literal, array, object, constructor)
6
7use 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
15/// Parse match expression
16pub 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            // Process remaining pairs
38            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                        // This might be a guard or the body
45                        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::expression => super::super::parse_expression(pair),
108        _ => super::super::parse_expression(pair),
109    }
110}
111
112/// Parse pattern
113pub fn parse_pattern(pair: Pair<Rule>) -> Result<Pattern> {
114    let pair_loc = pair_location(&pair);
115    match pair.as_rule() {
116        Rule::pattern => {
117            let inner = pair
118                .into_inner()
119                .next()
120                .ok_or_else(|| ShapeError::ParseError {
121                    message: "expected pattern content".to_string(),
122                    location: Some(pair_loc),
123                })?;
124            parse_pattern(inner)
125        }
126        Rule::pattern_wildcard => Ok(Pattern::Wildcard),
127        Rule::pattern_typed => {
128            let mut inner = pair.into_inner();
129            let name = inner
130                .next()
131                .ok_or_else(|| ShapeError::ParseError {
132                    message: "expected identifier in typed pattern".to_string(),
133                    location: Some(pair_loc.clone()),
134                })?
135                .as_str()
136                .to_string();
137            let type_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
138                message: "expected type annotation in typed pattern".to_string(),
139                location: Some(pair_loc),
140            })?;
141            let type_annotation = crate::parser::parse_type_annotation(type_pair)?;
142            Ok(Pattern::Typed {
143                name,
144                type_annotation,
145            })
146        }
147        Rule::pattern_identifier => Ok(Pattern::Identifier(pair.as_str().to_string())),
148        Rule::pattern_literal => {
149            let literal_pair = pair
150                .into_inner()
151                .next()
152                .ok_or_else(|| ShapeError::ParseError {
153                    message: "expected literal in pattern".to_string(),
154                    location: Some(pair_loc.clone()),
155                })?;
156            let literal = super::super::literals::parse_literal(literal_pair)?;
157            match literal {
158                Expr::Literal(lit, _) => Ok(Pattern::Literal(lit)),
159                _ => Err(ShapeError::ParseError {
160                    message: "expected literal in pattern".to_string(),
161                    location: Some(pair_loc),
162                }),
163            }
164        }
165        Rule::pattern_array => {
166            let mut patterns = Vec::new();
167            for inner in pair.into_inner() {
168                patterns.push(parse_pattern(inner)?);
169            }
170            Ok(Pattern::Array(patterns))
171        }
172        Rule::pattern_object => {
173            let mut fields = Vec::new();
174            for field in pair.into_inner() {
175                if field.as_rule() == Rule::pattern_field {
176                    let mut field_inner = field.into_inner();
177                    let name = field_inner
178                        .next()
179                        .ok_or_else(|| ShapeError::ParseError {
180                            message: "expected field name in object pattern".to_string(),
181                            location: Some(pair_loc.clone()),
182                        })?
183                        .as_str()
184                        .to_string();
185                    // Shorthand: `{x, y}` is equivalent to `{x: x, y: y}`
186                    let pattern = if let Some(pattern_pair) = field_inner.next() {
187                        parse_pattern(pattern_pair)?
188                    } else {
189                        Pattern::Identifier(name.clone())
190                    };
191                    fields.push((name, pattern));
192                }
193            }
194            Ok(Pattern::Object(fields))
195        }
196        Rule::pattern_constructor => {
197            let mut inner = pair.into_inner();
198            let ctor_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
199                message: "expected constructor pattern".to_string(),
200                location: Some(pair_loc.clone()),
201            })?;
202            parse_constructor_pattern(ctor_pair)
203        }
204        _ => Err(ShapeError::ParseError {
205            message: format!("unexpected pattern rule: {:?}", pair.as_rule()),
206            location: Some(pair_loc),
207        }),
208    }
209}
210
211fn parse_constructor_pattern(pair: Pair<Rule>) -> Result<Pattern> {
212    let pair_loc = pair_location(&pair);
213    match pair.as_rule() {
214        Rule::pattern_qualified_constructor => {
215            let inner = pair.into_inner();
216            let mut ident_segments = Vec::new();
217            let mut payload_pair = None;
218            for child in inner {
219                match child.as_rule() {
220                    Rule::ident | Rule::variant_ident => {
221                        ident_segments.push(child.as_str().to_string())
222                    }
223                    Rule::pattern_constructor_payload => payload_pair = Some(child),
224                    _ => {}
225                }
226            }
227            if ident_segments.len() < 2 {
228                return Err(ShapeError::ParseError {
229                    message: "expected Enum::Variant in constructor pattern".to_string(),
230                    location: Some(pair_loc),
231                });
232            }
233            let variant = ident_segments.pop().unwrap();
234            let enum_path = if ident_segments.len() == 1 {
235                crate::ast::TypePath::simple(ident_segments.remove(0))
236            } else {
237                crate::ast::TypePath::from_segments(ident_segments)
238            };
239            let fields = if let Some(payload) = payload_pair {
240                parse_constructor_payload(payload)?
241            } else {
242                crate::ast::PatternConstructorFields::Unit
243            };
244            Ok(Pattern::Constructor {
245                enum_name: Some(enum_path),
246                variant,
247                fields,
248            })
249        }
250        Rule::pattern_unqualified_constructor => {
251            let mut inner = pair.into_inner();
252            let name_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
253                message: "expected constructor name in pattern".to_string(),
254                location: Some(pair_loc.clone()),
255            })?;
256            let variant = match name_pair.as_rule() {
257                Rule::pattern_constructor_name => name_pair
258                    .clone()
259                    .into_inner()
260                    .next()
261                    .map(|p| p.as_str().to_string())
262                    .unwrap_or_else(|| name_pair.as_str().to_string()),
263                Rule::pattern_constructor_keyword => name_pair.as_str().to_string(),
264                _ => name_pair.as_str().to_string(),
265            };
266            let fields = if let Some(payload_pair) = inner.next() {
267                parse_constructor_payload(payload_pair)?
268            } else {
269                crate::ast::PatternConstructorFields::Unit
270            };
271            Ok(Pattern::Constructor {
272                enum_name: None,
273                variant,
274                fields,
275            })
276        }
277        _ => Err(ShapeError::ParseError {
278            message: format!("unexpected constructor pattern rule: {:?}", pair.as_rule()),
279            location: Some(pair_loc),
280        }),
281    }
282}
283
284fn parse_constructor_payload(pair: Pair<Rule>) -> Result<crate::ast::PatternConstructorFields> {
285    let pair_loc = pair_location(&pair);
286    match pair.as_rule() {
287        Rule::pattern_constructor_payload => {
288            let inner = pair
289                .into_inner()
290                .next()
291                .ok_or_else(|| ShapeError::ParseError {
292                    message: "expected constructor payload".to_string(),
293                    location: Some(pair_loc),
294                })?;
295            parse_constructor_payload(inner)
296        }
297        Rule::pattern_constructor_tuple => {
298            let mut patterns = Vec::new();
299            for inner in pair.into_inner() {
300                patterns.push(parse_pattern(inner)?);
301            }
302            Ok(crate::ast::PatternConstructorFields::Tuple(patterns))
303        }
304        Rule::pattern_constructor_struct => {
305            let mut fields = Vec::new();
306            for field in pair.into_inner() {
307                if field.as_rule() == Rule::pattern_field {
308                    let field_loc = pair_location(&field);
309                    let mut field_inner = field.into_inner();
310                    let name = field_inner
311                        .next()
312                        .ok_or_else(|| ShapeError::ParseError {
313                            message: "expected field name in constructor pattern".to_string(),
314                            location: Some(field_loc.clone()),
315                        })?
316                        .as_str()
317                        .to_string();
318                    let pattern = if let Some(pattern_pair) = field_inner.next() {
319                        parse_pattern(pattern_pair)?
320                    } else {
321                        // Shorthand: { radius } == { radius: radius }
322                        crate::ast::Pattern::Identifier(name.clone())
323                    };
324                    fields.push((name, pattern));
325                }
326            }
327            Ok(crate::ast::PatternConstructorFields::Struct(fields))
328        }
329        _ => Err(ShapeError::ParseError {
330            message: format!("unexpected constructor payload rule: {:?}", pair.as_rule()),
331            location: Some(pair_loc),
332        }),
333    }
334}