use crate::ast::expression::Expression; use crate::ast::ir_builder::IrBuilder; use crate::diagnostic::Diagnostic; use crate::ir::ir_assign::IrAssign; use crate::ir::ir_statement::IrStatement; use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::variable_symbol::VariableSymbol; use crate::symbol_table::SymbolTable; use crate::symbol_table::util::try_insert_symbol_into; use crate::type_info::TypeInfo; use crate::types_table::TypesTable; use std::cell::RefCell; use std::rc::Rc; pub struct LetStatement { declared_name: Rc, declared_name_source_range: SourceRange, is_mut: bool, initializer: Box, scope_id: Option, } impl LetStatement { pub fn new( declared_name: &str, declared_name_source_range: SourceRange, is_mut: bool, initializer: Expression, ) -> Self { Self { declared_name: declared_name.into(), declared_name_source_range, is_mut, initializer: initializer.into(), scope_id: None, } } pub fn declared_name(&self) -> &str { &self.declared_name } pub fn initializer(&self) -> &Expression { &self.initializer } pub fn initializer_mut(&mut self) -> &mut Expression { &mut self.initializer } pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { self.scope_id = Some(container_scope); self.initializer.init_scopes(symbol_table, container_scope); } pub fn scope_id(&self) -> usize { self.scope_id.unwrap() } fn make_and_insert_variable_symbol( &self, symbol_table: &mut SymbolTable, ) -> Option { let variable_symbol = Rc::new(VariableSymbol::new( &self.declared_name, &self.declared_name_source_range, self.is_mut, self.scope_id.unwrap(), )); try_insert_symbol_into(Symbol::Variable(variable_symbol), symbol_table).err() } pub fn analyze_constructor_local_names( &self, symbol_table: &mut SymbolTable, class_symbol: &ClassSymbol, ) -> Vec { let mut diagnostics = Vec::new(); diagnostics.append( &mut self .initializer .check_constructor_local_names(symbol_table, class_symbol), ); if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) { diagnostics.push(diagnostic); } diagnostics } pub fn analyze_method_local_names( &self, symbol_table: &mut SymbolTable, class_symbol: &ClassSymbol, ) -> Vec { let mut diagnostics = Vec::new(); diagnostics.append( &mut self .initializer .check_method_local_names(symbol_table, class_symbol), ); if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) { diagnostics.push(diagnostic); } diagnostics } pub fn analyze_static_fn_local_names(&self, symbol_table: &mut SymbolTable) -> Vec { let mut diagnostics = Vec::new(); diagnostics.append(&mut self.initializer.check_static_fn_local_names(symbol_table)); if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) { diagnostics.push(diagnostic); } diagnostics } pub fn gather_local_type(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) { let initializer_type_info = self .initializer .type_info(symbol_table, types_table) .clone(); let variable_symbol = symbol_table .get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name) .unwrap(); types_table .variable_types_mut() .insert(variable_symbol, initializer_type_info); } pub fn type_check( &mut self, symbol_table: &SymbolTable, types_table: &mut TypesTable, ) -> Result<(), Vec> { self.initializer.type_check(symbol_table, types_table)?; // TODO: this is wrong. We need to check assignability let initializer_type_info = self .initializer .type_info(symbol_table, types_table) .clone(); let variable_symbol = symbol_table .get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name) .unwrap(); types_table .variable_types_mut() .insert(variable_symbol, initializer_type_info); Ok(()) } fn make_vr_variable(&self, builder: &mut IrBuilder, destination_type: &TypeInfo) -> IrVariable { IrVariable::new_vr( self.declared_name().into(), builder.current_block().id(), destination_type, ) } fn make_stack_variable( &self, builder: &mut IrBuilder, destination_type: &TypeInfo, offset: isize, ) -> IrVariable { IrVariable::new_stack_with_offset( self.declared_name().into(), builder.current_block().id(), destination_type, offset, ) } pub fn get_destination_symbol(&self, symbol_table: &SymbolTable) -> Rc { symbol_table .get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name) .unwrap() } pub fn to_ir( &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, types_table: &TypesTable, ) { let init_operation = self .initializer .to_ir_operation(builder, symbol_table, types_table); let destination_symbol = self.get_destination_symbol(symbol_table); let destination_type = types_table .variable_types() .get(&destination_symbol) .unwrap(); let destination_vr_variable = self.make_vr_variable(builder, destination_type); let as_rc = Rc::new(RefCell::new(destination_vr_variable)); let ir_assign = IrAssign::new(as_rc.clone(), init_operation); builder .local_variables_mut() .insert(destination_symbol, as_rc.clone()); builder .current_block_mut() .add_statement(IrStatement::Assign(ir_assign)); } pub fn to_repl_ir( &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, types_table: &TypesTable, destination_stack_offset: isize, ) -> Rc> { let init_operation = self .initializer .to_ir_operation(builder, symbol_table, types_table); let destination_symbol = self.get_destination_symbol(symbol_table); let destination_type = types_table .variable_types() .get(&destination_symbol) .unwrap(); let destination_stack_variable = self.make_stack_variable(builder, destination_type, destination_stack_offset); let as_rc = Rc::new(RefCell::new(destination_stack_variable)); let ir_assign = IrAssign::new(as_rc.clone(), init_operation); // do not need to save variable to builder as a new one is created for each repl function builder .current_block_mut() .add_statement(IrStatement::Assign(ir_assign)); as_rc } }