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