Refactoring top-level constructs.

This commit is contained in:
Jesse Brault 2026-03-17 11:26:57 -05:00
parent 86fcbb494b
commit 42a5b994d2
7 changed files with 530 additions and 321 deletions

View File

@ -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();

View File

@ -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<Diagnostic> = 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<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = 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<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = 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<IrClass>, Vec<IrFunction>) {

View File

@ -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<Diagnostic>> {
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<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = 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<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = 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<Diagnostic>> {
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<RefCell<ClassSymbol>>>,
diagnostics: &mut Vec<Diagnostic>,
) {
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<RefCell<ClassSymbol>>>,
diagnostics: &mut Vec<Diagnostic>,
) {
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<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = self
.parameters
.iter_mut()
.map(|parameter| parameter.check_name_usages(symbol_table, class_context))
.filter_map(Result::err)
.flatten()
.collect();
let mut diagnostics: Vec<Diagnostic> = 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<Diagnostic>,
) {
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<Diagnostic>,
) {
handle_diagnostics!(self.return_type.type_check(symbol_table), diagnostics);
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let diagnostics: Vec<Diagnostic> = 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<Diagnostic> = vec![];
self.type_check_parameters(symbol_table, &mut diagnostics);
self.type_check_return_type(symbol_table, &mut diagnostics);
diagnostics_result!(diagnostics)
}
}

View File

@ -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<RefCell<ClassSymbol>>>) {
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<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> {
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<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> {
// handle self case
self.maybe_insert_self_parameter(class_context);
let mut diagnostics: Vec<Diagnostic> = 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<Diagnostic>,
) {
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<Diagnostic>,
) {
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<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> {
// 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<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> {
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<Diagnostic>,
) {
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<RefCell<ClassSymbol>>>,
diagnostics: &mut Vec<Diagnostic>,
) {
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<Diagnostic>,
) {
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<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> {
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<Diagnostic>,
) {
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<Diagnostic>,
) {
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<Diagnostic>,
) {
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<Diagnostic>> {
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<Rc<RefCell<ClassSymbol>>>,
) -> 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<RefCell<ClassSymbol>>>,
) {
// 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<RefCell<ClassSymbol>>>,
) -> 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,
)
}

View File

@ -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<T>(
ts: &mut [T],
mut phase: impl FnMut(&mut T) -> Result<(), Vec<Diagnostic>>,
diagnostics: &mut Vec<Diagnostic>,
) {
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<T>(
ts: &mut [T],
mut phase: impl FnMut(usize, &mut T) -> Result<(), Vec<Diagnostic>>,
diagnostics: &mut Vec<Diagnostic>,
) {
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<T>(
insert_result: Result<T, SymbolInsertError>,
declared_name_source_range: &SourceRange,
) -> Result<T, Vec<Diagnostic>> {
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<T, R>(
ts: &mut [T],
mut f: impl FnMut(&mut T) -> Result<R, Vec<Diagnostic>>,
diagnostics: &mut Vec<Diagnostic>,
) -> Vec<R> {
let mut rs: Vec<R> = vec![];
for t in &mut ts[..] {
match f(t) {
Ok(r) => rs.push(r),
Err(mut t_diagnostics) => {
diagnostics.append(&mut t_diagnostics);
}
}
}
rs
}

View File

@ -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;

View File

@ -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 ) => {