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}