swamp_script_analyzer/
variable.rs1use 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 create_local_variable(
55 &mut self,
56 variable: &swamp_script_ast::Node,
57 is_mutable: Option<&swamp_script_ast::Node>,
58 variable_type_ref: &Type,
59 ) -> Result<VariableRef, Error> {
60 if variable_type_ref == &Type::Unit {
61 let debug_text = self.get_text(variable);
62 error!(
63 ?debug_text,
64 "panic, tries to create a local variable as a unit"
65 );
66 }
67 assert_ne!(*variable_type_ref, Type::Unit);
68 self.create_local_variable_resolved(
69 &self.to_node(variable),
70 Option::from(&self.to_node_option(is_mutable)),
71 variable_type_ref,
72 )
73 }
74
75 pub(crate) fn create_variable(
76 &mut self,
77 variable: &swamp_script_ast::Variable,
78 variable_type_ref: &Type,
79 ) -> Result<VariableRef, Error> {
80 self.create_local_variable(
81 &variable.name,
82 Option::from(&variable.is_mutable),
83 variable_type_ref,
84 )
85 }
86
87 pub(crate) fn create_local_variable_resolved(
88 &mut self,
89 variable: &Node,
90 is_mutable: Option<&Node>,
91 variable_type_ref: &Type,
92 ) -> Result<VariableRef, Error> {
93 if let Some(_existing_variable) = self.try_find_local_variable(variable) {
94 return Err(
95 self.create_err_resolved(ErrorKind::OverwriteVariableNotAllowedHere, variable)
96 );
97 }
98 let variable_str = self.get_text_resolved(variable).to_string();
99
100 let scope_index = self.scope.block_scope_stack.len() - 1;
101
102 let index = { self.scope.gen_variable_index() };
103
104 let should_insert_in_scope = !variable_str.starts_with('_');
105 let variables = &mut self
106 .scope
107 .block_scope_stack
108 .last_mut()
109 .expect("block scope should have at least one scope")
110 .variables;
111
112 let resolved_variable = Variable {
113 name: variable.clone(),
114 assigned_name: variable_str.clone(),
115
116 resolved_type: variable_type_ref.clone(),
117 mutable_node: is_mutable.cloned(),
118 scope_index,
119 variable_index: variables.len(),
120 unique_id_within_function: index,
121 is_unused: !should_insert_in_scope,
122 };
123
124 let variable_ref = Rc::new(resolved_variable);
125
126 if !should_insert_in_scope && is_mutable.is_some() {
127 return Err(self.create_err_resolved(ErrorKind::UnusedVariablesCanNotBeMut, variable));
128 }
129 if should_insert_in_scope {
130 variables
131 .insert(variable_str, variable_ref.clone())
132 .expect("should have checked earlier for variable");
133 self.function_variables.push(variable_ref.clone());
134 }
135
136 Ok(variable_ref)
137 }
138
139 #[allow(clippy::unnecessary_wraps)]
140 pub(crate) fn create_local_variable_generated(
141 &mut self,
142 variable_str: &str,
143 is_mutable: bool,
144 variable_type_ref: &Type,
145 ) -> Result<VariableRef, Error> {
146 let scope_index = self.scope.block_scope_stack.len() - 1;
147
148 let index_within_function = self.scope.gen_variable_index();
149
150 let variables = &mut self
151 .scope
152 .block_scope_stack
153 .last_mut()
154 .expect("block scope should have at least one scope")
155 .variables;
156
157 let should_insert_in_scope = !variable_str.starts_with('_');
158
159 let resolved_variable = Variable {
160 name: Node::default(),
161 assigned_name: variable_str.to_string(),
162 resolved_type: variable_type_ref.clone(),
163 mutable_node: if is_mutable {
164 Some(Node::default())
165 } else {
166 None
167 },
168 scope_index,
169 variable_index: variables.len(),
170 unique_id_within_function: index_within_function,
171 is_unused: !should_insert_in_scope,
172 };
173
174 let variable_ref = Rc::new(resolved_variable);
175
176 if should_insert_in_scope {
177 variables
178 .insert(variable_str.to_string(), variable_ref.clone())
179 .expect("should have checked earlier for variable");
180 }
181
182 Ok(variable_ref)
183 }
184
185 pub(crate) fn create_variable_binding_for_with(
186 &mut self,
187 ast_variable: &swamp_script_ast::Variable,
188 converted_expression: MutOrImmutableExpression,
189 ) -> Result<Expression, Error> {
190 let expression_type = converted_expression.ty().clone();
191 let variable_ref = self.create_local_variable(
192 &ast_variable.name,
193 ast_variable.is_mutable.as_ref(),
194 &expression_type,
195 )?;
196 let expr_kind =
197 ExpressionKind::VariableDefinition(variable_ref, Box::from(converted_expression));
198
199 let expr = self.create_expr(expr_kind, expression_type, &ast_variable.name);
200
201 Ok(expr)
202 }
203}