use super::{SemanticAnalyzer, SemanticError, SymbolKind, Type};
use crate::ast::{ExpressionKind, ExpressionNode, Param, StatementKind, StatementNode};
use crate::lexer::Span;
impl SemanticAnalyzer {
pub(super) fn find_free_variables_in_block(
&self,
body: &[StatementNode],
local_vars: &std::collections::HashSet<String>,
) -> Result<Vec<(String, Type)>, SemanticError> {
let mut free_vars = std::collections::HashMap::new();
let mut local_vars = local_vars.clone();
for stmt in body {
self.find_free_variables_in_statement(stmt, &mut local_vars, &mut free_vars)?;
}
Ok(free_vars.into_iter().collect())
}
fn find_free_variables_in_statement(
&self,
stmt: &StatementNode,
local_vars: &mut std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
match &stmt.kind {
StatementKind::Expression(expr) | StatementKind::Return(Some(expr)) => {
self.find_free_variables_in_expression(expr, local_vars, free_vars)?;
}
StatementKind::AutoDecl(name, _, expr) => {
self.handle_variable_declaration(name, expr, local_vars, free_vars)?;
}
StatementKind::TypedDecl(name, _, expr) => {
self.handle_variable_declaration(name, expr, local_vars, free_vars)?;
}
StatementKind::ConstDecl(name, _, expr) => {
self.handle_variable_declaration(name, expr, local_vars, free_vars)?;
}
StatementKind::If {
cond,
then_block,
else_block,
} => {
self.handle_if_statement(cond, then_block, else_block, local_vars, free_vars)?;
}
StatementKind::While { cond, body } => {
self.handle_while_statement(cond, body, local_vars, free_vars)?;
}
StatementKind::For {
var, iter, body, ..
} => {
self.handle_for_statement(var, iter, body, local_vars, free_vars)?;
}
StatementKind::Block(stmts) => {
self.handle_block_statement(stmts, local_vars, free_vars)?;
}
_ => {}
}
Ok(())
}
fn handle_variable_declaration(
&self,
name: &str,
expr: &ExpressionNode,
local_vars: &mut std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(expr, local_vars, free_vars)?;
local_vars.insert(name.to_string());
Ok(())
}
fn handle_if_statement(
&self,
cond: &ExpressionNode,
then_block: &[StatementNode],
else_block: &Option<Vec<StatementNode>>,
local_vars: &mut std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(cond, local_vars, free_vars)?;
for s in then_block {
self.find_free_variables_in_statement(s, local_vars, free_vars)?;
}
if let Some(else_stmts) = else_block {
for s in else_stmts {
self.find_free_variables_in_statement(s, local_vars, free_vars)?;
}
}
Ok(())
}
fn handle_while_statement(
&self,
cond: &ExpressionNode,
body: &[StatementNode],
local_vars: &mut std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(cond, local_vars, free_vars)?;
for s in body {
self.find_free_variables_in_statement(s, local_vars, free_vars)?;
}
Ok(())
}
fn handle_for_statement(
&self,
var: &str,
iter: &ExpressionNode,
body: &[StatementNode],
local_vars: &mut std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(iter, local_vars, free_vars)?;
local_vars.insert(var.to_string());
for s in body {
self.find_free_variables_in_statement(s, local_vars, free_vars)?;
}
Ok(())
}
fn handle_block_statement(
&self,
stmts: &[StatementNode],
local_vars: &mut std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
for s in stmts {
self.find_free_variables_in_statement(s, local_vars, free_vars)?;
}
Ok(())
}
fn find_free_variables_in_expression(
&self,
expr: &ExpressionNode,
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
match &expr.kind {
ExpressionKind::Identifier(name) => {
self.handle_identifier_expression(name, local_vars, free_vars)?;
}
ExpressionKind::Binary { left, right, .. } => {
self.handle_binary_expression(left, right, local_vars, free_vars)?;
}
ExpressionKind::Unary {
expr: inner,
op_span: _,
..
} => {
self.handle_unary_expression(inner, local_vars, free_vars)?;
}
ExpressionKind::Call { func, args } => {
self.handle_call_expression(func, args, local_vars, free_vars)?;
}
ExpressionKind::FieldAccess { expr: inner, .. } => {
self.handle_field_access_expression(inner, local_vars, free_vars)?;
}
ExpressionKind::ListAccess { expr: inner, index } => {
self.handle_list_access_expression(inner, index, local_vars, free_vars)?;
}
ExpressionKind::ListLiteral(elements) => {
self.handle_list_literal_expression(elements, local_vars, free_vars)?;
}
ExpressionKind::MapLiteral { entries, .. } => {
self.handle_map_literal_expression(entries, local_vars, free_vars)?;
}
ExpressionKind::SetOrMapLiteral(elements) => {
self.handle_set_literal_expression(elements, local_vars, free_vars)?;
}
ExpressionKind::TupleLiteral(elements) => {
self.handle_tuple_literal_expression(elements, local_vars, free_vars)?;
}
ExpressionKind::If {
cond,
then_expr,
else_expr,
} => {
self.handle_if_expression(cond, then_expr, else_expr, local_vars, free_vars)?;
}
ExpressionKind::Lambda { params, body, .. } => {
self.handle_lambda_expression(params, body, local_vars, free_vars)?;
}
_ => {}
}
Ok(())
}
fn handle_identifier_expression(
&self,
name: &str,
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
if !local_vars.contains(name) {
if let Some(symbol) = self.symbol_table.lookup(name)
&& matches!(symbol.kind, SymbolKind::Variable)
&& let Some(var_type) = &symbol.type_
{
free_vars.insert(name.to_string(), var_type.clone());
}
}
Ok(())
}
fn handle_binary_expression(
&self,
left: &ExpressionNode,
right: &ExpressionNode,
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(left, local_vars, free_vars)?;
self.find_free_variables_in_expression(right, local_vars, free_vars)?;
Ok(())
}
fn handle_unary_expression(
&self,
inner: &ExpressionNode,
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(inner, local_vars, free_vars)?;
Ok(())
}
fn handle_call_expression(
&self,
func: &ExpressionNode,
args: &[ExpressionNode],
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(func, local_vars, free_vars)?;
for arg in args {
self.find_free_variables_in_expression(arg, local_vars, free_vars)?;
}
Ok(())
}
fn handle_field_access_expression(
&self,
inner: &ExpressionNode,
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(inner, local_vars, free_vars)?;
Ok(())
}
fn handle_list_access_expression(
&self,
inner: &ExpressionNode,
index: &ExpressionNode,
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(inner, local_vars, free_vars)?;
self.find_free_variables_in_expression(index, local_vars, free_vars)?;
Ok(())
}
fn handle_list_literal_expression(
&self,
elements: &[ExpressionNode],
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
for elem in elements {
self.find_free_variables_in_expression(elem, local_vars, free_vars)?;
}
Ok(())
}
fn handle_map_literal_expression(
&self,
entries: &[(ExpressionNode, ExpressionNode)],
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
for (key, val) in entries {
self.find_free_variables_in_expression(key, local_vars, free_vars)?;
self.find_free_variables_in_expression(val, local_vars, free_vars)?;
}
Ok(())
}
fn handle_set_literal_expression(
&self,
elements: &[ExpressionNode],
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
for elem in elements {
self.find_free_variables_in_expression(elem, local_vars, free_vars)?;
}
Ok(())
}
fn handle_tuple_literal_expression(
&self,
elements: &[ExpressionNode],
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
for elem in elements {
self.find_free_variables_in_expression(elem, local_vars, free_vars)?;
}
Ok(())
}
fn handle_if_expression(
&self,
cond: &ExpressionNode,
then_expr: &ExpressionNode,
else_expr: &ExpressionNode,
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
self.find_free_variables_in_expression(cond, local_vars, free_vars)?;
self.find_free_variables_in_expression(then_expr, local_vars, free_vars)?;
self.find_free_variables_in_expression(else_expr, local_vars, free_vars)?;
Ok(())
}
fn handle_lambda_expression(
&self,
params: &[Param],
body: &[StatementNode],
local_vars: &std::collections::HashSet<String>,
free_vars: &mut std::collections::HashMap<String, Type>,
) -> Result<(), SemanticError> {
let mut inner_local_vars = local_vars.clone();
for param in params {
inner_local_vars.insert(param.name.clone());
}
for stmt in body {
self.find_free_variables_in_statement(stmt, &mut inner_local_vars, free_vars)?;
}
Ok(())
}
pub(super) fn check_not_modifying_constant(
&mut self,
expr: &ExpressionNode,
op_span: &Span,
) -> Result<(), SemanticError> {
if let ExpressionKind::Identifier(name) = &expr.kind {
if let Some(symbol) = self.symbol_table.lookup(name)
&& symbol.kind == SymbolKind::Constant
{
return Err(SemanticError::with_help(
format!("Cannot modify constant '{}'", name),
*op_span,
"Constants cannot be modified after initialization",
));
}
} else if let ExpressionKind::FieldAccess {
expr: obj_expr,
field,
} = &expr.kind
{
let obj_type = self.get_expression_type(obj_expr)?;
if let Type::Named(class_name, _) = &obj_type
&& let Some(symbol) = self.symbol_table.lookup(class_name)
&& let Some((_, is_const)) = symbol.fields.get(field)
&& *is_const
{
return Err(SemanticError::with_help(
format!("Cannot modify const field '{}'", field),
*op_span,
"Const fields cannot be modified after initialization. Remove the 'const' modifier from the field declaration if mutation is needed.",
));
}
}
Ok(())
}
}