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