Refactoring top-level constructs.
This commit is contained in:
parent
86fcbb494b
commit
42a5b994d2
@ -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();
|
||||
|
||||
@ -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>) {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
79
dmc-lib/src/ast/helpers.rs
Normal file
79
dmc-lib/src/ast/helpers.rs
Normal 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
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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 ) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user