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 crate::err::{Error, ErrorKind};
7use swamp_semantic::{NormalPattern, Pattern, PatternElement};
8use swamp_types::prelude::*;
9
10impl Analyzer<'_> {
11    fn find_variant_in_pattern(
12        &self,
13        expression_type: &Type,
14        ast_name: &swamp_ast::Node,
15    ) -> Result<EnumVariantType, Error> {
16        let enum_type_ref = match expression_type {
17            Type::Enum(enum_type_ref) => enum_type_ref,
18            _ => Err(self.create_err(ErrorKind::ExpectedEnumInPattern, ast_name))?,
19        };
20
21        let variant_name = self.get_text(ast_name).to_string();
22
23        enum_type_ref.get_variant(&variant_name).map_or_else(
24            || Err(self.create_err(ErrorKind::UnknownEnumVariantTypeInPattern, ast_name)),
25            |found_variant| Ok(found_variant.clone()),
26        )
27    }
28
29    pub(crate) fn analyze_pattern(
30        &mut self,
31        ast_pattern: &swamp_ast::Pattern,
32        expected_condition_type: &Type,
33    ) -> Result<(Pattern, bool, bool), Error> {
34        match ast_pattern {
35            swamp_ast::Pattern::Wildcard(node) => {
36                Ok((Pattern::Wildcard(self.to_node(node)), false, false))
37            }
38            swamp_ast::Pattern::NormalPattern(node, normal_pattern, maybe_guard) => {
39                let (normal_pattern, was_pushed, wanted_mutable) =
40                    self.analyze_normal_pattern(node, normal_pattern, expected_condition_type)?;
41
42                let resolved_guard = if let Some(guard_clause) = maybe_guard {
43                    match guard_clause {
44                        swamp_ast::GuardClause::Wildcard(_) => None,
45                        swamp_ast::GuardClause::Expression(clause_expr) => {
46                            Some(self.analyze_bool_argument_expression(clause_expr)?)
47                        }
48                    }
49                } else {
50                    None
51                };
52                Ok((
53                    Pattern::Normal(normal_pattern, resolved_guard),
54                    was_pushed,
55                    wanted_mutable,
56                ))
57            }
58        }
59    }
60
61    #[allow(clippy::too_many_lines)]
62    pub(crate) fn analyze_normal_pattern(
63        &mut self,
64        node: &swamp_ast::Node,
65        ast_normal_pattern: &swamp_ast::NormalPattern,
66        expected_condition_type: &Type,
67    ) -> Result<(NormalPattern, bool, bool), Error> {
68        let mut anyone_wants_mutable = false;
69        match ast_normal_pattern {
70            swamp_ast::NormalPattern::PatternList(elements) => {
71                let mut resolved_elements = Vec::new();
72                let mut scope_is_pushed = false;
73                for element in elements {
74                    match element {
75                        swamp_ast::PatternElement::Variable(var) => {
76                            if !scope_is_pushed {
77                                self.push_block_scope("pattern_list one variable");
78                                scope_is_pushed = true;
79                            }
80                            if var.is_mutable.is_some() {
81                                anyone_wants_mutable = true;
82                            }
83                            let variable_ref = self.create_local_variable(
84                                &var.name,
85                                Option::from(&var.is_mutable),
86                                expected_condition_type,
87                            )?;
88                            resolved_elements.push(PatternElement::Variable(variable_ref));
89                        }
90                        swamp_ast::PatternElement::Expression(expr) => {
91                            return Err(self.create_err(
92                                ErrorKind::ExpressionsNotAllowedInLetPattern,
93                                &expr.node,
94                            ));
95                        }
96                        swamp_ast::PatternElement::Wildcard(node) => {
97                            resolved_elements.push(PatternElement::Wildcard(self.to_node(node)));
98                        }
99                    }
100                }
101                Ok((
102                    NormalPattern::PatternList(resolved_elements),
103                    scope_is_pushed,
104                    anyone_wants_mutable,
105                ))
106            }
107
108            swamp_ast::NormalPattern::EnumPattern(variant_name, maybe_elements) => {
109                let mut scope_was_pushed = false;
110                let enum_variant_type_ref =
111                    self.find_variant_in_pattern(expected_condition_type, variant_name)?;
112
113                if let Some(elements) = maybe_elements {
114                    let mut resolved_elements = Vec::new();
115                    match enum_variant_type_ref {
116                        EnumVariantType::Tuple(ref tuple_type) => {
117                            // For tuples, elements must be in order but can be partial
118                            if elements.len() > tuple_type.fields_in_order.len() {
119                                return Err(self.create_err(
120                                    ErrorKind::TooManyTupleFields {
121                                        max: tuple_type.fields_in_order.len(),
122                                        got: elements.len(),
123                                    },
124                                    variant_name,
125                                ));
126                            }
127
128                            if !scope_was_pushed {
129                                self.push_block_scope("enum tuple");
130                                scope_was_pushed = true;
131                            }
132
133                            // Only zip with as many fields as we have elements
134                            for (element, field_type) in
135                                elements.iter().zip(&tuple_type.fields_in_order)
136                            {
137                                match element {
138                                    swamp_ast::PatternElement::Variable(var) => {
139                                        if var.is_mutable.is_some() {
140                                            anyone_wants_mutable = true;
141                                        }
142                                        let variable_ref = self.create_local_variable(
143                                            &var.name,
144                                            var.is_mutable.as_ref(),
145                                            field_type,
146                                        )?;
147                                        resolved_elements
148                                            .push(PatternElement::Variable(variable_ref));
149                                    }
150                                    swamp_ast::PatternElement::Wildcard(node) => {
151                                        resolved_elements
152                                            .push(PatternElement::Wildcard(self.to_node(node)));
153                                    }
154                                    swamp_ast::PatternElement::Expression(expr) => {
155                                        return Err(self.create_err(
156                                            ErrorKind::ExpressionsNotAllowedInLetPattern,
157                                            &expr.node,
158                                        ));
159                                    }
160                                }
161                            }
162                        }
163                        EnumVariantType::Struct(ref struct_type) => {
164                            if !scope_was_pushed {
165                                self.push_block_scope("enum struct");
166                                scope_was_pushed = true;
167                            }
168                            // For structs, can match any subset of fields in any order
169                            for element in elements {
170                                match element {
171                                    swamp_ast::PatternElement::Variable(var) => {
172                                        let var_name_str = self.get_text(&var.name).to_string();
173                                        // Check if the field exists
174                                        let field_index = struct_type
175                                            .anon_struct
176                                            .field_name_sorted_fields
177                                            .get_index(&var_name_str)
178                                            .ok_or_else(|| {
179                                                self.create_err(ErrorKind::UnknownField, &var.name)
180                                            })?;
181
182                                        let field_type = struct_type
183                                            .anon_struct
184                                            .field_name_sorted_fields
185                                            .get(&var_name_str)
186                                            .ok_or_else(|| {
187                                                self.create_err(ErrorKind::UnknownField, &var.name)
188                                            })?;
189
190                                        if var.is_mutable.is_some() {
191                                            anyone_wants_mutable = true;
192                                        }
193
194                                        let variable_ref = self.create_local_variable(
195                                            &var.name,
196                                            Option::from(&var.is_mutable),
197                                            &field_type.field_type,
198                                        )?;
199
200                                        resolved_elements.push(
201                                            PatternElement::VariableWithFieldIndex(
202                                                variable_ref,
203                                                field_index,
204                                            ),
205                                        );
206                                    }
207                                    swamp_ast::PatternElement::Wildcard(node) => {
208                                        resolved_elements
209                                            .push(PatternElement::Wildcard(self.to_node(node)));
210                                    }
211                                    swamp_ast::PatternElement::Expression(expr) => {
212                                        return Err(self.create_err(
213                                            ErrorKind::ExpressionsNotAllowedInLetPattern,
214                                            &expr.node,
215                                        ));
216                                    }
217                                }
218                            }
219                        }
220                        EnumVariantType::Nothing(_) => {
221                            if !elements.is_empty() {
222                                return Err(self
223                                    .create_err(ErrorKind::EnumVariantHasNoFields, variant_name));
224                            }
225                        }
226                    }
227
228                    Ok((
229                        NormalPattern::EnumPattern(enum_variant_type_ref, Some(resolved_elements)),
230                        scope_was_pushed,
231                        anyone_wants_mutable,
232                    ))
233                } else {
234                    Ok((
235                        NormalPattern::EnumPattern(enum_variant_type_ref, None),
236                        false,
237                        anyone_wants_mutable,
238                    ))
239                }
240            }
241
242            swamp_ast::NormalPattern::Literal(ast_literal) => Ok((
243                self.analyze_pattern_literal(node, ast_literal, expected_condition_type)?,
244                false,
245                anyone_wants_mutable,
246            )),
247        }
248    }
249}