1use 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 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 index_within_function = { self.scope.gen_variable_index() };
87 let variables = &mut self
88 .scope
89 .block_scope_stack
90 .last_mut()
91 .expect("block scope should have at least one scope")
92 .variables;
93 let variable_index = variables.len();
94 let should_insert_in_scope = !variable_name_str.starts_with('_');
95
96 let resolved_variable = Variable {
97 name,
98 assigned_name: variable_name_str.clone(),
99 resolved_type: variable_type_ref.clone(),
100 mutable_node,
101 scope_index,
102 variable_index,
103 unique_id_within_function: index_within_function,
104 is_unused: !should_insert_in_scope,
105 };
106
107 let variable_ref = Rc::new(resolved_variable);
108
109 {
110 variables
111 .insert(variable_name_str, variable_ref.clone())
112 .expect("should have checked earlier for variable");
113 }
114
115 Ok((variable_ref, false))
116 }
117 pub(crate) fn create_local_variable(
118 &mut self,
119 variable: &swamp_script_ast::Node,
120 is_mutable: Option<&swamp_script_ast::Node>,
121 variable_type_ref: &Type,
122 ) -> Result<VariableRef, Error> {
123 if variable_type_ref == &Type::Unit {
124 let debug_text = self.get_text(variable);
125 error!(
126 ?debug_text,
127 "panic, tries to create a local variable as a unit"
128 );
129 }
130 assert_ne!(*variable_type_ref, Type::Unit);
131 self.create_local_variable_resolved(
132 &self.to_node(variable),
133 Option::from(&self.to_node_option(is_mutable)),
134 variable_type_ref,
135 )
136 }
137
138 pub(crate) fn create_variable(
139 &mut self,
140 variable: &swamp_script_ast::Variable,
141 variable_type_ref: &Type,
142 ) -> Result<VariableRef, Error> {
143 self.create_local_variable(
144 &variable.name,
145 Option::from(&variable.is_mutable),
146 variable_type_ref,
147 )
148 }
149
150 pub(crate) fn create_local_variable_resolved(
151 &mut self,
152 variable: &Node,
153 is_mutable: Option<&Node>,
154 variable_type_ref: &Type,
155 ) -> Result<VariableRef, Error> {
156 if let Some(_existing_variable) = self.try_find_local_variable(variable) {
157 return Err(
158 self.create_err_resolved(ErrorKind::OverwriteVariableNotAllowedHere, variable)
159 );
160 }
161 let variable_str = self.get_text_resolved(variable).to_string();
162
163 let scope_index = self.scope.block_scope_stack.len() - 1;
164
165 let index = { self.scope.gen_variable_index() };
166
167 let should_insert_in_scope = !variable_str.starts_with('_');
168 let variables = &mut self
169 .scope
170 .block_scope_stack
171 .last_mut()
172 .expect("block scope should have at least one scope")
173 .variables;
174
175 let resolved_variable = Variable {
176 name: variable.clone(),
177 assigned_name: variable_str.clone(),
178
179 resolved_type: variable_type_ref.clone(),
180 mutable_node: is_mutable.cloned(),
181 scope_index,
182 variable_index: variables.len(),
183 unique_id_within_function: index,
184 is_unused: !should_insert_in_scope,
185 };
186
187 let variable_ref = Rc::new(resolved_variable);
188
189 if !should_insert_in_scope && is_mutable.is_some() {
190 return Err(self.create_err_resolved(ErrorKind::UnusedVariablesCanNotBeMut, variable));
191 }
192 if should_insert_in_scope {
193 variables
194 .insert(variable_str, variable_ref.clone())
195 .expect("should have checked earlier for variable");
196 self.function_variables.push(variable_ref.clone());
197 }
198
199 Ok(variable_ref)
200 }
201
202 #[allow(clippy::unnecessary_wraps)]
203 pub(crate) fn create_local_variable_generated(
204 &mut self,
205 variable_str: &str,
206 is_mutable: bool,
207 variable_type_ref: &Type,
208 ) -> Result<VariableRef, Error> {
209 let scope_index = self.scope.block_scope_stack.len() - 1;
210
211 let index_within_function = self.scope.gen_variable_index();
212
213 let variables = &mut self
214 .scope
215 .block_scope_stack
216 .last_mut()
217 .expect("block scope should have at least one scope")
218 .variables;
219
220 let should_insert_in_scope = !variable_str.starts_with('_');
221
222 let resolved_variable = Variable {
223 name: Node::default(),
224 assigned_name: variable_str.to_string(),
225 resolved_type: variable_type_ref.clone(),
226 mutable_node: if is_mutable {
227 Some(Node::default())
228 } else {
229 None
230 },
231 scope_index,
232 variable_index: variables.len(),
233 unique_id_within_function: index_within_function,
234 is_unused: !should_insert_in_scope,
235 };
236
237 let variable_ref = Rc::new(resolved_variable);
238
239 if should_insert_in_scope {
240 variables
241 .insert(variable_str.to_string(), variable_ref.clone())
242 .expect("should have checked earlier for variable");
243 }
244
245 Ok(variable_ref)
246 }
247
248 pub(crate) fn create_variable_binding_for_with(
249 &mut self,
250 ast_variable: &swamp_script_ast::Variable,
251 converted_expression: MutOrImmutableExpression,
252 ) -> Result<Expression, Error> {
253 let expression_type = converted_expression.ty().clone();
254 let variable_ref = self.create_local_variable(
255 &ast_variable.name,
256 ast_variable.is_mutable.as_ref(),
257 &expression_type,
258 )?;
259 let expr_kind =
260 ExpressionKind::VariableDefinition(variable_ref, Box::from(converted_expression));
261
262 let expr = self.create_expr(expr_kind, expression_type, &ast_variable.name);
263
264 Ok(expr)
265 }
266}