use crate::asm::asm_instruction::{AsmInstruction, Return}; use crate::ast::assemble_context::AssembleContext; use crate::ast::parameter::Parameter; use crate::ast::statement::Statement; use crate::ast::type_use::TypeUse; use crate::constants_table::ConstantsTable; use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; use crate::symbol::FunctionSymbol; use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::type_info::TypeInfo; use std::ops::Neg; pub struct Function { declared_name: String, declared_name_source_range: SourceRange, parameters: Vec, return_type: Option, statements: Vec, } impl Function { pub fn new( declared_name: &str, declared_name_source_range: SourceRange, parameters: Vec, return_type: Option, statements: Vec, ) -> Self { Self { declared_name: declared_name.to_string(), declared_name_source_range, parameters, return_type, statements, } } pub fn declared_name(&self) -> &str { &self.declared_name } pub fn statements(&self) -> Vec<&Statement> { self.statements.iter().collect() } pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { let mut diagnostics = vec![]; // insert function symbol let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( self.declared_name(), false, self.return_type .as_ref() .map(|return_type| return_type.to_type_info()) .unwrap_or(TypeInfo::Void), )); // get function symbol if successful let function_symbol = match insert_result { Ok(function_symbol) => function_symbol, Err(symbol_insert_error) => { return match symbol_insert_error { SymbolInsertError::AlreadyDeclared(already_declared) => { diagnostics.push(Diagnostic::new( &format!( "Function {} already declared in current scope", already_declared.name() ), self.declared_name_source_range.start(), self.declared_name_source_range.end(), )); diagnostics } }; } }; // handle parameters symbol_table.push_scope(&format!("parameter_scope({})", self.declared_name)); let mut parameter_symbols = vec![]; let parameter_count = self.parameters.len(); let stack_frame_offset_base = (parameter_count as isize).neg(); for (i, parameter) in self.parameters.iter_mut().enumerate() { match parameter.gather_declared_names(symbol_table) { Ok(parameter_symbol) => { parameter_symbol .borrow_mut() .set_stack_frame_offset(stack_frame_offset_base + (i as isize)); parameter_symbols.push(parameter_symbol); } Err(mut parameter_diagnostics) => { diagnostics.append(&mut parameter_diagnostics); } } } function_symbol .borrow_mut() .set_parameters(parameter_symbols); symbol_table.push_scope(&format!("body_scope({})", self.declared_name)); for statement in &mut self.statements { diagnostics.append(&mut statement.gather_declared_names(symbol_table)); } symbol_table.pop_scope(); // body symbol_table.pop_scope(); // params diagnostics } pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { let mut diagnostics = vec![]; // parameters diagnostics.append( &mut self .parameters .iter_mut() .map(|parameter| parameter.check_name_usages(symbol_table)) .filter_map(|result| result.err()) .flatten() .collect(), ); // statements for statement in &mut self.statements { diagnostics.append(&mut statement.check_name_usages(symbol_table)); } diagnostics } pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { let mut diagnostics = vec![]; // parameters diagnostics.append( &mut self .parameters .iter_mut() .map(|parameter| parameter.type_check(symbol_table)) .filter_map(|result| result.err()) .flatten() .collect(), ); // statements for statement in &mut self.statements { diagnostics.append(&mut statement.type_check(symbol_table)); } diagnostics } pub fn assemble( &self, context: &mut AssembleContext, symbol_table: &SymbolTable, constants_table: &mut ConstantsTable, ) { context.new_function(&self.declared_name, &self.declared_name_source_range); context.new_block(&format!("{}_enter", self.declared_name)); for (i, statement) in self.statements.iter().enumerate() { let is_last = i == self.statements.len() - 1; statement.assemble(context, symbol_table, constants_table, is_last); } // return context.instruction(AsmInstruction::Return(Return::new(self.parameters.len()))); context.complete_function(); } }