swamp_script_analyzer/
variable.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 */
5use crate::Analyzer;
6use crate::err::{Error, ErrorKind};
7use std::rc::Rc;
8use swamp_script_node::Node;
9use swamp_script_semantic::{
10    BlockScopeMode, Expression, ExpressionKind, MutOrImmutableExpression, Variable, VariableRef,
11};
12use swamp_script_types::prelude::*;
13use tracing::error;
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_script_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_script_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_script_ast::Node,
57        is_mutable: Option<&swamp_script_ast::Node>,
58        variable_type_ref: &Type,
59    ) -> Result<VariableRef, Error> {
60        if variable_type_ref == &Type::Unit {
61            let debug_text = self.get_text(variable);
62            error!(
63                ?debug_text,
64                "panic, tries to create a local variable as a unit"
65            );
66        }
67        assert_ne!(*variable_type_ref, Type::Unit);
68        self.create_local_variable_resolved(
69            &self.to_node(variable),
70            Option::from(&self.to_node_option(is_mutable)),
71            variable_type_ref,
72        )
73    }
74
75    pub(crate) fn create_variable(
76        &mut self,
77        variable: &swamp_script_ast::Variable,
78        variable_type_ref: &Type,
79    ) -> Result<VariableRef, Error> {
80        self.create_local_variable(
81            &variable.name,
82            Option::from(&variable.is_mutable),
83            variable_type_ref,
84        )
85    }
86
87    pub(crate) fn create_local_variable_resolved(
88        &mut self,
89        variable: &Node,
90        is_mutable: Option<&Node>,
91        variable_type_ref: &Type,
92    ) -> Result<VariableRef, Error> {
93        if let Some(_existing_variable) = self.try_find_local_variable(variable) {
94            return Err(
95                self.create_err_resolved(ErrorKind::OverwriteVariableNotAllowedHere, variable)
96            );
97        }
98        let variable_str = self.get_text_resolved(variable).to_string();
99
100        let scope_index = self.scope.block_scope_stack.len() - 1;
101
102        let index = { self.scope.gen_variable_index() };
103
104        let should_insert_in_scope = !variable_str.starts_with('_');
105        let variables = &mut self
106            .scope
107            .block_scope_stack
108            .last_mut()
109            .expect("block scope should have at least one scope")
110            .variables;
111
112        let resolved_variable = Variable {
113            name: variable.clone(),
114            assigned_name: variable_str.clone(),
115
116            resolved_type: variable_type_ref.clone(),
117            mutable_node: is_mutable.cloned(),
118            scope_index,
119            variable_index: variables.len(),
120            unique_id_within_function: index,
121            is_unused: !should_insert_in_scope,
122        };
123
124        let variable_ref = Rc::new(resolved_variable);
125
126        if !should_insert_in_scope && is_mutable.is_some() {
127            return Err(self.create_err_resolved(ErrorKind::UnusedVariablesCanNotBeMut, variable));
128        }
129        if should_insert_in_scope {
130            variables
131                .insert(variable_str, variable_ref.clone())
132                .expect("should have checked earlier for variable");
133            self.function_variables.push(variable_ref.clone());
134        }
135
136        Ok(variable_ref)
137    }
138
139    #[allow(clippy::unnecessary_wraps)]
140    pub(crate) fn create_local_variable_generated(
141        &mut self,
142        variable_str: &str,
143        is_mutable: bool,
144        variable_type_ref: &Type,
145    ) -> Result<VariableRef, Error> {
146        let scope_index = self.scope.block_scope_stack.len() - 1;
147
148        let index_within_function = self.scope.gen_variable_index();
149
150        let variables = &mut self
151            .scope
152            .block_scope_stack
153            .last_mut()
154            .expect("block scope should have at least one scope")
155            .variables;
156
157        let should_insert_in_scope = !variable_str.starts_with('_');
158
159        let resolved_variable = Variable {
160            name: Node::default(),
161            assigned_name: variable_str.to_string(),
162            resolved_type: variable_type_ref.clone(),
163            mutable_node: if is_mutable {
164                Some(Node::default())
165            } else {
166                None
167            },
168            scope_index,
169            variable_index: variables.len(),
170            unique_id_within_function: index_within_function,
171            is_unused: !should_insert_in_scope,
172        };
173
174        let variable_ref = Rc::new(resolved_variable);
175
176        if should_insert_in_scope {
177            variables
178                .insert(variable_str.to_string(), variable_ref.clone())
179                .expect("should have checked earlier for variable");
180        }
181
182        Ok(variable_ref)
183    }
184
185    pub(crate) fn create_variable_binding_for_with(
186        &mut self,
187        ast_variable: &swamp_script_ast::Variable,
188        converted_expression: MutOrImmutableExpression,
189    ) -> Result<Expression, Error> {
190        let expression_type = converted_expression.ty().clone();
191        let variable_ref = self.create_local_variable(
192            &ast_variable.name,
193            ast_variable.is_mutable.as_ref(),
194            &expression_type,
195        )?;
196        let expr_kind =
197            ExpressionKind::VariableDefinition(variable_ref, Box::from(converted_expression));
198
199        let expr = self.create_expr(expr_kind, expression_type, &ast_variable.name);
200
201        Ok(expr)
202    }
203}