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::expression => super::super::parse_expression(pair),
108 _ => super::super::parse_expression(pair),
109 }
110}
111
112pub 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 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 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}