swamp_analyzer/
variable.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 source_map_node::Node;
8use std::rc::Rc;
9use swamp_semantic::{
10    BlockScopeMode, Expression, ExpressionKind, MutOrImmutableExpression, Variable, VariableRef,
11};
12use swamp_types::prelude::*;
13
14impl Analyzer<'_> {
15    fn try_find_local_variable(&self, node: &Node) -> Option<&VariableRef> {
16        let current_scope = self
17            .scope
18            .block_scope_stack
19            .iter()
20            .last()
21            .expect("no scope stack available");
22
23        let variable_text = self.get_text_resolved(node).to_string();
24
25        current_scope.variables.get(&variable_text)
26    }
27
28    #[allow(unused)]
29    pub(crate) fn find_variable(
30        &self,
31        variable: &swamp_ast::Variable,
32    ) -> Result<VariableRef, Error> {
33        self.try_find_variable(&variable.name).map_or_else(
34            || Err(self.create_err(ErrorKind::UnknownVariable, &variable.name)),
35            Ok,
36        )
37    }
38
39    pub(crate) fn try_find_variable(&self, node: &swamp_ast::Node) -> Option<VariableRef> {
40        let variable_text = self.get_text(node);
41
42        for scope in self.scope.block_scope_stack.iter().rev() {
43            if let Some(value) = scope.variables.get(&variable_text.to_string()) {
44                return Some(value.clone());
45            }
46            if scope.mode == BlockScopeMode::Closed {
47                break;
48            }
49        }
50
51        None
52    }
53
54    pub(crate) fn create_local_variable(
55        &mut self,
56        variable: &swamp_ast::Node,
57        is_mutable: Option<&swamp_ast::Node>,
58        variable_type_ref: &Type,
59    ) -> Result<VariableRef, Error> {
60        debug_assert!(variable_type_ref.is_concrete());
61        self.create_local_variable_resolved(
62            &self.to_node(variable),
63            Option::from(&self.to_node_option(is_mutable)),
64            variable_type_ref,
65        )
66    }
67
68    pub(crate) fn create_variable(
69        &mut self,
70        variable: &swamp_ast::Variable,
71        variable_type_ref: &Type,
72    ) -> Result<VariableRef, Error> {
73        self.create_local_variable(
74            &variable.name,
75            Option::from(&variable.is_mutable),
76            variable_type_ref,
77        )
78    }
79
80    pub(crate) fn create_local_variable_resolved(
81        &mut self,
82        variable: &Node,
83        is_mutable: Option<&Node>,
84        variable_type_ref: &Type,
85    ) -> Result<VariableRef, Error> {
86        if let Some(_existing_variable) = self.try_find_local_variable(variable) {
87            return Err(
88                self.create_err_resolved(ErrorKind::OverwriteVariableNotAllowedHere, variable)
89            );
90        }
91        let variable_str = self.get_text_resolved(variable).to_string();
92
93        let scope_index = self.scope.block_scope_stack.len() - 1;
94
95        let index = { self.scope.gen_variable_index() };
96
97        let should_insert_in_scope = !variable_str.starts_with('_');
98        let variables = &mut self
99            .scope
100            .block_scope_stack
101            .last_mut()
102            .expect("block scope should have at least one scope")
103            .variables;
104
105        let resolved_variable = Variable {
106            name: variable.clone(),
107            assigned_name: variable_str.clone(),
108
109            resolved_type: variable_type_ref.clone(),
110            mutable_node: is_mutable.cloned(),
111            scope_index,
112            variable_index: variables.len(),
113            unique_id_within_function: index,
114            is_unused: !should_insert_in_scope,
115        };
116
117        let variable_ref = Rc::new(resolved_variable);
118
119        if !should_insert_in_scope && is_mutable.is_some() {
120            return Err(self.create_err_resolved(ErrorKind::UnusedVariablesCanNotBeMut, variable));
121        }
122        if should_insert_in_scope {
123            variables
124                .insert(variable_str, variable_ref.clone())
125                .expect("should have checked earlier for variable");
126            self.function_variables.push(variable_ref.clone());
127        }
128
129        Ok(variable_ref)
130    }
131
132    #[allow(clippy::unnecessary_wraps)]
133    pub(crate) fn create_local_variable_generated(
134        &mut self,
135        variable_str: &str,
136        is_mutable: bool,
137        variable_type_ref: &Type,
138    ) -> Result<VariableRef, Error> {
139        let scope_index = self.scope.block_scope_stack.len() - 1;
140
141        let index_within_function = self.scope.gen_variable_index();
142
143        let variables = &mut self
144            .scope
145            .block_scope_stack
146            .last_mut()
147            .expect("block scope should have at least one scope")
148            .variables;
149
150        let should_insert_in_scope = !variable_str.starts_with('_');
151
152        let resolved_variable = Variable {
153            name: Node::default(),
154            assigned_name: variable_str.to_string(),
155            resolved_type: variable_type_ref.clone(),
156            mutable_node: if is_mutable {
157                Some(Node::default())
158            } else {
159                None
160            },
161            scope_index,
162            variable_index: variables.len(),
163            unique_id_within_function: index_within_function,
164            is_unused: !should_insert_in_scope,
165        };
166
167        let variable_ref = Rc::new(resolved_variable);
168
169        if should_insert_in_scope {
170            variables
171                .insert(variable_str.to_string(), variable_ref.clone())
172                .expect("should have checked earlier for variable");
173        }
174
175        Ok(variable_ref)
176    }
177
178    pub(crate) fn create_variable_binding_for_with(
179        &mut self,
180        ast_variable: &swamp_ast::Variable,
181        converted_expression: MutOrImmutableExpression,
182    ) -> Result<Expression, Error> {
183        let expression_type = converted_expression.ty().clone();
184        let variable_ref = self.create_local_variable(
185            &ast_variable.name,
186            ast_variable.is_mutable.as_ref(),
187            &expression_type,
188        )?;
189        let expr_kind =
190            ExpressionKind::VariableDefinition(variable_ref, Box::from(converted_expression));
191
192        let expr = self.create_expr(expr_kind, expression_type, &ast_variable.name);
193
194        Ok(expr)
195    }
196}