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