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 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 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}