use crate::Analyzer;
use crate::err::{Error, ErrorKind};
use std::rc::Rc;
use swamp_script_node::Node;
use swamp_script_semantic::{
BlockScopeMode, Expression, ExpressionKind, MutOrImmutableExpression, Variable, VariableRef,
};
use swamp_script_types::prelude::*;
use tracing::error;
impl Analyzer<'_> {
fn try_find_local_variable(&self, node: &Node) -> Option<&VariableRef> {
let current_scope = self
.scope
.block_scope_stack
.iter()
.last()
.expect("no scope stack available");
let variable_text = self.get_text_resolved(node).to_string();
current_scope.variables.get(&variable_text)
}
#[allow(unused)]
pub(crate) fn find_variable(
&self,
variable: &swamp_script_ast::Variable,
) -> Result<VariableRef, Error> {
self.try_find_variable(&variable.name).map_or_else(
|| Err(self.create_err(ErrorKind::UnknownVariable, &variable.name)),
Ok,
)
}
pub(crate) fn try_find_variable(&self, node: &swamp_script_ast::Node) -> Option<VariableRef> {
let variable_text = self.get_text(node);
for scope in self.scope.block_scope_stack.iter().rev() {
if let Some(value) = scope.variables.get(&variable_text.to_string()) {
return Some(value.clone());
}
if scope.mode == BlockScopeMode::Closed {
break;
}
}
None
}
pub(crate) fn create_local_variable(
&mut self,
variable: &swamp_script_ast::Node,
is_mutable: Option<&swamp_script_ast::Node>,
variable_type_ref: &Type,
) -> Result<VariableRef, Error> {
if variable_type_ref == &Type::Unit {
let debug_text = self.get_text(variable);
error!(
?debug_text,
"panic, tries to create a local variable as a unit"
);
}
assert_ne!(*variable_type_ref, Type::Unit);
self.create_local_variable_resolved(
&self.to_node(variable),
Option::from(&self.to_node_option(is_mutable)),
variable_type_ref,
)
}
pub(crate) fn create_variable(
&mut self,
variable: &swamp_script_ast::Variable,
variable_type_ref: &Type,
) -> Result<VariableRef, Error> {
self.create_local_variable(
&variable.name,
Option::from(&variable.is_mutable),
variable_type_ref,
)
}
pub(crate) fn create_local_variable_resolved(
&mut self,
variable: &Node,
is_mutable: Option<&Node>,
variable_type_ref: &Type,
) -> Result<VariableRef, Error> {
if let Some(_existing_variable) = self.try_find_local_variable(variable) {
return Err(
self.create_err_resolved(ErrorKind::OverwriteVariableNotAllowedHere, variable)
);
}
let variable_str = self.get_text_resolved(variable).to_string();
let scope_index = self.scope.block_scope_stack.len() - 1;
let index = { self.scope.gen_variable_index() };
let should_insert_in_scope = !variable_str.starts_with('_');
let variables = &mut self
.scope
.block_scope_stack
.last_mut()
.expect("block scope should have at least one scope")
.variables;
let resolved_variable = Variable {
name: variable.clone(),
assigned_name: variable_str.clone(),
resolved_type: variable_type_ref.clone(),
mutable_node: is_mutable.cloned(),
scope_index,
variable_index: variables.len(),
unique_id_within_function: index,
is_unused: !should_insert_in_scope,
};
let variable_ref = Rc::new(resolved_variable);
if !should_insert_in_scope && is_mutable.is_some() {
return Err(self.create_err_resolved(ErrorKind::UnusedVariablesCanNotBeMut, variable));
}
if should_insert_in_scope {
variables
.insert(variable_str, variable_ref.clone())
.expect("should have checked earlier for variable");
self.function_variables.push(variable_ref.clone());
}
Ok(variable_ref)
}
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn create_local_variable_generated(
&mut self,
variable_str: &str,
is_mutable: bool,
variable_type_ref: &Type,
) -> Result<VariableRef, Error> {
let scope_index = self.scope.block_scope_stack.len() - 1;
let index_within_function = self.scope.gen_variable_index();
let variables = &mut self
.scope
.block_scope_stack
.last_mut()
.expect("block scope should have at least one scope")
.variables;
let should_insert_in_scope = !variable_str.starts_with('_');
let resolved_variable = Variable {
name: Node::default(),
assigned_name: variable_str.to_string(),
resolved_type: variable_type_ref.clone(),
mutable_node: if is_mutable {
Some(Node::default())
} else {
None
},
scope_index,
variable_index: variables.len(),
unique_id_within_function: index_within_function,
is_unused: !should_insert_in_scope,
};
let variable_ref = Rc::new(resolved_variable);
if should_insert_in_scope {
variables
.insert(variable_str.to_string(), variable_ref.clone())
.expect("should have checked earlier for variable");
}
Ok(variable_ref)
}
pub(crate) fn create_variable_binding_for_with(
&mut self,
ast_variable: &swamp_script_ast::Variable,
converted_expression: MutOrImmutableExpression,
) -> Result<Expression, Error> {
let expression_type = converted_expression.ty().clone();
let variable_ref = self.create_local_variable(
&ast_variable.name,
ast_variable.is_mutable.as_ref(),
&expression_type,
)?;
let expr_kind =
ExpressionKind::VariableDefinition(variable_ref, Box::from(converted_expression));
let expr = self.create_expr(expr_kind, expression_type, &ast_variable.name);
Ok(expr)
}
}