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