use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::helpers::{ collect_diagnostics_into_enumerated_mut, collect_diagnostics_into_mut, collect_parameter_symbols_into, insert_declared_types_into, insert_resolved_names_into, insert_resolved_types_into, resolve_parameter_names_into, }; use crate::ast::ir_builder::IrBuilder; use crate::ast::parameter::Parameter; use crate::ast::statement::Statement; use crate::ast::type_use::TypeUse; use crate::ast::{FunctionReturnTypes, NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes}; use crate::diagnostic::{Diagnostic, Diagnostics}; use crate::ir::ir_function::IrFunction; use crate::ir::ir_parameter::IrParameter; use crate::ir::ir_parameter_or_variable::IrParameterOrVariable; use crate::source_range::SourceRange; use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; use crate::types_table::TypesTable; use crate::{diagnostics_result, handle_diagnostics}; use std::ops::Neg; use std::rc::Rc; pub struct Function { node_id: NodeId, declared_name: Rc, declared_name_source_range: SourceRange, is_public: bool, parameters: Vec, return_type: Option, statements: Vec, scope_id: Option, } impl Function { pub fn new( node_id: NodeId, declared_name: &str, declared_name_source_range: SourceRange, is_public: bool, parameters: Vec, return_type: Option, statements: Vec, ) -> Self { Self { node_id, declared_name: declared_name.into(), declared_name_source_range, is_public, parameters, return_type, statements, scope_id: None, } } pub fn node_id(&self) -> NodeId { self.node_id } pub fn declared_name(&self) -> &str { &self.declared_name } pub fn statements(&self) -> Vec<&Statement> { self.statements.iter().collect() } pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { self.scope_id = Some(container_scope); let function_scope = symbol_table.push_function_scope(&format!("function_scope({})", self.declared_name)); for parameter in &mut self.parameters { parameter.init_scopes(symbol_table, function_scope); } if let Some(type_use) = &mut self.return_type { type_use.init_scopes(symbol_table, function_scope); } let body_scope = symbol_table.push_block_scope(&format!("body_scope({})", self.declared_name)); for statement in &mut self.statements { statement.init_scopes(symbol_table, body_scope); } symbol_table.pop_scope(); // body symbol_table.pop_scope(); // function } /// Return value contains self FunctionSymbol followed by all symbols (including self symbol). pub fn declared_symbols( &self, fqn_context: &FqnContext, is_method: bool, ) -> (Rc, Vec) { let mut all_symbols: Vec = vec![]; let mut parameter_symbols = Vec::new(); collect_parameter_symbols_into(&self.parameters, &mut all_symbols, &mut parameter_symbols); let function_symbol = Rc::new(FunctionSymbol::new( &self.declared_name, self.declared_name_source_range.clone(), fqn_context.resolve(self.declared_name()), false, is_method, self.scope_id.unwrap(), parameter_symbols, )); all_symbols.push(Symbol::Function(function_symbol.clone())); (function_symbol, all_symbols) } fn resolve_names_common(&self, symbol_table: &SymbolTable) -> (NodesToSymbols, Diagnostics) { let mut diagnostics = Diagnostics::new(); let mut nodes_to_symbols = NodesToSymbols::new(); resolve_parameter_names_into( &self.parameters, symbol_table, &mut nodes_to_symbols, &mut diagnostics, ); if let Some(type_use) = &self.return_type { let (ns, mut ds) = type_use.resolve_names(symbol_table); insert_resolved_names_into(ns, &mut nodes_to_symbols); diagnostics.append(&mut ds); } (nodes_to_symbols, diagnostics) } pub fn resolve_names_static( &self, symbol_table: &mut SymbolTable, ) -> (NodesToSymbols, Diagnostics) { let (mut nodes_to_symbols, mut diagnostics) = self.resolve_names_common(symbol_table); for statement in &self.statements { let (ns, mut ds) = statement.resolve_names_static(symbol_table); insert_resolved_names_into(ns, &mut nodes_to_symbols); diagnostics.append(&mut ds); } (nodes_to_symbols, diagnostics) } pub fn resolve_names_method( &self, symbol_table: &mut SymbolTable, self_class_symbol: &ClassSymbol, ) -> (NodesToSymbols, Diagnostics) { let (mut nodes_to_symbols, mut diagnostics) = self.resolve_names_common(symbol_table); for statement in &self.statements { let (ns, mut ds) = statement.resolve_names_method(symbol_table, self_class_symbol); insert_resolved_names_into(ns, &mut nodes_to_symbols); diagnostics.append(&mut ds); } (nodes_to_symbols, diagnostics) } #[deprecated] pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec { let mut diagnostics = Vec::new(); for parameter in &self.parameters { diagnostics.append(&mut parameter.check_names(symbol_table)); } if let Some(type_use) = &self.return_type { diagnostics.append(&mut type_use.check_names(symbol_table)); } diagnostics } #[deprecated] pub fn analyze_method_local_names( &self, symbol_table: &mut SymbolTable, class_symbol: &ClassSymbol, ) -> Vec { self.statements .iter() .flat_map(|statement| statement.analyze_method_local_names(symbol_table, class_symbol)) .collect() } #[deprecated] pub fn analyze_static_fn_local_names(&self, symbol_table: &mut SymbolTable) -> Vec { self.statements .iter() .flat_map(|statement| statement.analyze_static_fn_local_names(symbol_table)) .collect() } pub fn declared_types(&self, names_table: &NodesToSymbols) -> (SymbolsToTypes, Diagnostics) { let mut diagnostics = Diagnostics::new(); let mut declared_types = SymbolsToTypes::new(); for parameter in &self.parameters { let symbol = names_table.get(¶meter.node_id()).unwrap(); let (type_info, mut ds) = parameter.declared_type(names_table); declared_types.insert(symbol.clone(), type_info); diagnostics.append(&mut ds); } (declared_types, diagnostics) } pub fn resolve_types( &self, nodes_to_symbols: &NodesToSymbols, symbols_to_types: &SymbolsToTypes, ) -> (SymbolsToTypes, NodesToTypes, Diagnostics) { let mut diagnostics = Diagnostics::new(); let mut nodes_to_types = NodesToTypes::new(); let mut symbols_to_types = symbols_to_types.clone(); for statement in &self.statements { let (sts, nts, mut ds) = statement.resolve_types(nodes_to_symbols, &symbols_to_types); insert_declared_types_into(sts, &mut symbols_to_types); // merge! insert_resolved_types_into(nts, &mut nodes_to_types); diagnostics.append(&mut ds); } // todo: check last statement for return type (symbols_to_types, nodes_to_types, diagnostics) } #[deprecated] pub fn gather_types(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) { let function_symbol = symbol_table .get_function_symbol_owned(self.scope_id.unwrap(), self.declared_name()) .unwrap(); // self type (the signature) types_table.function_types_mut().insert( function_symbol.clone(), TypeInfo::Function(function_symbol.clone()), ); // put return type (temporary, this is deprecated) if let Some(type_use) = &self.return_type { let resolved_return_type = type_use.type_info(symbol_table, types_table).clone(); types_table .function_return_types_mut() .insert(function_symbol, resolved_return_type); } else { types_table .function_return_types_mut() .insert(function_symbol, TypeInfo::Void); } // parameters for parameter in &self.parameters { parameter.gather_types_into(symbol_table, types_table); } } fn get_return_type_info( types_table: &TypesTable, function_symbol: &FunctionSymbol, ) -> TypeInfo { types_table .function_return_types() .get(function_symbol) .cloned() .unwrap() } /// Type checks parameters. fn type_check_parameters( &mut self, symbol_table: &SymbolTable, types_table: &TypesTable, diagnostics: &mut Vec, ) { collect_diagnostics_into_mut( &mut self.parameters, |p| p.type_check(symbol_table, types_table), diagnostics, ) } /// Type checks return type. fn type_check_return_type( &mut self, symbol_table: &SymbolTable, types_table: &TypesTable, diagnostics: &mut Vec, ) { if let Some(type_use) = &mut self.return_type { handle_diagnostics!(type_use.type_check(symbol_table, types_table), diagnostics); } } /// Type checks statements, making sure the last statement matches return type, if necessary. fn type_check_statements( &mut self, symbol_table: &SymbolTable, types_table: &mut TypesTable, diagnostics: &mut Vec, function_symbol: &FunctionSymbol, ) { let return_type_info = Self::get_return_type_info(types_table, function_symbol); let statements_len = self.statements.len(); collect_diagnostics_into_enumerated_mut( &mut self.statements, |i, s| { let is_last = i == statements_len - 1; if is_last { s.type_check(symbol_table, types_table, Some(&return_type_info)) } else { s.type_check(symbol_table, types_table, None) } }, diagnostics, ); } #[deprecated] pub fn type_check( &mut self, symbol_table: &SymbolTable, types_table: &mut TypesTable, ) -> Result<(), Vec> { let mut diagnostics = vec![]; let function_symbol = symbol_table .get_function_symbol(self.scope_id.unwrap(), self.declared_name()) .unwrap(); // parameters self.type_check_parameters(symbol_table, types_table, &mut diagnostics); // return type self.type_check_return_type(symbol_table, types_table, &mut diagnostics); // statements self.type_check_statements(symbol_table, types_table, &mut diagnostics, function_symbol); diagnostics_result!(diagnostics) } /// Converts all parameters to ir. Saves the IrParameter to the associated parameter symbol. fn parameters_to_ir( &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, types_table: &TypesTable, ) { for (i, parameter) in self.parameters.iter().enumerate() { let parameter_symbol = symbol_table .get_parameter_symbol_owned(parameter.scope_id(), parameter.declared_name()) .unwrap(); let parameter_type_info = types_table .parameter_types() .get(¶meter_symbol) .unwrap(); let stack_offset = (self.parameters.len() as isize).neg() + (i as isize); let ir_parameter = IrParameter::new( parameter_symbol.declared_name(), parameter_type_info.clone(), stack_offset, ); let as_rc = Rc::new(ir_parameter); builder.push_parameter(¶meter_symbol, as_rc.clone()); } } /// If `class_context.is_some()`, set parameter 0 to the self parameter/variable on the builder. fn handle_method_case(&self, builder: &mut IrBuilder, class_context: Option<&ClassSymbol>) { // if we are a method, we need to set the self parameter on the builder if class_context.is_some() { let parameter_0 = builder.parameters()[0].clone(); // put it in the self parameter builder.set_self_parameter_or_variable(IrParameterOrVariable::IrParameter(parameter_0)); } } /// Convert all statements to ir. fn statements_to_ir( &self, builder: &mut IrBuilder, symbol_table: &SymbolTable, types_table: &TypesTable, function_symbol: &FunctionSymbol, ) { let return_type_info = Self::get_return_type_info(types_table, function_symbol); let should_return_value = !matches!(return_type_info, TypeInfo::Void); for (i, statement) in self.statements.iter().enumerate() { let is_last = i == self.statements.len() - 1; statement.to_ir( builder, symbol_table, types_table, should_return_value && is_last, ); } } pub fn to_ir( &self, symbol_table: &SymbolTable, types_table: &TypesTable, class_context: Option<&ClassSymbol>, ) -> IrFunction { let mut builder = IrBuilder::new(); let function_symbol = symbol_table .get_function_symbol(self.scope_id.unwrap(), self.declared_name()) .unwrap(); // parameters self.parameters_to_ir(&mut builder, symbol_table, types_table); let entry_block_id = builder.new_block(); // preamble self.handle_method_case(&mut builder, class_context); // body self.statements_to_ir(&mut builder, symbol_table, types_table, function_symbol); builder.finish_block(); let entry_block = builder.get_block(entry_block_id).clone(); IrFunction::new( fqn_parts_to_string(function_symbol.fqn_parts()), builder.parameters().iter().map(|p| (*p).clone()).collect(), &Self::get_return_type_info(types_table, function_symbol), entry_block, ) } pub fn lower_static( &self, nodes_to_symbols: &NodesToSymbols, symbols_to_types: &SymbolsToTypes, nodes_to_types: &NodesToTypes, function_return_types: &FunctionReturnTypes, ) -> IrFunction { let mut builder = IrBuilder::new(); let function_symbol = nodes_to_symbols .get(&self.node_id) .unwrap() .unwrap_function_symbol(); // put parameters in builder for (i, parameter_symbol) in function_symbol.parameters().iter().enumerate() { let parameter_type_info = symbols_to_types .get(&Symbol::Parameter(parameter_symbol.clone())) .unwrap(); let stack_offset = (function_symbol.parameters().len() as isize).neg() + (i as isize); let ir_parameter = Rc::new(IrParameter::new( parameter_symbol.declared_name(), parameter_type_info.clone(), stack_offset, )); builder.push_parameter(parameter_symbol, ir_parameter); } let entry_block_id = builder.new_block(); // lower statements let return_type_info = function_return_types.get(function_symbol).unwrap(); let should_return_value = !matches!(return_type_info, TypeInfo::Void); for (i, statement) in self.statements.iter().enumerate() { let is_last = i == self.statements.len() - 1; statement.lower( &mut builder, nodes_to_symbols, symbols_to_types, nodes_to_types, should_return_value && is_last, ); } builder.finish_block(); let entry_block = builder.get_block(entry_block_id).clone(); IrFunction::new( fqn_parts_to_string(function_symbol.fqn_parts()), builder.parameters().iter().map(|p| (*p).clone()).collect(), return_type_info, entry_block, ) } }