1use crate::Analyzer;
6use source_map_node::Node;
7use std::rc::Rc;
8use swamp_semantic::err::ErrorKind;
9use swamp_semantic::{
10 ArgumentExpression, BlockScopeMode, Expression, ExpressionKind, Variable, VariableRef,
11 VariableType,
12};
13use swamp_semantic::{ScopeInfo, VariableScopes};
14use swamp_types::prelude::*;
15
16pub(crate) const fn allocate_next_register(scope: &mut ScopeInfo) -> usize {
19 scope.total_scopes.current_register += 1;
20 scope.total_scopes.current_register
21}
22
23pub(crate) const fn allocate_next_register_from_variable_scopes(
26 scope: &mut VariableScopes,
27) -> usize {
28 scope.current_register += 1;
29 scope.current_register
30}
31
32impl Analyzer<'_> {
33 fn try_find_local_variable(&self, node: &Node) -> Option<&VariableRef> {
34 let current_scope = self
35 .scope
36 .active_scope
37 .block_scope_stack
38 .iter()
39 .last()
40 .expect("no scope stack available");
41
42 let variable_text = self.get_text_resolved(node).to_string();
43
44 current_scope.lookup.get(&variable_text)
45 }
46
47 #[allow(unused)]
48 pub(crate) fn find_variable(&mut self, variable: &swamp_ast::Variable) -> VariableRef {
49 if let Some(x) = self.try_find_variable(&variable.name) {
50 x
51 } else {
52 self.add_err(ErrorKind::UnknownVariable, &variable.name);
53 VariableRef::from(Variable::create_err(self.types().unit()))
54 }
55 }
56
57 pub(crate) fn try_find_variable(&self, node: &swamp_ast::Node) -> Option<VariableRef> {
58 let variable_text = self.get_text(node);
59
60 for scope in self.scope.active_scope.block_scope_stack.iter().rev() {
61 if let Some(value) = scope.lookup.get(&variable_text.to_string()) {
62 return Some(value.clone());
63 }
64 if scope.mode == BlockScopeMode::Closed {
65 break;
66 }
67 }
68
69 None
70 }
71
72 pub(crate) fn create_parameter_resolved(
73 &mut self,
74 variable: &Node,
75 is_mutable: Option<&Node>,
76 variable_type_ref: &TypeRef,
77 ) {
78 let (variable_ref, _name_str) = self.create_variable_like_resolved(
79 variable,
80 is_mutable,
81 variable_type_ref,
82 VariableType::Parameter,
83 );
84 }
85
86 pub(crate) fn create_local_variable(
87 &mut self,
88 variable: &swamp_ast::Node,
89 is_mutable: Option<&swamp_ast::Node>,
90 variable_type_ref: &TypeRef,
91 concrete_check: bool,
92 ) -> VariableRef {
93 let debug_text = self.get_text(variable);
94 if !debug_text.starts_with('_')
95 && concrete_check
96 && !variable_type_ref.can_be_stored_in_variable()
97 {
98 self.add_err(
99 ErrorKind::VariableTypeMustBeBlittable(variable_type_ref.clone()),
100 variable,
101 );
102 return Rc::new(Variable::create_err(self.types().unit()));
103 }
104 self.create_local_variable_resolved(
105 &self.to_node(variable),
106 Option::from(&self.to_node_option(is_mutable)),
107 variable_type_ref,
108 )
109 }
110
111 pub(crate) fn create_variable(
112 &mut self,
113 variable: &swamp_ast::Variable,
114 variable_type_ref: &TypeRef,
115 ) -> VariableRef {
116 self.create_local_variable(
117 &variable.name,
118 Option::from(&variable.is_mutable),
119 variable_type_ref,
120 true,
121 )
122 }
123
124 pub(crate) fn create_local_variable_resolved(
125 &mut self,
126 variable: &Node,
127 is_mutable: Option<&Node>,
128 variable_type_ref: &TypeRef,
129 ) -> VariableRef {
130 let (variable_ref, variable_str) = self.create_variable_like_resolved(
131 variable,
132 is_mutable,
133 variable_type_ref,
134 VariableType::Local,
135 );
136
137 variable_ref
138 }
139
140 pub(crate) fn create_variable_like_resolved(
141 &mut self,
142 variable: &Node,
143 is_mutable: Option<&Node>,
144 variable_type_ref: &TypeRef,
145 variable_type: VariableType,
146 ) -> (VariableRef, String) {
147 if let Some(_existing_variable) = self.try_find_local_variable(variable) {
148 self.add_err_resolved(ErrorKind::OverwriteVariableNotAllowedHere, variable);
149
150 let error_var_ref = VariableRef::new(Variable {
151 name: Default::default(),
152 assigned_name: "err".to_string(),
153 resolved_type: self.types().unit(),
154 mutable_node: None,
155 variable_type,
156 scope_index: 0,
157 variable_index: 0,
158 unique_id_within_function: 0,
159 virtual_register: 0,
160 is_unused: false,
161 });
162
163 return (error_var_ref, "err".to_string());
164 }
165 let variable_str = self.get_text_resolved(variable).to_string();
166
167 let scope_index = self.scope.active_scope.block_scope_stack.len() - 1;
168
169 let index = { self.scope.active_scope.emit_variable_index() };
170
171 let should_insert_in_scope = !variable_str.starts_with('_');
172
173 let variables_len = &mut self
174 .scope
175 .active_scope
176 .block_scope_stack
177 .last_mut()
178 .expect("block scope should have at least one scope")
179 .variables
180 .len();
181
182 if !should_insert_in_scope && is_mutable.is_some() {
184 self.add_err_resolved(ErrorKind::UnusedVariablesCanNotBeMut, variable);
185
186 let error_var_ref = VariableRef::new(Variable {
187 name: Default::default(),
188 assigned_name: "err".to_string(),
189 resolved_type: self.types().unit(),
190 mutable_node: None,
191 variable_type,
192 scope_index: 0,
193 variable_index: 0,
194 unique_id_within_function: 0,
195 virtual_register: 0,
196 is_unused: false,
197 });
198
199 return (error_var_ref, "err".to_string());
200 }
201
202 let virtual_register = allocate_next_register(&mut self.scope);
204
205 let resolved_variable = Variable {
206 name: variable.clone(),
207 assigned_name: variable_str.clone(),
208 variable_type,
209 resolved_type: variable_type_ref.clone(),
210 mutable_node: is_mutable.cloned(),
211 scope_index,
212 variable_index: *variables_len,
213 unique_id_within_function: index,
214 virtual_register: virtual_register as u8,
215 is_unused: !should_insert_in_scope,
216 };
217
218 let variable_ref = Rc::new(resolved_variable);
219
220 if should_insert_in_scope {
221 let lookups = &mut self
222 .scope
223 .active_scope
224 .block_scope_stack
225 .last_mut()
226 .expect("block scope should have at least one scope")
227 .lookup;
228 lookups
229 .insert(variable_str.clone(), variable_ref.clone())
230 .expect("should have checked earlier for variable");
231 }
232
233 let variables = &mut self
234 .scope
235 .active_scope
236 .block_scope_stack
237 .last_mut()
238 .expect("block scope should have at least one scope")
239 .variables;
240 variables
241 .insert(variable_ref.unique_id_within_function, variable_ref.clone())
242 .expect("should have checked earlier for variable");
243
244 self.scope
245 .total_scopes
246 .all_variables
247 .push(variable_ref.clone());
248
249 (variable_ref, variable_str)
250 }
251
252 #[allow(clippy::unnecessary_wraps)]
253 pub(crate) fn create_local_variable_generated(
254 &mut self,
255 variable_str: &str,
256 is_mutable: bool,
257 variable_type_ref: &TypeRef,
258 ) -> VariableRef {
259 let scope_index = self.scope.active_scope.block_scope_stack.len() - 1;
260
261 let index_within_function = self.scope.active_scope.emit_variable_index();
262
263 let variables_len = self
264 .scope
265 .active_scope
266 .block_scope_stack
267 .last_mut()
268 .expect("block scope should have at least one scope")
269 .variables
270 .len();
271
272 let is_marked_as_unused = variable_str.starts_with('_');
273
274 let actual_name = if is_marked_as_unused {
275 format!("_{index_within_function}")
276 } else {
277 variable_str.to_string()
278 };
279
280 let virtual_register = allocate_next_register(&mut self.scope);
283
284 let resolved_variable = Variable {
285 name: Node::default(),
286 assigned_name: actual_name.clone(),
287 resolved_type: variable_type_ref.clone(),
288 variable_type: VariableType::Local,
289 mutable_node: if is_mutable {
290 Some(Node::default())
291 } else {
292 None
293 },
294 scope_index,
295 variable_index: variables_len,
296 unique_id_within_function: index_within_function,
297 virtual_register: virtual_register as u8,
298 is_unused: is_marked_as_unused,
299 };
300
301 let variable_ref = Rc::new(resolved_variable);
302
303 if !is_marked_as_unused {
304 let lookups = &mut self
305 .scope
306 .active_scope
307 .block_scope_stack
308 .last_mut()
309 .expect("block scope should have at least one scope")
310 .lookup;
311 lookups
312 .insert(actual_name, variable_ref.clone())
313 .expect("should have checked earlier for variable");
314 }
315
316 let variables = &mut self
317 .scope
318 .active_scope
319 .block_scope_stack
320 .last_mut()
321 .expect("block scope should have at least one scope")
322 .variables;
323 variables
324 .insert(variable_ref.unique_id_within_function, variable_ref.clone())
325 .expect("should have checked earlier for variable");
326
327 self.scope
328 .total_scopes
329 .all_variables
330 .push(variable_ref.clone());
331
332 variable_ref
333 }
334
335 #[allow(clippy::too_many_lines)]
336 pub(crate) fn create_variable_binding_for_with(
337 &mut self,
338 ast_variable: &swamp_ast::Variable,
339 converted_expression: ArgumentExpression,
340 ) -> Expression {
341 let expression_type = converted_expression.ty();
342
343 match converted_expression {
344 ArgumentExpression::Expression(expr) => {
345 let variable_ref = self.create_local_variable(
348 &ast_variable.name,
349 ast_variable.is_mutable.as_ref(),
350 &expression_type,
351 false,
352 );
353
354 let var_def_kind = ExpressionKind::VariableDefinition(variable_ref, Box::new(expr));
355 let unit_type = self.types().unit();
356 self.create_expr(var_def_kind, unit_type, &ast_variable.name)
357 }
358 ArgumentExpression::BorrowMutableReference(loc) => {
359 if ast_variable.is_mutable.is_none() {
361 return self.create_err(ErrorKind::VariableIsNotMutable, &ast_variable.name);
362 }
363
364 let is_scalar = match &*expression_type.kind {
366 TypeKind::Int | TypeKind::Float | TypeKind::Bool | TypeKind::String { .. } => {
367 true
368 }
369 _ => false,
370 };
371
372 if is_scalar {
373 let original_expr = ExpressionKind::VariableAccess(loc.starting_variable);
376 let original_expr_wrapped = self.create_expr(
377 original_expr,
378 expression_type.clone(),
379 &ast_variable.name,
380 );
381
382 let variable_ref = self.create_local_variable(
384 &ast_variable.name,
385 ast_variable.is_mutable.as_ref(),
386 &expression_type,
387 false,
388 );
389
390 let var_def_kind = ExpressionKind::VariableDefinition(
391 variable_ref,
392 Box::new(original_expr_wrapped),
393 );
394 let unit_type = self.types().unit();
395 self.create_expr(var_def_kind, unit_type, &ast_variable.name)
396 } else {
397 let variable_ref = self.create_local_variable(
399 &ast_variable.name,
400 ast_variable.is_mutable.as_ref(),
401 &expression_type,
402 false,
403 );
404
405 let expr_kind = ExpressionKind::VariableDefinitionLValue(variable_ref, loc);
407 let unit_type = self.types().unit();
408 self.create_expr(expr_kind, unit_type, &ast_variable.name)
409 }
410 }
411 }
412 }
413
414 pub const fn types(&mut self) -> &mut TypeCache {
415 &mut self.shared.state.types
416 }
417}