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 mut inner = pair.into_inner();
216            let enum_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
217                message: "expected enum name in constructor pattern".to_string(),
218                location: Some(pair_loc.clone()),
219            })?;
220            let variant_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
221                message: "expected variant name in constructor pattern".to_string(),
222                location: Some(pair_loc.clone()),
223            })?;
224            let enum_name = Some(enum_pair.as_str().to_string());
225            let variant = variant_pair.as_str().to_string();
226            let fields = if let Some(payload) = inner.next() {
227                parse_constructor_payload(payload)?
228            } else {
229                crate::ast::PatternConstructorFields::Unit
230            };
231            Ok(Pattern::Constructor {
232                enum_name,
233                variant,
234                fields,
235            })
236        }
237        Rule::pattern_unqualified_constructor => {
238            let mut inner = pair.into_inner();
239            let name_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
240                message: "expected constructor name in pattern".to_string(),
241                location: Some(pair_loc.clone()),
242            })?;
243            let variant = match name_pair.as_rule() {
244                Rule::pattern_constructor_name => name_pair
245                    .clone()
246                    .into_inner()
247                    .next()
248                    .map(|p| p.as_str().to_string())
249                    .unwrap_or_else(|| name_pair.as_str().to_string()),
250                Rule::pattern_constructor_keyword => name_pair.as_str().to_string(),
251                _ => name_pair.as_str().to_string(),
252            };
253            let fields = if let Some(payload_pair) = inner.next() {
254                parse_constructor_payload(payload_pair)?
255            } else {
256                crate::ast::PatternConstructorFields::Unit
257            };
258            Ok(Pattern::Constructor {
259                enum_name: None,
260                variant,
261                fields,
262            })
263        }
264        _ => Err(ShapeError::ParseError {
265            message: format!("unexpected constructor pattern rule: {:?}", pair.as_rule()),
266            location: Some(pair_loc),
267        }),
268    }
269}
270
271fn parse_constructor_payload(pair: Pair<Rule>) -> Result<crate::ast::PatternConstructorFields> {
272    let pair_loc = pair_location(&pair);
273    match pair.as_rule() {
274        Rule::pattern_constructor_payload => {
275            let inner = pair
276                .into_inner()
277                .next()
278                .ok_or_else(|| ShapeError::ParseError {
279                    message: "expected constructor payload".to_string(),
280                    location: Some(pair_loc),
281                })?;
282            parse_constructor_payload(inner)
283        }
284        Rule::pattern_constructor_tuple => {
285            let mut patterns = Vec::new();
286            for inner in pair.into_inner() {
287                patterns.push(parse_pattern(inner)?);
288            }
289            Ok(crate::ast::PatternConstructorFields::Tuple(patterns))
290        }
291        Rule::pattern_constructor_struct => {
292            let mut fields = Vec::new();
293            for field in pair.into_inner() {
294                if field.as_rule() == Rule::pattern_field {
295                    let field_loc = pair_location(&field);
296                    let mut field_inner = field.into_inner();
297                    let name = field_inner
298                        .next()
299                        .ok_or_else(|| ShapeError::ParseError {
300                            message: "expected field name in constructor pattern".to_string(),
301                            location: Some(field_loc.clone()),
302                        })?
303                        .as_str()
304                        .to_string();
305                    let pattern = if let Some(pattern_pair) = field_inner.next() {
306                        parse_pattern(pattern_pair)?
307                    } else {
308                        // Shorthand: { radius } == { radius: radius }
309                        crate::ast::Pattern::Identifier(name.clone())
310                    };
311                    fields.push((name, pattern));
312                }
313            }
314            Ok(crate::ast::PatternConstructorFields::Struct(fields))
315        }
316        _ => Err(ShapeError::ParseError {
317            message: format!("unexpected constructor payload rule: {:?}", pair.as_rule()),
318            location: Some(pair_loc),
319        }),
320    }
321}