swamp_script_analyzer/
pattern.rs

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