swamp_analyzer/pattern.rs
1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use crate::Analyzer;
6use swamp_semantic::err::ErrorKind;
7use swamp_semantic::{NormalPattern, Pattern, PatternElement};
8use swamp_types::prelude::EnumVariantType as TypesEnumVariantType;
9use swamp_types::prelude::{EnumVariantCommon, TypeKind, TypeRef};
10
11impl Analyzer<'_> {
12 fn find_variant_in_pattern(
13 &mut self,
14 expression_type: &TypeRef,
15 ast_name: &swamp_ast::Node,
16 ) -> TypesEnumVariantType {
17 let enum_type_ref = if let TypeKind::Enum(enum_type_ref) = &*expression_type.kind {
18 enum_type_ref
19 } else {
20 self.add_err(ErrorKind::ExpectedEnumInPattern, ast_name);
21
22 return TypesEnumVariantType {
23 common: EnumVariantCommon {
24 name: Default::default(),
25 assigned_name: String::new(),
26 container_index: 0,
27 },
28 payload_type: self.types().unit(),
29 };
30 };
31
32 let variant_name = self.get_text(ast_name).to_string();
33
34 if let Some(variant) = enum_type_ref.get_variant(&variant_name) {
35 variant.clone()
36 } else {
37 self.add_err(ErrorKind::UnknownEnumVariantTypeInPattern, ast_name);
38 TypesEnumVariantType {
39 common: EnumVariantCommon {
40 name: Default::default(),
41 assigned_name: String::new(),
42 container_index: 0,
43 },
44 payload_type: self.types().unit(),
45 }
46 }
47 }
48
49 pub(crate) fn analyze_pattern(
50 &mut self,
51 ast_pattern: &swamp_ast::Pattern,
52 expected_condition_type: &TypeRef,
53 ) -> (Pattern, bool, bool) {
54 match ast_pattern {
55 swamp_ast::Pattern::Wildcard(node) => {
56 (Pattern::Wildcard(self.to_node(node)), false, false)
57 }
58 swamp_ast::Pattern::NormalPattern(node, normal_pattern, maybe_guard) => {
59 let (normal_pattern, was_pushed, wanted_mutable) =
60 self.analyze_normal_pattern(node, normal_pattern, expected_condition_type);
61
62 let resolved_guard = if let Some(guard_clause) = maybe_guard {
63 match guard_clause {
64 swamp_ast::GuardClause::Wildcard(_) => None,
65 swamp_ast::GuardClause::Expression(clause_expr) => {
66 Some(self.analyze_bool_argument_expression(clause_expr))
67 }
68 }
69 } else {
70 None
71 };
72 (
73 Pattern::Normal(normal_pattern, resolved_guard),
74 was_pushed,
75 wanted_mutable,
76 )
77 }
78 }
79 }
80
81 #[allow(clippy::too_many_lines)]
82 pub(crate) fn analyze_normal_pattern(
83 &mut self,
84 node: &swamp_ast::Node,
85 ast_normal_pattern: &swamp_ast::NormalPattern,
86 expected_condition_type: &TypeRef,
87 ) -> (NormalPattern, bool, bool) {
88 let mut anyone_wants_mutable = false;
89 match ast_normal_pattern {
90 swamp_ast::NormalPattern::PatternList(elements) => {
91 let mut resolved_elements = Vec::new();
92 let mut scope_is_pushed = false;
93 for element in elements {
94 match element {
95 swamp_ast::PatternElement::Variable(var) => {
96 if !scope_is_pushed {
97 self.push_block_scope("pattern_list one variable");
98 scope_is_pushed = true;
99 }
100 if var.is_mutable.is_some() {
101 anyone_wants_mutable = true;
102 }
103 let variable_ref = self.create_local_variable(
104 &var.name,
105 Option::from(&var.is_mutable),
106 expected_condition_type,
107 false,
108 );
109 resolved_elements.push(PatternElement::Variable(variable_ref));
110 }
111 swamp_ast::PatternElement::Expression(expr) => {
112 return (
113 NormalPattern::Literal(self.create_err(
114 ErrorKind::ExpressionsNotAllowedInLetPattern,
115 &expr.node,
116 )),
117 false,
118 false,
119 );
120 }
121 swamp_ast::PatternElement::Wildcard(node) => {
122 resolved_elements.push(PatternElement::Wildcard(self.to_node(node)));
123 }
124 }
125 }
126 (
127 NormalPattern::PatternList(resolved_elements),
128 scope_is_pushed,
129 anyone_wants_mutable,
130 )
131 }
132
133 swamp_ast::NormalPattern::EnumPattern(variant_name, maybe_elements) => {
134 let mut scope_was_pushed = false;
135 let enum_variant_type_ref =
136 self.find_variant_in_pattern(expected_condition_type, variant_name);
137
138 if let Some(elements) = maybe_elements {
139 let mut resolved_elements = Vec::new();
140 match &*enum_variant_type_ref.payload_type.kind {
141 TypeKind::Tuple(fields_in_order) => {
142 // For tuples, elements must be in order but can be partial
143 if elements.len() > fields_in_order.len() {
144 return (
145 NormalPattern::Literal(self.create_err(
146 ErrorKind::TooManyTupleFields {
147 max: fields_in_order.len(),
148 got: elements.len(),
149 },
150 variant_name,
151 )),
152 false,
153 false,
154 );
155 }
156
157 if !scope_was_pushed {
158 self.push_block_scope("enum tuple");
159 scope_was_pushed = true;
160 }
161
162 // Only zip with as many fields as we have elements
163 for (tuple_element_index, (element, field_type)) in
164 elements.iter().zip(fields_in_order).enumerate()
165 {
166 match element {
167 swamp_ast::PatternElement::Variable(var) => {
168 if var.is_mutable.is_some() {
169 anyone_wants_mutable = true;
170 }
171
172 let variable_ref = self.create_local_variable(
173 &var.name,
174 var.is_mutable.as_ref(),
175 field_type,
176 false,
177 );
178 /*
179 resolved_elements
180 .push(PatternElement::Variable(variable_ref));
181
182 */
183
184 resolved_elements.push(
185 PatternElement::VariableWithFieldIndex(
186 variable_ref,
187 tuple_element_index,
188 ),
189 );
190 }
191 swamp_ast::PatternElement::Wildcard(node) => {
192 resolved_elements
193 .push(PatternElement::Wildcard(self.to_node(node)));
194 }
195 swamp_ast::PatternElement::Expression(expr) => {
196 return (
197 NormalPattern::Literal(self.create_err(
198 ErrorKind::ExpressionsNotAllowedInLetPattern,
199 &expr.node,
200 )),
201 false,
202 false,
203 );
204 }
205 }
206 }
207 }
208 TypeKind::AnonymousStruct(anon_struct_type) => {
209 if !scope_was_pushed {
210 self.push_block_scope("enum struct");
211 scope_was_pushed = true;
212 }
213 // For structs, can match any subset of fields in any order
214 for element in elements {
215 match element {
216 swamp_ast::PatternElement::Variable(var) => {
217 let var_name_str = self.get_text(&var.name).to_string();
218 // Check if the field exists
219 let Some(field_index) = anon_struct_type
220 .field_name_sorted_fields
221 .get_index(&var_name_str)
222 else {
223 return (
224 NormalPattern::Literal(self.create_err(
225 ErrorKind::UnknownField,
226 &var.name,
227 )),
228 false,
229 false,
230 );
231 };
232
233 let Some(field_type) = anon_struct_type
234 .field_name_sorted_fields
235 .get(&var_name_str)
236 else {
237 return (
238 NormalPattern::Literal(self.create_err(
239 ErrorKind::UnknownField,
240 &var.name,
241 )),
242 false,
243 false,
244 );
245 };
246
247 if var.is_mutable.is_some() {
248 anyone_wants_mutable = true;
249 }
250
251 let variable_ref = self.create_local_variable(
252 &var.name,
253 Option::from(&var.is_mutable),
254 &field_type.field_type,
255 false,
256 );
257
258 resolved_elements.push(
259 PatternElement::VariableWithFieldIndex(
260 variable_ref,
261 field_index,
262 ),
263 );
264 }
265 swamp_ast::PatternElement::Wildcard(node) => {
266 resolved_elements
267 .push(PatternElement::Wildcard(self.to_node(node)));
268 }
269 swamp_ast::PatternElement::Expression(expr) => {
270 return (
271 NormalPattern::Literal(self.create_err(
272 ErrorKind::ExpressionsNotAllowedInLetPattern,
273 &expr.node,
274 )),
275 false,
276 false,
277 );
278 }
279 }
280 }
281 }
282 TypeKind::Unit => {
283 if !elements.is_empty() {
284 return (
285 NormalPattern::Literal(self.create_err(
286 ErrorKind::EnumVariantHasNoFields,
287 variant_name,
288 )),
289 false,
290 false,
291 );
292 }
293 }
294 _ => {}
295 }
296
297 (
298 NormalPattern::EnumPattern(enum_variant_type_ref, Some(resolved_elements)),
299 scope_was_pushed,
300 anyone_wants_mutable,
301 )
302 } else {
303 (
304 NormalPattern::EnumPattern(enum_variant_type_ref, None),
305 false,
306 anyone_wants_mutable,
307 )
308 }
309 }
310
311 swamp_ast::NormalPattern::Literal(ast_literal) => (
312 self.analyze_pattern_literal(node, ast_literal, expected_condition_type),
313 false,
314 anyone_wants_mutable,
315 ),
316 }
317 }
318}