From 42a5b994d22fb46c8f8317f610150c6fd74e579e Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Tue, 17 Mar 2026 11:26:57 -0500 Subject: [PATCH] Refactoring top-level constructs. --- dmc-lib/src/ast/class.rs | 9 +- dmc-lib/src/ast/compilation_unit.rs | 119 ++++----- dmc-lib/src/ast/extern_function.rs | 222 ++++++++------- dmc-lib/src/ast/function.rs | 400 +++++++++++++++++----------- dmc-lib/src/ast/helpers.rs | 79 ++++++ dmc-lib/src/ast/mod.rs | 1 + dmc-lib/src/ast/util.rs | 21 ++ 7 files changed, 530 insertions(+), 321 deletions(-) create mode 100644 dmc-lib/src/ast/helpers.rs diff --git a/dmc-lib/src/ast/class.rs b/dmc-lib/src/ast/class.rs index 87e9599..f7809fa 100644 --- a/dmc-lib/src/ast/class.rs +++ b/dmc-lib/src/ast/class.rs @@ -11,14 +11,13 @@ use crate::ir::ir_class::{IrClass, IrField}; use crate::ir::ir_function::IrFunction; use crate::maybe_return_diagnostics; use crate::source_range::SourceRange; -use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; +use crate::symbol::Symbol; use crate::symbol_table::{SymbolInsertError, SymbolTable}; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; -use std::process::id; use std::rc::Rc; pub struct Class { @@ -342,10 +341,8 @@ impl Class { } for function in &self.functions { - ir_functions.push(function.to_ir( - symbol_table, - Some(self.class_symbol.as_ref().unwrap().clone()), - )); + ir_functions + .push(function.to_ir(symbol_table, Some(self.class_symbol.as_ref().unwrap()))); } let class_symbol = self.class_symbol.as_ref().unwrap().borrow(); diff --git a/dmc-lib/src/ast/compilation_unit.rs b/dmc-lib/src/ast/compilation_unit.rs index 1bf30ea..5b4a3c0 100644 --- a/dmc-lib/src/ast/compilation_unit.rs +++ b/dmc-lib/src/ast/compilation_unit.rs @@ -2,7 +2,9 @@ use crate::ast::class::Class; use crate::ast::extern_function::ExternFunction; use crate::ast::fqn_context::FqnContext; use crate::ast::function::Function; +use crate::ast::helpers::iter_phase; use crate::diagnostic::Diagnostic; +use crate::diagnostics_result; use crate::ir::ir_class::IrClass; use crate::ir::ir_function::IrFunction; use crate::symbol_table::SymbolTable; @@ -47,96 +49,75 @@ impl CompilationUnit { let mut fqn_context = FqnContext::new(); // in the future, we'll push the pkg/ns on here let mut diagnostics: Vec = vec![]; - self.functions - .iter_mut() - .map(|f| f.gather_declared_names(symbol_table, &fqn_context, None)) - .filter_map(Result::err) - .flatten() - .for_each(|diagnostic| diagnostics.push(diagnostic)); + iter_phase( + &mut self.functions, + |f| f.gather_declared_names(symbol_table, &fqn_context, None), + &mut diagnostics, + ); - self.extern_functions - .iter_mut() - .map(|f| f.gather_declared_names(symbol_table, &fqn_context)) - .filter_map(Result::err) - .flatten() - .for_each(|diagnostic| diagnostics.push(diagnostic)); + iter_phase( + &mut self.extern_functions, + |ef| ef.gather_declared_names(symbol_table, &fqn_context), + &mut diagnostics, + ); - self.classes - .iter_mut() - .map(|c| c.gather_declared_names(symbol_table, &mut fqn_context)) - .filter_map(Result::err) - .flatten() - .for_each(|diagnostic| diagnostics.push(diagnostic)); + iter_phase( + &mut self.classes, + |c| c.gather_declared_names(symbol_table, &mut fqn_context), + &mut diagnostics, + ); symbol_table.pop_scope(); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + diagnostics_result!(diagnostics) } pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let mut diagnostics: Vec = vec![]; - self.functions - .iter_mut() - .map(|f| f.check_name_usages(symbol_table, None)) - .filter_map(Result::err) - .flatten() - .for_each(|diagnostic| diagnostics.push(diagnostic)); + iter_phase( + &mut self.functions, + |f| f.check_name_usages(symbol_table, None), + &mut diagnostics, + ); - self.extern_functions - .iter_mut() - .map(|f| f.check_name_usages(symbol_table, None)) - .filter_map(Result::err) - .flatten() - .for_each(|diagnostic| diagnostics.push(diagnostic)); + iter_phase( + &mut self.extern_functions, + |ef| ef.check_name_usages(symbol_table, None), + &mut diagnostics, + ); - self.classes - .iter_mut() - .map(|c| c.check_name_usages(symbol_table)) - .filter_map(Result::err) - .flatten() - .for_each(|diagnostic| diagnostics.push(diagnostic)); + iter_phase( + &mut self.classes, + |c| c.check_name_usages(symbol_table), + &mut diagnostics, + ); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + diagnostics_result!(diagnostics) } pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let mut diagnostics: Vec = vec![]; - self.functions - .iter_mut() - .map(|f| f.type_check(symbol_table)) - .filter_map(Result::err) - .flatten() - .for_each(|diagnostic| diagnostics.push(diagnostic)); + iter_phase( + &mut self.functions, + |f| f.type_check(symbol_table), + &mut diagnostics, + ); - self.extern_functions - .iter_mut() - .map(|f| f.type_check(symbol_table)) - .filter_map(Result::err) - .flatten() - .for_each(|diagnostic| diagnostics.push(diagnostic)); + iter_phase( + &mut self.extern_functions, + |ef| ef.type_check(symbol_table), + &mut diagnostics, + ); - self.classes - .iter_mut() - .map(|c| c.type_check(symbol_table)) - .filter_map(Result::err) - .flatten() - .for_each(|diagnostic| diagnostics.push(diagnostic)); + iter_phase( + &mut self.classes, + |c| c.type_check(symbol_table), + &mut diagnostics, + ); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + diagnostics_result!(diagnostics) } pub fn to_ir(&self, symbol_table: &SymbolTable) -> (Vec, Vec) { diff --git a/dmc-lib/src/ast/extern_function.rs b/dmc-lib/src/ast/extern_function.rs index 1495fd1..29a9adf 100644 --- a/dmc-lib/src/ast/extern_function.rs +++ b/dmc-lib/src/ast/extern_function.rs @@ -1,11 +1,13 @@ use crate::ast::fqn_context::FqnContext; +use crate::ast::helpers::{gather_oks, iter_phase, map_symbol_insert_result}; use crate::ast::parameter::Parameter; use crate::ast::type_use::TypeUse; use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::function_symbol::FunctionSymbol; -use crate::symbol_table::{SymbolInsertError, SymbolTable}; +use crate::symbol_table::SymbolTable; +use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics, ok_or_return}; use std::cell::RefCell; use std::rc::Rc; @@ -37,13 +39,12 @@ impl ExternFunction { &self.declared_name } - pub fn gather_declared_names( + /// Inserts and saves a `FunctionSymbol`. + fn insert_save_function_symbol( &mut self, symbol_table: &mut SymbolTable, fqn_context: &FqnContext, ) -> Result<(), Vec> { - let mut diagnostics = vec![]; - let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( &self.declared_name, self.declared_name_source_range.clone(), @@ -51,61 +52,108 @@ impl ExternFunction { true, )); - 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.symbol().borrow().declared_name() - ), - self.declared_name_source_range.start(), - self.declared_name_source_range.end(), - )); - Err(diagnostics) - } - }; - } - }; + let function_symbol = ok_or_return!(map_symbol_insert_result( + insert_result, + &self.declared_name_source_range + )); + + self.function_symbol = Some(function_symbol); + + Ok(()) + } + + /// Gathers parameters' declared names, and sets `ParameterSymbols` on the `FunctionSymbol`. + fn gather_parameters_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { + let mut diagnostics: Vec = vec![]; + + let parameter_symbols = gather_oks( + &mut self.parameters, + |p| p.gather_declared_names(symbol_table), + &mut diagnostics, + ); + + // Only continue if we have all the parameters and no diagnostics. + maybe_return_diagnostics!(diagnostics); + + self.function_symbol + .as_mut() + .unwrap() + .borrow_mut() + .set_parameters(parameter_symbols); + + Ok(()) + } + + /// Gathers return type names + fn gather_return_type_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { + let mut diagnostics: Vec = vec![]; + handle_diagnostics!( + self.return_type.gather_declared_names(symbol_table), + diagnostics + ); + diagnostics_result!(diagnostics) + } + + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + fqn_context: &FqnContext, + ) -> Result<(), Vec> { + self.insert_save_function_symbol(symbol_table, fqn_context)?; symbol_table .push_function_scope(&format!("extern_function_scope({})", &self.declared_name)); - let mut parameter_symbols = vec![]; - for parameter in &mut self.parameters { - let parameter_result = parameter.gather_declared_names(symbol_table); - match parameter_result { - Ok(parameter_symbol) => { - parameter_symbols.push(parameter_symbol); - } - Err(mut parameter_diagnostics) => { - diagnostics.append(&mut parameter_diagnostics); - } - } - } - function_symbol - .borrow_mut() - .set_parameters(parameter_symbols); - - self.function_symbol = Some(function_symbol); + self.gather_parameters_names(symbol_table) + .inspect_err(|_| symbol_table.pop_scope())?; // pop scope if we had diagnostics! // handle return type - match self.return_type.gather_declared_names(symbol_table) { - Ok(_) => {} - Err(mut type_use_diagnostics) => { - diagnostics.append(&mut type_use_diagnostics); - } - } + self.gather_return_type_names(symbol_table) + .inspect_err(|_| symbol_table.pop_scope())?; symbol_table.pop_scope(); // function scope + Ok(()) + } - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + fn check_parameters_names( + &mut self, + symbol_table: &SymbolTable, + class_context: Option<&Rc>>, + diagnostics: &mut Vec, + ) { + iter_phase( + &mut self.parameters, + |p| p.check_name_usages(symbol_table, class_context), + diagnostics, + ); + } + + fn check_return_type_names( + &mut self, + symbol_table: &SymbolTable, + class_context: Option<&Rc>>, + diagnostics: &mut Vec, + ) { + handle_diagnostics!( + self.return_type + .check_name_usages(symbol_table, class_context), + diagnostics + ); + } + + /// Sets the `FunctionSymbol`'s return type info. + fn set_function_symbol_return_type(&mut self) { + self.function_symbol + .as_mut() + .unwrap() + .borrow_mut() + .set_return_type_info(self.return_type.type_info().clone()); } pub fn check_name_usages( @@ -113,50 +161,46 @@ impl ExternFunction { symbol_table: &SymbolTable, class_context: Option<&Rc>>, ) -> Result<(), Vec> { - let mut diagnostics: Vec = self - .parameters - .iter_mut() - .map(|parameter| parameter.check_name_usages(symbol_table, class_context)) - .filter_map(Result::err) - .flatten() - .collect(); + let mut diagnostics: Vec = vec![]; - match self - .return_type - .check_name_usages(symbol_table, class_context) - { - Ok(_) => {} - Err(mut return_type_diagnostics) => { - diagnostics.append(&mut return_type_diagnostics); - } - } + self.check_parameters_names(symbol_table, class_context, &mut diagnostics); - if diagnostics.is_empty() { - // set return type info on symbol now that its available - self.function_symbol - .as_mut() - .unwrap() - .borrow_mut() - .set_return_type_info(self.return_type.type_info().clone()); + self.check_return_type_names(symbol_table, class_context, &mut diagnostics); - Ok(()) - } else { - Err(diagnostics) - } + maybe_return_diagnostics!(diagnostics); + + // set return type info on symbol now that its available + self.set_function_symbol_return_type(); + + Ok(()) + } + + fn type_check_parameters( + &mut self, + symbol_table: &SymbolTable, + diagnostics: &mut Vec, + ) { + iter_phase( + &mut self.parameters, + |p| p.type_check(symbol_table), + diagnostics, + ); + } + + fn type_check_return_type( + &mut self, + symbol_table: &SymbolTable, + diagnostics: &mut Vec, + ) { + handle_diagnostics!(self.return_type.type_check(symbol_table), diagnostics); } pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - let diagnostics: Vec = self - .parameters - .iter_mut() - .map(|parameter| parameter.type_check(symbol_table)) - .filter_map(Result::err) - .flatten() - .collect(); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + let mut diagnostics: Vec = vec![]; + + self.type_check_parameters(symbol_table, &mut diagnostics); + self.type_check_return_type(symbol_table, &mut diagnostics); + + diagnostics_result!(diagnostics) } } diff --git a/dmc-lib/src/ast/function.rs b/dmc-lib/src/ast/function.rs index f0f7377..00ceffb 100644 --- a/dmc-lib/src/ast/function.rs +++ b/dmc-lib/src/ast/function.rs @@ -1,11 +1,13 @@ use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_util::fqn_parts_to_string; +use crate::ast::helpers::{ + gather_oks, iter_phase, iter_phase_enumerated, map_symbol_insert_result, +}; use crate::ast::ir_builder::IrBuilder; use crate::ast::parameter::Parameter; use crate::ast::statement::Statement; use crate::ast::type_use::TypeUse; use crate::diagnostic::Diagnostic; -use crate::handle_diagnostics; use crate::ir::ir_function::IrFunction; use crate::ir::ir_parameter::IrParameter; use crate::ir::ir_parameter_or_variable::IrParameterOrVariable; @@ -13,8 +15,9 @@ 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::{SymbolInsertError, SymbolTable}; +use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics, ok_or_return}; use std::cell::RefCell; use std::ops::Neg; use std::rc::Rc; @@ -57,19 +60,31 @@ impl Function { self.statements.iter().collect() } - pub fn gather_declared_names( + /// Inserts a "self" parameter at index 0. + fn insert_self_parameter(&mut self) { + self.parameters.insert( + 0, + Parameter::new( + "self", + SourceRange::new(0, 0), + TypeUse::new("Self", SourceRange::new(0, 0), vec![]), + ), + ); + } + + /// If `class_context` is `Some`, calls `insert_self_parameter()`. + fn maybe_insert_self_parameter(&mut self, class_context: Option<&Rc>>) { + if class_context.is_some() { + self.insert_self_parameter(); + } + } + + /// Inserts and saves to self a `FunctionSymbol` + fn insert_save_function_symbol( &mut self, symbol_table: &mut SymbolTable, fqn_context: &FqnContext, - class_context: Option<&Rc>>, ) -> Result<(), Vec> { - let mut diagnostics = vec![]; - - if !diagnostics.is_empty() { - return Err(diagnostics); - } - - // insert function symbol let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( self.declared_name(), self.declared_name_source_range.clone(), @@ -78,109 +93,126 @@ impl Function { )); // 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.symbol().borrow().declared_name() - ), - self.declared_name_source_range.start(), - self.declared_name_source_range.end(), - )); - Err(diagnostics) - } - }; - } - }; + let function_symbol = ok_or_return!(map_symbol_insert_result( + insert_result, + &self.declared_name_source_range + )); // save function symbol for later self.function_symbol = Some(function_symbol.clone()); - // handle parameters - symbol_table.push_function_scope(&format!("function_scope({})", self.declared_name)); + Ok(()) + } - // first, if we are in a class context, insert a "fake" self parameter - if let Some(class_symbol) = class_context { - self.parameters.insert( - 0, - Parameter::new( - "self", - SourceRange::new(0, 0), - TypeUse::new("Self", SourceRange::new(0, 0), vec![]), - ), - ); - } + /// Gathers all declared names in parameters + fn gather_parameters_names( + &mut self, + symbol_table: &mut SymbolTable, + class_context: Option<&Rc>>, + ) -> Result<(), Vec> { + // handle self case + self.maybe_insert_self_parameter(class_context); + + let mut diagnostics: Vec = vec![]; // now gather all names for the params - let mut parameter_symbols = vec![]; - for parameter in &mut self.parameters { - match parameter.gather_declared_names(symbol_table) { - Ok(parameter_symbol) => { - parameter_symbols.push(parameter_symbol); - } - Err(mut parameter_diagnostics) => { - diagnostics.append(&mut parameter_diagnostics); - } - } - } + let parameter_symbols = gather_oks( + &mut self.parameters, + |p| p.gather_declared_names(symbol_table), + &mut diagnostics, + ); + + // important: if there were diagnostics we don't have all the param symbols + // n.b.: passing pop_scope callback because we don't want to mess up the scopes for others + maybe_return_diagnostics!(diagnostics); // set params on function symbol - function_symbol + self.function_symbol + .as_mut() + .unwrap() .borrow_mut() .set_parameters(parameter_symbols); + Ok(()) + } + + /// Gathers all return type names + fn gather_return_type_names( + &mut self, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, + ) { + iter_phase( + self.return_type.as_mut_slice(), + |type_use| type_use.gather_declared_names(symbol_table), + diagnostics, + ); + } + + /// Gathers all statements' names + fn gather_statements_names( + &mut self, + symbol_table: &mut SymbolTable, + diagnostics: &mut Vec, + ) { + iter_phase( + &mut self.statements, + |s| s.gather_declared_names(symbol_table), + diagnostics, + ); + } + + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + fqn_context: &FqnContext, + class_context: Option<&Rc>>, + ) -> Result<(), Vec> { + // function symbol + self.insert_save_function_symbol(symbol_table, fqn_context)?; + + // parameters + symbol_table.push_function_scope(&format!("function_scope({})", self.declared_name)); + self.gather_parameters_names(symbol_table, class_context) + .inspect_err(|_| symbol_table.pop_scope())?; // if we errored, we need to fix scopes // return type - if let Some(type_use) = &mut self.return_type { - match type_use.gather_declared_names(symbol_table) { - Ok(_) => {} - Err(mut type_use_diagnostics) => { - diagnostics.append(&mut type_use_diagnostics); - } - } - } + let mut diagnostics = vec![]; + self.gather_return_type_names(symbol_table, &mut diagnostics); + // enter main block symbol_table.push_block_scope(&format!("main_block_scope({})", self.declared_name)); - for statement in &mut self.statements { - match statement.gather_declared_names(symbol_table) { - Ok(_) => {} - Err(mut statement_diagnostics) => { - diagnostics.append(&mut statement_diagnostics); - } - } - } + + // statements + self.gather_statements_names(symbol_table, &mut diagnostics); + symbol_table.pop_scope(); // main block scope symbol_table.pop_scope(); // function scope - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + diagnostics_result!(diagnostics) } - pub fn check_name_usages( + /// Checks parameter name usages + fn check_parameter_names( &mut self, symbol_table: &SymbolTable, class_context: Option<&Rc>>, - ) -> Result<(), Vec> { - let mut diagnostics = vec![]; - - // parameters - diagnostics.append( - &mut self - .parameters - .iter_mut() - .map(|parameter| parameter.check_name_usages(symbol_table, class_context)) - .filter_map(|result| result.err()) - .flatten() - .collect(), + diagnostics: &mut Vec, + ) { + iter_phase( + &mut self.parameters, + |p| p.check_name_usages(symbol_table, class_context), + diagnostics, ); + } - // return type + /// Checks return type name usages, if there is a `TypeUse` present. If `None`, sets the + /// `FunctionSymbol`'s return type to `TypeInfo::Void`. + fn check_return_type_names( + &mut self, + symbol_table: &SymbolTable, + class_context: Option<&Rc>>, + diagnostics: &mut Vec, + ) { if let Some(type_use) = &mut self.return_type { match type_use.check_name_usages(symbol_table, class_context) { Ok(_) => { @@ -203,81 +235,111 @@ impl Function { .borrow_mut() .set_return_type_info(TypeInfo::Void); } + } + + /// Checks all statement name usages. + fn check_statement_names( + &mut self, + symbol_table: &SymbolTable, + diagnostics: &mut Vec, + ) { + iter_phase( + &mut self.statements, + |s| s.check_name_usages(symbol_table), + diagnostics, + ); + } + + pub fn check_name_usages( + &mut self, + symbol_table: &SymbolTable, + class_context: Option<&Rc>>, + ) -> Result<(), Vec> { + let mut diagnostics = vec![]; + + // parameters + self.check_parameter_names(symbol_table, class_context, &mut diagnostics); + + // return type + self.check_return_type_names(symbol_table, class_context, &mut diagnostics); // statements - for statement in &mut self.statements { - match statement.check_name_usages(symbol_table) { - Ok(_) => {} - Err(mut statement_diagnostics) => { - diagnostics.append(&mut statement_diagnostics); - } - } - } + self.check_statement_names(symbol_table, &mut diagnostics); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) + diagnostics_result!(diagnostics) + } + + /// Gets the return type `TypeInfo`. This function is only safe to call after an `Ok` call to + /// `check_return_type_names()`. + fn get_return_type_info(&self) -> TypeInfo { + let function_symbol = self.function_symbol.as_ref().unwrap().borrow(); + function_symbol.return_type_info().clone() + } + + /// Type checks parameters. + fn type_check_parameters( + &mut self, + symbol_table: &SymbolTable, + diagnostics: &mut Vec, + ) { + iter_phase( + &mut self.parameters, + |p| p.type_check(symbol_table), + diagnostics, + ) + } + + /// Type checks return type. + fn type_check_return_type( + &mut self, + symbol_table: &SymbolTable, + diagnostics: &mut Vec, + ) { + if let Some(type_use) = &mut self.return_type { + handle_diagnostics!(type_use.type_check(symbol_table), diagnostics); } } + /// Type checks statements, making sure the last statement matches return type, if necessary. + fn type_check_statements( + &mut self, + symbol_table: &SymbolTable, + diagnostics: &mut Vec, + ) { + let return_type_info = self.get_return_type_info(); + let statements_len = self.statements.len(); + + iter_phase_enumerated( + &mut self.statements, + |i, s| { + let is_last = i == statements_len - 1; + if is_last { + s.type_check(symbol_table, Some(&return_type_info)) + } else { + s.type_check(symbol_table, None) + } + }, + diagnostics, + ); + } + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let mut diagnostics = vec![]; // parameters - diagnostics.append( - &mut self - .parameters - .iter_mut() - .map(|parameter| parameter.type_check(symbol_table)) - .filter_map(Result::err) - .flatten() - .collect(), - ); - - let function_symbol = self.function_symbol.as_ref().unwrap(); - let return_type_info = function_symbol.borrow().return_type_info().clone(); - let statements_len = self.statements.len(); + self.type_check_parameters(symbol_table, &mut diagnostics); // return type - if let Some(type_use) = &mut self.return_type { - handle_diagnostics!(type_use.type_check(symbol_table), diagnostics); - } + self.type_check_return_type(symbol_table, &mut diagnostics); // statements - diagnostics.append( - &mut self - .statements - .iter_mut() - .enumerate() - .map(|(i, statement)| { - let is_last = i == statements_len - 1; - if is_last { - statement.type_check(symbol_table, Some(&return_type_info)) - } else { - statement.type_check(symbol_table, None) - } - }) - .filter_map(Result::err) - .flatten() - .collect(), - ); + self.type_check_statements(symbol_table, &mut diagnostics); - if diagnostics.is_empty() { - Ok(()) - } else { - Err(diagnostics) - } + diagnostics_result!(diagnostics) } - pub fn to_ir( - &self, - symbol_table: &SymbolTable, - class_context: Option>>, - ) -> IrFunction { - let mut builder = IrBuilder::new(); - - // parameters + /// Converts all parameters to ir. Saves the IrParameter to the associated parameter symbol. + fn parameters_to_ir(&self, builder: &mut IrBuilder) { for (i, parameter) in self.parameters.iter().enumerate() { let parameter_symbol = parameter.parameter_symbol(); let stack_offset = (self.parameters.len() as isize).neg() + (i as isize); @@ -290,33 +352,57 @@ impl Function { builder.parameters_mut().push(as_rc.clone()); parameter_symbol.borrow_mut().set_ir_parameter(as_rc); } + } - let entry_block_id = builder.new_block(); - - // preamble + /// 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<&Rc>>, + ) { // if we are a method, we need to set the self parameter on the builder - if let Some(_) = class_context { + 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)); } + } - let function_symbol = self.function_symbol.as_ref().unwrap().borrow(); - let return_type_info = function_symbol.return_type_info(); - + /// Convert all statements to ir. + fn statements_to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { + let return_type_info = self.get_return_type_info(); 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(&mut builder, symbol_table, should_return_value && is_last); + statement.to_ir(builder, symbol_table, should_return_value && is_last); } + } + + pub fn to_ir( + &self, + symbol_table: &SymbolTable, + class_context: Option<&Rc>>, + ) -> IrFunction { + let mut builder = IrBuilder::new(); + + // parameters + self.parameters_to_ir(&mut builder); + + 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); + builder.finish_block(); let entry_block = builder.get_block(entry_block_id).clone(); IrFunction::new( fqn_parts_to_string(self.function_symbol.as_ref().unwrap().borrow().fqn_parts()), builder.parameters(), - return_type_info, + &self.get_return_type_info(), entry_block, ) } diff --git a/dmc-lib/src/ast/helpers.rs b/dmc-lib/src/ast/helpers.rs new file mode 100644 index 0000000..f1fd023 --- /dev/null +++ b/dmc-lib/src/ast/helpers.rs @@ -0,0 +1,79 @@ +use crate::diagnostic::{Diagnostic, SecondaryLabel}; +use crate::error_codes::SYMBOL_ALREADY_DECLARED; +use crate::source_range::SourceRange; +use crate::symbol_table::SymbolInsertError; + +/// Iterates through all `ts`, running the `phase` function, and pushing returned `Diagnostic`s +/// into `diagnostics`. +pub fn iter_phase( + ts: &mut [T], + mut phase: impl FnMut(&mut T) -> Result<(), Vec>, + diagnostics: &mut Vec, +) { + ts.iter_mut() + .map(|t| phase(t)) + .filter_map(Result::err) + .flatten() + .for_each(|d| diagnostics.push(d)); +} + +/// Like `iter_phase` but enumerated. +pub fn iter_phase_enumerated( + ts: &mut [T], + mut phase: impl FnMut(usize, &mut T) -> Result<(), Vec>, + diagnostics: &mut Vec, +) { + ts.iter_mut() + .enumerate() + .map(|(i, t)| phase(i, t)) + .filter_map(Result::err) + .flatten() + .for_each(|d| diagnostics.push(d)); +} + +pub fn map_symbol_insert_result( + insert_result: Result, + declared_name_source_range: &SourceRange, +) -> Result> { + match insert_result { + Ok(symbol) => Ok(symbol), + Err(symbol_insert_error) => match symbol_insert_error { + SymbolInsertError::AlreadyDeclared(already_declared) => { + let already_declared_symbol = already_declared.symbol().borrow(); + let diagnostic = Diagnostic::new( + &format!( + "Symbol {} already declared in current scope.", + already_declared_symbol.declared_name() + ), + declared_name_source_range.start(), + declared_name_source_range.end(), + ) + .with_reporter(file!(), line!()) + .with_error_code(SYMBOL_ALREADY_DECLARED) + .with_secondary_labels(&[SecondaryLabel::new( + already_declared_symbol.declared_name_source_range().start(), + already_declared_symbol.declared_name_source_range().end(), + Some("Symbol already declared here.".to_string()), + )]); + Err(vec![diagnostic]) + } + }, + } +} + +pub fn gather_oks( + ts: &mut [T], + mut f: impl FnMut(&mut T) -> Result>, + diagnostics: &mut Vec, +) -> Vec { + let mut rs: Vec = vec![]; + for t in &mut ts[..] { + match f(t) { + Ok(r) => rs.push(r), + Err(mut t_diagnostics) => { + diagnostics.append(&mut t_diagnostics); + } + } + } + rs +} diff --git a/dmc-lib/src/ast/mod.rs b/dmc-lib/src/ast/mod.rs index d969d3f..c7327cc 100644 --- a/dmc-lib/src/ast/mod.rs +++ b/dmc-lib/src/ast/mod.rs @@ -14,6 +14,7 @@ pub mod fqn_context; pub mod fqn_util; pub mod function; pub mod generic_parameter; +mod helpers; pub mod identifier; pub mod integer_literal; pub mod ir_builder; diff --git a/dmc-lib/src/ast/util.rs b/dmc-lib/src/ast/util.rs index ffded57..12a27d0 100644 --- a/dmc-lib/src/ast/util.rs +++ b/dmc-lib/src/ast/util.rs @@ -22,6 +22,27 @@ macro_rules! handle_diagnostics { }; } +#[macro_export] +macro_rules! ok_or_return { + ( $result: expr, $diagnostics: expr ) => { + match $result { + Ok(inner) => inner, + Err(mut diagnostics) => { + $diagnostics.append(&mut diagnostics); + return Err($diagnostics); + } + } + }; + ( $result: expr ) => { + match $result { + Ok(inner) => inner, + Err(diagnostics) => { + return Err(diagnostics); + } + } + }; +} + #[macro_export] macro_rules! maybe_return_diagnostics { ( $diagnostics: expr ) => {