Compare commits

...

12 Commits

Author SHA1 Message Date
Jesse Brault
916b6377ac All examples working again. 2026-03-21 18:18:28 -05:00
Jesse Brault
bb2b539f9b All tests passing again. 2026-03-21 18:04:13 -05:00
Jesse Brault
4d6aa3ffd4 Fixing name checking for constructor assignment destinations. WIP. 2026-03-21 17:53:35 -05:00
Jesse Brault
344761022b More fixes to type info, etc. WIP. 2026-03-21 17:42:10 -05:00
Jesse Brault
fc83cf7827 Preparing for intrinsic class symbols. WIP. 2026-03-21 11:36:42 -05:00
Jesse Brault
7f1d507f4f Fixing more bugs. WIP. 2026-03-21 10:55:47 -05:00
Jesse Brault
912f208705 Fixing bugs. WIP. 2026-03-20 23:57:09 -05:00
Jesse Brault
4e8fa159c0 Fewer tests failing. WIP. 2026-03-20 20:39:30 -05:00
Jesse Brault
cf92356585 Major refactor of frontend ast semantic analysis. WIP. 2026-03-20 19:36:06 -05:00
Jesse Brault
8724c07ae2 Refactor class ast node. 2026-03-18 15:12:51 -05:00
Jesse Brault
7665a92678 Sketching class. 2026-03-18 10:37:38 -05:00
Jesse Brault
42a5b994d2 Refactoring top-level constructs. 2026-03-17 11:26:57 -05:00
57 changed files with 3973 additions and 2472 deletions

View File

@ -2,7 +2,7 @@ mod repl;
mod run; mod run;
use crate::repl::repl; use crate::repl::repl;
use crate::run::run; use crate::run::compile_and_run_script;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use std::path::PathBuf; use std::path::PathBuf;
@ -42,7 +42,7 @@ fn main() {
show_ir, show_ir,
register_count, register_count,
} => { } => {
run(script, *show_asm, *show_ir, *register_count); compile_and_run_script(script, *show_asm, *show_ir, *register_count);
} }
SubCommand::Repl { register_count } => { SubCommand::Repl { register_count } => {
repl(*register_count); repl(*register_count);

View File

@ -8,6 +8,7 @@ use dmc_lib::lexer::Lexer;
use dmc_lib::parser::parse_expression; use dmc_lib::parser::parse_expression;
use dmc_lib::symbol_table::SymbolTable; use dmc_lib::symbol_table::SymbolTable;
use dmc_lib::token::TokenKind; use dmc_lib::token::TokenKind;
use dmc_lib::types_table::TypesTable;
use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::constant::{Constant, StringConstant};
use dvm_lib::vm::function::Function; use dvm_lib::vm::function::Function;
use dvm_lib::vm::operand::Operand; use dvm_lib::vm::operand::Operand;
@ -97,24 +98,30 @@ fn compile_expression(
let mut expression = parse_result?; let mut expression = parse_result?;
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
let mut types_table = TypesTable::new();
// "fake" scopes // "fake" scopes
symbol_table.push_module_scope("__repl_module"); symbol_table.push_module_scope("__repl_module");
symbol_table.push_function_scope("__repl_function"); let function_scope_id = symbol_table.push_function_scope("__repl_function");
expression.gather_declared_names(&mut symbol_table)?; expression.init_scopes(&mut symbol_table, function_scope_id);
symbol_table.pop_scope(); // function symbol_table.pop_scope(); // function
symbol_table.pop_scope(); // module symbol_table.pop_scope(); // module
expression.check_name_usages(&symbol_table)?; let diagnostics = expression.check_static_fn_local_names(&symbol_table);
expression.type_check(&symbol_table)?; if !diagnostics.is_empty() {
return Err(diagnostics);
}
expression.type_check(&symbol_table, &mut types_table)?;
// synthesize a function // synthesize a function
let mut ir_builder = IrBuilder::new(); let mut ir_builder = IrBuilder::new();
let entry_block_id = ir_builder.new_block(); let entry_block_id = ir_builder.new_block();
let maybe_ir_expression = expression.to_ir_expression(&mut ir_builder, &symbol_table); let maybe_ir_expression =
expression.to_ir_expression(&mut ir_builder, &symbol_table, &types_table);
// if Some, return the value // if Some, return the value
ir_builder ir_builder
@ -126,8 +133,8 @@ fn compile_expression(
let mut ir_function = IrFunction::new( let mut ir_function = IrFunction::new(
"__repl".into(), "__repl".into(),
&[], vec![],
expression.type_info(), expression.type_info(&symbol_table, &types_table),
entry_block.clone(), entry_block.clone(),
); );

View File

@ -5,8 +5,10 @@ use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use dm_std_lib::add_all_std_core; use dm_std_lib::add_all_std_core;
use dmc_lib::constants_table::ConstantsTable; use dmc_lib::constants_table::ConstantsTable;
use dmc_lib::diagnostic::Diagnostic; use dmc_lib::diagnostic::Diagnostic;
use dmc_lib::intrinsics::{insert_intrinsic_symbols, insert_intrinsic_types};
use dmc_lib::parser::parse_compilation_unit; use dmc_lib::parser::parse_compilation_unit;
use dmc_lib::symbol_table::SymbolTable; use dmc_lib::symbol_table::SymbolTable;
use dmc_lib::types_table::TypesTable;
use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::constant::{Constant, StringConstant};
use dvm_lib::vm::function::Function; use dvm_lib::vm::function::Function;
use dvm_lib::vm::operand::Operand; use dvm_lib::vm::operand::Operand;
@ -14,45 +16,46 @@ use dvm_lib::vm::{CallStack, DvmContext, call};
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usize) { pub fn compile_and_run_script(
script: &PathBuf,
show_ir: bool,
show_asm: bool,
register_count: usize,
) {
let input = std::fs::read_to_string(script).unwrap(); let input = std::fs::read_to_string(script).unwrap();
let mut files: SimpleFiles<&str, &str> = SimpleFiles::new(); let mut files: SimpleFiles<&str, &str> = SimpleFiles::new();
let script_file_id = files.add(script.to_str().unwrap(), &input); let script_file_id = files.add(script.to_str().unwrap(), &input);
let parse_result = parse_compilation_unit(&input); match run(&input, show_ir, show_asm, register_count) {
let mut compilation_unit = match parse_result { Ok(_) => {}
Ok(compilation_unit) => compilation_unit,
Err(diagnostics) => { Err(diagnostics) => {
check_and_report_diagnostics(&files, script_file_id, &diagnostics); check_and_report_diagnostics(&files, script_file_id, &diagnostics);
unreachable!();
} }
}; }
}
fn run(
input: &str,
show_ir: bool,
show_asm: bool,
register_count: usize,
) -> Result<(), Vec<Diagnostic>> {
let mut compilation_unit = parse_compilation_unit(&input)?;
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
symbol_table.push_module_scope("global scope");
insert_intrinsic_symbols(&mut symbol_table);
let mut types_table = TypesTable::new();
insert_intrinsic_types(&symbol_table, &mut types_table);
match compilation_unit.gather_declared_names(&mut symbol_table) { compilation_unit.init_scopes(&mut symbol_table);
Ok(_) => {} compilation_unit.gather_symbols_into(&mut symbol_table)?;
Err(diagnostics) => { compilation_unit.check_names(&mut symbol_table)?;
report_and_exit(&diagnostics, script_file_id, &files); compilation_unit.gather_types_into(&symbol_table, &mut types_table)?;
} compilation_unit.type_check(&symbol_table, &mut types_table)?;
}
match compilation_unit.check_name_usages(&symbol_table) { let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table, &types_table);
Ok(_) => {}
Err(diagnostics) => {
report_and_exit(&diagnostics, script_file_id, &files);
}
}
match compilation_unit.type_check(&symbol_table) {
Ok(_) => {}
Err(diagnostics) => {
report_and_exit(&diagnostics, script_file_id, &files);
}
}
let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table);
if show_ir { if show_ir {
for ir_function in &ir_functions { for ir_function in &ir_functions {
@ -117,6 +120,8 @@ pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usiz
if let Some(value) = result { if let Some(value) = result {
println!("{}", value); println!("{}", value);
} }
Ok(())
} }
fn check_and_report_diagnostics( fn check_and_report_diagnostics(

View File

@ -6,8 +6,10 @@ use crate::error_codes::{ASSIGN_LHS_IMMUTABLE, ASSIGN_MISMATCHED_TYPES, ASSIGN_N
use crate::ir::ir_assign::IrAssign; use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_set_field::IrSetField; use crate::ir::ir_set_field::IrSetField;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
pub struct AssignStatement { pub struct AssignStatement {
destination: Box<Expression>, destination: Box<Expression>,
@ -26,68 +28,68 @@ impl AssignStatement {
&self.destination &self.destination
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
self.destination.init_scopes(symbol_table, container_scope);
self.value.init_scopes(symbol_table, container_scope);
}
pub fn check_constructor_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
[
self.destination
.check_constructor_destination_names(symbol_table, class_symbol),
self.value
.check_constructor_local_names(symbol_table, class_symbol),
]
.into_iter()
.flatten()
.collect()
}
pub fn check_method_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
[
self.destination
.check_method_local_names(symbol_table, class_symbol),
self.value
.check_method_local_names(symbol_table, class_symbol),
]
.into_iter()
.flatten()
.collect()
}
pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
[
self.destination.check_static_fn_local_names(symbol_table),
self.value.check_static_fn_local_names(symbol_table),
]
.into_iter()
.flatten()
.collect()
}
pub fn type_check(
&mut self, &mut self,
symbol_table: &mut SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![]; let mut diagnostics: Vec<Diagnostic> = vec![];
match self.value.gather_declared_names(symbol_table) { match self.value.type_check(symbol_table, types_table) {
Ok(_) => {} Ok(_) => {}
Err(mut value_diagnostics) => { Err(mut value_diagnostics) => {
diagnostics.append(&mut value_diagnostics); diagnostics.append(&mut value_diagnostics);
} }
} }
match self.destination.gather_declared_names(symbol_table) { match self.destination.type_check(symbol_table, types_table) {
Ok(_) => {}
Err(mut destination_diagnostics) => {
diagnostics.append(&mut destination_diagnostics);
}
}
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
}
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
match self.value.check_name_usages(symbol_table) {
Ok(_) => {}
Err(mut value_diagnostics) => {
diagnostics.append(&mut value_diagnostics);
}
}
match self.destination.check_name_usages(symbol_table) {
Ok(_) => {}
Err(mut destination_diagnostics) => {
diagnostics.append(&mut destination_diagnostics);
}
}
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
match self.value.type_check(symbol_table) {
Ok(_) => {}
Err(mut value_diagnostics) => {
diagnostics.append(&mut value_diagnostics);
}
}
match self.destination.type_check(symbol_table) {
Ok(_) => {} Ok(_) => {}
Err(mut destination_diagnostics) => { Err(mut destination_diagnostics) => {
diagnostics.append(&mut destination_diagnostics); diagnostics.append(&mut destination_diagnostics);
@ -97,40 +99,73 @@ impl AssignStatement {
// check destination is l value // check destination is l value
match &*self.destination { match &*self.destination {
Expression::Identifier(identifier) => { Expression::Identifier(identifier) => {
let expressible_symbol = identifier.expressible_symbol(); let expressible_symbol = symbol_table
.find_expressible_symbol(identifier.scope_id(), identifier.name())
.unwrap();
let is_mut = match &expressible_symbol {
ExpressibleSymbol::Field(field_symbol) => field_symbol.is_mut(),
ExpressibleSymbol::Variable(variable_symbol) => variable_symbol.is_mut(),
_ => false,
};
// check mutable // check mutable
if !expressible_symbol.is_mut() { if !is_mut {
let secondary_label = SecondaryLabel::new( let secondary_label =
expressible_symbol.source_range().start(), if let Some(source_range) = expressible_symbol.source_range() {
expressible_symbol.source_range().end(), Some(SecondaryLabel::new(
Some("Destination (declared here) is immutable.".to_string()), source_range.start(),
); source_range.end(),
let diagnostic = Diagnostic::new( Some("Destination (declared here) is immutable.".to_string()),
))
} else {
None
};
let mut diagnostic = Diagnostic::new(
"Destination is immutable and not re-assignable.", "Destination is immutable and not re-assignable.",
self.destination.source_range().start(), self.destination.source_range().start(),
self.destination.source_range().end(), self.destination.source_range().end(),
) )
.with_primary_label_message("Attempt to mutate immutable destination.") .with_primary_label_message("Attempt to mutate immutable destination.")
.with_reporter(file!(), line!()) .with_reporter(file!(), line!())
.with_error_code(ASSIGN_LHS_IMMUTABLE) .with_error_code(ASSIGN_LHS_IMMUTABLE);
.with_secondary_labels(&[secondary_label]);
if let Some(secondary_label) = secondary_label {
diagnostic = diagnostic.with_secondary_labels(&[secondary_label]);
}
diagnostics.push(diagnostic); diagnostics.push(diagnostic);
} }
// check assignable // check assignable
let lhs_type = expressible_symbol.type_info(); let lhs_type = match &expressible_symbol {
let rhs_type = self.value.type_info(); ExpressibleSymbol::Field(field_symbol) => {
types_table.field_types().get(field_symbol).unwrap()
}
ExpressibleSymbol::Variable(variable_symbol) => {
types_table.variable_types().get(variable_symbol).unwrap()
}
_ => panic!(),
};
let rhs_type = self.value.type_info(symbol_table, types_table);
if !lhs_type.is_assignable_from(rhs_type) { if !lhs_type.is_assignable_from(rhs_type) {
let secondary_label = SecondaryLabel::new( let secondary_label =
expressible_symbol.source_range().start(), if let Some(source_range) = expressible_symbol.source_range() {
expressible_symbol.source_range().end(), Some(SecondaryLabel::new(
Some(format!( source_range.start(),
"Destination declared here is of type {}.", source_range.end(),
lhs_type Some(format!(
)), "Destination declared here is of type {}.",
); lhs_type
let diagnostic = Diagnostic::new( )),
))
} else {
None
};
let mut diagnostic = Diagnostic::new(
&format!( &format!(
"Mismatched types: right-hand side {} is not assignable to left {}.", "Mismatched types: right-hand side {} is not assignable to left {}.",
rhs_type, lhs_type rhs_type, lhs_type
@ -143,8 +178,12 @@ impl AssignStatement {
rhs_type, lhs_type rhs_type, lhs_type
)) ))
.with_error_code(ASSIGN_MISMATCHED_TYPES) .with_error_code(ASSIGN_MISMATCHED_TYPES)
.with_reporter(file!(), line!()) .with_reporter(file!(), line!());
.with_secondary_labels(&[secondary_label]);
if let Some(secondary_label) = secondary_label {
diagnostic = diagnostic.with_secondary_labels(&[secondary_label]);
}
diagnostics.push(diagnostic); diagnostics.push(diagnostic);
} }
} }
@ -168,29 +207,39 @@ impl AssignStatement {
} }
} }
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { pub fn to_ir(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) {
let destination_symbol = match &*self.destination { let destination_symbol = match &*self.destination {
Expression::Identifier(identifier) => identifier.expressible_symbol(), Expression::Identifier(identifier) => symbol_table
.find_expressible_symbol(identifier.scope_id(), identifier.name())
.unwrap(),
_ => unreachable!("Destination must be a mutable L value"), _ => unreachable!("Destination must be a mutable L value"),
}; };
let ir_statement = match destination_symbol { let ir_statement = match destination_symbol {
ExpressibleSymbol::Field(field_symbol) => { ExpressibleSymbol::Field(field_symbol) => {
let field_type = types_table.field_types().get(&field_symbol).unwrap();
let mut_field_pointer_variable = let mut_field_pointer_variable =
get_or_init_mut_field_pointer_variable(builder, field_symbol).clone(); get_or_init_mut_field_pointer_variable(builder, &field_symbol, field_type)
.clone();
let ir_set_field = IrSetField::new( let ir_set_field = IrSetField::new(
&mut_field_pointer_variable, &mut_field_pointer_variable,
self.value self.value
.to_ir_expression(builder, symbol_table) .to_ir_expression(builder, symbol_table, types_table)
.expect("Attempt to convert non-value to value"), .expect("Attempt to convert non-value to value"),
); );
IrStatement::SetField(ir_set_field) IrStatement::SetField(ir_set_field)
} }
ExpressibleSymbol::Variable(variable_symbol) => { ExpressibleSymbol::Variable(variable_symbol) => {
let vr_variable = variable_symbol.borrow().vr_variable().clone(); let vr_variable = builder.local_variables().get(&variable_symbol).unwrap();
let ir_assign = IrAssign::new( let ir_assign = IrAssign::new(
vr_variable, vr_variable.clone(),
self.value.to_ir_operation(builder, symbol_table), self.value
.to_ir_operation(builder, symbol_table, types_table),
); );
IrStatement::Assign(ir_assign) IrStatement::Assign(ir_assign)
} }
@ -203,12 +252,26 @@ impl AssignStatement {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::ast::compilation_unit::CompilationUnit;
use crate::diagnostic::Diagnostic;
use crate::error_codes::{ASSIGN_LHS_IMMUTABLE, ASSIGN_MISMATCHED_TYPES, ASSIGN_NO_L_VALUE}; use crate::error_codes::{ASSIGN_LHS_IMMUTABLE, ASSIGN_MISMATCHED_TYPES, ASSIGN_NO_L_VALUE};
use crate::parser::parse_compilation_unit; use crate::parser::parse_compilation_unit;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
fn compile_up_to_type_check(
compilation_unit: &mut CompilationUnit,
symbol_table: &mut SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
compilation_unit.init_scopes(symbol_table);
compilation_unit.gather_symbols_into(symbol_table)?;
compilation_unit.check_names(symbol_table)?;
compilation_unit.gather_types_into(symbol_table, types_table)
}
#[test] #[test]
fn finds_mismatched_types() { fn finds_mismatched_types() -> Result<(), Vec<Diagnostic>> {
let mut compilation_unit = parse_compilation_unit( let mut compilation_unit = parse_compilation_unit(
" "
fn main() fn main()
@ -219,52 +282,41 @@ mod tests {
) )
.unwrap(); .unwrap();
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
compilation_unit let mut types_table = TypesTable::new();
.gather_declared_names(&mut symbol_table) compile_up_to_type_check(&mut compilation_unit, &mut symbol_table, &mut types_table)?;
.unwrap(); let diagnostics = compilation_unit
compilation_unit.check_name_usages(&symbol_table).unwrap(); .type_check(&symbol_table, &mut types_table)
match compilation_unit.type_check(&symbol_table) { .unwrap_err();
Ok(_) => { assert_eq!(diagnostics.len(), 1);
panic!("Type check missed diagnostic."); assert_eq!(
} diagnostics[0].error_code().unwrap(),
Err(diagnostics) => { ASSIGN_MISMATCHED_TYPES
assert_eq!(diagnostics.len(), 1); );
assert_eq!( Ok(())
diagnostics[0].error_code().unwrap(),
ASSIGN_MISMATCHED_TYPES
);
}
}
} }
#[test] #[test]
fn finds_no_l_value() { fn finds_no_l_value() -> Result<(), Vec<Diagnostic>> {
let mut compilation_unit = parse_compilation_unit( let mut compilation_unit = parse_compilation_unit(
" "
fn main() fn main()
42 = 42 42 = 42
end end
", ",
) )?;
.unwrap();
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
compilation_unit let mut types_table = TypesTable::new();
.gather_declared_names(&mut symbol_table) compile_up_to_type_check(&mut compilation_unit, &mut symbol_table, &mut types_table)?;
.unwrap(); let diagnostics = compilation_unit
compilation_unit.check_name_usages(&symbol_table).unwrap(); .type_check(&symbol_table, &mut types_table)
match compilation_unit.type_check(&symbol_table) { .unwrap_err();
Ok(_) => { assert_eq!(diagnostics.len(), 1);
panic!("Type check missed diagnostic."); assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_NO_L_VALUE);
} Ok(())
Err(diagnostics) => {
assert_eq!(diagnostics.len(), 1);
assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_NO_L_VALUE);
}
}
} }
#[test] #[test]
fn finds_immutable_destination() { fn finds_immutable_destination() -> Result<(), Vec<Diagnostic>> {
let mut compilation_unit = parse_compilation_unit( let mut compilation_unit = parse_compilation_unit(
" "
fn main() fn main()
@ -272,21 +324,15 @@ mod tests {
x = 43 x = 43
end end
", ",
) )?;
.unwrap();
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
compilation_unit let mut types_table = TypesTable::new();
.gather_declared_names(&mut symbol_table) compile_up_to_type_check(&mut compilation_unit, &mut symbol_table, &mut types_table)?;
.unwrap(); let diagnostics = compilation_unit
compilation_unit.check_name_usages(&symbol_table).unwrap(); .type_check(&symbol_table, &mut types_table)
match compilation_unit.type_check(&symbol_table) { .unwrap_err();
Ok(_) => { assert_eq!(diagnostics.len(), 1);
panic!("Type check missed diagnostic."); assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_LHS_IMMUTABLE);
} Ok(())
Err(diagnostics) => {
assert_eq!(diagnostics.len(), 1);
assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_LHS_IMMUTABLE);
}
}
} }
} }

View File

@ -9,8 +9,10 @@ use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable; use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostic, handle_diagnostics, maybe_return_diagnostics}; use crate::{diagnostics_result, handle_diagnostic, handle_diagnostics, maybe_return_diagnostics};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -72,45 +74,79 @@ impl BinaryExpression {
self.type_info.as_ref().unwrap() self.type_info.as_ref().unwrap()
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.lhs.init_scopes(symbol_table, container_scope);
symbol_table: &mut SymbolTable, self.rhs.init_scopes(symbol_table, container_scope);
) -> Result<(), Vec<Diagnostic>> {
let diagnostics = [&mut self.lhs, &mut self.rhs]
.iter_mut()
.map(|expression| expression.gather_declared_names(symbol_table))
.filter_map(|result| result.err())
.flatten()
.collect::<Vec<Diagnostic>>();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn check_field_initializer_names(
let diagnostics: Vec<Diagnostic> = [&mut self.lhs, &mut self.rhs] &self,
.iter_mut() symbol_table: &SymbolTable,
.map(|expression| expression.check_name_usages(symbol_table)) class_symbol: &ClassSymbol,
.filter_map(Result::err) ) -> Vec<Diagnostic> {
.flatten() [
.collect(); self.lhs
if diagnostics.is_empty() { .check_field_initializer_names(symbol_table, class_symbol),
Ok(()) self.rhs
} else { .check_field_initializer_names(symbol_table, class_symbol),
Err(diagnostics) ]
} .into_iter()
.flatten()
.collect()
}
pub fn check_constructor_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
[
self.lhs
.check_constructor_local_names(symbol_table, class_symbol),
self.rhs
.check_constructor_local_names(symbol_table, class_symbol),
]
.into_iter()
.flatten()
.collect()
}
pub fn check_method_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
[
self.lhs
.check_method_local_names(symbol_table, class_symbol),
self.rhs
.check_method_local_names(symbol_table, class_symbol),
]
.into_iter()
.flatten()
.collect()
}
pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
[
self.lhs.check_static_fn_local_names(symbol_table),
self.rhs.check_static_fn_local_names(symbol_table),
]
.into_iter()
.flatten()
.collect()
} }
fn check_op( fn check_op(
&mut self, &mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
check: impl Fn(&TypeInfo, &TypeInfo) -> bool, check: impl Fn(&TypeInfo, &TypeInfo) -> bool,
op_result: impl Fn(&TypeInfo, &TypeInfo) -> TypeInfo, op_result: impl Fn(&TypeInfo, &TypeInfo) -> TypeInfo,
lazy_diagnostic_message: impl Fn(&TypeInfo, &TypeInfo) -> String, lazy_diagnostic_message: impl Fn(&TypeInfo, &TypeInfo) -> String,
) -> Result<(), Diagnostic> { ) -> Result<(), Diagnostic> {
let lhs_type_info = self.lhs.type_info(); let lhs_type_info = self.lhs.type_info(symbol_table, types_table);
let rhs_type_info = self.rhs.type_info(); let rhs_type_info = self.rhs.type_info(symbol_table, types_table);
if check(lhs_type_info, rhs_type_info) { if check(lhs_type_info, rhs_type_info) {
self.type_info = Some(op_result(lhs_type_info, rhs_type_info)); self.type_info = Some(op_result(lhs_type_info, rhs_type_info));
@ -128,11 +164,15 @@ impl BinaryExpression {
} }
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![]; let mut diagnostics: Vec<Diagnostic> = vec![];
handle_diagnostics!(self.lhs.type_check(symbol_table), diagnostics); handle_diagnostics!(self.lhs.type_check(symbol_table, types_table), diagnostics);
handle_diagnostics!(self.rhs.type_check(symbol_table), diagnostics); handle_diagnostics!(self.rhs.type_check(symbol_table, types_table), diagnostics);
maybe_return_diagnostics!(diagnostics); maybe_return_diagnostics!(diagnostics);
@ -140,6 +180,8 @@ impl BinaryExpression {
BinaryOperation::Multiply => { BinaryOperation::Multiply => {
handle_diagnostic!( handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_multiply(rhs), |lhs, rhs| lhs.can_multiply(rhs),
|lhs, rhs| lhs.multiply_result(rhs), |lhs, rhs| lhs.multiply_result(rhs),
|lhs, rhs| format!( |lhs, rhs| format!(
@ -153,6 +195,8 @@ impl BinaryExpression {
BinaryOperation::Divide => { BinaryOperation::Divide => {
handle_diagnostic!( handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_divide(rhs), |lhs, rhs| lhs.can_divide(rhs),
|lhs, rhs| lhs.divide_result(rhs), |lhs, rhs| lhs.divide_result(rhs),
|lhs, rhs| format!("Incompatible types: cannot divide {} by {}", lhs, rhs) |lhs, rhs| format!("Incompatible types: cannot divide {} by {}", lhs, rhs)
@ -163,6 +207,8 @@ impl BinaryExpression {
BinaryOperation::Modulo => { BinaryOperation::Modulo => {
handle_diagnostic!( handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_modulo(rhs), |lhs, rhs| lhs.can_modulo(rhs),
|lhs, rhs| lhs.modulo_result(rhs), |lhs, rhs| lhs.modulo_result(rhs),
|lhs, rhs| format!("Incompatible types: cannot modulo {} by {}", lhs, rhs) |lhs, rhs| format!("Incompatible types: cannot modulo {} by {}", lhs, rhs)
@ -173,6 +219,8 @@ impl BinaryExpression {
BinaryOperation::Add => { BinaryOperation::Add => {
handle_diagnostic!( handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_add(rhs), |lhs, rhs| lhs.can_add(rhs),
|lhs, rhs| lhs.add_result(&rhs), |lhs, rhs| lhs.add_result(&rhs),
|lhs, rhs| format!("Incompatible types: cannot add {} to {}.", rhs, lhs) |lhs, rhs| format!("Incompatible types: cannot add {} to {}.", rhs, lhs)
@ -183,6 +231,8 @@ impl BinaryExpression {
BinaryOperation::Subtract => { BinaryOperation::Subtract => {
handle_diagnostic!( handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_subtract(rhs), |lhs, rhs| lhs.can_subtract(rhs),
|lhs, rhs| lhs.subtract_result(rhs), |lhs, rhs| lhs.subtract_result(rhs),
|lhs, rhs| format!( |lhs, rhs| format!(
@ -196,6 +246,8 @@ impl BinaryExpression {
BinaryOperation::LeftShift => { BinaryOperation::LeftShift => {
handle_diagnostic!( handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_left_shift(rhs), |lhs, rhs| lhs.can_left_shift(rhs),
|lhs, rhs| lhs.left_shift_result(rhs), |lhs, rhs| lhs.left_shift_result(rhs),
|lhs, rhs| format!( |lhs, rhs| format!(
@ -209,6 +261,8 @@ impl BinaryExpression {
BinaryOperation::RightShift => { BinaryOperation::RightShift => {
handle_diagnostic!( handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_right_shift(rhs), |lhs, rhs| lhs.can_right_shift(rhs),
|lhs, rhs| lhs.right_shift_result(rhs), |lhs, rhs| lhs.right_shift_result(rhs),
|lhs, rhs| format!( |lhs, rhs| format!(
@ -222,6 +276,8 @@ impl BinaryExpression {
BinaryOperation::BitwiseAnd => { BinaryOperation::BitwiseAnd => {
handle_diagnostic!( handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_bitwise_and(rhs), |lhs, rhs| lhs.can_bitwise_and(rhs),
|lhs, rhs| lhs.bitwise_and_result(rhs), |lhs, rhs| lhs.bitwise_and_result(rhs),
|lhs, rhs| format!( |lhs, rhs| format!(
@ -234,6 +290,8 @@ impl BinaryExpression {
} }
BinaryOperation::BitwiseXor => handle_diagnostic!( BinaryOperation::BitwiseXor => handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_bitwise_xor(rhs), |lhs, rhs| lhs.can_bitwise_xor(rhs),
|lhs, rhs| lhs.bitwise_xor_result(rhs), |lhs, rhs| lhs.bitwise_xor_result(rhs),
|lhs, rhs| format!("Incompatible types: cannot bitwise xor {} by {}", lhs, rhs) |lhs, rhs| format!("Incompatible types: cannot bitwise xor {} by {}", lhs, rhs)
@ -243,6 +301,8 @@ impl BinaryExpression {
BinaryOperation::BitwiseOr => { BinaryOperation::BitwiseOr => {
handle_diagnostic!( handle_diagnostic!(
self.check_op( self.check_op(
symbol_table,
types_table,
|lhs, rhs| lhs.can_bitwise_or(rhs), |lhs, rhs| lhs.can_bitwise_or(rhs),
|lhs, rhs| lhs.bitwise_or_result(rhs), |lhs, rhs| lhs.bitwise_or_result(rhs),
|lhs, rhs| format!( |lhs, rhs| format!(
@ -262,14 +322,15 @@ impl BinaryExpression {
&self, &self,
builder: &mut IrBuilder, builder: &mut IrBuilder,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrOperation { ) -> IrOperation {
let lhs = self let lhs = self
.lhs .lhs
.to_ir_expression(builder, symbol_table) .to_ir_expression(builder, symbol_table, types_table)
.expect("Attempt to use a non-value expression in binary expression."); .expect("Attempt to use a non-value expression in binary expression.");
let rhs = self let rhs = self
.rhs .rhs
.to_ir_expression(builder, symbol_table) .to_ir_expression(builder, symbol_table, types_table)
.expect("Attempt to use a non-value expression in binary expression."); .expect("Attempt to use a non-value expression in binary expression.");
let ir_binary_operation = match self.op { let ir_binary_operation = match self.op {
BinaryOperation::Multiply => { BinaryOperation::Multiply => {
@ -304,8 +365,9 @@ impl BinaryExpression {
&self, &self,
builder: &mut IrBuilder, builder: &mut IrBuilder,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrExpression { ) -> IrExpression {
let ir_operation = self.to_ir_operation(builder, symbol_table); let ir_operation = self.to_ir_operation(builder, symbol_table, types_table);
let t_var = IrVariable::new_vr( let t_var = IrVariable::new_vr(
builder.new_t_var().into(), builder.new_t_var().into(),
builder.current_block().id(), builder.current_block().id(),

View File

@ -1,3 +1,4 @@
use crate::ast::diagnostic_factories::class_has_no_constructor;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::fqn_util::fqn_parts_to_string;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
@ -6,15 +7,16 @@ use crate::ir::ir_call::IrCall;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::callable_symbol::CallableSymbol; use crate::symbol::callable_symbol::CallableSymbol;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
pub struct Call { pub struct Call {
callee: Box<Expression>, callee: Box<Expression>,
arguments: Vec<Expression>, arguments: Vec<Expression>,
source_range: SourceRange, source_range: SourceRange,
return_type_info: Option<TypeInfo>,
} }
impl Call { impl Call {
@ -23,7 +25,6 @@ impl Call {
callee: callee.into(), callee: callee.into(),
arguments, arguments,
source_range, source_range,
return_type_info: None,
} }
} }
@ -35,66 +36,110 @@ impl Call {
self.arguments.iter().collect() self.arguments.iter().collect()
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
self.callee.init_scopes(symbol_table, container_scope);
for argument in &mut self.arguments {
argument.init_scopes(symbol_table, container_scope);
}
}
pub fn check_field_initializer_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = Vec::new();
diagnostics.append(
&mut self
.callee
.check_field_initializer_names(symbol_table, class_symbol),
);
for argument in &self.arguments {
diagnostics
.append(&mut argument.check_field_initializer_names(symbol_table, class_symbol))
}
diagnostics
}
pub fn check_constructor_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
let mut diagnostics = Vec::new();
for argument in &self.arguments {
diagnostics
.append(&mut argument.check_constructor_local_names(symbol_table, class_symbol))
}
diagnostics.append(
&mut self
.callee
.check_constructor_local_names(symbol_table, class_symbol),
);
diagnostics
}
pub fn check_method_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
let mut diagnostics = Vec::new();
for argument in &self.arguments {
diagnostics.append(&mut argument.check_method_local_names(symbol_table, class_symbol));
}
diagnostics.append(
&mut self
.callee
.check_method_local_names(symbol_table, class_symbol),
);
diagnostics
}
pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = Vec::new();
for argument in &self.arguments {
diagnostics.append(&mut argument.check_static_fn_local_names(symbol_table));
}
diagnostics.append(&mut self.callee.check_static_fn_local_names(symbol_table));
diagnostics
}
pub fn type_check(
&mut self, &mut self,
symbol_table: &mut SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
let mut to_gather = vec![]; self.callee.as_mut().type_check(symbol_table, types_table)?;
to_gather.push(self.callee.as_mut());
to_gather.extend(&mut self.arguments);
let diagnostics: Vec<Diagnostic> = to_gather
.iter_mut()
.map(|expression| expression.gather_declared_names(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
}
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut to_check = vec![];
to_check.push(self.callee.as_mut());
to_check.extend(&mut self.arguments);
let diagnostics: Vec<Diagnostic> = to_check
.iter_mut()
.map(|expression| expression.check_name_usages(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.callee.as_mut().type_check(symbol_table)?;
let mut diagnostics: Vec<Diagnostic> = self let mut diagnostics: Vec<Diagnostic> = self
.arguments .arguments
.iter_mut() .iter_mut()
.map(|argument| argument.type_check(symbol_table)) .map(|argument| argument.type_check(symbol_table, types_table))
.filter_map(Result::err) .filter_map(Result::err)
.flatten() .flatten()
.collect(); .collect();
// check that callee is callable // check that callee is callable
let callable_symbol = match self.callee.type_info() { let callable_symbol = match self.callee.type_info(symbol_table, types_table) {
TypeInfo::Function(function_symbol) => { TypeInfo::Function(function_symbol) => {
CallableSymbol::Function(function_symbol.clone()) CallableSymbol::Function(function_symbol.clone())
} }
TypeInfo::ClassInstance(class_symbol) => CallableSymbol::Class(class_symbol.clone()), TypeInfo::Class(class_symbol) => match class_symbol.constructor_symbol_owned() {
None => {
diagnostics.push(class_has_no_constructor(
class_symbol.declared_name(),
self.callee.source_range(),
));
return Err(diagnostics);
}
Some(constructor_symbol) => CallableSymbol::Constructor(constructor_symbol),
},
_ => { _ => {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
&format!( &format!(
"Receiver of type {} is not callable.", "Receiver of type {} is not callable.",
self.callee.type_info() self.callee.type_info(symbol_table, types_table)
), ),
self.callee.source_range().start(), self.callee.source_range().start(),
self.callee.source_range().end(), self.callee.source_range().end(),
@ -103,9 +148,6 @@ impl Call {
} }
}; };
// set return type
self.return_type_info = Some(callable_symbol.return_type_info());
// check arguments length // check arguments length
let parameters = callable_symbol.parameters(); let parameters = callable_symbol.parameters();
if parameters.len() != self.arguments.len() { if parameters.len() != self.arguments.len() {
@ -126,10 +168,10 @@ impl Call {
// check argument types // check argument types
for i in 0..parameters.len() { for i in 0..parameters.len() {
let parameter = &parameters[i].borrow(); let parameter = &parameters[i];
let argument = &self.arguments[i]; let argument = &self.arguments[i];
let parameter_type_info = parameter.type_info(); let parameter_type_info = types_table.parameter_types().get(parameter).unwrap();
let argument_type_info = argument.type_info(); let argument_type_info = argument.type_info(symbol_table, types_table);
if !parameter_type_info.is_assignable_from(argument_type_info) { if !parameter_type_info.is_assignable_from(argument_type_info) {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
&format!( &format!(
@ -149,20 +191,25 @@ impl Call {
} }
} }
pub fn return_type_info(&self) -> &TypeInfo { fn get_callee_symbol(&self, symbol_table: &SymbolTable) -> CallableSymbol {
self.return_type_info.as_ref().unwrap()
}
fn get_callee_symbol(&self) -> CallableSymbol {
match self.callee() { match self.callee() {
Expression::Identifier(identifier) => { Expression::Identifier(identifier) => {
let expressible_symbol = identifier.expressible_symbol(); let expressible_symbol = symbol_table
.find_expressible_symbol(identifier.scope_id(), identifier.name())
.unwrap();
match expressible_symbol { match expressible_symbol {
ExpressibleSymbol::Function(function_symbol) => { ExpressibleSymbol::Function(function_symbol) => {
CallableSymbol::Function(function_symbol.clone()) CallableSymbol::Function(function_symbol.clone())
} }
ExpressibleSymbol::Class(class_symbol) => { ExpressibleSymbol::Class(class_symbol) => {
CallableSymbol::Class(class_symbol.clone()) match class_symbol.constructor_symbol_owned() {
None => {
panic!("Attempt to get non-existent constructor symbol")
}
Some(constructor_symbol) => {
CallableSymbol::Constructor(constructor_symbol)
}
}
} }
_ => panic!("Calling things other than functions not yet supported."), _ => panic!("Calling things other than functions not yet supported."),
} }
@ -171,11 +218,33 @@ impl Call {
} }
} }
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrCall { pub fn return_type_info<'a>(
&self,
symbol_table: &SymbolTable,
types_table: &'a TypesTable,
) -> &'a TypeInfo {
match self.get_callee_symbol(symbol_table) {
CallableSymbol::Function(function_symbol) => types_table
.function_return_types()
.get(&function_symbol)
.unwrap(),
CallableSymbol::Constructor(constructor_symbol) => types_table
.constructor_return_types()
.get(&constructor_symbol)
.unwrap(),
}
}
pub fn to_ir(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrCall {
let arguments: Vec<IrExpression> = self let arguments: Vec<IrExpression> = self
.arguments .arguments
.iter() .iter()
.map(|argument| argument.to_ir_expression(builder, symbol_table)) .map(|argument| argument.to_ir_expression(builder, symbol_table, types_table))
.inspect(|expression| { .inspect(|expression| {
if expression.is_none() { if expression.is_none() {
panic!("Attempt to pass non-expression") panic!("Attempt to pass non-expression")
@ -183,25 +252,18 @@ impl Call {
}) })
.map(Option::unwrap) .map(Option::unwrap)
.collect(); .collect();
let callable_symbol = self.get_callee_symbol(); let callable_symbol = self.get_callee_symbol(symbol_table);
match callable_symbol { match callable_symbol {
CallableSymbol::Function(function_symbol) => IrCall::new( CallableSymbol::Function(function_symbol) => IrCall::new(
fqn_parts_to_string(function_symbol.borrow().fqn_parts()), fqn_parts_to_string(function_symbol.fqn_parts()),
arguments, arguments,
function_symbol.borrow().is_extern(), function_symbol.is_extern(),
),
CallableSymbol::Constructor(constructor_symbol) => IrCall::new(
fqn_parts_to_string(constructor_symbol.fqn_parts()),
arguments,
false,
), ),
CallableSymbol::Class(class_symbol) => {
let constructor_symbol = class_symbol
.borrow()
.constructor_symbol()
.cloned()
.expect("Default constructors not supported yet.");
IrCall::new(
fqn_parts_to_string(constructor_symbol.borrow().fqn_parts()),
arguments,
false,
)
}
} }
} }

View File

@ -1,3 +1,4 @@
use crate::ast::assign_statement::AssignStatement;
use crate::ast::constructor::Constructor; use crate::ast::constructor::Constructor;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::field::Field; use crate::ast::field::Field;
@ -5,20 +6,22 @@ use crate::ast::fqn_context::FqnContext;
use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::fqn_util::fqn_parts_to_string;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::ast::generic_parameter::GenericParameter; use crate::ast::generic_parameter::GenericParameter;
use crate::ast::helpers::{collect_diagnostics_mut, collect_diagnostics_single, resolve_ctor_name};
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
use crate::diagnostic::{Diagnostic, SecondaryLabel}; use crate::diagnostic::Diagnostic;
use crate::error_codes::{FIELD_MULTIPLE_INIT, FIELD_UNINIT};
use crate::ir::ir_class::{IrClass, IrField}; use crate::ir::ir_class::{IrClass, IrField};
use crate::ir::ir_function::IrFunction; use crate::ir::ir_function::IrFunction;
use crate::maybe_return_diagnostics;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol; use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; use crate::symbol::variable_symbol::VariableSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::SymbolTable;
use std::cell::RefCell; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics, ok_or_err_diagnostics};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::process::id;
use std::rc::Rc; use std::rc::Rc;
pub struct Class { pub struct Class {
@ -28,7 +31,9 @@ pub struct Class {
constructor: Option<Constructor>, constructor: Option<Constructor>,
fields: Vec<Field>, fields: Vec<Field>,
functions: Vec<Function>, functions: Vec<Function>,
class_symbol: Option<Rc<RefCell<ClassSymbol>>>, scope_id: Option<usize>,
self_class_scope_id: Option<usize>,
self_class_body_scope_id: Option<usize>,
} }
impl Class { impl Class {
@ -40,326 +45,437 @@ impl Class {
fields: Vec<Field>, fields: Vec<Field>,
functions: Vec<Function>, functions: Vec<Function>,
) -> Self { ) -> Self {
Class { Self {
declared_name: declared_name.into(), declared_name: declared_name.into(),
declared_name_source_range, declared_name_source_range,
generic_parameters, generic_parameters,
constructor, constructor,
fields, fields,
functions, functions,
class_symbol: None, scope_id: None,
self_class_scope_id: None,
self_class_body_scope_id: None,
} }
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.scope_id = Some(container_scope);
symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, let class_scope_id =
) -> Result<(), Vec<Diagnostic>> { symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
// 1. insert class symbol self.self_class_scope_id = Some(class_scope_id);
let to_insert = ClassSymbol::new(
for generic_parameter in &mut self.generic_parameters {
generic_parameter.init_scopes(symbol_table, class_scope_id);
}
let class_body_scope_id = symbol_table
.push_class_body_scope(&format!("class_body_scope({})", self.declared_name));
self.self_class_body_scope_id = Some(class_body_scope_id);
for field in &mut self.fields {
field.init_scopes(symbol_table, class_body_scope_id);
}
if let Some(constructor) = &mut self.constructor {
constructor.init_scopes(symbol_table, class_body_scope_id);
}
for function in &mut self.functions {
function.init_scopes(symbol_table, class_body_scope_id);
}
symbol_table.pop_scope();
symbol_table.pop_scope();
}
pub fn make_symbols(&self, fqn_context: &mut FqnContext) -> Vec<Symbol> {
let mut all_symbols: Vec<Symbol> = Vec::new();
let mut generic_parameter_symbols = Vec::new();
for generic_parameter in &self.generic_parameters {
let symbol = Rc::new(generic_parameter.make_symbol());
all_symbols.push(Symbol::GenericParameter(symbol.clone()));
generic_parameter_symbols.push(symbol);
}
let mut field_symbols = Vec::new();
for (field_index, field) in self.fields.iter().enumerate() {
let symbol = Rc::new(field.make_symbol(field_index));
all_symbols.push(Symbol::Field(symbol.clone()));
field_symbols.push(symbol);
}
fqn_context.push(self.declared_name.clone()); // namespace this class' members
let constructor_symbol = if let Some(constructor) = &self.constructor {
let (constructor_symbol, mut symbols) = constructor.make_symbols(fqn_context);
all_symbols.append(&mut symbols);
constructor_symbol
} else {
Rc::new(ConstructorSymbol::new(
&self.declared_name_source_range,
resolve_ctor_name(fqn_context),
false,
true,
self.self_class_body_scope_id.unwrap(),
vec![],
))
};
all_symbols.push(Symbol::Constructor(constructor_symbol.clone()));
let mut function_symbols = Vec::new();
for function in &self.functions {
let (function_symbol, mut symbols) = function.make_symbols(fqn_context, true);
all_symbols.append(&mut symbols);
function_symbols.push(function_symbol);
}
fqn_context.pop(); // un-namespace
let class_symbol = Rc::new(ClassSymbol::new(
&self.declared_name, &self.declared_name,
self.declared_name_source_range.clone(), Some(self.declared_name_source_range.clone()),
fqn_context.resolve(&self.declared_name), fqn_context.resolve(&self.declared_name),
false, false,
); self.scope_id.unwrap(),
generic_parameter_symbols,
Some(constructor_symbol),
field_symbols,
function_symbols,
));
all_symbols.push(Symbol::Class(class_symbol.clone()));
// 1a. Push class name on fqn all_symbols
fqn_context.push(self.declared_name.clone()); }
let class_symbol = symbol_table pub fn check_names(&self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
.insert_class_symbol(to_insert) let mut diagnostics: Vec<Diagnostic> = Vec::new();
.map_err(|e| match e {
SymbolInsertError::AlreadyDeclared(already_declared) => {
let symbol = already_declared.symbol().borrow();
vec![
Diagnostic::new(
&format!(
"Symbol {} already declared in current scope.",
already_declared.symbol().borrow().declared_name(),
),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
)
.with_secondary_labels(&[SecondaryLabel::new(
symbol.declared_name_source_range().start(),
symbol.declared_name_source_range().end(),
Some("Symbol declared here.".to_string()),
)])
.with_reporter(file!(), line!()),
]
}
})?;
// save symbol for later for generic_parameter in &self.generic_parameters {
self.class_symbol = Some(class_symbol); diagnostics.append(&mut generic_parameter.check_names(symbol_table));
// 2. push scope
symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
// 3a. gather generic parameters
let mut generic_parameter_symbols: Vec<Rc<RefCell<GenericParameterSymbol>>> = vec![];
let mut generic_parameter_diagnostics: Vec<Diagnostic> = vec![];
for generic_parameter in &mut self.generic_parameters {
match generic_parameter.gather_declared_names(symbol_table) {
Ok(generic_parameter_symbol) => {
generic_parameter_symbols.push(generic_parameter_symbol);
}
Err(mut d) => {
generic_parameter_diagnostics.append(&mut d);
}
}
} }
maybe_return_diagnostics!(generic_parameter_diagnostics); for field in &self.fields {
// save generics to class symbol diagnostics.append(&mut field.check_names(symbol_table));
self.class_symbol }
.as_mut()
if let Some(constructor) = &self.constructor {
diagnostics.append(&mut constructor.check_names(symbol_table));
}
for function in &self.functions {
diagnostics.append(&mut function.check_names(symbol_table));
}
diagnostics
}
fn get_class_symbol_owned(&self, symbol_table: &SymbolTable) -> Rc<ClassSymbol> {
symbol_table
.get_class_symbol(self.scope_id.unwrap(), &self.declared_name)
.cloned()
.unwrap() .unwrap()
.borrow_mut() }
.set_generic_parameters(generic_parameter_symbols);
// 3b. gather fields pub fn check_field_initializer_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
let mut field_symbols: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> = HashMap::new(); let class_symbol = self.get_class_symbol_owned(symbol_table);
let mut fields_diagnostics: Vec<Diagnostic> = vec![]; self.fields
.iter()
.flat_map(|field| field.check_field_initializer_names(symbol_table, &class_symbol))
.collect()
}
for (index, field) in self.fields.iter_mut().enumerate() { pub fn analyze_local_names(&self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
match field.gather_declared_names(symbol_table, index) { let class_symbol = self.get_class_symbol_owned(symbol_table);
Ok(field_symbol) => { let mut diagnostics: Vec<Diagnostic> = Vec::new();
field_symbols.insert(field.declared_name_owned(), field_symbol); if let Some(constructor) = &self.constructor {
} diagnostics.append(&mut constructor.analyze_local_names(symbol_table, &class_symbol));
Err(mut d) => { }
fields_diagnostics.append(&mut d); for function in &self.functions {
} diagnostics
}; .append(&mut function.analyze_method_local_names(symbol_table, &class_symbol));
}
diagnostics
}
pub fn gather_types(
&self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
// class type
let class_symbol = self.get_class_symbol_owned(symbol_table);
types_table
.class_types_mut()
.insert(class_symbol.clone(), TypeInfo::Class(class_symbol.clone()));
// constructor return type
// this works for both declared and default constructors
let constructor_symbol = symbol_table
.get_constructor_symbol_owned(self.self_class_body_scope_id.unwrap())
.unwrap();
types_table
.constructor_return_types_mut()
.insert(constructor_symbol, TypeInfo::Class(class_symbol));
let mut diagnostics = Vec::new();
// generic params
for generic_parameter in &self.generic_parameters {
handle_diagnostics!(
generic_parameter.gather_types(symbol_table, types_table),
diagnostics
);
} }
if !fields_diagnostics.is_empty() { // field types
return Err(fields_diagnostics); for field in &self.fields {
} else { handle_diagnostics!(field.gather_types(symbol_table, types_table), diagnostics);
self.class_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_fields(field_symbols);
} }
// 4. gather constructor // now the constructor (parameters, etc.)
if let Some(constructor) = &self.constructor {
constructor.gather_types_into(symbol_table, types_table);
}
// function return types
for function in &self.functions {
function.gather_types(symbol_table, types_table);
}
diagnostics_result!(diagnostics)
}
fn type_check_generics(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
collect_diagnostics_mut(&mut self.generic_parameters, |gp| {
gp.type_check(symbol_table, types_table)
})
}
fn type_check_fields(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
collect_diagnostics_mut(&mut self.fields, |f| {
f.type_check(symbol_table, types_table)
})
}
fn type_check_constructor(
&mut self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
if let Some(constructor) = &mut self.constructor { if let Some(constructor) = &mut self.constructor {
let constructor_symbol = constructor.type_check(symbol_table, types_table)?;
constructor.gather_declared_names(symbol_table, fqn_context)?;
self.class_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_constructor_symbol(Some(constructor_symbol));
} }
// 5. gather functions
// note: for each function, insert at index 0 a self parameter
let functions_diagnostics: Vec<Diagnostic> = self
.functions
.iter_mut()
.map(|function| {
function.gather_declared_names(
symbol_table,
fqn_context,
self.class_symbol.as_ref(),
)
})
.filter_map(Result::err)
.flatten()
.collect();
if !functions_diagnostics.is_empty() {
return Err(functions_diagnostics);
}
// 6. pop scope
symbol_table.pop_scope();
// 7. pop fqn part
fqn_context.pop();
Ok(()) Ok(())
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { fn type_check_functions(
let generics_diagnostics: Vec<Diagnostic> = self &mut self,
.generic_parameters symbol_table: &SymbolTable,
.iter_mut() types_table: &mut TypesTable,
.map(|generic_parameter| { ) -> Result<(), Vec<Diagnostic>> {
generic_parameter.check_name_usages(symbol_table, self.class_symbol.as_ref()) collect_diagnostics_mut(&mut self.functions, |f| {
}) f.type_check(symbol_table, types_table)
.filter_map(Result::err) })
.flatten() }
.collect();
maybe_return_diagnostics!(generics_diagnostics);
self.constructor /// Returns all field names with declared initializers.
.as_mut() fn field_names_with_initializers(&self) -> HashSet<&str> {
.map(|constructor| { let mut set: HashSet<&str> = HashSet::new();
constructor.check_name_usages(symbol_table, self.class_symbol.as_ref()) for field in &self.fields {
}) if field.initializer().is_some() {
.transpose()?; set.insert(field.declared_name());
}
let fields_diagnostics: Vec<Diagnostic> = self
.fields
.iter_mut()
.map(|field| field.check_name_usages(symbol_table, self.class_symbol.as_ref()))
.filter_map(Result::err)
.flatten()
.collect();
if !fields_diagnostics.is_empty() {
return Err(fields_diagnostics);
} }
set
}
if let Some(constructor) = &mut self.constructor { /// If the destination of the given [AssignStatement] matches a field, returns an
constructor.check_name_usages(symbol_table, self.class_symbol.as_ref())?; /// `Ok(Some(field_name))` only if the field is not already in the `fields_already_init` set,
} /// AND, if the field is immutable, the field is not initialized more than once in the
/// constructor. Otherwise, returns an `Err(Diagnostic)`.
let functions_diagnostics: Vec<Diagnostic> = self fn check_ctor_assign_statement<'a>(
.functions &self,
.iter_mut() assign_statement: &'a AssignStatement,
.map(|function| function.check_name_usages(symbol_table, self.class_symbol.as_ref())) fields_already_init: &HashSet<&&str>,
.filter_map(Result::err) class_symbol: &ClassSymbol,
.flatten() ) -> Result<Option<&'a str>, Diagnostic> {
.collect(); match assign_statement.destination() {
Expression::Identifier(identifier) => {
if functions_diagnostics.is_empty() { // find matching field symbol, if there is one
Ok(()) if let Some(field_symbol) = class_symbol.fields().get(identifier.name()) {
} else { // check that we don't init more than once IF field is immutable
Err(functions_diagnostics) if fields_already_init.contains(&identifier.name()) && !field_symbol.is_mut() {
let diagnostic = Diagnostic::new(
&format!("Immutable field {} cannot be initialized more than once in constructor.", identifier.name()),
identifier.source_range().start(),
identifier.source_range().end(),
).with_reporter(file!(), line!())
.with_error_code(FIELD_MULTIPLE_INIT);
Err(diagnostic)
} else {
Ok(Some(identifier.name()))
}
} else {
Ok(None)
}
}
_ => panic!("Found a non-L Value destination"),
} }
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { /// Returns an `Ok(HashSet<&str>)` containing the names of all fields initialized in the
/// constructor, provided that the following are true:
///
/// - The field is not initialized more than once in the constructor
/// - The field is not also initialized with a declared initializer.
///
/// If the above are not met, returns `Err(diagnostics)`.
fn get_fields_init_in_ctor<'a>(
&self,
fields_with_declared_initializers: &HashSet<&str>,
symbol_table: &SymbolTable,
) -> Result<HashSet<&str>, Vec<Diagnostic>> {
let mut constructor_inits: HashSet<&str> = HashSet::new();
let mut diagnostics: Vec<Diagnostic> = vec![]; let mut diagnostics: Vec<Diagnostic> = vec![];
self.fields
.iter_mut()
.map(|field| field.type_check(symbol_table))
.filter_map(Result::err)
.flatten()
.for_each(|diagnostic| diagnostics.push(diagnostic));
if !diagnostics.is_empty() {
return Err(diagnostics);
}
if let Some(constructor) = &mut self.constructor {
match constructor.type_check(symbol_table) {
Ok(_) => {}
Err(mut constructor_diagnostics) => {
diagnostics.append(&mut constructor_diagnostics);
}
}
}
if !diagnostics.is_empty() {
return Err(diagnostics);
}
self.functions
.iter_mut()
.map(|function| function.type_check(symbol_table))
.filter_map(Result::err)
.flatten()
.for_each(|diagnostic| diagnostics.push(diagnostic));
if !diagnostics.is_empty() {
return Err(diagnostics);
}
// We need to determine if fields are initialized or not (the latter is an error).
// First phase: check all fields, then check constructor, leaving pending those things that
// are fields <- initialized by constructor. Then circle back to fields and check all are
// initialized
let mut initialized_field_names: HashSet<&str> = HashSet::new();
for field in &self.fields {
if field.initializer().is_some() {
initialized_field_names.insert(field.declared_name());
}
}
// check constructor
if let Some(constructor) = &self.constructor { if let Some(constructor) = &self.constructor {
let class_symbol = symbol_table
.get_class_symbol(self.scope_id.unwrap(), &self.declared_name)
.unwrap();
for statement in constructor.statements() { for statement in constructor.statements() {
match statement { match statement {
Statement::Assign(assign_statement) => match assign_statement.destination() { Statement::Assign(assign_statement) => {
Expression::Identifier(identifier) => { let fields_init_so_far = constructor_inits
if self .union(fields_with_declared_initializers)
.class_symbol .collect::<HashSet<_>>();
.as_ref() match self.check_ctor_assign_statement(
.unwrap() assign_statement,
.borrow() &fields_init_so_far,
.fields() &class_symbol,
.contains_key(identifier.name()) ) {
{ Ok(maybe_init_field) => match maybe_init_field {
initialized_field_names.insert(identifier.name()); None => {}
Some(init_field) => {
constructor_inits.insert(init_field);
}
},
Err(diagnostic) => {
diagnostics.push(diagnostic);
} }
} }
_ => panic!("Found a non L Value assign lhs"), }
},
_ => {} _ => {}
} }
} }
} }
ok_or_err_diagnostics!(constructor_inits, diagnostics)
// check that all fields are present in the hash set
for field in &self.fields {
if !initialized_field_names.contains(field.declared_name()) {
diagnostics.push(
Diagnostic::new(
&format!("Field {} is not initialized.", field.declared_name()),
field.declared_name_source_range().start(),
field.declared_name_source_range().end(),
)
.with_primary_label_message(
"Must be initialized in declaration or constructor.",
)
.with_reporter(file!(), line!()),
)
}
}
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn to_ir(&self, symbol_table: &SymbolTable) -> (IrClass, Vec<IrFunction>) { /// Checks that all declared fields in this `Class` are present in the `all_inits` set. If so,
/// returns `Ok`, else `Err`.
fn check_all_fields_in_init_set(
&self,
all_inits: &HashSet<&&str>,
) -> Result<(), Vec<Diagnostic>> {
collect_diagnostics_single(&self.fields, |field| {
if all_inits.contains(&field.declared_name()) {
Ok(())
} else {
Err(Diagnostic::new(
&format!("Field {} is not initialized.", field.declared_name()),
field.declared_name_source_range().start(),
field.declared_name_source_range().end(),
)
.with_primary_label_message("Must be initialized in declaration or constructor.")
.with_reporter(file!(), line!())
.with_error_code(FIELD_UNINIT))
}
})
}
/// Checks that all fields are initialized, either at their declaration or in the constructor.
/// Immutable fields may be only initialized once, either at their declaration or once in the
/// constructor. Mutable fields may be initialized either at their declaration, or at least once
/// in the constructor.
fn check_field_initialization(
&self,
symbol_table: &SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
// We need to determine if fields are initialized or not (the latter is an error).
// First phase: check all fields, then check constructor, leaving pending those things that
// are fields <- initialized by constructor. Then circle back to fields and check all are
// initialized
let field_names_with_initializers = self.field_names_with_initializers();
let field_names_init_in_constructor =
self.get_fields_init_in_ctor(&field_names_with_initializers, symbol_table)?;
let combined = field_names_with_initializers
.union(&field_names_init_in_constructor)
.collect::<HashSet<_>>();
// check that all fields are present in the hash set
self.check_all_fields_in_init_set(&combined)?;
Ok(())
}
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
self.type_check_generics(symbol_table, types_table)?;
self.type_check_fields(symbol_table, types_table)?;
self.type_check_constructor(symbol_table, types_table)?;
self.type_check_functions(symbol_table, types_table)?;
self.check_field_initialization(symbol_table)?;
Ok(())
}
pub fn to_ir(
&self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> (IrClass, Vec<IrFunction>) {
let self_class_symbol = symbol_table
.get_class_symbol(self.scope_id.unwrap(), &self.declared_name)
.unwrap();
let mut ir_functions: Vec<IrFunction> = vec![]; let mut ir_functions: Vec<IrFunction> = vec![];
if let Some(constructor) = &self.constructor { if let Some(constructor) = &self.constructor {
ir_functions.push(constructor.to_ir( ir_functions.push(constructor.to_ir(
self.class_symbol.as_ref().unwrap(), self_class_symbol,
&self.fields, &self.fields,
symbol_table, symbol_table,
types_table,
)) ))
} }
for function in &self.functions { for function in &self.functions {
ir_functions.push(function.to_ir( ir_functions.push(function.to_ir(symbol_table, types_table, Some(self_class_symbol)));
symbol_table,
Some(self.class_symbol.as_ref().unwrap().clone()),
));
} }
let class_symbol = self.class_symbol.as_ref().unwrap().borrow();
let ir_class = IrClass::new( let ir_class = IrClass::new(
class_symbol.declared_name_owned(), self_class_symbol.declared_name_owned(),
fqn_parts_to_string(class_symbol.fqn_parts()).into(), fqn_parts_to_string(self_class_symbol.fqn_parts()).into(),
self.fields self.fields
.iter() .iter()
.map(|field| { .map(|field| {
let field_symbol = symbol_table
.get_field_symbol_owned(field.scope_id(), field.declared_name())
.unwrap();
let field_type = types_table.field_types().get(&field_symbol).unwrap();
IrField::new( IrField::new(
field.declared_name().into(), field.declared_name().into(),
field.field_symbol().borrow().field_index(), field_symbol.field_index(),
field.field_symbol().borrow().type_info().clone(), field_type.clone(),
) )
}) })
.collect(), .collect(),

View File

@ -2,10 +2,13 @@ use crate::ast::class::Class;
use crate::ast::extern_function::ExternFunction; use crate::ast::extern_function::ExternFunction;
use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_context::FqnContext;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::ast::helpers::{collect_diagnostics_into_mut, try_insert_symbols_into};
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_class::IrClass; use crate::ir::ir_class::IrClass;
use crate::ir::ir_function::IrFunction; use crate::ir::ir_function::IrFunction;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics, ok_or_err_diagnostics};
pub struct CompilationUnit { pub struct CompilationUnit {
functions: Vec<Function>, functions: Vec<Function>,
@ -38,118 +41,125 @@ impl CompilationUnit {
&self.classes &self.classes
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable) {
&mut self, let compilation_unit_scope = symbol_table.push_module_scope("compilation_unit_scope");
for class in &mut self.classes {
class.init_scopes(symbol_table, compilation_unit_scope);
}
for function in &mut self.functions {
function.init_scopes(symbol_table, compilation_unit_scope);
}
for extern_function in &mut self.extern_functions {
extern_function.init_scopes(symbol_table, compilation_unit_scope);
}
symbol_table.pop_scope();
}
pub fn gather_symbols_into(
&self,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
symbol_table.push_module_scope("compilation_unit_scope"); let mut diagnostics = vec![];
let mut fqn_context = FqnContext::new();
let mut fqn_context = FqnContext::new(); // in the future, we'll push the pkg/ns on here for class in &self.classes {
let mut diagnostics: Vec<Diagnostic> = vec![]; handle_diagnostics!(
try_insert_symbols_into(class.make_symbols(&mut fqn_context), symbol_table),
self.functions diagnostics
.iter_mut() );
.map(|f| f.gather_declared_names(symbol_table, &fqn_context, None))
.filter_map(Result::err)
.flatten()
.for_each(|diagnostic| diagnostics.push(diagnostic));
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));
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));
symbol_table.pop_scope();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
} }
for function in &self.functions {
let (_, symbols) = function.make_symbols(&mut fqn_context, false);
handle_diagnostics!(try_insert_symbols_into(symbols, symbol_table), diagnostics);
}
for extern_function in &self.extern_functions {
let (_, symbols) = extern_function.make_symbols(&mut fqn_context);
handle_diagnostics!(try_insert_symbols_into(symbols, symbol_table), diagnostics);
}
diagnostics_result!(diagnostics)
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn check_names(&self, symbol_table: &mut SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![]; let mut diagnostics = vec![];
for class in &self.classes {
self.functions diagnostics.append(&mut class.check_names(symbol_table));
.iter_mut() diagnostics.append(&mut class.check_field_initializer_names(symbol_table));
.map(|f| f.check_name_usages(symbol_table, None)) diagnostics.append(&mut class.analyze_local_names(symbol_table));
.filter_map(Result::err)
.flatten()
.for_each(|diagnostic| diagnostics.push(diagnostic));
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));
self.classes
.iter_mut()
.map(|c| c.check_name_usages(symbol_table))
.filter_map(Result::err)
.flatten()
.for_each(|diagnostic| diagnostics.push(diagnostic));
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
} }
for function in &self.functions {
diagnostics.append(&mut function.check_names(symbol_table));
diagnostics.append(&mut function.analyze_static_fn_local_names(symbol_table));
}
for extern_function in &self.extern_functions {
diagnostics.append(&mut extern_function.check_names(symbol_table));
}
diagnostics_result!(diagnostics)
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn gather_types_into(
let mut diagnostics: Vec<Diagnostic> = vec![]; &self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = Vec::new();
self.functions for class in &self.classes {
.iter_mut() handle_diagnostics!(class.gather_types(symbol_table, types_table), diagnostics);
.map(|f| f.type_check(symbol_table))
.filter_map(Result::err)
.flatten()
.for_each(|diagnostic| diagnostics.push(diagnostic));
self.extern_functions
.iter_mut()
.map(|f| f.type_check(symbol_table))
.filter_map(Result::err)
.flatten()
.for_each(|diagnostic| diagnostics.push(diagnostic));
self.classes
.iter_mut()
.map(|c| c.type_check(symbol_table))
.filter_map(Result::err)
.flatten()
.for_each(|diagnostic| diagnostics.push(diagnostic));
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
} }
for function in &self.functions {
function.gather_types(symbol_table, types_table);
}
for extern_function in &self.extern_functions {
extern_function.gather_types(symbol_table, types_table);
}
diagnostics_result!(diagnostics)
} }
pub fn to_ir(&self, symbol_table: &SymbolTable) -> (Vec<IrClass>, Vec<IrFunction>) { pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
collect_diagnostics_into_mut(
&mut self.functions,
|f| f.type_check(symbol_table, types_table),
&mut diagnostics,
);
collect_diagnostics_into_mut(
&mut self.extern_functions,
|ef| ef.type_check(symbol_table, types_table),
&mut diagnostics,
);
collect_diagnostics_into_mut(
&mut self.classes,
|c| c.type_check(symbol_table, types_table),
&mut diagnostics,
);
diagnostics_result!(diagnostics)
}
pub fn to_ir(
&self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> (Vec<IrClass>, Vec<IrFunction>) {
let mut functions: Vec<IrFunction> = vec![]; let mut functions: Vec<IrFunction> = vec![];
let mut classes: Vec<IrClass> = vec![]; let mut classes: Vec<IrClass> = vec![];
self.functions self.functions
.iter() .iter()
.map(|f| f.to_ir(symbol_table, None)) .map(|f| f.to_ir(symbol_table, types_table, None))
.for_each(|f| functions.push(f)); .for_each(|f| functions.push(f));
for class in &self.classes { for class in &self.classes {
let (class, mut class_functions) = class.to_ir(symbol_table); let (class, mut class_functions) = class.to_ir(symbol_table, types_table);
functions.append(&mut class_functions); functions.append(&mut class_functions);
classes.push(class); classes.push(class);
} }

View File

@ -1,6 +1,7 @@
use crate::ast::field::Field; use crate::ast::field::Field;
use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_context::FqnContext;
use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::fqn_util::fqn_parts_to_string;
use crate::ast::helpers::{collect_parameter_symbols_into, resolve_ctor_name};
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::ast::parameter::Parameter; use crate::ast::parameter::Parameter;
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
@ -21,9 +22,9 @@ use crate::source_range::SourceRange;
use crate::symbol::Symbol; use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::constructor_symbol::ConstructorSymbol; use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol_table::SymbolTable;
use crate::symbol_table::{SymbolInsertError, SymbolTable};
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use std::cell::RefCell; use std::cell::RefCell;
use std::ops::Neg; use std::ops::Neg;
use std::rc::Rc; use std::rc::Rc;
@ -33,6 +34,7 @@ pub struct Constructor {
ctor_keyword_source_range: SourceRange, ctor_keyword_source_range: SourceRange,
parameters: Vec<Parameter>, parameters: Vec<Parameter>,
statements: Vec<Statement>, statements: Vec<Statement>,
scope_id: Option<usize>,
} }
impl Constructor { impl Constructor {
@ -47,6 +49,7 @@ impl Constructor {
ctor_keyword_source_range, ctor_keyword_source_range,
parameters, parameters,
statements, statements,
scope_id: None,
} }
} }
@ -54,87 +57,77 @@ impl Constructor {
&self.statements &self.statements
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.scope_id = Some(container_scope);
symbol_table: &mut SymbolTable,
fqn_context: &FqnContext,
) -> Result<Rc<RefCell<ConstructorSymbol>>, Vec<Diagnostic>> {
// insert constructor symbol
let to_insert = ConstructorSymbol::new(
self.ctor_keyword_source_range.clone(),
fqn_context.resolve("ctor"), // ctor is a keyword at the language level, should not be callable via normal means
false,
);
let constructor_symbol =
symbol_table
.insert_constructor_symbol(to_insert)
.map_err(|symbol_insert_error| match symbol_insert_error {
SymbolInsertError::AlreadyDeclared(_) => {
vec![
Diagnostic::new(
"Cannot declare more than one constructor.",
self.ctor_keyword_source_range.start(),
self.ctor_keyword_source_range.end(),
)
.with_reporter(file!(), line!()),
]
}
})?;
symbol_table.push_function_scope("ctor_scope"); let function_scope = symbol_table.push_function_scope("constructor_scope");
let mut parameter_symbols: Vec<Rc<RefCell<ParameterSymbol>>> = vec![];
let mut parameters_diagnostics = vec![];
for parameter in &mut self.parameters { for parameter in &mut self.parameters {
match parameter.gather_declared_names(symbol_table) { parameter.init_scopes(symbol_table, function_scope);
Ok(parameter_symbol) => {
parameter_symbols.push(parameter_symbol);
}
Err(mut ds) => {
parameters_diagnostics.append(&mut ds);
}
}
} }
if !parameters_diagnostics.is_empty() { let body_scope = symbol_table.push_block_scope("body_scope");
symbol_table.pop_scope(); for statement in &mut self.statements {
return Err(parameters_diagnostics); statement.init_scopes(symbol_table, body_scope);
} else {
constructor_symbol
.borrow_mut()
.set_parameters(parameter_symbols);
} }
symbol_table.push_block_scope("ctor_main_block"); symbol_table.pop_scope();
symbol_table.pop_scope();
}
let statements_diagnostics = self pub fn make_symbols(&self, fqn_context: &FqnContext) -> (Rc<ConstructorSymbol>, Vec<Symbol>) {
.statements let mut all_symbols: Vec<Symbol> = Vec::new();
.iter_mut()
.map(|stmt| stmt.gather_declared_names(symbol_table))
.filter_map(Result::err)
.flatten()
.collect::<Vec<_>>();
symbol_table.pop_scope(); // block let mut parameter_symbols = Vec::new();
symbol_table.pop_scope(); // function collect_parameter_symbols_into(&self.parameters, &mut all_symbols, &mut parameter_symbols);
if statements_diagnostics.is_empty() { let constructor_symbol = Rc::new(ConstructorSymbol::new(
Ok(constructor_symbol) &self.ctor_keyword_source_range,
} else { resolve_ctor_name(fqn_context),
Err(statements_diagnostics) false,
false,
self.scope_id.unwrap(),
parameter_symbols,
));
all_symbols.push(Symbol::Constructor(constructor_symbol.clone()));
(constructor_symbol, all_symbols)
}
pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = Vec::new();
for parameter in &self.parameters {
diagnostics.append(&mut parameter.check_names(symbol_table));
}
diagnostics
}
pub fn analyze_local_names(
&self,
symbol_table: &mut SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
self.statements
.iter()
.flat_map(|s| s.analyze_constructor_local_names(symbol_table, class_symbol))
.collect()
}
pub fn gather_types_into(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) {
for parameter in &self.parameters {
parameter.gather_types_into(symbol_table, types_table);
} }
} }
pub fn check_name_usages( pub fn type_check(
&mut self, &mut self,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
class_context: Option<&Rc<RefCell<ClassSymbol>>>, types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
let parameters_diagnostics: Vec<Diagnostic> = self let parameters_diagnostics: Vec<Diagnostic> = self
.parameters .parameters
.iter_mut() .iter_mut()
.map(|param| param.check_name_usages(symbol_table, class_context)) .map(|param| param.type_check(symbol_table, types_table))
.filter_map(Result::err) .filter_map(Result::err)
.flatten() .flatten()
.collect(); .collect();
@ -146,35 +139,7 @@ impl Constructor {
let statements_diagnostics: Vec<Diagnostic> = self let statements_diagnostics: Vec<Diagnostic> = self
.statements .statements
.iter_mut() .iter_mut()
.map(|statement| statement.check_name_usages(symbol_table)) .map(|statement| statement.type_check(symbol_table, types_table, None))
.filter_map(Result::err)
.flatten()
.collect();
if statements_diagnostics.is_empty() {
Ok(())
} else {
Err(statements_diagnostics)
}
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let parameters_diagnostics: Vec<Diagnostic> = self
.parameters
.iter_mut()
.map(|param| param.type_check(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if !parameters_diagnostics.is_empty() {
return Err(parameters_diagnostics);
}
let statements_diagnostics: Vec<Diagnostic> = self
.statements
.iter_mut()
.map(|statement| statement.type_check(symbol_table, None))
.filter_map(Result::err) .filter_map(Result::err)
.flatten() .flatten()
.collect(); .collect();
@ -188,9 +153,10 @@ impl Constructor {
pub fn to_ir( pub fn to_ir(
&self, &self,
class_symbol: &Rc<RefCell<ClassSymbol>>, class_symbol: &Rc<ClassSymbol>,
fields: &[Field], fields: &[Field],
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrFunction { ) -> IrFunction {
let mut ir_builder = IrBuilder::new(); let mut ir_builder = IrBuilder::new();
@ -200,17 +166,22 @@ impl Constructor {
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, parameter)| { .map(|(i, parameter)| {
let mut parameter_symbol = parameter.parameter_symbol().borrow_mut(); let parameter_symbol = symbol_table
.get_parameter_symbol_owned(parameter.scope_id(), parameter.declared_name())
.unwrap();
let parameter_type = types_table
.parameter_types()
.get(&parameter_symbol)
.unwrap();
let offset = (parameters_count as isize).neg() + i as isize; let offset = (parameters_count as isize).neg() + i as isize;
let ir_parameter = Rc::new(IrParameter::new( let ir_parameter = Rc::new(IrParameter::new(
parameter_symbol.declared_name(), parameter_symbol.declared_name(),
parameter_symbol.type_info().clone(), parameter_type.clone(),
offset, offset,
)); ));
// make sure to save ir_parameter to symbol so others can access it // make sure to save ir_parameter to symbol so others can access it
let to_save = ir_parameter.clone(); ir_builder.push_parameter(&parameter_symbol, ir_parameter.clone());
parameter_symbol.set_ir_parameter(to_save);
ir_parameter ir_parameter
}) })
@ -222,7 +193,7 @@ impl Constructor {
let alloc_assign_destination = IrVariable::new_vr( let alloc_assign_destination = IrVariable::new_vr(
ir_builder.new_t_var().into(), ir_builder.new_t_var().into(),
ir_builder.current_block().id(), ir_builder.current_block().id(),
&TypeInfo::ClassInstance(class_symbol.clone()), &TypeInfo::Class(class_symbol.clone()),
); );
let self_variable = Rc::new(RefCell::new(alloc_assign_destination)); let self_variable = Rc::new(RefCell::new(alloc_assign_destination));
@ -232,7 +203,7 @@ impl Constructor {
let alloc_assign = IrAssign::new( let alloc_assign = IrAssign::new(
self_variable.clone(), self_variable.clone(),
IrOperation::Allocate(IrAllocate::new(class_symbol.borrow().declared_name_owned())), IrOperation::Allocate(IrAllocate::new(class_symbol.declared_name_owned())),
); );
ir_builder ir_builder
.current_block_mut() .current_block_mut()
@ -241,16 +212,20 @@ impl Constructor {
// next, initialize fields that have an initializer in their declaration // next, initialize fields that have an initializer in their declaration
for field in fields { for field in fields {
if let Some(initializer) = field.initializer() { if let Some(initializer) = field.initializer() {
let field_symbol = symbol_table
.get_field_symbol_owned(field.scope_id(), field.declared_name())
.unwrap();
let field_type = types_table.field_types().get(&field_symbol).unwrap();
// get a mut ref to the field // get a mut ref to the field
let ir_get_field_ref_mut = IrGetFieldRefMut::new( let ir_get_field_ref_mut = IrGetFieldRefMut::new(
IrParameterOrVariable::Variable(self_variable.clone()), IrParameterOrVariable::Variable(self_variable.clone()),
field.field_symbol().borrow().field_index(), field_symbol.field_index(),
); );
let field_mut_ref_variable_name: Rc<str> = ir_builder.new_t_var().into(); let field_mut_ref_variable_name: Rc<str> = ir_builder.new_t_var().into();
let field_mut_ref_variable = Rc::new(RefCell::new(IrVariable::new_vr( let field_mut_ref_variable = Rc::new(RefCell::new(IrVariable::new_vr(
field_mut_ref_variable_name.clone(), field_mut_ref_variable_name.clone(),
ir_builder.current_block().id(), ir_builder.current_block().id(),
field.field_symbol().borrow().type_info(), field_type,
))); )));
let field_mut_ref_assign = IrAssign::new( let field_mut_ref_assign = IrAssign::new(
field_mut_ref_variable.clone(), field_mut_ref_variable.clone(),
@ -273,7 +248,7 @@ impl Constructor {
.clone(); .clone();
let ir_expression = initializer let ir_expression = initializer
.to_ir_expression(&mut ir_builder, symbol_table) .to_ir_expression(&mut ir_builder, symbol_table, types_table)
.unwrap(); .unwrap();
let ir_set_field = IrSetField::new( let ir_set_field = IrSetField::new(
&field_mut_ref_variable, // dumb that we clone it and then ref it &field_mut_ref_variable, // dumb that we clone it and then ref it
@ -287,7 +262,7 @@ impl Constructor {
// do "declared" statements of constructor // do "declared" statements of constructor
for statement in &self.statements { for statement in &self.statements {
statement.to_ir(&mut ir_builder, symbol_table, false); statement.to_ir(&mut ir_builder, symbol_table, types_table, false);
} }
// return complete self object // return complete self object
@ -301,17 +276,13 @@ impl Constructor {
ir_builder.finish_block(); ir_builder.finish_block();
let entry_block = ir_builder.get_block(entry_block_id); let entry_block = ir_builder.get_block(entry_block_id);
let constructor_symbol = symbol_table
.get_constructor_symbol(self.scope_id.unwrap())
.unwrap();
IrFunction::new( IrFunction::new(
fqn_parts_to_string( fqn_parts_to_string(constructor_symbol.fqn_parts()),
class_symbol ir_parameters,
.borrow() &TypeInfo::Class(class_symbol.clone()),
.constructor_symbol()
.unwrap()
.borrow()
.fqn_parts(),
), // fake function symbol
&ir_parameters, // make params
&TypeInfo::ClassInstance(class_symbol.clone()),
entry_block.clone(), entry_block.clone(),
) )
} }

View File

@ -0,0 +1,119 @@
use crate::diagnostic::{Diagnostic, SecondaryLabel};
use crate::error_codes::{
CLASS_NO_CONSTRUCTOR, FIELD_NO_TYPE_OR_INIT, OUTER_CLASS_FIELD_USED_IN_INIT,
OUTER_CLASS_METHOD_USED_IN_INIT, SELF_CONSTRUCTOR_USED_IN_INIT, SELF_FIELD_USED_IN_INIT,
SELF_METHOD_USED_IN_INIT, SYMBOL_ALREADY_DECLARED, SYMBOL_NOT_FOUND,
};
use crate::source_range::SourceRange;
use crate::symbol::Symbol;
pub fn symbol_not_found(name: &str, source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
&format!("Symbol {} not found in scope.", name),
source_range.start(),
source_range.end(),
)
.with_error_code(SYMBOL_NOT_FOUND)
}
pub fn field_has_no_type_or_init(name: &str, source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
&format!("Field {} has no declared type nor initializer.", name),
source_range.start(),
source_range.end(),
)
.with_error_code(FIELD_NO_TYPE_OR_INIT)
.with_primary_label_message("Declare a type and/or an initializer.")
}
pub fn cannot_reference_field_in_init(name: &str, source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
&format!("Cannot reference field {} during initialization.", name),
source_range.start(),
source_range.end(),
)
.with_error_code(SELF_FIELD_USED_IN_INIT)
}
pub fn symbol_already_declared(already_inserted: &Symbol, would_insert: &Symbol) -> Diagnostic {
let secondary_label = if let Some(source_range) = already_inserted.declared_name_source_range()
{
Some(SecondaryLabel::new(
source_range.start(),
source_range.end(),
Some("Symbol already declared here.".to_string()),
))
} else {
None
};
let diagnostic = Diagnostic::new(
&format!(
"Symbol {} already declared in current scope.",
would_insert.declared_name()
),
would_insert.declared_name_source_range().unwrap().start(), // unwrap should be okay, since this is user code
would_insert.declared_name_source_range().unwrap().end(),
)
.with_error_code(SYMBOL_ALREADY_DECLARED);
if let Some(secondary_label) = secondary_label {
diagnostic.with_secondary_labels(&[secondary_label])
} else {
diagnostic
}
}
pub fn self_constructor_used_in_init(source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
"Cannot call Self constructor during initialization.",
source_range.start(),
source_range.end(),
)
.with_error_code(SELF_CONSTRUCTOR_USED_IN_INIT)
}
pub fn self_field_used_in_init(source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
"Cannot reference Self field during initialization.",
source_range.start(),
source_range.end(),
)
.with_error_code(SELF_FIELD_USED_IN_INIT)
}
pub fn self_method_used_in_init(source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
"Cannot call Self method during initialization.",
source_range.start(),
source_range.end(),
)
.with_error_code(SELF_METHOD_USED_IN_INIT)
}
pub fn outer_class_field_usage(source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
"Cannot reference an outer class member.",
source_range.start(),
source_range.end(),
)
.with_error_code(OUTER_CLASS_FIELD_USED_IN_INIT)
}
pub fn outer_class_method_usage(source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
"Cannot call an outer class method.",
source_range.start(),
source_range.end(),
)
.with_error_code(OUTER_CLASS_METHOD_USED_IN_INIT)
}
pub fn class_has_no_constructor(name: &str, source_range: &SourceRange) -> Diagnostic {
Diagnostic::new(
&format!("Class {} has no constructor.", name),
source_range.start(),
source_range.end(),
)
.with_error_code(CLASS_NO_CONSTRUCTOR)
}

View File

@ -13,8 +13,10 @@ use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable; use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -29,61 +31,192 @@ pub enum Expression {
} }
impl Expression { impl Expression {
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
match self {
Expression::Binary(binary_expression) => {
binary_expression.init_scopes(symbol_table, container_scope);
}
Expression::Negative(negative_expression) => {
negative_expression.init_scopes(symbol_table, container_scope);
}
Expression::Call(call) => {
call.init_scopes(symbol_table, container_scope);
}
Expression::Identifier(identifier) => {
identifier.init_scope_id(container_scope);
}
_ => {}
}
}
pub fn check_field_initializer_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
match self {
Expression::Binary(binary_expression) => {
binary_expression.check_field_initializer_names(symbol_table, class_symbol)
}
Expression::Negative(negative_expression) => {
negative_expression.check_field_initializer_names(symbol_table, class_symbol)
}
Expression::Call(call) => {
call.check_field_initializer_names(symbol_table, class_symbol)
}
Expression::Identifier(identifier) => {
if let Some(diagnostic) =
identifier.check_name_as_field_initializer(symbol_table, class_symbol)
{
vec![diagnostic]
} else {
vec![]
}
}
_ => vec![],
}
}
pub fn check_constructor_destination_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
match self {
Expression::Binary(_) => {
panic!()
}
Expression::Negative(_) => {
panic!()
}
Expression::Call(_) => {
panic!()
}
Expression::Identifier(identifier) => {
if let Some(diagnostic) =
identifier.check_constructor_destination_name(symbol_table, class_symbol)
{
vec![diagnostic]
} else {
vec![]
}
}
_ => vec![],
}
}
pub fn check_constructor_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
match self {
Expression::Binary(binary_expression) => {
binary_expression.check_constructor_local_names(symbol_table, class_symbol)
}
Expression::Negative(negative_expression) => {
negative_expression.check_constructor_local_names(symbol_table, class_symbol)
}
Expression::Call(call) => {
call.check_constructor_local_names(symbol_table, class_symbol)
}
Expression::Identifier(identifier) => {
if let Some(diagnostic) =
identifier.check_constructor_local_name(symbol_table, class_symbol)
{
vec![diagnostic]
} else {
vec![]
}
}
_ => vec![],
}
}
pub fn check_method_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
match self {
Expression::Binary(binary_expression) => {
binary_expression.check_method_local_names(symbol_table, class_symbol)
}
Expression::Negative(negative_expression) => {
negative_expression.check_method_local_names(symbol_table, class_symbol)
}
Expression::Call(call) => call.check_method_local_names(symbol_table, class_symbol),
Expression::Identifier(identifier) => {
if let Some(diagnostic) =
identifier.check_method_local_name(symbol_table, class_symbol)
{
vec![diagnostic]
} else {
vec![]
}
}
_ => vec![],
}
}
pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
match self {
Expression::Binary(binary_expression) => {
binary_expression.check_static_fn_local_names(symbol_table)
}
Expression::Negative(negative_expression) => {
negative_expression.check_static_fn_local_names(symbol_table)
}
Expression::Call(call) => call.check_static_fn_local_names(symbol_table),
Expression::Identifier(identifier) => {
if let Some(diagnostic) = identifier.check_static_fn_local_name(symbol_table) {
vec![diagnostic]
} else {
vec![]
}
}
Expression::Integer(_) => {
vec![]
}
Expression::Double(_) => {
vec![]
}
Expression::String(_) => {
vec![]
}
}
}
pub fn type_check(
&mut self, &mut self,
symbol_table: &mut SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
match self { match self {
Expression::Binary(binary_expression) => { Expression::Binary(binary_expression) => {
binary_expression.gather_declared_names(symbol_table) binary_expression.type_check(symbol_table, types_table)
} }
Expression::Negative(negative_expression) => { Expression::Negative(negative_expression) => {
negative_expression.gather_declared_names(symbol_table) negative_expression.type_check(symbol_table, types_table)
} }
Expression::Call(call) => call.gather_declared_names(symbol_table), Expression::Call(call) => call.type_check(symbol_table, types_table),
Expression::Identifier(identifier) => identifier.gather_declared_names(symbol_table), Expression::Identifier(_) => Ok(()),
Expression::Integer(_) => Ok(()), Expression::Integer(_) => Ok(()),
Expression::Double(_) => Ok(()), Expression::Double(_) => Ok(()),
Expression::String(_) => Ok(()), Expression::String(_) => Ok(()),
} }
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn type_info<'a>(
match self { &'a self,
Expression::Binary(binary_expression) => { symbol_table: &SymbolTable,
binary_expression.check_name_usages(symbol_table) types_table: &'a TypesTable,
} ) -> &'a TypeInfo {
Expression::Negative(negative_expression) => {
negative_expression.check_name_usages(symbol_table)
}
Expression::Call(call) => call.check_name_usages(symbol_table),
Expression::Identifier(identifier) => identifier.check_name_usages(symbol_table),
Expression::Integer(_) => Ok(()),
Expression::Double(_) => Ok(()),
Expression::String(_) => Ok(()),
}
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
match self {
Expression::Binary(binary_expression) => binary_expression.type_check(symbol_table),
Expression::Negative(negative_expression) => {
negative_expression.type_check(symbol_table)
}
Expression::Call(call) => call.type_check(symbol_table),
Expression::Identifier(identifier) => identifier.type_check(symbol_table),
Expression::Integer(_) => Ok(()),
Expression::Double(_) => Ok(()),
Expression::String(_) => Ok(()),
}
}
pub fn type_info(&self) -> &TypeInfo {
match self { match self {
Expression::Binary(binary_expression) => binary_expression.type_info(), Expression::Binary(binary_expression) => binary_expression.type_info(),
Expression::Negative(negative_expression) => negative_expression.type_info(), Expression::Negative(negative_expression) => negative_expression.type_info(),
Expression::Call(call) => call.return_type_info(), Expression::Call(call) => call.return_type_info(symbol_table, types_table),
Expression::Identifier(identifier) => identifier.type_info(), Expression::Identifier(identifier) => identifier.type_info(symbol_table, types_table),
Expression::Integer(integer_literal) => integer_literal.type_info(), Expression::Integer(integer_literal) => integer_literal.type_info(),
Expression::Double(double_literal) => double_literal.type_info(), Expression::Double(double_literal) => double_literal.type_info(),
Expression::String(string_literal) => string_literal.type_info(), Expression::String(string_literal) => string_literal.type_info(),
@ -106,12 +239,15 @@ impl Expression {
&self, &self,
builder: &mut IrBuilder, builder: &mut IrBuilder,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrOperation { ) -> IrOperation {
match self { match self {
Expression::Binary(binary_expression) => { Expression::Binary(binary_expression) => {
binary_expression.to_ir_operation(builder, symbol_table) binary_expression.to_ir_operation(builder, symbol_table, types_table)
}
Expression::Call(call) => {
IrOperation::Call(call.to_ir(builder, symbol_table, types_table))
} }
Expression::Call(call) => IrOperation::Call(call.to_ir(builder, symbol_table)),
Expression::Integer(integer_literal) => { Expression::Integer(integer_literal) => {
IrOperation::Load(IrExpression::Int(integer_literal.value())) IrOperation::Load(IrExpression::Int(integer_literal.value()))
} }
@ -122,10 +258,10 @@ impl Expression {
IrOperation::Load(IrExpression::String(string_literal.content().into())) IrOperation::Load(IrExpression::String(string_literal.content().into()))
} }
Expression::Identifier(identifier) => { Expression::Identifier(identifier) => {
IrOperation::Load(identifier.expressible_symbol().ir_expression(builder)) IrOperation::Load(identifier.ir_expression(builder, symbol_table, types_table))
} }
Expression::Negative(negative_expression) => { Expression::Negative(negative_expression) => {
IrOperation::Load(negative_expression.to_ir(builder, symbol_table)) IrOperation::Load(negative_expression.to_ir(builder, symbol_table, types_table))
} }
} }
} }
@ -134,14 +270,21 @@ impl Expression {
&self, &self,
builder: &mut IrBuilder, builder: &mut IrBuilder,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Option<IrExpression> { ) -> Option<IrExpression> {
match self { match self {
Expression::Binary(binary_expression) => { Expression::Binary(binary_expression) => {
Some(binary_expression.to_ir_expression(builder, symbol_table)) Some(binary_expression.to_ir_expression(builder, symbol_table, types_table))
}
Expression::Negative(negative_expression) => {
Some(negative_expression.to_ir(builder, symbol_table, types_table))
} }
Expression::Call(call) => { Expression::Call(call) => {
let ir_call = call.to_ir(builder, symbol_table); let ir_call = call.to_ir(builder, symbol_table, types_table);
if matches!(call.return_type_info(), TypeInfo::Void) { if matches!(
call.return_type_info(symbol_table, types_table),
TypeInfo::Void
) {
builder builder
.current_block_mut() .current_block_mut()
.add_statement(IrStatement::Call(ir_call)); .add_statement(IrStatement::Call(ir_call));
@ -150,7 +293,7 @@ impl Expression {
let t_var = IrVariable::new_vr( let t_var = IrVariable::new_vr(
builder.new_t_var().into(), builder.new_t_var().into(),
builder.current_block().id(), builder.current_block().id(),
call.return_type_info(), call.return_type_info(symbol_table, types_table),
); );
let as_rc = Rc::new(RefCell::new(t_var)); let as_rc = Rc::new(RefCell::new(t_var));
let assign = IrAssign::new(as_rc.clone(), IrOperation::Call(ir_call)); let assign = IrAssign::new(as_rc.clone(), IrOperation::Call(ir_call));
@ -160,6 +303,9 @@ impl Expression {
Some(IrExpression::Variable(as_rc)) Some(IrExpression::Variable(as_rc))
} }
} }
Expression::Identifier(identifier) => {
Some(identifier.ir_expression(builder, symbol_table, types_table))
}
Expression::Integer(integer_literal) => { Expression::Integer(integer_literal) => {
Some(IrExpression::Int(integer_literal.value())) Some(IrExpression::Int(integer_literal.value()))
} }
@ -169,13 +315,6 @@ impl Expression {
Expression::String(string_literal) => { Expression::String(string_literal) => {
Some(IrExpression::String(string_literal.content().into())) Some(IrExpression::String(string_literal.content().into()))
} }
Expression::Identifier(identifier) => {
let expressible_symbol = identifier.expressible_symbol();
Some(expressible_symbol.ir_expression(builder))
}
Expression::Negative(negative_expression) => {
Some(negative_expression.to_ir(builder, symbol_table))
}
} }
} }
} }

View File

@ -3,8 +3,10 @@ use crate::ast::ir_builder::IrBuilder;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_return::IrReturn; use crate::ir::ir_return::IrReturn;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
pub struct ExpressionStatement { pub struct ExpressionStatement {
expression: Box<Expression>, expression: Box<Expression>,
@ -21,26 +23,42 @@ impl ExpressionStatement {
&self.expression &self.expression
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.expression.init_scopes(symbol_table, container_scope);
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
self.expression.gather_declared_names(symbol_table)
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn check_constructor_local_names(
self.expression.check_name_usages(symbol_table) &self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
self.expression
.check_constructor_local_names(symbol_table, class_symbol)
}
pub fn check_method_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
self.expression
.check_method_local_names(symbol_table, class_symbol)
}
pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
self.expression.check_static_fn_local_names(symbol_table)
} }
pub fn type_check( pub fn type_check(
&mut self, &mut self,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
must_return_type_info: Option<&TypeInfo>, must_return_type_info: Option<&TypeInfo>,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
self.expression.type_check(symbol_table)?; self.expression.type_check(symbol_table, types_table)?;
if must_return_type_info.is_some() { if must_return_type_info.is_some() {
let expression_type = self.expression.type_info(); let expression_type = self.expression.type_info(symbol_table, types_table);
let return_type = must_return_type_info.unwrap(); let return_type = must_return_type_info.unwrap();
if !return_type.is_assignable_from(expression_type) { if !return_type.is_assignable_from(expression_type) {
return Err(vec![Diagnostic::new( return Err(vec![Diagnostic::new(
@ -60,9 +78,12 @@ impl ExpressionStatement {
&self, &self,
builder: &mut IrBuilder, builder: &mut IrBuilder,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
should_return_value: bool, should_return_value: bool,
) { ) {
let ir_expression = self.expression.to_ir_expression(builder, symbol_table); let ir_expression = self
.expression
.to_ir_expression(builder, symbol_table, types_table);
if ir_expression.is_some() && should_return_value { if ir_expression.is_some() && should_return_value {
builder builder
.current_block_mut() .current_block_mut()

View File

@ -1,20 +1,23 @@
use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_context::FqnContext;
use crate::ast::helpers::{collect_diagnostics_into_mut, collect_parameter_symbols_into};
use crate::ast::parameter::Parameter; use crate::ast::parameter::Parameter;
use crate::ast::type_use::TypeUse; use crate::ast::type_use::TypeUse;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::Symbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::SymbolTable;
use std::cell::RefCell; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics};
use std::rc::Rc; use std::rc::Rc;
pub struct ExternFunction { pub struct ExternFunction {
declared_name: String, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
parameters: Vec<Parameter>, parameters: Vec<Parameter>,
return_type: TypeUse, return_type: TypeUse,
function_symbol: Option<Rc<RefCell<FunctionSymbol>>>, scope_id: Option<usize>,
} }
impl ExternFunction { impl ExternFunction {
@ -23,13 +26,13 @@ impl ExternFunction {
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
parameters: Vec<Parameter>, parameters: Vec<Parameter>,
return_type: TypeUse, return_type: TypeUse,
) -> ExternFunction { ) -> Self {
ExternFunction { Self {
declared_name: name.into(), declared_name: name.into(),
declared_name_source_range, declared_name_source_range,
parameters, parameters,
return_type, return_type,
function_symbol: None, scope_id: None,
} }
} }
@ -37,126 +40,110 @@ impl ExternFunction {
&self.declared_name &self.declared_name
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.scope_id = Some(container_scope);
symbol_table: &mut SymbolTable,
fqn_context: &FqnContext,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![];
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( let function_scope = symbol_table
.push_function_scope(&format!("extern_function_scope({})", self.declared_name));
for parameter in &mut self.parameters {
parameter.init_scopes(symbol_table, function_scope);
}
self.return_type.init_scopes(symbol_table, function_scope);
symbol_table.pop_scope();
}
pub fn make_symbols(&self, fqn_context: &FqnContext) -> (Rc<FunctionSymbol>, Vec<Symbol>) {
let mut all_symbols: Vec<Symbol> = Vec::new();
let mut parameter_symbols = Vec::new();
collect_parameter_symbols_into(&self.parameters, &mut all_symbols, &mut parameter_symbols);
let function_symbol = Rc::new(FunctionSymbol::new(
&self.declared_name, &self.declared_name,
self.declared_name_source_range.clone(), self.declared_name_source_range.clone(),
fqn_context.resolve(self.declared_name()), fqn_context.resolve(self.declared_name()),
true, true,
false,
self.scope_id.unwrap(),
parameter_symbols,
)); ));
all_symbols.push(Symbol::Function(function_symbol.clone()));
let function_symbol = match insert_result { (function_symbol, all_symbols)
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)
}
};
}
};
symbol_table pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
.push_function_scope(&format!("extern_function_scope({})", &self.declared_name)); let mut diagnostics: Vec<Diagnostic> = Vec::new();
for parameter in &self.parameters {
let mut parameter_symbols = vec![]; diagnostics.append(&mut parameter.check_names(symbol_table));
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 diagnostics.append(&mut self.return_type.check_names(symbol_table));
.borrow_mut() diagnostics
.set_parameters(parameter_symbols); }
self.function_symbol = Some(function_symbol); pub fn gather_types(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) {
let function_symbol = symbol_table
.get_function_symbol_owned(self.scope_id.unwrap(), self.declared_name())
.unwrap();
// handle return type // self function type
match self.return_type.gather_declared_names(symbol_table) { types_table.function_types_mut().insert(
Ok(_) => {} function_symbol.clone(),
Err(mut type_use_diagnostics) => { TypeInfo::Function(function_symbol.clone()),
diagnostics.append(&mut type_use_diagnostics); );
}
}
symbol_table.pop_scope(); // function scope // return type (temporary)
let resolved_return_type = self
.return_type
.type_info(symbol_table, types_table)
.clone();
types_table
.function_return_types_mut()
.insert(function_symbol, resolved_return_type);
if diagnostics.is_empty() { // parameters
Ok(()) for parameter in &self.parameters {
} else { parameter.gather_types_into(symbol_table, types_table);
Err(diagnostics)
} }
} }
pub fn check_name_usages( fn type_check_parameters(
&mut self, &mut self,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
class_context: Option<&Rc<RefCell<ClassSymbol>>>, types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> { diagnostics: &mut Vec<Diagnostic>,
let mut diagnostics: Vec<Diagnostic> = self ) {
.parameters collect_diagnostics_into_mut(
.iter_mut() &mut self.parameters,
.map(|parameter| parameter.check_name_usages(symbol_table, class_context)) |p| p.type_check(symbol_table, types_table),
.filter_map(Result::err) diagnostics,
.flatten() );
.collect();
match self
.return_type
.check_name_usages(symbol_table, class_context)
{
Ok(_) => {}
Err(mut return_type_diagnostics) => {
diagnostics.append(&mut return_type_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());
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { fn type_check_return_type(
let diagnostics: Vec<Diagnostic> = self &mut self,
.parameters symbol_table: &SymbolTable,
.iter_mut() types_table: &TypesTable,
.map(|parameter| parameter.type_check(symbol_table)) diagnostics: &mut Vec<Diagnostic>,
.filter_map(Result::err) ) {
.flatten() handle_diagnostics!(
.collect(); self.return_type.type_check(symbol_table, types_table),
if diagnostics.is_empty() { diagnostics
Ok(()) );
} else { }
Err(diagnostics)
} pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
self.type_check_parameters(symbol_table, types_table, &mut diagnostics);
self.type_check_return_type(symbol_table, types_table, &mut diagnostics);
diagnostics_result!(diagnostics)
} }
} }

View File

@ -1,11 +1,12 @@
use crate::ast::diagnostic_factories::field_has_no_type_or_init;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::type_use::TypeUse; use crate::ast::type_use::TypeUse;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::SymbolTable;
use std::cell::RefCell; use crate::types_table::TypesTable;
use std::rc::Rc; use std::rc::Rc;
pub struct Field { pub struct Field {
@ -15,7 +16,7 @@ pub struct Field {
is_mut: bool, is_mut: bool,
declared_type: Option<Box<TypeUse>>, declared_type: Option<Box<TypeUse>>,
initializer: Option<Box<Expression>>, initializer: Option<Box<Expression>>,
field_symbol: Option<Rc<RefCell<FieldSymbol>>>, scope_id: Option<usize>,
} }
impl Field { impl Field {
@ -34,7 +35,7 @@ impl Field {
is_mut, is_mut,
declared_type: declared_type.map(Box::new), declared_type: declared_type.map(Box::new),
initializer: initializer.map(Box::new), initializer: initializer.map(Box::new),
field_symbol: None, scope_id: None,
} }
} }
@ -54,87 +55,104 @@ impl Field {
self.initializer.as_ref().map(Box::as_ref) self.initializer.as_ref().map(Box::as_ref)
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.scope_id = Some(container_scope);
symbol_table: &mut SymbolTable, if let Some(type_use) = &mut self.declared_type {
field_index: usize, type_use.init_scopes(symbol_table, container_scope);
) -> Result<Rc<RefCell<FieldSymbol>>, Vec<Diagnostic>> { }
// 1. insert field symbol if let Some(expression) = &mut self.initializer {
let to_insert = FieldSymbol::new( expression.init_scopes(symbol_table, container_scope);
}
}
pub fn scope_id(&self) -> usize {
self.scope_id.unwrap()
}
pub fn make_symbol(&self, field_index: usize) -> FieldSymbol {
FieldSymbol::new(
&self.declared_name, &self.declared_name,
self.declared_name_source_range.clone(), self.declared_name_source_range.clone(),
self.is_mut, self.is_mut,
); self.scope_id.unwrap(),
field_index,
let field_symbol = symbol_table )
.insert_field_symbol(to_insert)
.map_err(|e| match e {
SymbolInsertError::AlreadyDeclared(already_declared) => {
vec![Diagnostic::new(
&format!(
"Symbol {} already declared in current scope.",
already_declared.symbol().borrow().declared_name()
),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
)]
}
})?;
// save for later
let to_return = field_symbol.clone();
self.field_symbol = Some(field_symbol);
// set field index on symbol
self.field_symbol
.as_ref()
.unwrap()
.borrow_mut()
.set_field_index(field_index);
// 2. gather type_use and initializer, if present
if let Some(type_use) = &mut self.declared_type {
type_use.gather_declared_names(symbol_table)?;
}
if let Some(initializer) = &mut self.initializer {
initializer.gather_declared_names(symbol_table)?;
}
Ok(to_return)
} }
pub fn check_name_usages( pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
&mut self, let mut diagnostics: Vec<Diagnostic> = Vec::new();
symbol_table: &SymbolTable, if let Some(type_use) = &self.declared_type {
class_context: Option<&Rc<RefCell<ClassSymbol>>>, diagnostics.append(&mut type_use.check_names(symbol_table));
) -> Result<(), Vec<Diagnostic>> {
if let Some(type_use) = &mut self.declared_type {
type_use.check_name_usages(symbol_table, class_context)?;
} }
diagnostics
}
// This is going to get hairy, because users might attempt to use a field in an initializer pub fn check_field_initializer_names(
// (for either this field, or another one) before it's actually initialized. As such, we &self,
// need a way to prevent lookup of current class' fields in the initializer. symbol_table: &SymbolTable,
// For now, the following is okay so long as we don't start referencing things in the class_symbol: &ClassSymbol,
// initializers. ) -> Vec<Diagnostic> {
if let Some(initializer) = self.initializer.as_mut() { if let Some(initializer) = &self.initializer {
initializer.check_name_usages(symbol_table)?; initializer.check_field_initializer_names(symbol_table, class_symbol)
} else {
vec![]
}
}
pub fn gather_types(
&self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
// self field
let field_symbol = symbol_table
.get_field_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
.unwrap();
match &self.declared_type {
Some(declared_type) => {
let resolved_type = declared_type.type_info(symbol_table, types_table).clone();
types_table
.field_types_mut()
.insert(field_symbol, resolved_type);
}
None => match &self.initializer {
Some(initializer) => {
let initializer_type = initializer.type_info(symbol_table, types_table).clone();
types_table
.field_types_mut()
.insert(field_symbol, initializer_type);
}
None => {
// this is an error
return Err(vec![field_has_no_type_or_init(
self.declared_name(),
self.declared_name_source_range(),
)]);
}
},
} }
Ok(()) Ok(())
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![]; let mut diagnostics: Vec<Diagnostic> = vec![];
if let Some(type_use) = &mut self.declared_type { if let Some(type_use) = &mut self.declared_type {
if let Some(mut type_use_diagnostics) = type_use.type_check(symbol_table).err() { if let Some(mut type_use_diagnostics) =
type_use.type_check(symbol_table, types_table).err()
{
diagnostics.append(&mut type_use_diagnostics); diagnostics.append(&mut type_use_diagnostics);
} }
} }
if let Some(initializer) = &mut self.initializer { if let Some(initializer) = &mut self.initializer {
if let Some(mut initializer_diagnostics) = initializer.type_check(symbol_table).err() { if let Some(mut initializer_diagnostics) =
initializer.type_check(symbol_table, types_table).err()
{
diagnostics.append(&mut initializer_diagnostics); diagnostics.append(&mut initializer_diagnostics);
} }
} }
@ -143,67 +161,31 @@ impl Field {
return Err(diagnostics); return Err(diagnostics);
} }
// Now check that types are assignable, and update field symbol's type info // Now check that types are assignable
let field_type_info = match self.declared_type.as_ref() {
match self.declared_type.as_ref() { Some(type_use) => match self.initializer.as_ref() {
Some(type_use) => { Some(initializer) => {
match self.initializer.as_ref() { let initializer_type_info = initializer.type_info(symbol_table, types_table);
Some(initializer) => { let declared_type_info = type_use.type_info(symbol_table, types_table);
let initializer_type_info = initializer.type_info(); if declared_type_info.is_assignable_from(initializer_type_info) {
let declared_type_info = type_use.type_info(); Ok(())
if declared_type_info.is_assignable_from(initializer_type_info) { } else {
declared_type_info Err(vec![
} else { Diagnostic::new(
return Err(vec![ &format!(
Diagnostic::new( "Mismatched types: {} is not assignable to {}",
&format!( initializer_type_info, declared_type_info
"Mismatched types: {} is not assignable to {}", ),
initializer_type_info, declared_type_info initializer.source_range().start(),
), initializer.source_range().end(),
initializer.source_range().start(), )
initializer.source_range().end(), .with_reporter(file!(), line!()),
) ])
.with_reporter(file!(), line!()),
]);
}
}
None => {
// easy: the declared type
type_use.type_info()
}
} }
} }
None => { None => Ok(()),
// type is the initializer },
match self.initializer.as_ref() { None => Ok(()),
Some(initializer) => initializer.type_info(),
None => {
// this is an error
return Err(vec![Diagnostic::new(
"Field must have either a declared type or initializer, or both.",
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
).with_reporter(file!(), line!())]);
}
}
}
};
// update field symbol's type info
self.field_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_type_info(field_type_info.clone());
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
} }
} }
pub fn field_symbol(&self) -> &Rc<RefCell<FieldSymbol>> {
self.field_symbol.as_ref().unwrap()
}
} }

View File

@ -1,11 +1,14 @@
use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_context::FqnContext;
use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::fqn_util::fqn_parts_to_string;
use crate::ast::helpers::{
collect_diagnostics_into_enumerated_mut, collect_diagnostics_into_mut,
collect_parameter_symbols_into,
};
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::ast::parameter::Parameter; use crate::ast::parameter::Parameter;
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
use crate::ast::type_use::TypeUse; use crate::ast::type_use::TypeUse;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::handle_diagnostics;
use crate::ir::ir_function::IrFunction; use crate::ir::ir_function::IrFunction;
use crate::ir::ir_parameter::IrParameter; use crate::ir::ir_parameter::IrParameter;
use crate::ir::ir_parameter_or_variable::IrParameterOrVariable; use crate::ir::ir_parameter_or_variable::IrParameterOrVariable;
@ -13,20 +16,21 @@ use crate::source_range::SourceRange;
use crate::symbol::Symbol; use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use std::cell::RefCell; use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics};
use std::ops::Neg; use std::ops::Neg;
use std::rc::Rc; use std::rc::Rc;
pub struct Function { pub struct Function {
declared_name: String, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
is_public: bool, is_public: bool,
parameters: Vec<Parameter>, parameters: Vec<Parameter>,
return_type: Option<TypeUse>, return_type: Option<TypeUse>,
statements: Vec<Statement>, statements: Vec<Statement>,
function_symbol: Option<Rc<RefCell<FunctionSymbol>>>, scope_id: Option<usize>,
} }
impl Function { impl Function {
@ -39,13 +43,13 @@ impl Function {
statements: Vec<Statement>, statements: Vec<Statement>,
) -> Self { ) -> Self {
Self { Self {
declared_name: declared_name.to_string(), declared_name: declared_name.into(),
declared_name_source_range, declared_name_source_range,
is_public, is_public,
parameters, parameters,
return_type, return_type,
statements, statements,
function_symbol: None, scope_id: None,
} }
} }
@ -57,266 +61,281 @@ impl Function {
self.statements.iter().collect() self.statements.iter().collect()
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.scope_id = Some(container_scope);
symbol_table: &mut SymbolTable, let function_scope =
fqn_context: &FqnContext, symbol_table.push_function_scope(&format!("function_scope({})", self.declared_name));
class_context: Option<&Rc<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![];
if !diagnostics.is_empty() { for parameter in &mut self.parameters {
return Err(diagnostics); parameter.init_scopes(symbol_table, function_scope);
}
if let Some(type_use) = &mut self.return_type {
type_use.init_scopes(symbol_table, function_scope);
} }
// insert function symbol let body_scope =
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( symbol_table.push_block_scope(&format!("body_scope({})", self.declared_name));
self.declared_name(),
for statement in &mut self.statements {
statement.init_scopes(symbol_table, body_scope);
}
symbol_table.pop_scope(); // body
symbol_table.pop_scope(); // function
}
pub fn make_symbols(
&self,
fqn_context: &FqnContext,
is_method: bool,
) -> (Rc<FunctionSymbol>, Vec<Symbol>) {
let mut all_symbols: Vec<Symbol> = vec![];
let mut parameter_symbols = Vec::new();
collect_parameter_symbols_into(&self.parameters, &mut all_symbols, &mut parameter_symbols);
let function_symbol = Rc::new(FunctionSymbol::new(
&self.declared_name,
self.declared_name_source_range.clone(), self.declared_name_source_range.clone(),
fqn_context.resolve(self.declared_name()), fqn_context.resolve(self.declared_name()),
false, false,
is_method,
self.scope_id.unwrap(),
parameter_symbols,
)); ));
all_symbols.push(Symbol::Function(function_symbol.clone()));
// get function symbol if successful (function_symbol, all_symbols)
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)
}
};
}
};
// save function symbol for later pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
self.function_symbol = Some(function_symbol.clone()); let mut diagnostics = Vec::new();
for parameter in &self.parameters {
// handle parameters diagnostics.append(&mut parameter.check_names(symbol_table));
symbol_table.push_function_scope(&format!("function_scope({})", self.declared_name));
// 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![]),
),
);
} }
if let Some(type_use) = &self.return_type {
// now gather all names for the params diagnostics.append(&mut type_use.check_names(symbol_table));
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);
}
}
} }
diagnostics
}
// set params on function symbol pub fn analyze_method_local_names(
function_symbol &self,
.borrow_mut() symbol_table: &mut SymbolTable,
.set_parameters(parameter_symbols); class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
self.statements
.iter()
.flat_map(|statement| statement.analyze_method_local_names(symbol_table, class_symbol))
.collect()
}
// return type pub fn analyze_static_fn_local_names(&self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
if let Some(type_use) = &mut self.return_type { self.statements
match type_use.gather_declared_names(symbol_table) { .iter()
Ok(_) => {} .flat_map(|statement| statement.analyze_static_fn_local_names(symbol_table))
Err(mut type_use_diagnostics) => { .collect()
diagnostics.append(&mut type_use_diagnostics); }
}
}
}
symbol_table.push_block_scope(&format!("main_block_scope({})", self.declared_name)); pub fn gather_types(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) {
for statement in &mut self.statements { let function_symbol = symbol_table
match statement.gather_declared_names(symbol_table) { .get_function_symbol_owned(self.scope_id.unwrap(), self.declared_name())
Ok(_) => {} .unwrap();
Err(mut statement_diagnostics) => {
diagnostics.append(&mut statement_diagnostics);
}
}
}
symbol_table.pop_scope(); // main block scope
symbol_table.pop_scope(); // function scope
if diagnostics.is_empty() { // self type (the signature)
Ok(()) types_table.function_types_mut().insert(
function_symbol.clone(),
TypeInfo::Function(function_symbol.clone()),
);
// put return type (temporary, this is deprecated)
if let Some(type_use) = &self.return_type {
let resolved_return_type = type_use.type_info(symbol_table, types_table).clone();
types_table
.function_return_types_mut()
.insert(function_symbol, resolved_return_type);
} else { } else {
Err(diagnostics) types_table
.function_return_types_mut()
.insert(function_symbol, TypeInfo::Void);
}
// parameters
for parameter in &self.parameters {
parameter.gather_types_into(symbol_table, types_table);
} }
} }
pub fn check_name_usages( fn get_return_type_info(
types_table: &TypesTable,
function_symbol: &FunctionSymbol,
) -> TypeInfo {
types_table
.function_return_types()
.get(function_symbol)
.cloned()
.unwrap()
}
/// Type checks parameters.
fn type_check_parameters(
&mut self, &mut self,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
class_context: Option<&Rc<RefCell<ClassSymbol>>>, types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> { diagnostics: &mut Vec<Diagnostic>,
let mut diagnostics = vec![]; ) {
collect_diagnostics_into_mut(
&mut self.parameters,
|p| p.type_check(symbol_table, types_table),
diagnostics,
)
}
// parameters /// Type checks return type.
diagnostics.append( fn type_check_return_type(
&mut self &mut self,
.parameters symbol_table: &SymbolTable,
.iter_mut() types_table: &TypesTable,
.map(|parameter| parameter.check_name_usages(symbol_table, class_context)) diagnostics: &mut Vec<Diagnostic>,
.filter_map(|result| result.err()) ) {
.flatten()
.collect(),
);
// return type
if let Some(type_use) = &mut self.return_type { if let Some(type_use) = &mut self.return_type {
match type_use.check_name_usages(symbol_table, class_context) { handle_diagnostics!(type_use.type_check(symbol_table, types_table), diagnostics);
Ok(_) => {
// set return type info on function symbol
self.function_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_return_type_info(type_use.type_info().clone());
}
Err(mut type_use_diagnostics) => {
diagnostics.append(&mut type_use_diagnostics);
}
}
} else {
// we don't have a given return type, so it's void
self.function_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_return_type_info(TypeInfo::Void);
}
// statements
for statement in &mut self.statements {
match statement.check_name_usages(symbol_table) {
Ok(_) => {}
Err(mut statement_diagnostics) => {
diagnostics.append(&mut statement_diagnostics);
}
}
}
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
} }
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { /// Type checks statements, making sure the last statement matches return type, if necessary.
let mut diagnostics = vec![]; fn type_check_statements(
&mut self,
// parameters symbol_table: &SymbolTable,
diagnostics.append( types_table: &mut TypesTable,
&mut self diagnostics: &mut Vec<Diagnostic>,
.parameters function_symbol: &FunctionSymbol,
.iter_mut() ) {
.map(|parameter| parameter.type_check(symbol_table)) let return_type_info = Self::get_return_type_info(types_table, function_symbol);
.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(); let statements_len = self.statements.len();
collect_diagnostics_into_enumerated_mut(
&mut self.statements,
|i, s| {
let is_last = i == statements_len - 1;
if is_last {
s.type_check(symbol_table, types_table, Some(&return_type_info))
} else {
s.type_check(symbol_table, types_table, None)
}
},
diagnostics,
);
}
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![];
let function_symbol = symbol_table
.get_function_symbol(self.scope_id.unwrap(), self.declared_name())
.unwrap();
// parameters
self.type_check_parameters(symbol_table, types_table, &mut diagnostics);
// return type // return type
if let Some(type_use) = &mut self.return_type { self.type_check_return_type(symbol_table, types_table, &mut diagnostics);
handle_diagnostics!(type_use.type_check(symbol_table), diagnostics);
}
// statements // statements
diagnostics.append( self.type_check_statements(symbol_table, types_table, &mut diagnostics, function_symbol);
&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(),
);
if diagnostics.is_empty() { diagnostics_result!(diagnostics)
Ok(()) }
} else {
Err(diagnostics) /// Converts all parameters to ir. Saves the IrParameter to the associated parameter symbol.
fn parameters_to_ir(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) {
for (i, parameter) in self.parameters.iter().enumerate() {
let parameter_symbol = symbol_table
.get_parameter_symbol_owned(parameter.scope_id(), parameter.declared_name())
.unwrap();
let parameter_type_info = types_table
.parameter_types()
.get(&parameter_symbol)
.unwrap();
let stack_offset = (self.parameters.len() as isize).neg() + (i as isize);
let ir_parameter = IrParameter::new(
parameter_symbol.declared_name(),
parameter_type_info.clone(),
stack_offset,
);
let as_rc = Rc::new(ir_parameter);
builder.push_parameter(&parameter_symbol, as_rc.clone());
}
}
/// If `class_context.is_some()`, set parameter 0 to the self parameter/variable on the builder.
fn handle_method_case(&self, builder: &mut IrBuilder, class_context: Option<&ClassSymbol>) {
// if we are a method, we need to set the self parameter on the builder
if class_context.is_some() {
let parameter_0 = builder.parameters()[0].clone();
// put it in the self parameter
builder.set_self_parameter_or_variable(IrParameterOrVariable::IrParameter(parameter_0));
}
}
/// Convert all statements to ir.
fn statements_to_ir(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
function_symbol: &FunctionSymbol,
) {
let return_type_info = Self::get_return_type_info(types_table, function_symbol);
let should_return_value = !matches!(return_type_info, TypeInfo::Void);
for (i, statement) in self.statements.iter().enumerate() {
let is_last = i == self.statements.len() - 1;
statement.to_ir(
builder,
symbol_table,
types_table,
should_return_value && is_last,
);
} }
} }
pub fn to_ir( pub fn to_ir(
&self, &self,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
class_context: Option<Rc<RefCell<ClassSymbol>>>, types_table: &TypesTable,
class_context: Option<&ClassSymbol>,
) -> IrFunction { ) -> IrFunction {
let mut builder = IrBuilder::new(); let mut builder = IrBuilder::new();
let function_symbol = symbol_table
.get_function_symbol(self.scope_id.unwrap(), self.declared_name())
.unwrap();
// parameters // parameters
for (i, parameter) in self.parameters.iter().enumerate() { self.parameters_to_ir(&mut builder, symbol_table, types_table);
let parameter_symbol = parameter.parameter_symbol();
let stack_offset = (self.parameters.len() as isize).neg() + (i as isize);
let ir_parameter = IrParameter::new(
parameter_symbol.borrow().declared_name(),
parameter_symbol.borrow().type_info().clone(),
stack_offset,
);
let as_rc = Rc::new(ir_parameter);
builder.parameters_mut().push(as_rc.clone());
parameter_symbol.borrow_mut().set_ir_parameter(as_rc);
}
let entry_block_id = builder.new_block(); let entry_block_id = builder.new_block();
// preamble // preamble
// if we are a method, we need to set the self parameter on the builder self.handle_method_case(&mut builder, class_context);
if let Some(_) = class_context {
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(); // body
let return_type_info = function_symbol.return_type_info(); self.statements_to_ir(&mut builder, symbol_table, types_table, function_symbol);
let should_return_value = !matches!(return_type_info, TypeInfo::Void);
for (i, statement) in self.statements.iter().enumerate() {
let is_last = i == self.statements.len() - 1;
statement.to_ir(&mut builder, symbol_table, should_return_value && is_last);
}
builder.finish_block(); builder.finish_block();
let entry_block = builder.get_block(entry_block_id).clone(); let entry_block = builder.get_block(entry_block_id).clone();
IrFunction::new( IrFunction::new(
fqn_parts_to_string(self.function_symbol.as_ref().unwrap().borrow().fqn_parts()), fqn_parts_to_string(function_symbol.fqn_parts()),
builder.parameters(), builder.parameters().iter().map(|p| (*p).clone()).collect(),
return_type_info, &Self::get_return_type_info(types_table, function_symbol),
entry_block, entry_block,
) )
} }

View File

@ -1,11 +1,11 @@
use crate::ast::type_use::TypeUse; use crate::ast::type_use::TypeUse;
use crate::diagnostic::{Diagnostic, SecondaryLabel}; use crate::diagnostic::Diagnostic;
use crate::error_codes::SYMBOL_ALREADY_DECLARED;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::SymbolTable;
use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics}; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -13,6 +13,7 @@ pub struct GenericParameter {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
extends: Vec<TypeUse>, extends: Vec<TypeUse>,
scope_id: Option<usize>,
generic_parameter_symbol: Option<Rc<RefCell<GenericParameterSymbol>>>, generic_parameter_symbol: Option<Rc<RefCell<GenericParameterSymbol>>>,
} }
@ -26,94 +27,68 @@ impl GenericParameter {
declared_name: declared_name.into(), declared_name: declared_name.into(),
declared_name_source_range, declared_name_source_range,
extends, extends,
scope_id: None,
generic_parameter_symbol: None, generic_parameter_symbol: None,
} }
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.scope_id = Some(container_scope);
symbol_table: &mut SymbolTable,
) -> Result<Rc<RefCell<GenericParameterSymbol>>, Vec<Diagnostic>> {
// insert symbol
let to_insert =
GenericParameterSymbol::new(&self.declared_name, &self.declared_name_source_range);
let to_return = match symbol_table.insert_generic_parameter_symbol(to_insert) {
Ok(generic_parameter_symbol) => generic_parameter_symbol,
Err(symbol_insert_error) => {
return match symbol_insert_error {
SymbolInsertError::AlreadyDeclared(already_declared) => {
let already_declared_symbol = already_declared.symbol().borrow();
let already_declared_source_range =
already_declared_symbol.declared_name_source_range();
let diagnostic = Diagnostic::new(
&format!("Symbol {} already declared in scope.", self.declared_name),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
)
.with_reporter(file!(), line!())
.with_error_code(SYMBOL_ALREADY_DECLARED)
.with_secondary_labels(&[SecondaryLabel::new(
already_declared_source_range.start(),
already_declared_source_range.end(),
Some("Symbol already declared here.".to_string()),
)]);
Err(vec![diagnostic])
}
};
}
};
// save param symbol
self.generic_parameter_symbol = Some(to_return.clone());
let mut diagnostics: Vec<Diagnostic> = vec![];
for type_use in &mut self.extends { for type_use in &mut self.extends {
handle_diagnostics!(type_use.gather_declared_names(symbol_table), diagnostics); type_use.init_scopes(symbol_table, container_scope);
}
if diagnostics.is_empty() {
Ok(to_return)
} else {
Err(diagnostics)
} }
} }
pub fn check_name_usages( pub fn make_symbol(&self) -> GenericParameterSymbol {
&mut self, GenericParameterSymbol::new(
&self.declared_name,
&self.declared_name_source_range,
self.scope_id.unwrap(),
)
}
pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
self.extends
.iter()
.flat_map(|type_use| type_use.check_names(symbol_table))
.collect()
}
pub fn gather_types(
&self,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
class_context: Option<&Rc<RefCell<ClassSymbol>>>, types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![]; // self param
// check the extends type uses let generic_parameter_symbol = symbol_table
for type_use in &mut self.extends { .get_generic_parameter_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
.unwrap();
types_table.generic_parameter_types_mut().insert(
generic_parameter_symbol.clone(),
TypeInfo::GenericType(generic_parameter_symbol),
);
let mut diagnostics = Vec::new();
for type_use in &self.extends {
handle_diagnostics!( handle_diagnostics!(
type_use.check_name_usages(symbol_table, class_context), type_use.gather_types(symbol_table, types_table),
diagnostics diagnostics
); );
} }
maybe_return_diagnostics!(diagnostics);
// now that each extends type use has type info, set the type infos on the generic parameter diagnostics_result!(diagnostics)
let extends_type_infos = self
.extends
.iter()
.map(|type_use| type_use.type_info().clone())
.collect::<Vec<_>>();
self.generic_parameter_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_extends(extends_type_infos);
Ok(())
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![]; let mut diagnostics: Vec<Diagnostic> = vec![];
// check extends type uses // check extends type uses
for type_use in &mut self.extends { for type_use in &mut self.extends {
handle_diagnostics!(type_use.type_check(symbol_table), diagnostics); handle_diagnostics!(type_use.type_check(symbol_table, types_table), diagnostics);
} }
diagnostics_result!(diagnostics) diagnostics_result!(diagnostics)
} }

133
dmc-lib/src/ast/helpers.rs Normal file
View File

@ -0,0 +1,133 @@
use crate::ast::diagnostic_factories::symbol_already_declared;
use crate::ast::fqn_context::FqnContext;
use crate::ast::parameter::Parameter;
use crate::diagnostic::Diagnostic;
use crate::diagnostics_result;
use crate::symbol::Symbol;
use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol_table::SymbolTable;
use std::rc::Rc;
/// Iterates through all `ts`, running the `f` function, and pushing returned `Diagnostic`s
/// into `diagnostics`.
pub fn collect_diagnostics_into_mut<T>(
ts: &mut [T],
mut f: impl FnMut(&mut T) -> Result<(), Vec<Diagnostic>>,
diagnostics: &mut Vec<Diagnostic>,
) {
ts.iter_mut()
.map(|t| f(t))
.filter_map(Result::err)
.flatten()
.for_each(|d| diagnostics.push(d));
}
/// Like `collect_diagnostics_into` but enumerated.
pub fn collect_diagnostics_into_enumerated_mut<T>(
ts: &mut [T],
mut f: impl FnMut(usize, &mut T) -> Result<(), Vec<Diagnostic>>,
diagnostics: &mut Vec<Diagnostic>,
) {
ts.iter_mut()
.enumerate()
.map(|(i, t)| f(i, t))
.filter_map(Result::err)
.flatten()
.for_each(|d| diagnostics.push(d));
}
pub fn collect_diagnostics_mut<T>(
ts: &mut [T],
mut f: impl FnMut(&mut T) -> Result<(), Vec<Diagnostic>>,
) -> Result<(), Vec<Diagnostic>> {
let diagnostics = ts
.iter_mut()
.map(|t| f(t))
.filter_map(Result::err)
.flatten()
.collect::<Vec<_>>();
diagnostics_result!(diagnostics)
}
pub fn collect_diagnostics<T>(
ts: &[T],
f: impl Fn(&T) -> Result<(), Vec<Diagnostic>>,
) -> Result<(), Vec<Diagnostic>> {
let diagnostics = ts
.iter()
.map(|t| f(t))
.filter_map(Result::err)
.flatten()
.collect::<Vec<_>>();
diagnostics_result!(diagnostics)
}
pub fn collect_diagnostics_single<T>(
ts: &[T],
f: impl Fn(&T) -> Result<(), Diagnostic>,
) -> Result<(), Vec<Diagnostic>> {
let diagnostics = ts
.iter()
.map(|t| f(t))
.filter_map(Result::err)
.collect::<Vec<_>>();
diagnostics_result!(diagnostics)
}
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
}
pub fn resolve_ctor_name(fqn_context: &FqnContext) -> Vec<Rc<str>> {
fqn_context.resolve("ctor") // ctor is a keyword at the language level, should not be callable via normal means
}
pub fn try_insert_symbol_into(
symbol: Symbol,
symbol_table: &mut SymbolTable,
) -> Result<(), Diagnostic> {
let maybe_already_inserted = symbol_table.get_symbol(symbol.scope_id(), symbol.declared_name());
if let Some(already_inserted) = maybe_already_inserted {
Err(symbol_already_declared(&already_inserted, &symbol))
} else {
symbol_table.insert_symbol(symbol);
Ok(())
}
}
pub fn try_insert_symbols_into(
symbols: Vec<Symbol>,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
let diagnostics: Vec<Diagnostic> = symbols
.into_iter()
.map(|symbol| try_insert_symbol_into(symbol, symbol_table))
.filter_map(Result::err)
.collect();
diagnostics_result!(diagnostics)
}
pub fn collect_parameter_symbols_into(
parameters: &[Parameter],
all_symbols: &mut Vec<Symbol>,
parameter_symbols: &mut Vec<Rc<ParameterSymbol>>,
) {
for parameter in parameters {
let symbol = Rc::new(parameter.make_symbol());
all_symbols.push(Symbol::Parameter(symbol.clone()));
parameter_symbols.push(symbol);
}
}

View File

@ -1,25 +1,39 @@
use crate::ast::diagnostic_factories::{
outer_class_field_usage, outer_class_method_usage, self_constructor_used_in_init,
self_field_used_in_init, self_method_used_in_init, symbol_not_found,
};
use crate::ast::ir_builder::IrBuilder;
use crate::ast::ir_util::get_or_init_field_pointer_variable;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_read_field::IrReadField;
use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol;
use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use std::cell::RefCell;
use std::rc::Rc;
pub struct Identifier { pub struct Identifier {
name: String, name: String,
scope_id: Option<usize>,
expressible_symbol: Option<ExpressibleSymbol>,
type_info: Option<TypeInfo>,
source_range: SourceRange, source_range: SourceRange,
scope_id: Option<usize>,
} }
impl Identifier { impl Identifier {
pub fn new(name: &str, source_range: SourceRange) -> Self { pub fn new(name: &str, source_range: SourceRange) -> Self {
Self { Self {
name: name.into(), name: name.into(),
scope_id: None,
expressible_symbol: None,
type_info: None,
source_range, source_range,
scope_id: None,
} }
} }
@ -27,45 +41,294 @@ impl Identifier {
&self.name &self.name
} }
pub fn gather_declared_names( pub fn init_scope_id(&mut self, container_scope: usize) {
&mut self, self.scope_id = Some(container_scope);
symbol_table: &SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
self.scope_id = Some(symbol_table.current_scope_id());
Ok(())
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn scope_id(&self) -> usize {
let maybe_expressible_symbol = self.scope_id.unwrap()
symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); }
match maybe_expressible_symbol {
None => Err(vec![Diagnostic::new( /// Check against recursively constructing this class.
&format!("Unable to resolve symbol {}", self.name), fn check_self_constructor_use(
self.source_range.start(), &self,
self.source_range.end(), context_class_symbol: &ClassSymbol,
)]), class_symbol: &ClassSymbol,
Some(expressible_symbol) => { ) -> Option<Diagnostic> {
self.expressible_symbol = Some(expressible_symbol); // this is not future-proof, as we will eventually allow reference to the self class, which
Ok(()) // would (theoretically) be assigned to an instance field
} if context_class_symbol == class_symbol {
Some(self_constructor_used_in_init(&self.source_range))
} else {
None
} }
} }
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { /// Check against using this or outer class' bare fields.
let type_info = self.expressible_symbol.as_ref().unwrap().type_info(); fn check_self_or_outer_field_use(
self.type_info = Some(type_info); &self,
Ok(()) context_class_symbol: &ClassSymbol,
field_symbol: &FieldSymbol,
) -> Option<Diagnostic> {
// Usage of a bare field will always be an error, whether in this class or an outer class
if context_class_symbol
.fields()
.contains_key(field_symbol.declared_name())
{
Some(self_field_used_in_init(&self.source_range))
} else {
Some(outer_class_field_usage(&self.source_range))
}
} }
pub fn type_info(&self) -> &TypeInfo { /// Check against using self or outer class methods.
self.type_info.as_ref().unwrap() fn check_self_or_outer_method_use(
&self,
context_class_symbol: &ClassSymbol,
function_symbol: &FunctionSymbol,
) -> Option<Diagnostic> {
if context_class_symbol
.functions()
.contains_key(function_symbol.declared_name())
{
// Can only use Self static functions, which we don't have yet
Some(self_method_used_in_init(&self.source_range))
} else if function_symbol.is_method() {
// Can only use outer class static functions, which we don't have yet
Some(outer_class_method_usage(&self.source_range))
} else {
None
}
} }
pub fn expressible_symbol(&self) -> &ExpressibleSymbol { pub fn check_name_as_field_initializer(
self.expressible_symbol.as_ref().unwrap() &self,
symbol_table: &SymbolTable,
context_class_symbol: &ClassSymbol,
) -> Option<Diagnostic> {
let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
if let Some(symbol) = symbol {
match symbol {
ExpressibleSymbol::Class(class_symbol) => {
self.check_self_constructor_use(context_class_symbol, &class_symbol)
}
ExpressibleSymbol::Field(field_symbol) => {
self.check_self_or_outer_field_use(context_class_symbol, &field_symbol)
}
ExpressibleSymbol::Function(function_symbol) => {
self.check_self_or_outer_method_use(context_class_symbol, &function_symbol)
}
ExpressibleSymbol::Parameter(_) => {
// Cannot get here, because classes cannot currently be declared in functions
unreachable!()
}
ExpressibleSymbol::Variable(_) => {
// Cannot get here, as classes cannot currently be declared in functions
unreachable!()
}
}
} else {
Some(symbol_not_found(&self.name, &self.source_range))
}
}
pub fn check_constructor_destination_name(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Option<Diagnostic> {
let expressible_symbol =
symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
if let Some(expressible_symbol) = expressible_symbol {
match expressible_symbol {
ExpressibleSymbol::Class(_) => {
panic!("Class is not an L value")
}
ExpressibleSymbol::Field(field_symbol) => {
// This is just a stop-gap for now. We need to decide if we are going to do
// field assignment analysis (whether it's initialized already, if it's mut,
// etc.) during name checking or during type checking.
None
}
ExpressibleSymbol::Function(_) => {
panic!("Function is not an L value")
}
ExpressibleSymbol::Parameter(_) => {
panic!("Parameter is not an L value")
}
ExpressibleSymbol::Variable(variable_symbol) => {
// Again, a stop-gap.
None
}
}
} else {
Some(symbol_not_found(&self.name, &self.source_range))
}
}
pub fn check_constructor_local_name(
&self,
symbol_table: &SymbolTable,
context_class_symbol: &ClassSymbol,
) -> Option<Diagnostic> {
let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
if let Some(symbol) = symbol {
match symbol {
ExpressibleSymbol::Class(class_symbol) => {
self.check_self_constructor_use(context_class_symbol, &class_symbol)
}
ExpressibleSymbol::Field(field_symbol) => {
self.check_self_or_outer_field_use(context_class_symbol, &field_symbol)
}
ExpressibleSymbol::Function(function_symbol) => {
self.check_self_or_outer_method_use(context_class_symbol, &function_symbol)
}
ExpressibleSymbol::Parameter(_) => None,
ExpressibleSymbol::Variable(_) => None,
}
} else {
Some(symbol_not_found(&self.name, &self.source_range))
}
}
pub fn check_method_local_name(
&self,
symbol_table: &SymbolTable,
context_class_symbol: &ClassSymbol,
) -> Option<Diagnostic> {
let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
if let Some(symbol) = symbol {
match symbol {
ExpressibleSymbol::Class(_) => {
None // all class usages should be ok
}
ExpressibleSymbol::Field(field_symbol) => {
// Must be a reference to a field in this class
if context_class_symbol
.fields()
.contains_key(field_symbol.declared_name())
{
None
} else {
Some(outer_class_field_usage(&self.source_range))
}
}
ExpressibleSymbol::Function(function_symbol) => {
// Must be a method in this class
if function_symbol.is_method()
&& !context_class_symbol
.functions()
.contains_key(function_symbol.declared_name())
{
Some(outer_class_method_usage(&self.source_range))
} else {
None
}
}
ExpressibleSymbol::Parameter(_) => {
None // ok
}
ExpressibleSymbol::Variable(_) => {
None // ok
}
}
} else {
Some(symbol_not_found(&self.name, &self.source_range))
}
}
/// WARNING: this is not appropriate (yet) for class static functions.
pub fn check_static_fn_local_name(&self, symbol_table: &SymbolTable) -> Option<Diagnostic> {
if symbol_table
.find_expressible_symbol(self.scope_id.unwrap(), &self.name)
.is_some()
{
None
} else {
Some(symbol_not_found(&self.name, &self.source_range))
}
} }
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {
&self.source_range &self.source_range
} }
pub fn type_info<'a>(
&self,
symbol_table: &SymbolTable,
types_table: &'a TypesTable,
) -> &'a TypeInfo {
let expressible_symbol = symbol_table
.find_expressible_symbol(self.scope_id.unwrap(), &self.name)
.unwrap();
match expressible_symbol {
ExpressibleSymbol::Class(class_symbol) => {
types_table.class_types().get(&class_symbol).unwrap()
}
ExpressibleSymbol::Field(field_symbol) => {
types_table.field_types().get(&field_symbol).unwrap()
}
ExpressibleSymbol::Function(function_symbol) => types_table
.function_types()
.get(&function_symbol)
.expect(&format!(
"Unable to get function type for {:?}",
function_symbol
)),
ExpressibleSymbol::Parameter(parameter_symbol) => types_table
.parameter_types()
.get(&parameter_symbol)
.unwrap(),
ExpressibleSymbol::Variable(variable_symbol) => {
types_table.variable_types().get(&variable_symbol).unwrap()
}
}
}
pub fn ir_expression(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrExpression {
let expressible_symbol = symbol_table
.find_expressible_symbol(self.scope_id.unwrap(), &self.name)
.unwrap();
match expressible_symbol {
ExpressibleSymbol::Class(class_symbol) => {
todo!()
}
ExpressibleSymbol::Field(field_symbol) => {
let field_type = types_table.field_types().get(&field_symbol).unwrap();
let read_destination = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
field_type,
);
let read_destination_as_rc = Rc::new(RefCell::new(read_destination));
let ir_read_field = IrReadField::new(
get_or_init_field_pointer_variable(builder, &field_symbol, field_type).clone(),
);
builder
.current_block_mut()
.add_statement(IrStatement::Assign(IrAssign::new(
read_destination_as_rc.clone(),
IrOperation::ReadField(ir_read_field),
)));
IrExpression::Variable(read_destination_as_rc)
}
ExpressibleSymbol::Function(_) => {
panic!("Cannot yet get ir-variable for FunctionSymbol")
}
ExpressibleSymbol::Parameter(parameter_symbol) => {
let parameters_map = builder.parameters_map();
let ir_parameter = parameters_map.get(&parameter_symbol).unwrap();
IrExpression::Parameter(ir_parameter.clone())
}
ExpressibleSymbol::Variable(variable_symbol) => {
let ir_variable = builder.local_variables().get(&variable_symbol).unwrap();
IrExpression::Variable(ir_variable.clone())
}
}
}
} }

View File

@ -3,12 +3,15 @@ use crate::ir::ir_parameter::IrParameter;
use crate::ir::ir_parameter_or_variable::IrParameterOrVariable; use crate::ir::ir_parameter_or_variable::IrParameterOrVariable;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable; use crate::ir::ir_variable::IrVariable;
use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol::variable_symbol::VariableSymbol;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
pub struct IrBuilder { pub struct IrBuilder {
parameters: Vec<Rc<IrParameter>>, parameters: Vec<(Rc<ParameterSymbol>, Rc<IrParameter>)>,
local_variables: HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
block_counter: usize, block_counter: usize,
t_var_counter: usize, t_var_counter: usize,
blocks: HashMap<usize, Rc<RefCell<IrBlock>>>, blocks: HashMap<usize, Rc<RefCell<IrBlock>>>,
@ -22,6 +25,7 @@ impl IrBuilder {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
parameters: vec![], parameters: vec![],
local_variables: HashMap::new(),
block_counter: 0, block_counter: 0,
t_var_counter: 0, t_var_counter: 0,
blocks: HashMap::new(), blocks: HashMap::new(),
@ -32,12 +36,37 @@ impl IrBuilder {
} }
} }
pub fn parameters(&self) -> &[Rc<IrParameter>] { pub fn local_variables(&self) -> &HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>> {
&self.parameters &self.local_variables
} }
pub fn parameters_mut(&mut self) -> &mut Vec<Rc<IrParameter>> { pub fn local_variables_mut(
&mut self.parameters &mut self,
) -> &mut HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>> {
&mut self.local_variables
}
pub fn parameters(&self) -> Vec<&Rc<IrParameter>> {
self.parameters
.iter()
.map(|(_, ir_parameter)| ir_parameter)
.collect()
}
pub fn parameters_map(&self) -> HashMap<Rc<ParameterSymbol>, Rc<IrParameter>> {
let mut map = HashMap::new();
for (name, ir_parameter) in &self.parameters {
map.insert(name.clone(), ir_parameter.clone());
}
map
}
pub fn push_parameter(
&mut self,
parameter_symbol: &Rc<ParameterSymbol>,
parameter: Rc<IrParameter>,
) {
self.parameters.push((parameter_symbol.clone(), parameter));
} }
pub fn new_block(&mut self) -> usize { pub fn new_block(&mut self) -> usize {

View File

@ -5,25 +5,26 @@ use crate::ir::ir_get_field_ref_mut::IrGetFieldRefMut;
use crate::ir::ir_operation::IrOperation; use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable; use crate::ir::ir_variable::IrVariable;
use crate::symbol::Symbol;
use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::field_symbol::FieldSymbol;
use crate::type_info::TypeInfo;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub fn get_or_init_field_pointer_variable<'a>( pub fn get_or_init_field_pointer_variable<'a>(
builder: &'a mut IrBuilder, builder: &'a mut IrBuilder,
field_symbol: &Rc<RefCell<FieldSymbol>>, field_symbol: &Rc<FieldSymbol>,
field_type: &TypeInfo,
) -> &'a Rc<RefCell<IrVariable>> { ) -> &'a Rc<RefCell<IrVariable>> {
// This following should work because blocks are flat in the ir; if a variable is defined in the // This following should work because blocks are flat in the ir; if a variable is defined in the
// ir block from this point forward, it's available to all subsequent blocks. // ir block from this point forward, it's available to all subsequent blocks.
if !builder if !builder
.field_pointer_variables() .field_pointer_variables()
.contains_key(field_symbol.borrow().declared_name()) .contains_key(field_symbol.declared_name())
{ {
let field_ref_variable = IrVariable::new_vr( let field_ref_variable = IrVariable::new_vr(
builder.new_t_var().into(), builder.new_t_var().into(),
builder.current_block().id(), builder.current_block().id(),
field_symbol.borrow().type_info(), field_type,
); );
let as_rc = Rc::new(RefCell::new(field_ref_variable)); let as_rc = Rc::new(RefCell::new(field_ref_variable));
let to_insert = as_rc.clone(); let to_insert = as_rc.clone();
@ -34,31 +35,32 @@ pub fn get_or_init_field_pointer_variable<'a>(
as_rc, as_rc,
IrOperation::GetFieldRef(IrGetFieldRef::new( IrOperation::GetFieldRef(IrGetFieldRef::new(
self_parameter_or_variable.clone(), self_parameter_or_variable.clone(),
field_symbol.borrow().field_index(), field_symbol.field_index(),
)), )),
))); )));
builder builder
.field_pointer_variables_mut() .field_pointer_variables_mut()
.insert(field_symbol.borrow().declared_name_owned(), to_insert); .insert(field_symbol.declared_name_owned(), to_insert);
} }
builder builder
.field_pointer_variables() .field_pointer_variables()
.get(field_symbol.borrow().declared_name()) .get(field_symbol.declared_name())
.unwrap() .unwrap()
} }
pub fn get_or_init_mut_field_pointer_variable<'a>( pub fn get_or_init_mut_field_pointer_variable<'a>(
builder: &'a mut IrBuilder, builder: &'a mut IrBuilder,
field_symbol: &Rc<RefCell<FieldSymbol>>, field_symbol: &Rc<FieldSymbol>,
field_type: &TypeInfo,
) -> &'a Rc<RefCell<IrVariable>> { ) -> &'a Rc<RefCell<IrVariable>> {
if !builder if !builder
.field_mut_pointer_variables() .field_mut_pointer_variables()
.contains_key(field_symbol.borrow().declared_name()) .contains_key(field_symbol.declared_name())
{ {
let mut_field_pointer_variable = IrVariable::new_vr( let mut_field_pointer_variable = IrVariable::new_vr(
builder.new_t_var().into(), builder.new_t_var().into(),
builder.current_block().id(), builder.current_block().id(),
field_symbol.borrow().type_info(), field_type,
); );
let as_rc = Rc::new(RefCell::new(mut_field_pointer_variable)); let as_rc = Rc::new(RefCell::new(mut_field_pointer_variable));
let to_insert = as_rc.clone(); let to_insert = as_rc.clone();
@ -69,15 +71,15 @@ pub fn get_or_init_mut_field_pointer_variable<'a>(
as_rc, as_rc,
IrOperation::GetFieldRefMut(IrGetFieldRefMut::new( IrOperation::GetFieldRefMut(IrGetFieldRefMut::new(
self_parameter_or_variable.clone(), self_parameter_or_variable.clone(),
field_symbol.borrow().field_index(), field_symbol.field_index(),
)), )),
))); )));
builder builder
.field_mut_pointer_variables_mut() .field_mut_pointer_variables_mut()
.insert(field_symbol.borrow().declared_name_owned(), to_insert); .insert(field_symbol.declared_name_owned(), to_insert);
} }
builder builder
.field_mut_pointer_variables() .field_mut_pointer_variables()
.get(field_symbol.borrow().declared_name()) .get(field_symbol.declared_name())
.unwrap() .unwrap()
} }

View File

@ -1,12 +1,16 @@
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::helpers::try_insert_symbol_into;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_assign::IrAssign; use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable; use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::variable_symbol::VariableSymbol; use crate::symbol::variable_symbol::VariableSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -46,76 +50,131 @@ impl LetStatement {
&mut self.initializer &mut self.initializer
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.scope_id = Some(container_scope);
self.initializer.init_scopes(symbol_table, container_scope);
}
fn make_and_insert_variable_symbol(
&self,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Option<Diagnostic> {
let mut diagnostics = vec![]; let variable_symbol = Rc::new(VariableSymbol::new(
match self.initializer_mut().gather_declared_names(symbol_table) {
Ok(_) => {}
Err(mut initializer_diagnostics) => {
diagnostics.append(&mut initializer_diagnostics);
}
}
let insert_result = symbol_table.insert_variable_symbol(VariableSymbol::new(
&self.declared_name, &self.declared_name,
self.declared_name_source_range.clone(), &self.declared_name_source_range,
self.is_mut, self.is_mut,
self.scope_id.unwrap(),
)); ));
if let Err(symbol_insert_error) = insert_result { try_insert_symbol_into(Symbol::Variable(variable_symbol), symbol_table).err()
match symbol_insert_error {
SymbolInsertError::AlreadyDeclared(already_declared) => {
diagnostics.push(Diagnostic::new(
&format!(
"Symbol {} already declared in current scope",
already_declared.symbol().borrow().declared_name()
),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
))
}
}
}
self.scope_id = Some(symbol_table.current_scope_id());
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn analyze_constructor_local_names(
self.initializer.check_name_usages(symbol_table) &self,
symbol_table: &mut SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
let mut diagnostics = Vec::new();
diagnostics.append(
&mut self
.initializer
.check_constructor_local_names(symbol_table, class_symbol),
);
if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) {
diagnostics.push(diagnostic);
}
diagnostics
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn analyze_method_local_names(
self.initializer.type_check(symbol_table)?; &self,
let initializer_type_info = self.initializer.type_info(); symbol_table: &mut SymbolTable,
let variable_symbol = class_symbol: &ClassSymbol,
symbol_table.get_variable_symbol(self.scope_id.unwrap(), &self.declared_name); ) -> Vec<Diagnostic> {
variable_symbol let mut diagnostics = Vec::new();
.borrow_mut() diagnostics.append(
.set_type_info(initializer_type_info.clone()); &mut self
.initializer
.check_method_local_names(symbol_table, class_symbol),
);
if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) {
diagnostics.push(diagnostic);
}
diagnostics
}
pub fn analyze_static_fn_local_names(&self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
let mut diagnostics = Vec::new();
diagnostics.append(&mut self.initializer.check_static_fn_local_names(symbol_table));
if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) {
diagnostics.push(diagnostic);
}
diagnostics
}
pub fn gather_local_type(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) {
let initializer_type_info = self
.initializer
.type_info(symbol_table, types_table)
.clone();
let variable_symbol = symbol_table
.get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
.unwrap();
types_table
.variable_types_mut()
.insert(variable_symbol, initializer_type_info);
}
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
self.initializer.type_check(symbol_table, types_table)?;
let initializer_type_info = self
.initializer
.type_info(symbol_table, types_table)
.clone();
let variable_symbol = symbol_table
.get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
.unwrap();
types_table
.variable_types_mut()
.insert(variable_symbol, initializer_type_info);
Ok(()) Ok(())
} }
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { pub fn to_ir(
let init_operation = self.initializer.to_ir_operation(builder, symbol_table); &self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) {
let init_operation = self
.initializer
.to_ir_operation(builder, symbol_table, types_table);
let destination_symbol = symbol_table
.get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
.unwrap();
let destination_type = types_table
.variable_types()
.get(&destination_symbol)
.unwrap();
let destination_symbol =
symbol_table.get_variable_symbol(self.scope_id.unwrap(), &self.declared_name);
let destination_vr_variable = IrVariable::new_vr( let destination_vr_variable = IrVariable::new_vr(
self.declared_name().into(), self.declared_name().into(),
builder.current_block().id(), builder.current_block().id(),
self.initializer.type_info(), destination_type,
); );
let as_rc = Rc::new(RefCell::new(destination_vr_variable)); let as_rc = Rc::new(RefCell::new(destination_vr_variable));
let ir_assign = IrAssign::new(as_rc.clone(), init_operation); let ir_assign = IrAssign::new(as_rc.clone(), init_operation);
destination_symbol.borrow_mut().set_vr_variable(as_rc);
builder
.local_variables_mut()
.insert(destination_symbol, as_rc.clone());
builder builder
.current_block_mut() .current_block_mut()

View File

@ -4,6 +4,7 @@ pub mod call;
pub mod class; pub mod class;
pub mod compilation_unit; pub mod compilation_unit;
pub mod constructor; pub mod constructor;
mod diagnostic_factories;
pub mod double_literal; pub mod double_literal;
pub mod expression; pub mod expression;
pub mod expression_statement; pub mod expression_statement;
@ -14,6 +15,7 @@ pub mod fqn_context;
pub mod fqn_util; pub mod fqn_util;
pub mod function; pub mod function;
pub mod generic_parameter; pub mod generic_parameter;
mod helpers;
pub mod identifier; pub mod identifier;
pub mod integer_literal; pub mod integer_literal;
pub mod ir_builder; pub mod ir_builder;

View File

@ -8,8 +8,10 @@ use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable; use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -40,21 +42,49 @@ impl NegativeExpression {
&mut self.operand &mut self.operand
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
self.operand.init_scopes(symbol_table, container_scope);
}
pub fn check_field_initializer_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
self.operand
.check_field_initializer_names(symbol_table, class_symbol)
}
pub fn check_constructor_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
self.operand
.check_constructor_local_names(symbol_table, class_symbol)
}
pub fn check_method_local_names(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
self.operand
.check_method_local_names(symbol_table, class_symbol)
}
pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
self.operand.check_static_fn_local_names(symbol_table)
}
pub fn type_check(
&mut self, &mut self,
symbol_table: &mut SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
self.operand.gather_declared_names(symbol_table) self.operand.type_check(symbol_table, types_table)?;
}
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { let type_info = self.operand.type_info(symbol_table, types_table);
self.operand.check_name_usages(symbol_table)
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.operand.type_check(symbol_table)?;
let type_info = self.operand.type_info();
if type_info.can_negate() { if type_info.can_negate() {
self.type_info = Some(type_info.negate_result()); self.type_info = Some(type_info.negate_result());
Ok(()) Ok(())
@ -67,10 +97,15 @@ impl NegativeExpression {
} }
} }
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) -> IrExpression { pub fn to_ir(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrExpression {
let operand_as_ir = self let operand_as_ir = self
.operand .operand
.to_ir_expression(builder, symbol_table) .to_ir_expression(builder, symbol_table, types_table)
.expect("Attempt to negate non-value expression"); .expect("Attempt to negate non-value expression");
match operand_as_ir { match operand_as_ir {

View File

@ -1,17 +1,16 @@
use crate::ast::type_use::TypeUse; use crate::ast::type_use::TypeUse;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::SymbolTable;
use std::cell::RefCell; use crate::types_table::TypesTable;
use std::rc::Rc; use std::rc::Rc;
pub struct Parameter { pub struct Parameter {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
type_use: TypeUse, type_use: TypeUse,
parameter_symbol: Option<Rc<RefCell<ParameterSymbol>>>, scope_id: Option<usize>,
} }
impl Parameter { impl Parameter {
@ -24,71 +23,51 @@ impl Parameter {
declared_name: declared_name.into(), declared_name: declared_name.into(),
declared_name_source_range, declared_name_source_range,
type_use, type_use,
parameter_symbol: None, scope_id: None,
} }
} }
pub fn gather_declared_names( pub fn declared_name(&self) -> &str {
&mut self, &self.declared_name
symbol_table: &mut SymbolTable, }
) -> Result<Rc<RefCell<ParameterSymbol>>, Vec<Diagnostic>> {
// insert parameter symbol pub fn scope_id(&self) -> usize {
let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new( self.scope_id.unwrap()
}
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
self.scope_id = Some(container_scope);
self.type_use.init_scopes(symbol_table, container_scope);
}
pub fn make_symbol(&self) -> ParameterSymbol {
ParameterSymbol::new(
&self.declared_name, &self.declared_name,
self.declared_name_source_range.clone(), self.declared_name_source_range.clone(),
)); self.scope_id.unwrap(),
match insert_result { )
Ok(parameter_symbol) => {
self.parameter_symbol = Some(parameter_symbol.clone());
}
Err(symbol_insert_error) => {
return match symbol_insert_error {
SymbolInsertError::AlreadyDeclared(already_declared) => {
Err(vec![Diagnostic::new(
&format!(
"Parameter {} already declared.",
already_declared.symbol().borrow().declared_name()
),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
)])
}
};
}
}
// type use
self.type_use.gather_declared_names(symbol_table)?;
Ok(self.parameter_symbol.clone().unwrap())
} }
pub fn check_name_usages( pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
self.type_use.check_names(symbol_table)
}
pub fn gather_types_into(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) {
let type_info = self.type_use.type_info(symbol_table, types_table).clone();
let parameter_symbol = symbol_table
.get_parameter_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
.unwrap();
types_table
.parameter_types_mut()
.insert(parameter_symbol, type_info);
}
pub fn type_check(
&mut self, &mut self,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
class_context: Option<&Rc<RefCell<ClassSymbol>>>, types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
// check the type use self.type_use.type_check(symbol_table, types_table)?;
self.type_use
.check_name_usages(symbol_table, class_context)?;
// set type on parameter symbol
self.parameter_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_type_info(self.type_use.type_info().clone());
Ok(()) Ok(())
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.type_use.type_check(symbol_table)?;
Ok(())
}
pub fn parameter_symbol(&self) -> &Rc<RefCell<ParameterSymbol>> {
self.parameter_symbol
.as_ref()
.expect("parameter symbol not initialized")
}
} }

View File

@ -3,8 +3,10 @@ use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::ast::let_statement::LetStatement; use crate::ast::let_statement::LetStatement;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
pub enum Statement { pub enum Statement {
Let(LetStatement), Let(LetStatement),
@ -13,42 +15,84 @@ pub enum Statement {
} }
impl Statement { impl Statement {
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
match self { match self {
Statement::Let(let_statement) => let_statement.gather_declared_names(symbol_table), Statement::Let(let_statement) => {
let_statement.init_scopes(symbol_table, container_scope);
}
Statement::Expression(expression_statement) => { Statement::Expression(expression_statement) => {
expression_statement.gather_declared_names(symbol_table) expression_statement.init_scopes(symbol_table, container_scope);
} }
Statement::Assign(assign_statement) => { Statement::Assign(assign_statement) => {
assign_statement.gather_declared_names(symbol_table) assign_statement.init_scopes(symbol_table, container_scope);
} }
} }
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn analyze_constructor_local_names(
&self,
symbol_table: &mut SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
match self { match self {
Statement::Let(let_statement) => let_statement.check_name_usages(symbol_table), Statement::Let(let_statement) => {
Statement::Expression(expression_statement) => { let_statement.analyze_constructor_local_names(symbol_table, class_symbol)
expression_statement.check_name_usages(symbol_table) }
Statement::Expression(expression_statement) => {
expression_statement.check_constructor_local_names(symbol_table, class_symbol)
}
Statement::Assign(assign_statement) => {
assign_statement.check_constructor_local_names(symbol_table, class_symbol)
}
}
}
pub fn analyze_method_local_names(
&self,
symbol_table: &mut SymbolTable,
class_symbol: &ClassSymbol,
) -> Vec<Diagnostic> {
match self {
Statement::Let(let_statement) => {
let_statement.analyze_method_local_names(symbol_table, class_symbol)
}
Statement::Expression(expression_statement) => {
expression_statement.check_method_local_names(symbol_table, class_symbol)
}
Statement::Assign(assign_statement) => {
assign_statement.check_method_local_names(symbol_table, class_symbol)
}
}
}
pub fn analyze_static_fn_local_names(&self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
match self {
Statement::Let(let_statement) => {
let_statement.analyze_static_fn_local_names(symbol_table)
}
Statement::Expression(expression_statement) => {
expression_statement.check_static_fn_local_names(symbol_table)
}
Statement::Assign(assign_statement) => {
assign_statement.check_static_fn_local_names(symbol_table)
} }
Statement::Assign(assign_statement) => assign_statement.check_name_usages(symbol_table),
} }
} }
pub fn type_check( pub fn type_check(
&mut self, &mut self,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
types_table: &mut TypesTable,
must_return_type_info: Option<&TypeInfo>, must_return_type_info: Option<&TypeInfo>,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
match self { match self {
Statement::Let(let_statement) => let_statement.type_check(symbol_table), Statement::Let(let_statement) => let_statement.type_check(symbol_table, types_table),
Statement::Expression(expression_statement) => { Statement::Expression(expression_statement) => {
expression_statement.type_check(symbol_table, must_return_type_info) expression_statement.type_check(symbol_table, types_table, must_return_type_info)
}
Statement::Assign(assign_statement) => {
assign_statement.type_check(symbol_table, types_table)
} }
Statement::Assign(assign_statement) => assign_statement.type_check(symbol_table),
} }
} }
@ -56,17 +100,18 @@ impl Statement {
&self, &self,
builder: &mut IrBuilder, builder: &mut IrBuilder,
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
types_table: &TypesTable,
should_return_value: bool, should_return_value: bool,
) { ) {
match self { match self {
Statement::Let(let_statement) => { Statement::Let(let_statement) => {
let_statement.to_ir(builder, symbol_table); let_statement.to_ir(builder, symbol_table, types_table);
} }
Statement::Expression(expression_statement) => { Statement::Expression(expression_statement) => {
expression_statement.to_ir(builder, symbol_table, should_return_value); expression_statement.to_ir(builder, symbol_table, types_table, should_return_value);
} }
Statement::Assign(assign_statement) => { Statement::Assign(assign_statement) => {
assign_statement.to_ir(builder, symbol_table); assign_statement.to_ir(builder, symbol_table, types_table);
} }
} }
} }

View File

@ -1,19 +1,19 @@
use crate::ast::diagnostic_factories::symbol_not_found;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::error_codes::INCORRECT_GENERIC_ARGUMENTS; use crate::error_codes::INCORRECT_GENERIC_ARGUMENTS;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::type_symbol::TypeSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics}; use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics};
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub struct TypeUse { pub struct TypeUse {
declared_name: String, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
generic_arguments: Vec<TypeUse>, generic_arguments: Vec<TypeUse>,
scope_id: Option<usize>, scope_id: Option<usize>,
type_info: Option<TypeInfo>,
} }
impl TypeUse { impl TypeUse {
@ -27,7 +27,6 @@ impl TypeUse {
declared_name_source_range, declared_name_source_range,
generic_arguments, generic_arguments,
scope_id: None, scope_id: None,
type_info: None,
} }
} }
@ -35,63 +34,88 @@ impl TypeUse {
&self.declared_name &self.declared_name
} }
pub fn gather_declared_names( pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
&mut self, self.scope_id = Some(container_scope);
symbol_table: &mut SymbolTable, for type_use in &mut self.generic_arguments {
) -> Result<(), Vec<Diagnostic>> { type_use.init_scopes(symbol_table, container_scope);
self.scope_id = Some(symbol_table.current_scope_id());
let mut inner_diagnostics: Vec<Diagnostic> = vec![];
for generic_argument in &mut self.generic_arguments {
handle_diagnostics!(
generic_argument.gather_declared_names(symbol_table),
inner_diagnostics
);
} }
diagnostics_result!(inner_diagnostics)
} }
pub fn check_name_usages( pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
&mut self, let mut diagnostics: Vec<Diagnostic> = Vec::new();
symbol_table: &SymbolTable,
class_context: Option<&Rc<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
if let Some(type_info) = TypeInfo::from_declared_name( // find this name
&self.declared_name, let maybe_type_symbol =
self.scope_id.unwrap(), symbol_table.find_type_symbol(self.scope_id.unwrap(), &self.declared_name);
symbol_table, if maybe_type_symbol.is_none() {
class_context, diagnostics.push(symbol_not_found(
) { self.declared_name(),
self.type_info = Some(type_info); &self.declared_name_source_range,
} else { ));
let diagnostic = Diagnostic::new(
&format!("Unable to resolve symbol {}", self.declared_name),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
)
.with_reporter(file!(), line!());
diagnostics.push(diagnostic);
} }
for generic_argument in &mut self.generic_arguments { // check generic args
for type_use in &self.generic_arguments {
diagnostics.append(&mut type_use.check_names(symbol_table));
}
diagnostics
}
pub fn gather_types(
&self,
symbol_table: &SymbolTable,
types_table: &mut TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = Vec::new();
for type_use in &self.generic_arguments {
handle_diagnostics!( handle_diagnostics!(
generic_argument.check_name_usages(symbol_table, class_context), type_use.gather_types(symbol_table, types_table),
diagnostics diagnostics
); );
} }
diagnostics_result!(diagnostics) diagnostics_result!(diagnostics)
} }
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn type_info<'a>(
&self,
symbol_table: &SymbolTable,
types_table: &'a TypesTable,
) -> &'a TypeInfo {
let type_symbol = symbol_table
.find_type_symbol(self.scope_id.unwrap(), self.declared_name())
.unwrap();
match type_symbol {
TypeSymbol::Class(class_symbol) => {
types_table
.class_types()
.get(&class_symbol)
.expect(&format!(
"Could not get TypeInfo for {}",
self.declared_name
))
}
TypeSymbol::GenericParameter(generic_parameter_symbol) => types_table
.generic_parameter_types()
.get(&generic_parameter_symbol)
.expect(&format!(
"Could not get TypeInfo for {}",
self.declared_name
)),
}
}
pub fn type_check(
&mut self,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![]; let mut diagnostics: Vec<Diagnostic> = vec![];
match self.type_info() { match self.type_info(symbol_table, types_table) {
TypeInfo::ClassInstance(class_symbol) => { TypeInfo::Class(class_symbol) => {
// check number of params/args match // check number of params/args match
let borrowed_class_symbol = class_symbol.borrow(); let generic_parameters = class_symbol.generic_parameters();
let generic_parameters = borrowed_class_symbol.generic_parameters();
if generic_parameters.len() != self.generic_arguments.len() { if generic_parameters.len() != self.generic_arguments.len() {
let diagnostic = Diagnostic::new( let diagnostic = Diagnostic::new(
&format!( &format!(
@ -110,12 +134,12 @@ impl TypeUse {
maybe_return_diagnostics!(diagnostics); maybe_return_diagnostics!(diagnostics);
// check that each arg is assignable to the param's extends // check that each arg is assignable to the param's extends
for i in 0..self.generic_arguments.len() { // for i in 0..self.generic_arguments.len() {
let generic_parameter_symbol = generic_parameters[i].borrow(); // let generic_parameter_symbol = &generic_parameters[i];
if generic_parameter_symbol.extends().len() > 0 { // if generic_parameter_symbol.extends().len() > 0 {
unimplemented!("Generic extends not implemented yet.") // unimplemented!("Generic extends not implemented yet.")
} // }
} // }
} }
_ => { _ => {
// cannot extend a non-class type (except for Any) // cannot extend a non-class type (except for Any)
@ -123,7 +147,7 @@ impl TypeUse {
let diagnostic = Diagnostic::new( let diagnostic = Diagnostic::new(
&format!( &format!(
"Type {} does not accept generic arguments.", "Type {} does not accept generic arguments.",
self.type_info() self.type_info(symbol_table, types_table)
), ),
self.declared_name_source_range.start(), self.declared_name_source_range.start(),
self.declared_name_source_range.end(), self.declared_name_source_range.end(),
@ -137,15 +161,14 @@ impl TypeUse {
// recurse on generic arguments // recurse on generic arguments
for generic_argument in &mut self.generic_arguments { for generic_argument in &mut self.generic_arguments {
handle_diagnostics!(generic_argument.type_check(_symbol_table), diagnostics); handle_diagnostics!(
generic_argument.type_check(symbol_table, types_table),
diagnostics
);
} }
diagnostics_result!(diagnostics) diagnostics_result!(diagnostics)
} }
pub fn type_info(&self) -> &TypeInfo {
self.type_info.as_ref().unwrap()
}
} }
#[cfg(test)] #[cfg(test)]
@ -153,11 +176,14 @@ mod tests {
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::parser::parse_compilation_unit; use crate::parser::parse_compilation_unit;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
#[test] #[test]
fn type_check_generics() -> Result<(), Vec<Diagnostic>> { fn type_check_generics() -> Result<(), Vec<Diagnostic>> {
let mut compilation_unit = parse_compilation_unit( let mut compilation_unit = parse_compilation_unit(
" "
class String end
class Foo<T> class Foo<T>
ctor(t: T) end ctor(t: T) end
end end
@ -167,10 +193,13 @@ mod tests {
)?; )?;
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
let mut types_table = TypesTable::new();
compilation_unit.gather_declared_names(&mut symbol_table)?; compilation_unit.init_scopes(&mut symbol_table);
compilation_unit.check_name_usages(&mut symbol_table)?; compilation_unit.gather_symbols_into(&mut symbol_table)?;
compilation_unit.type_check(&mut symbol_table)?; compilation_unit.check_names(&mut symbol_table)?;
compilation_unit.gather_types_into(&symbol_table, &mut types_table)?;
compilation_unit.type_check(&mut symbol_table, &mut types_table)?;
Ok(()) Ok(())
} }

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_export]
macro_rules! maybe_return_diagnostics { macro_rules! maybe_return_diagnostics {
( $diagnostics: expr ) => { ( $diagnostics: expr ) => {
@ -41,3 +62,24 @@ macro_rules! diagnostics_result {
} }
}; };
} }
#[macro_export]
macro_rules! ok_or_err_diagnostics {
( $to_return: expr, $diagnostics: expr ) => {
if $diagnostics.is_empty() {
Ok($to_return)
} else {
Err($diagnostics)
}
};
}
#[macro_export]
macro_rules! push_ok_or_push_errs {
( $result: expr, $oks: expr, $errs: expr ) => {
match $result {
Ok(inner) => $oks.push(inner),
Err(mut errs) => $errs.append(&mut errs),
}
};
}

View File

@ -1,5 +1,6 @@
pub type ErrorCode = usize; pub type ErrorCode = usize;
pub const SYMBOL_NOT_FOUND: ErrorCode = 13;
pub const SYMBOL_ALREADY_DECLARED: ErrorCode = 14; pub const SYMBOL_ALREADY_DECLARED: ErrorCode = 14;
pub const BINARY_INCOMPATIBLE_TYPES: ErrorCode = 15; pub const BINARY_INCOMPATIBLE_TYPES: ErrorCode = 15;
pub const ASSIGN_MISMATCHED_TYPES: ErrorCode = 16; pub const ASSIGN_MISMATCHED_TYPES: ErrorCode = 16;
@ -7,3 +8,12 @@ pub const ASSIGN_NO_L_VALUE: ErrorCode = 17;
pub const ASSIGN_LHS_IMMUTABLE: ErrorCode = 18; pub const ASSIGN_LHS_IMMUTABLE: ErrorCode = 18;
pub const INCORRECT_GENERIC_ARGUMENTS: ErrorCode = 19; pub const INCORRECT_GENERIC_ARGUMENTS: ErrorCode = 19;
pub const GENERIC_ARGUMENT_TYPE_MISMATCH: ErrorCode = 20; pub const GENERIC_ARGUMENT_TYPE_MISMATCH: ErrorCode = 20;
pub const FIELD_MULTIPLE_INIT: ErrorCode = 21;
pub const FIELD_UNINIT: ErrorCode = 22;
pub const SELF_FIELD_USED_IN_INIT: ErrorCode = 23;
pub const SELF_METHOD_USED_IN_INIT: ErrorCode = 24;
pub const SELF_CONSTRUCTOR_USED_IN_INIT: ErrorCode = 25;
pub const OUTER_CLASS_FIELD_USED_IN_INIT: ErrorCode = 26;
pub const OUTER_CLASS_METHOD_USED_IN_INIT: ErrorCode = 27;
pub const FIELD_NO_TYPE_OR_INIT: ErrorCode = 28;
pub const CLASS_NO_CONSTRUCTOR: ErrorCode = 29;

View File

@ -0,0 +1,48 @@
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo;
use crate::types_table::TypesTable;
use std::rc::Rc;
fn create_simple_primitive(name: &str) -> ClassSymbol {
ClassSymbol::new(
&Rc::from(name),
None,
vec![Rc::from(name)],
true,
0, // global
vec![],
None,
vec![],
vec![],
)
}
pub fn insert_intrinsic_symbols(symbol_table: &mut SymbolTable) {
let primitives = [
create_simple_primitive("Int"),
create_simple_primitive("Double"),
create_simple_primitive("String"),
create_simple_primitive("Void"),
create_simple_primitive("Any"),
];
for primitive in primitives {
symbol_table.insert_class_symbol(Rc::new(primitive));
}
}
pub fn insert_intrinsic_types(symbol_table: &SymbolTable, types_table: &mut TypesTable) {
let primitives = ["Int", "Double", "String", "Void", "Any"];
for primitive in primitives {
let symbol = symbol_table.get_class_symbol(0, primitive).unwrap().clone();
let type_info = match primitive {
"Int" => TypeInfo::Integer,
"Double" => TypeInfo::Double,
"String" => TypeInfo::String,
"Void" => TypeInfo::Void,
"Any" => TypeInfo::Any,
_ => unreachable!(),
};
types_table.class_types_mut().insert(symbol, type_info);
}
}

View File

@ -121,11 +121,13 @@ impl Display for IrBlock {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::diagnostic::Diagnostic;
use crate::parser::parse_compilation_unit; use crate::parser::parse_compilation_unit;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
#[test] #[test]
fn overlapping_assignments_bug_when_k_2() { fn overlapping_assignments_bug_when_k_2() -> Result<(), Vec<Diagnostic>> {
let mut compilation_unit = parse_compilation_unit( let mut compilation_unit = parse_compilation_unit(
" "
fn main() fn main()
@ -135,18 +137,16 @@ mod tests {
let x = a + b + c let x = a + b + c
end end
", ",
) )?;
.unwrap();
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
compilation_unit let mut types_table = TypesTable::new();
.gather_declared_names(&mut symbol_table)
.expect("gather failed"); compilation_unit.init_scopes(&mut symbol_table);
compilation_unit compilation_unit.gather_symbols_into(&mut symbol_table)?;
.check_name_usages(&mut symbol_table) compilation_unit.check_names(&mut symbol_table)?;
.expect("name check failed"); compilation_unit.gather_types_into(&symbol_table, &mut types_table)?;
compilation_unit compilation_unit.type_check(&symbol_table, &mut types_table)?;
.type_check(&mut symbol_table)
.expect("type check failed");
let main = compilation_unit let main = compilation_unit
.functions() .functions()
@ -154,8 +154,10 @@ mod tests {
.find(|f| f.declared_name() == "main") .find(|f| f.declared_name() == "main")
.unwrap(); .unwrap();
let mut main_ir = main.to_ir(&symbol_table, None); let mut main_ir = main.to_ir(&symbol_table, &types_table, None);
let (register_assignments, _) = main_ir.assign_registers(2); let (register_assignments, _) = main_ir.assign_registers(2);
assert_eq!(register_assignments.len(), 4); assert_eq!(register_assignments.len(), 4);
Ok(())
} }
} }

View File

@ -51,9 +51,9 @@ impl IrField {
TypeInfo::Integer => VmTypeInfo::Int, TypeInfo::Integer => VmTypeInfo::Int,
TypeInfo::Double => VmTypeInfo::Double, TypeInfo::Double => VmTypeInfo::Double,
TypeInfo::String => VmTypeInfo::String, TypeInfo::String => VmTypeInfo::String,
TypeInfo::ClassInstance(class_symbol) => VmTypeInfo::ClassInstance( TypeInfo::Class(class_symbol) => {
fqn_parts_to_string(class_symbol.borrow().fqn_parts()).into(), VmTypeInfo::ClassInstance(fqn_parts_to_string(class_symbol.fqn_parts()).into())
), }
TypeInfo::GenericType(_) => VmTypeInfo::Any, TypeInfo::GenericType(_) => VmTypeInfo::Any,
_ => panic!(), _ => panic!(),
}, },

View File

@ -21,7 +21,7 @@ pub struct IrFunction {
impl IrFunction { impl IrFunction {
pub fn new( pub fn new(
fqn: Rc<str>, fqn: Rc<str>,
parameters: &[Rc<IrParameter>], parameters: Vec<Rc<IrParameter>>,
return_type_info: &TypeInfo, return_type_info: &TypeInfo,
entry: Rc<RefCell<IrBlock>>, entry: Rc<RefCell<IrBlock>>,
) -> Self { ) -> Self {

View File

@ -2,12 +2,14 @@ pub mod ast;
pub mod constants_table; pub mod constants_table;
pub mod diagnostic; pub mod diagnostic;
pub mod error_codes; pub mod error_codes;
pub mod intrinsics;
pub mod ir; pub mod ir;
pub mod lexer; pub mod lexer;
pub mod parser; pub mod parser;
mod scope; pub mod scope;
pub mod source_range; pub mod source_range;
pub mod symbol; pub mod symbol;
pub mod symbol_table; pub mod symbol_table;
pub mod token; pub mod token;
pub mod type_info; pub mod type_info;
pub mod types_table;

View File

@ -1,12 +1,12 @@
use crate::symbol::variable_symbol::VariableSymbol; use crate::symbol::variable_symbol::VariableSymbol;
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::rc::Rc; use std::rc::Rc;
pub struct BlockScope { pub struct BlockScope {
debug_name: String, debug_name: String,
parent_id: usize, parent_id: usize,
variable_symbols: HashMap<Rc<str>, Rc<RefCell<VariableSymbol>>>, variable_symbols: HashMap<Rc<str>, Rc<VariableSymbol>>,
} }
impl BlockScope { impl BlockScope {
@ -18,11 +18,11 @@ impl BlockScope {
} }
} }
pub fn variable_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<VariableSymbol>>> { pub fn variable_symbols(&self) -> &HashMap<Rc<str>, Rc<VariableSymbol>> {
&self.variable_symbols &self.variable_symbols
} }
pub fn variable_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<VariableSymbol>>> { pub fn variable_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<VariableSymbol>> {
&mut self.variable_symbols &mut self.variable_symbols
} }
@ -30,3 +30,9 @@ impl BlockScope {
self.parent_id self.parent_id
} }
} }
impl Debug for BlockScope {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "BlockScope({}, {})", self.debug_name, self.parent_id)
}
}

View File

@ -0,0 +1,71 @@
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
pub struct ClassBodyScope {
debug_name: String,
parent_id: usize,
class_symbols: HashMap<Rc<str>, Rc<ClassSymbol>>,
field_symbols: HashMap<Rc<str>, Rc<FieldSymbol>>,
function_symbols: HashMap<Rc<str>, Rc<FunctionSymbol>>,
constructor_symbol: Option<Rc<ConstructorSymbol>>,
}
impl ClassBodyScope {
pub fn new(debug_name: &str, parent_id: usize) -> Self {
Self {
debug_name: debug_name.into(),
parent_id,
class_symbols: HashMap::new(),
field_symbols: HashMap::new(),
function_symbols: HashMap::new(),
constructor_symbol: None,
}
}
pub fn class_symbols(&self) -> &HashMap<Rc<str>, Rc<ClassSymbol>> {
&self.class_symbols
}
pub fn class_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<ClassSymbol>> {
&mut self.class_symbols
}
pub fn field_symbols(&self) -> &HashMap<Rc<str>, Rc<FieldSymbol>> {
&self.field_symbols
}
pub fn field_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<FieldSymbol>> {
&mut self.field_symbols
}
pub fn function_symbols(&self) -> &HashMap<Rc<str>, Rc<FunctionSymbol>> {
&self.function_symbols
}
pub fn function_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<FunctionSymbol>> {
&mut self.function_symbols
}
pub fn constructor_symbol(&self) -> Option<&Rc<ConstructorSymbol>> {
self.constructor_symbol.as_ref()
}
pub fn constructor_symbol_mut(&mut self) -> &mut Option<Rc<ConstructorSymbol>> {
&mut self.constructor_symbol
}
pub fn parent_id(&self) -> usize {
self.parent_id
}
}
impl Debug for ClassBodyScope {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ClassBodyScope({}, {})", self.debug_name, self.parent_id)
}
}

View File

@ -1,20 +1,12 @@
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::rc::Rc; use std::rc::Rc;
pub struct ClassScope { pub struct ClassScope {
debug_name: String, debug_name: String,
parent_id: usize, parent_id: usize,
generic_parameter_symbols: HashMap<Rc<str>, Rc<RefCell<GenericParameterSymbol>>>, generic_parameter_symbols: HashMap<Rc<str>, Rc<GenericParameterSymbol>>,
class_symbols: HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>,
field_symbols: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>,
function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
} }
impl ClassScope { impl ClassScope {
@ -23,58 +15,26 @@ impl ClassScope {
debug_name: debug_name.into(), debug_name: debug_name.into(),
parent_id, parent_id,
generic_parameter_symbols: HashMap::new(), generic_parameter_symbols: HashMap::new(),
class_symbols: HashMap::new(),
field_symbols: HashMap::new(),
function_symbols: HashMap::new(),
constructor_symbol: None,
} }
} }
pub fn generic_parameter_symbols(
&self,
) -> &HashMap<Rc<str>, Rc<RefCell<GenericParameterSymbol>>> {
&self.generic_parameter_symbols
}
pub fn generic_parameter_symbols_mut(
&mut self,
) -> &mut HashMap<Rc<str>, Rc<RefCell<GenericParameterSymbol>>> {
&mut self.generic_parameter_symbols
}
pub fn class_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>> {
&self.class_symbols
}
pub fn class_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>> {
&mut self.class_symbols
}
pub fn field_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> {
&self.field_symbols
}
pub fn field_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> {
&mut self.field_symbols
}
pub fn function_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>> {
&self.function_symbols
}
pub fn function_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>> {
&mut self.function_symbols
}
pub fn constructor_symbol(&self) -> Option<&Rc<RefCell<ConstructorSymbol>>> {
self.constructor_symbol.as_ref()
}
pub fn constructor_symbol_mut(&mut self) -> &mut Option<Rc<RefCell<ConstructorSymbol>>> {
&mut self.constructor_symbol
}
pub fn parent_id(&self) -> usize { pub fn parent_id(&self) -> usize {
self.parent_id self.parent_id
} }
pub fn generic_parameter_symbols(&self) -> &HashMap<Rc<str>, Rc<GenericParameterSymbol>> {
&self.generic_parameter_symbols
}
pub fn generic_parameter_symbols_mut(
&mut self,
) -> &mut HashMap<Rc<str>, Rc<GenericParameterSymbol>> {
&mut self.generic_parameter_symbols
}
}
impl Debug for ClassScope {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ClassScope({}, {})", self.debug_name, self.parent_id)
}
} }

View File

@ -1,12 +1,12 @@
use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::parameter_symbol::ParameterSymbol;
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::rc::Rc; use std::rc::Rc;
pub struct FunctionScope { pub struct FunctionScope {
debug_name: String, debug_name: String,
parent_id: usize, parent_id: usize,
parameter_symbols: HashMap<Rc<str>, Rc<RefCell<ParameterSymbol>>>, parameter_symbols: HashMap<Rc<str>, Rc<ParameterSymbol>>,
} }
impl FunctionScope { impl FunctionScope {
@ -18,11 +18,11 @@ impl FunctionScope {
} }
} }
pub fn parameter_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<ParameterSymbol>>> { pub fn parameter_symbols(&self) -> &HashMap<Rc<str>, Rc<ParameterSymbol>> {
&self.parameter_symbols &self.parameter_symbols
} }
pub fn parameter_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<ParameterSymbol>>> { pub fn parameter_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<ParameterSymbol>> {
&mut self.parameter_symbols &mut self.parameter_symbols
} }
@ -30,3 +30,9 @@ impl FunctionScope {
self.parent_id self.parent_id
} }
} }
impl Debug for FunctionScope {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "FunctionScope({}, {})", self.debug_name, self.parent_id)
}
}

View File

@ -1,9 +1,12 @@
use crate::scope::block_scope::BlockScope; use crate::scope::block_scope::BlockScope;
use crate::scope::class_body_scope::ClassBodyScope;
use crate::scope::class_scope::ClassScope; use crate::scope::class_scope::ClassScope;
use crate::scope::function_scope::FunctionScope; use crate::scope::function_scope::FunctionScope;
use crate::scope::module_scope::ModuleScope; use crate::scope::module_scope::ModuleScope;
use std::fmt::{Debug, Formatter};
pub mod block_scope; pub mod block_scope;
pub mod class_body_scope;
pub mod class_scope; pub mod class_scope;
pub mod function_scope; pub mod function_scope;
pub mod module_scope; pub mod module_scope;
@ -11,6 +14,7 @@ pub mod module_scope;
pub enum Scope { pub enum Scope {
Module(ModuleScope), Module(ModuleScope),
Class(ClassScope), Class(ClassScope),
ClassBody(ClassBodyScope),
Function(FunctionScope), Function(FunctionScope),
Block(BlockScope), Block(BlockScope),
} }
@ -20,8 +24,31 @@ impl Scope {
match self { match self {
Scope::Module(module_scope) => module_scope.parent_id(), Scope::Module(module_scope) => module_scope.parent_id(),
Scope::Class(class_scope) => Some(class_scope.parent_id()), Scope::Class(class_scope) => Some(class_scope.parent_id()),
Scope::ClassBody(class_body_scope) => Some(class_body_scope.parent_id()),
Scope::Function(function_scope) => Some(function_scope.parent_id()), Scope::Function(function_scope) => Some(function_scope.parent_id()),
Scope::Block(block_scope) => Some(block_scope.parent_id()), Scope::Block(block_scope) => Some(block_scope.parent_id()),
} }
} }
} }
impl Debug for Scope {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Scope::Module(module_scope) => {
write!(f, "{:?}", module_scope)
}
Scope::Class(class_scope) => {
write!(f, "{:?}", class_scope)
}
Scope::ClassBody(class_body_scope) => {
write!(f, "{:?}", class_body_scope)
}
Scope::Function(function_scope) => {
write!(f, "{:?}", function_scope)
}
Scope::Block(block_scope) => {
write!(f, "{:?}", block_scope)
}
}
}
}

View File

@ -1,14 +1,14 @@
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::rc::Rc; use std::rc::Rc;
pub struct ModuleScope { pub struct ModuleScope {
debug_name: String, debug_name: String,
parent_id: Option<usize>, parent_id: Option<usize>,
class_symbols: HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>, class_symbols: HashMap<Rc<str>, Rc<ClassSymbol>>,
function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>, function_symbols: HashMap<Rc<str>, Rc<FunctionSymbol>>,
} }
impl ModuleScope { impl ModuleScope {
@ -21,19 +21,19 @@ impl ModuleScope {
} }
} }
pub fn class_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>> { pub fn class_symbols(&self) -> &HashMap<Rc<str>, Rc<ClassSymbol>> {
&self.class_symbols &self.class_symbols
} }
pub fn class_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>> { pub fn class_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<ClassSymbol>> {
&mut self.class_symbols &mut self.class_symbols
} }
pub fn function_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>> { pub fn function_symbols(&self) -> &HashMap<Rc<str>, Rc<FunctionSymbol>> {
&self.function_symbols &self.function_symbols
} }
pub fn function_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>> { pub fn function_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<FunctionSymbol>> {
&mut self.function_symbols &mut self.function_symbols
} }
@ -41,3 +41,9 @@ impl ModuleScope {
self.parent_id self.parent_id
} }
} }
impl Debug for ModuleScope {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ModuleScope({}, {:?})", self.debug_name, self.parent_id)
}
}

View File

@ -1,34 +1,19 @@
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub enum CallableSymbol { pub enum CallableSymbol {
Function(Rc<RefCell<FunctionSymbol>>), Function(Rc<FunctionSymbol>),
Class(Rc<RefCell<ClassSymbol>>), Constructor(Rc<ConstructorSymbol>),
} }
impl CallableSymbol { impl CallableSymbol {
pub fn return_type_info(&self) -> TypeInfo { pub fn parameters(&self) -> Vec<Rc<ParameterSymbol>> {
match self { match self {
CallableSymbol::Function(function) => function.borrow().return_type_info().clone(), CallableSymbol::Function(function_symbol) => function_symbol.parameters().to_vec(),
CallableSymbol::Class(class_symbol) => TypeInfo::ClassInstance(class_symbol.clone()), CallableSymbol::Constructor(constructor_symbol) => {
} constructor_symbol.parameters().to_vec()
}
pub fn parameters(&self) -> Vec<Rc<RefCell<ParameterSymbol>>> {
match self {
CallableSymbol::Function(function_symbol) => {
function_symbol.borrow().parameters().to_vec()
}
CallableSymbol::Class(class_symbol) => {
if let Some(constructor_symbol) = class_symbol.borrow().constructor_symbol() {
constructor_symbol.borrow().parameters().to_vec()
} else {
vec![]
}
} }
} }
} }

View File

@ -1,89 +1,115 @@
use crate::ast::fqn_util::fqn_parts_to_string;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol;
use crate::symbol::constructor_symbol::ConstructorSymbol; use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::rc::Rc; use std::rc::Rc;
pub struct ClassSymbol { pub struct ClassSymbol {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: Option<SourceRange>,
fqn_parts: Vec<Rc<str>>, fqn_parts: Vec<Rc<str>>,
is_extern: bool, is_extern: bool,
generic_parameters: Vec<Rc<RefCell<GenericParameterSymbol>>>, scope_id: usize,
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>, generic_parameters: Vec<Rc<GenericParameterSymbol>>,
fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>, constructor_symbol: Option<Rc<ConstructorSymbol>>,
functions: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>, fields: HashMap<Rc<str>, Rc<FieldSymbol>>,
functions: HashMap<Rc<str>, Rc<FunctionSymbol>>,
} }
impl ClassSymbol { impl ClassSymbol {
pub fn new( pub fn new(
declared_name: &Rc<str>, declared_name: &Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: Option<SourceRange>,
fqn_parts: Vec<Rc<str>>, fqn_parts: Vec<Rc<str>>,
is_extern: bool, is_extern: bool,
scope_id: usize,
generic_parameters: Vec<Rc<GenericParameterSymbol>>,
constructor_symbol: Option<Rc<ConstructorSymbol>>,
fields: Vec<Rc<FieldSymbol>>,
functions: Vec<Rc<FunctionSymbol>>,
) -> Self { ) -> Self {
Self { Self {
declared_name: declared_name.clone(), declared_name: declared_name.clone(),
declared_name_source_range, declared_name_source_range,
fqn_parts, fqn_parts,
is_extern, is_extern,
generic_parameters: vec![], scope_id,
constructor_symbol: None, generic_parameters,
fields: HashMap::new(), constructor_symbol,
functions: HashMap::new(), fields: fields
.into_iter()
.map(|fs| (fs.declared_name_owned(), fs))
.collect(),
functions: functions
.into_iter()
.map(|fs| (fs.declared_name_owned(), fs))
.collect(),
} }
} }
pub fn declared_name(&self) -> &str {
&self.declared_name
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
pub fn declared_name_source_range(&self) -> Option<&SourceRange> {
self.declared_name_source_range.as_ref()
}
pub fn fqn_parts(&self) -> &[Rc<str>] { pub fn fqn_parts(&self) -> &[Rc<str>] {
&self.fqn_parts &self.fqn_parts
} }
pub fn set_generic_parameters( pub fn scope_id(&self) -> usize {
&mut self, self.scope_id
generic_parameters: Vec<Rc<RefCell<GenericParameterSymbol>>>,
) {
self.generic_parameters = generic_parameters;
} }
pub fn generic_parameters(&self) -> &[Rc<RefCell<GenericParameterSymbol>>] { pub fn generic_parameters(&self) -> &[Rc<GenericParameterSymbol>] {
&self.generic_parameters &self.generic_parameters
} }
pub fn set_constructor_symbol( pub fn constructor_symbol(&self) -> Option<&ConstructorSymbol> {
&mut self, self.constructor_symbol.as_ref().map(|s| s.as_ref())
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
) {
self.constructor_symbol = constructor_symbol;
} }
pub fn set_fields(&mut self, fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>) { pub fn constructor_symbol_owned(&self) -> Option<Rc<ConstructorSymbol>> {
self.fields = fields; self.constructor_symbol.as_ref().cloned()
} }
pub fn fields(&self) -> &HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> { pub fn fields(&self) -> &HashMap<Rc<str>, Rc<FieldSymbol>> {
&self.fields &self.fields
} }
/// A value of `None` indicates that there is no declared constructor for this class. pub fn functions(&self) -> &HashMap<Rc<str>, Rc<FunctionSymbol>> {
pub fn constructor_symbol(&self) -> Option<&Rc<RefCell<ConstructorSymbol>>> { &self.functions
self.constructor_symbol.as_ref()
} }
} }
impl Symbol for ClassSymbol { impl Eq for ClassSymbol {}
fn declared_name(&self) -> &str {
&self.declared_name
}
fn declared_name_owned(&self) -> Rc<str> { impl PartialEq for ClassSymbol {
self.declared_name.clone() fn eq(&self, other: &Self) -> bool {
} self.declared_name == other.declared_name && self.scope_id == other.scope_id
}
fn declared_name_source_range(&self) -> &SourceRange { }
&self.declared_name_source_range
impl Hash for ClassSymbol {
fn hash<H: Hasher>(&self, state: &mut H) {
self.declared_name.hash(state);
self.scope_id.hash(state);
}
}
impl Debug for ClassSymbol {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ClassSymbol({})", fqn_parts_to_string(&self.fqn_parts))
} }
} }

View File

@ -1,40 +1,58 @@
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol;
use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::parameter_symbol::ParameterSymbol;
use std::cell::RefCell; use std::hash::{Hash, Hasher};
use std::rc::Rc; use std::rc::Rc;
pub struct ConstructorSymbol { pub struct ConstructorSymbol {
ctor_keyword_source_range: SourceRange, keyword_source_range: SourceRange,
fqn_parts: Vec<Rc<str>>, fqn_parts: Vec<Rc<str>>,
is_extern: bool, is_extern: bool,
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>, is_default: bool,
scope_id: usize,
parameters: Vec<Rc<ParameterSymbol>>,
} }
impl ConstructorSymbol { impl ConstructorSymbol {
pub fn new( pub fn new(
ctor_keyword_source_range: SourceRange, keyword_source_range: &SourceRange,
fqn_parts: Vec<Rc<str>>, fqn_parts: Vec<Rc<str>>,
is_extern: bool, is_extern: bool,
is_default: bool,
scope_id: usize,
parameters: Vec<Rc<ParameterSymbol>>,
) -> Self { ) -> Self {
Self { Self {
ctor_keyword_source_range, keyword_source_range: keyword_source_range.clone(),
fqn_parts, fqn_parts,
is_extern, is_extern,
parameters: None, is_default,
scope_id,
parameters,
} }
} }
pub fn declared_name(&self) -> &str {
"ctor"
}
pub fn declared_name_owned(&self) -> Rc<str> {
Rc::from(self.declared_name())
}
pub fn declared_name_source_range(&self) -> &SourceRange {
&self.keyword_source_range
}
pub fn fqn_parts(&self) -> &[Rc<str>] { pub fn fqn_parts(&self) -> &[Rc<str>] {
&self.fqn_parts &self.fqn_parts
} }
pub fn set_parameters(&mut self, parameters: Vec<Rc<RefCell<ParameterSymbol>>>) { pub fn scope_id(&self) -> usize {
self.parameters = Some(parameters); self.scope_id
} }
pub fn parameters(&self) -> &[Rc<RefCell<ParameterSymbol>>] { pub fn parameters(&self) -> &[Rc<ParameterSymbol>] {
self.parameters.as_ref().unwrap() &self.parameters
} }
pub fn is_extern(&self) -> bool { pub fn is_extern(&self) -> bool {
@ -42,16 +60,16 @@ impl ConstructorSymbol {
} }
} }
impl Symbol for ConstructorSymbol { impl Eq for ConstructorSymbol {}
fn declared_name(&self) -> &str {
"ctor"
}
fn declared_name_owned(&self) -> Rc<str> { impl PartialEq for ConstructorSymbol {
Rc::from(self.declared_name()) fn eq(&self, other: &Self) -> bool {
} self.scope_id == other.scope_id
}
fn declared_name_source_range(&self) -> &SourceRange { }
&self.ctor_keyword_source_range
impl Hash for ConstructorSymbol {
fn hash<H: Hasher>(&self, state: &mut H) {
self.scope_id.hash(state);
} }
} }

View File

@ -1,111 +1,34 @@
use crate::ast::ir_builder::IrBuilder;
use crate::ast::ir_util::get_or_init_field_pointer_variable;
use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_read_field::IrReadField;
use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol::variable_symbol::VariableSymbol; use crate::symbol::variable_symbol::VariableSymbol;
use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub enum ExpressibleSymbol { pub enum ExpressibleSymbol {
Class(Rc<RefCell<ClassSymbol>>), Class(Rc<ClassSymbol>),
Field(Rc<RefCell<FieldSymbol>>), Field(Rc<FieldSymbol>),
Function(Rc<RefCell<FunctionSymbol>>), Function(Rc<FunctionSymbol>),
Parameter(Rc<RefCell<ParameterSymbol>>), Parameter(Rc<ParameterSymbol>),
Variable(Rc<RefCell<VariableSymbol>>), Variable(Rc<VariableSymbol>),
} }
impl ExpressibleSymbol { impl ExpressibleSymbol {
pub fn type_info(&self) -> TypeInfo { pub fn source_range(&self) -> Option<&SourceRange> {
match self { match self {
ExpressibleSymbol::Class(class_symbol) => TypeInfo::ClassInstance(class_symbol.clone()), ExpressibleSymbol::Class(class_symbol) => class_symbol.declared_name_source_range(),
ExpressibleSymbol::Field(field_symbol) => field_symbol.borrow().type_info().clone(), ExpressibleSymbol::Field(field_symbol) => {
Some(field_symbol.declared_name_source_range())
}
ExpressibleSymbol::Function(function_symbol) => { ExpressibleSymbol::Function(function_symbol) => {
TypeInfo::Function(function_symbol.clone()) // n.b. not the return type! Some(function_symbol.declared_name_source_range())
} }
ExpressibleSymbol::Parameter(parameter_symbol) => { ExpressibleSymbol::Parameter(parameter_symbol) => {
parameter_symbol.borrow().type_info().clone() Some(parameter_symbol.declared_name_source_range())
} }
ExpressibleSymbol::Variable(variable_symbol) => { ExpressibleSymbol::Variable(variable_symbol) => {
variable_symbol.borrow().type_info().clone() Some(variable_symbol.declared_name_source_range())
}
}
}
pub fn is_mut(&self) -> bool {
match self {
ExpressibleSymbol::Field(field_symbol) => field_symbol.borrow().is_mut(),
ExpressibleSymbol::Variable(variable_symbol) => variable_symbol.borrow().is_mut(),
_ => false,
}
}
pub fn source_range(&self) -> SourceRange {
match self {
ExpressibleSymbol::Class(class_symbol) => {
class_symbol.borrow().declared_name_source_range().clone()
}
ExpressibleSymbol::Field(field_symbol) => {
field_symbol.borrow().declared_name_source_range().clone()
}
ExpressibleSymbol::Function(function_symbol) => function_symbol
.borrow()
.declared_name_source_range()
.clone(),
ExpressibleSymbol::Parameter(parameter_symbol) => parameter_symbol
.borrow()
.declared_name_source_range()
.clone(),
ExpressibleSymbol::Variable(variable_symbol) => variable_symbol
.borrow()
.declared_name_source_range()
.clone(),
}
}
pub fn ir_expression(&self, builder: &mut IrBuilder) -> IrExpression {
match self {
ExpressibleSymbol::Class(class_symbol) => {
todo!()
}
ExpressibleSymbol::Field(field_symbol) => {
// now we need to read the field into a variable and return an expression pointing
// to that variable
let read_destination = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
field_symbol.borrow().type_info(),
);
let read_destination_as_rc = Rc::new(RefCell::new(read_destination));
let ir_read_field = IrReadField::new(
get_or_init_field_pointer_variable(builder, field_symbol).clone(),
);
builder
.current_block_mut()
.add_statement(IrStatement::Assign(IrAssign::new(
read_destination_as_rc.clone(),
IrOperation::ReadField(ir_read_field),
)));
IrExpression::Variable(read_destination_as_rc)
}
ExpressibleSymbol::Function(_) => {
panic!("Cannot get ir_variable for FunctionSymbol");
}
ExpressibleSymbol::Parameter(parameter_symbol) => {
IrExpression::Parameter(parameter_symbol.borrow().ir_parameter().clone())
}
ExpressibleSymbol::Variable(variable_symbol) => {
IrExpression::Variable(variable_symbol.borrow().vr_variable().clone())
} }
} }
} }

View File

@ -1,14 +1,13 @@
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol; use std::hash::{Hash, Hasher};
use crate::type_info::TypeInfo;
use std::rc::Rc; use std::rc::Rc;
pub struct FieldSymbol { pub struct FieldSymbol {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
is_mut: bool, is_mut: bool,
type_info: Option<TypeInfo>, scope_id: usize,
field_index: Option<usize>, field_index: usize,
} }
impl FieldSymbol { impl FieldSymbol {
@ -16,47 +15,54 @@ impl FieldSymbol {
declared_name: &Rc<str>, declared_name: &Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
is_mut: bool, is_mut: bool,
scope_id: usize,
field_index: usize,
) -> Self { ) -> Self {
Self { Self {
declared_name: declared_name.clone(), declared_name: declared_name.clone(),
declared_name_source_range, declared_name_source_range,
is_mut, is_mut,
type_info: None, scope_id,
field_index: None, field_index,
} }
} }
pub fn declared_name(&self) -> &str {
&self.declared_name
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
pub fn declared_name_source_range(&self) -> &SourceRange {
&self.declared_name_source_range
}
pub fn scope_id(&self) -> usize {
self.scope_id
}
pub fn is_mut(&self) -> bool { pub fn is_mut(&self) -> bool {
self.is_mut self.is_mut
} }
pub fn set_type_info(&mut self, type_info: TypeInfo) {
self.type_info = Some(type_info);
}
pub fn type_info(&self) -> &TypeInfo {
self.type_info.as_ref().unwrap()
}
pub fn set_field_index(&mut self, field_index: usize) {
self.field_index = Some(field_index);
}
pub fn field_index(&self) -> usize { pub fn field_index(&self) -> usize {
self.field_index.unwrap() self.field_index
} }
} }
impl Symbol for FieldSymbol { impl Eq for FieldSymbol {}
fn declared_name(&self) -> &str {
&self.declared_name
}
fn declared_name_owned(&self) -> Rc<str> { impl PartialEq for FieldSymbol {
self.declared_name.clone() fn eq(&self, other: &Self) -> bool {
} self.declared_name == other.declared_name && self.scope_id == other.scope_id
}
fn declared_name_source_range(&self) -> &SourceRange { }
&self.declared_name_source_range
impl Hash for FieldSymbol {
fn hash<H: Hasher>(&self, state: &mut H) {
self.declared_name.hash(state);
self.scope_id.hash(state);
} }
} }

View File

@ -1,8 +1,8 @@
use crate::ast::fqn_util::fqn_parts_to_string;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol;
use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::type_info::TypeInfo; use std::fmt::{Debug, Formatter};
use std::cell::RefCell; use std::hash::Hash;
use std::rc::Rc; use std::rc::Rc;
pub struct FunctionSymbol { pub struct FunctionSymbol {
@ -10,45 +10,57 @@ pub struct FunctionSymbol {
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
fqn_parts: Vec<Rc<str>>, fqn_parts: Vec<Rc<str>>,
is_extern: bool, is_extern: bool,
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>, is_method: bool,
return_type: Option<TypeInfo>, scope_id: usize,
parameters: Vec<Rc<ParameterSymbol>>,
} }
impl FunctionSymbol { impl FunctionSymbol {
pub fn new( pub fn new(
declared_name: &str, declared_name: &Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
fqn_parts: Vec<Rc<str>>, fqn_parts: Vec<Rc<str>>,
is_extern: bool, is_extern: bool,
is_method: bool,
scope_id: usize,
parameters: Vec<Rc<ParameterSymbol>>,
) -> Self { ) -> Self {
Self { Self {
declared_name: declared_name.into(), declared_name: declared_name.clone(),
declared_name_source_range, declared_name_source_range,
fqn_parts, fqn_parts,
is_extern, is_extern,
parameters: None, is_method,
return_type: None, scope_id,
parameters,
} }
} }
pub fn declared_name(&self) -> &str {
&self.declared_name
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
pub fn declared_name_source_range(&self) -> &SourceRange {
&self.declared_name_source_range
}
pub fn fqn_parts(&self) -> &[Rc<str>] { pub fn fqn_parts(&self) -> &[Rc<str>] {
&self.fqn_parts &self.fqn_parts
} }
pub fn set_parameters(&mut self, parameters: Vec<Rc<RefCell<ParameterSymbol>>>) { pub fn is_method(&self) -> bool {
self.parameters = Some(parameters); self.is_method
} }
pub fn parameters(&self) -> &[Rc<RefCell<ParameterSymbol>>] { pub fn scope_id(&self) -> usize {
self.parameters.as_ref().unwrap() self.scope_id
} }
pub fn parameters(&self) -> &[Rc<ParameterSymbol>] {
pub fn set_return_type_info(&mut self, return_type: TypeInfo) { &self.parameters
self.return_type = Some(return_type);
}
pub fn return_type_info(&self) -> &TypeInfo {
self.return_type.as_ref().unwrap()
} }
pub fn is_extern(&self) -> bool { pub fn is_extern(&self) -> bool {
@ -56,16 +68,27 @@ impl FunctionSymbol {
} }
} }
impl Symbol for FunctionSymbol { impl Eq for FunctionSymbol {}
fn declared_name(&self) -> &str {
&self.declared_name
}
fn declared_name_owned(&self) -> Rc<str> { impl PartialEq for FunctionSymbol {
self.declared_name.clone() fn eq(&self, other: &Self) -> bool {
} self.declared_name == other.declared_name && self.scope_id == other.scope_id
}
fn declared_name_source_range(&self) -> &SourceRange { }
&self.declared_name_source_range
impl Hash for FunctionSymbol {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.declared_name.hash(state);
self.scope_id.hash(state);
}
}
impl Debug for FunctionSymbol {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"FunctionSymbol({})",
fqn_parts_to_string(&self.fqn_parts)
)
} }
} }

View File

@ -1,42 +1,54 @@
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol; use std::hash::{Hash, Hasher};
use crate::type_info::TypeInfo;
use std::rc::Rc; use std::rc::Rc;
pub struct GenericParameterSymbol { pub struct GenericParameterSymbol {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
extends: Option<Vec<TypeInfo>>, scope_id: usize,
} }
impl GenericParameterSymbol { impl GenericParameterSymbol {
pub fn new(declared_name: &Rc<str>, declared_name_source_range: &SourceRange) -> Self { pub fn new(
declared_name: &Rc<str>,
declared_name_source_range: &SourceRange,
scope_id: usize,
) -> Self {
Self { Self {
declared_name: declared_name.clone(), declared_name: declared_name.clone(),
declared_name_source_range: declared_name_source_range.clone(), declared_name_source_range: declared_name_source_range.clone(),
extends: None, scope_id,
} }
} }
pub fn set_extends(&mut self, extends: Vec<TypeInfo>) { pub fn declared_name(&self) -> &str {
self.extends = Some(extends);
}
pub fn extends(&self) -> &[TypeInfo] {
self.extends.as_ref().unwrap()
}
}
impl Symbol for GenericParameterSymbol {
fn declared_name(&self) -> &str {
&self.declared_name &self.declared_name
} }
fn declared_name_owned(&self) -> Rc<str> { pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone() self.declared_name.clone()
} }
fn declared_name_source_range(&self) -> &SourceRange { pub fn declared_name_source_range(&self) -> &SourceRange {
&self.declared_name_source_range &self.declared_name_source_range
} }
pub fn scope_id(&self) -> usize {
self.scope_id
}
}
impl Eq for GenericParameterSymbol {}
impl PartialEq for GenericParameterSymbol {
fn eq(&self, other: &Self) -> bool {
self.declared_name == other.declared_name && self.scope_id == other.scope_id
}
}
impl Hash for GenericParameterSymbol {
fn hash<H: Hasher>(&self, state: &mut H) {
self.declared_name.hash(state);
self.scope_id.hash(state);
}
} }

View File

@ -1,4 +1,11 @@
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol::variable_symbol::VariableSymbol;
use std::rc::Rc; use std::rc::Rc;
pub mod callable_symbol; pub mod callable_symbol;
@ -12,8 +19,61 @@ pub mod parameter_symbol;
pub mod type_symbol; pub mod type_symbol;
pub mod variable_symbol; pub mod variable_symbol;
pub trait Symbol { #[derive(Eq, PartialEq, Hash)]
fn declared_name(&self) -> &str; pub enum Symbol {
fn declared_name_owned(&self) -> Rc<str>; Class(Rc<ClassSymbol>),
fn declared_name_source_range(&self) -> &SourceRange; GenericParameter(Rc<GenericParameterSymbol>),
Field(Rc<FieldSymbol>),
Constructor(Rc<ConstructorSymbol>),
Function(Rc<FunctionSymbol>),
Parameter(Rc<ParameterSymbol>),
Variable(Rc<VariableSymbol>),
}
impl Symbol {
pub fn scope_id(&self) -> usize {
match self {
Symbol::Class(class_symbol) => class_symbol.scope_id(),
Symbol::GenericParameter(generic_parameter_symbol) => {
generic_parameter_symbol.scope_id()
}
Symbol::Field(field_symbol) => field_symbol.scope_id(),
Symbol::Constructor(constructor_symbol) => constructor_symbol.scope_id(),
Symbol::Function(function_symbol) => function_symbol.scope_id(),
Symbol::Parameter(parameter_symbol) => parameter_symbol.scope_id(),
Symbol::Variable(variable_symbol) => variable_symbol.scope_id(),
}
}
pub fn declared_name(&self) -> &str {
match self {
Symbol::Class(class_symbol) => class_symbol.declared_name(),
Symbol::GenericParameter(generic_parameter_symbol) => {
generic_parameter_symbol.declared_name()
}
Symbol::Field(field_symbol) => field_symbol.declared_name(),
Symbol::Constructor(constructor_symbol) => constructor_symbol.declared_name(),
Symbol::Function(function_symbol) => function_symbol.declared_name(),
Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name(),
Symbol::Variable(variable_symbol) => variable_symbol.declared_name(),
}
}
pub fn declared_name_source_range(&self) -> Option<&SourceRange> {
match self {
Symbol::Class(class_symbol) => class_symbol.declared_name_source_range(),
Symbol::GenericParameter(generic_parameter_symbol) => {
Some(generic_parameter_symbol.declared_name_source_range())
}
Symbol::Field(field_symbol) => Some(field_symbol.declared_name_source_range()),
Symbol::Constructor(constructor_symbol) => {
Some(constructor_symbol.declared_name_source_range())
}
Symbol::Function(function_symbol) => Some(function_symbol.declared_name_source_range()),
Symbol::Parameter(parameter_symbol) => {
Some(parameter_symbol.declared_name_source_range())
}
Symbol::Variable(variable_symbol) => Some(variable_symbol.declared_name_source_range()),
}
}
} }

View File

@ -1,53 +1,54 @@
use crate::ir::ir_parameter::IrParameter;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol; use std::hash::{Hash, Hasher};
use crate::type_info::TypeInfo;
use std::rc::Rc; use std::rc::Rc;
pub struct ParameterSymbol { pub struct ParameterSymbol {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
type_info: Option<TypeInfo>, scope_id: usize,
ir_parameter: Option<Rc<IrParameter>>,
} }
impl ParameterSymbol { impl ParameterSymbol {
pub fn new(declared_name: &Rc<str>, declared_name_source_range: SourceRange) -> Self { pub fn new(
declared_name: &Rc<str>,
declared_name_source_range: SourceRange,
scope_id: usize,
) -> Self {
Self { Self {
declared_name: declared_name.clone(), declared_name: declared_name.clone(),
declared_name_source_range, declared_name_source_range,
type_info: None, scope_id,
ir_parameter: None,
} }
} }
pub fn set_type_info(&mut self, type_info: TypeInfo) { pub fn declared_name(&self) -> &str {
self.type_info = Some(type_info);
}
pub fn type_info(&self) -> &TypeInfo {
self.type_info.as_ref().unwrap()
}
pub fn set_ir_parameter(&mut self, ir_parameter: Rc<IrParameter>) {
self.ir_parameter = Some(ir_parameter);
}
pub fn ir_parameter(&self) -> &Rc<IrParameter> {
self.ir_parameter.as_ref().unwrap()
}
}
impl Symbol for ParameterSymbol {
fn declared_name(&self) -> &str {
&self.declared_name &self.declared_name
} }
fn declared_name_owned(&self) -> Rc<str> { pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone() self.declared_name.clone()
} }
fn declared_name_source_range(&self) -> &SourceRange { pub fn declared_name_source_range(&self) -> &SourceRange {
&self.declared_name_source_range &self.declared_name_source_range
} }
pub fn scope_id(&self) -> usize {
self.scope_id
}
}
impl Eq for ParameterSymbol {}
impl PartialEq for ParameterSymbol {
fn eq(&self, other: &Self) -> bool {
self.declared_name == other.declared_name && self.scope_id == other.scope_id
}
}
impl Hash for ParameterSymbol {
fn hash<H: Hasher>(&self, state: &mut H) {
self.declared_name.hash(state);
self.scope_id.hash(state);
}
} }

View File

@ -1,9 +1,8 @@
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub enum TypeSymbol { pub enum TypeSymbol {
Class(Rc<RefCell<ClassSymbol>>), Class(Rc<ClassSymbol>),
GenericParameter(Rc<RefCell<GenericParameterSymbol>>), GenericParameter(Rc<GenericParameterSymbol>),
} }

View File

@ -1,64 +1,61 @@
use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol; use std::hash::{Hash, Hasher};
use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub struct VariableSymbol { pub struct VariableSymbol {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
is_mut: bool, is_mut: bool,
type_info: Option<TypeInfo>, scope_id: usize,
vr_variable: Option<Rc<RefCell<IrVariable>>>,
} }
impl VariableSymbol { impl VariableSymbol {
pub fn new(name: &Rc<str>, declared_name_source_range: SourceRange, is_mut: bool) -> Self { pub fn new(
name: &Rc<str>,
declared_name_source_range: &SourceRange,
is_mut: bool,
scope_id: usize,
) -> Self {
Self { Self {
declared_name: name.clone(), declared_name: name.clone(),
declared_name_source_range, declared_name_source_range: declared_name_source_range.clone(),
is_mut, is_mut,
type_info: None, scope_id,
vr_variable: None,
} }
} }
pub fn declared_name(&self) -> &str {
&self.declared_name
}
pub fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
pub fn declared_name_source_range(&self) -> &SourceRange {
&self.declared_name_source_range
}
pub fn is_mut(&self) -> bool { pub fn is_mut(&self) -> bool {
self.is_mut self.is_mut
} }
pub fn set_type_info(&mut self, type_info: TypeInfo) { pub fn scope_id(&self) -> usize {
self.type_info = Some(type_info); self.scope_id
}
pub fn type_info(&self) -> &TypeInfo {
self.type_info
.as_ref()
.expect("TypeInfo not initialized. Did you type check?")
}
pub fn set_vr_variable(&mut self, ir_variable: Rc<RefCell<IrVariable>>) {
self.vr_variable = Some(ir_variable);
}
pub fn vr_variable(&self) -> &Rc<RefCell<IrVariable>> {
self.vr_variable
.as_ref()
.expect("ir_variable not yet initialized")
} }
} }
impl Symbol for VariableSymbol { impl Eq for VariableSymbol {}
fn declared_name(&self) -> &str {
&self.declared_name
}
fn declared_name_owned(&self) -> Rc<str> { impl PartialEq for VariableSymbol {
self.declared_name.clone() fn eq(&self, other: &Self) -> bool {
} self.declared_name == other.declared_name && self.scope_id == other.scope_id
}
fn declared_name_source_range(&self) -> &SourceRange { }
&self.declared_name_source_range
impl Hash for VariableSymbol {
fn hash<H: Hasher>(&self, state: &mut H) {
self.declared_name.hash(state);
self.scope_id.hash(state);
} }
} }

View File

@ -1,134 +1,210 @@
use crate::scope::Scope; use crate::scope::Scope;
use crate::scope::block_scope::BlockScope; use crate::scope::block_scope::BlockScope;
use crate::scope::class_body_scope::ClassBodyScope;
use crate::scope::class_scope::ClassScope; use crate::scope::class_scope::ClassScope;
use crate::scope::function_scope::FunctionScope; use crate::scope::function_scope::FunctionScope;
use crate::scope::module_scope::ModuleScope; use crate::scope::module_scope::ModuleScope;
use crate::symbol::Symbol; use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol;
use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol::type_symbol::TypeSymbol; use crate::symbol::type_symbol::TypeSymbol;
use std::cell::RefCell; use crate::symbol::variable_symbol::VariableSymbol;
use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
pub fn find_in_module_by_name( fn find_class_symbol_ref(
module_scope: &ModuleScope, symbols: &HashMap<Rc<str>, Rc<ClassSymbol>>,
name: &str, name: &str,
) -> Option<Rc<RefCell<dyn Symbol>>> { ) -> Option<Symbol> {
module_scope symbols
.function_symbols()
.get(name) .get(name)
.map(|symbol| symbol.clone() as Rc<RefCell<dyn Symbol>>) .map(|symbol| Symbol::Class(symbol.clone()))
.or_else(|| {
module_scope
.class_symbols()
.get(name)
.map(|symbol| symbol.clone() as Rc<RefCell<dyn Symbol>>)
})
} }
pub fn find_in_class_by_name( fn find_generic_parameter_symbol_ref(
class_scope: &ClassScope, symbols: &HashMap<Rc<str>, Rc<GenericParameterSymbol>>,
name: &str, name: &str,
) -> Option<Rc<RefCell<dyn Symbol>>> { ) -> Option<Symbol> {
class_scope symbols
.class_symbols()
.get(name) .get(name)
.map(|symbol| symbol.clone() as Rc<RefCell<dyn Symbol>>) .map(|symbol| Symbol::GenericParameter(symbol.clone()))
.or_else(|| {
class_scope
.function_symbols()
.get(name)
.map(|symbol| symbol.clone() as Rc<RefCell<dyn Symbol>>)
})
.or_else(|| {
class_scope
.field_symbols()
.get(name)
.map(|symbol| symbol.clone() as Rc<RefCell<dyn Symbol>>)
})
} }
pub fn find_in_function_by_name( fn find_field_symbol_ref(
function_scope: &FunctionScope, symbols: &HashMap<Rc<str>, Rc<FieldSymbol>>,
name: &str, name: &str,
) -> Option<Rc<RefCell<dyn Symbol>>> { ) -> Option<Symbol> {
function_scope symbols
.parameter_symbols()
.get(name) .get(name)
.map(|symbol| symbol.clone() as Rc<RefCell<dyn Symbol>>) .map(|symbol| Symbol::Field(symbol.clone()))
} }
pub fn find_in_block_by_name( fn find_function_symbol_ref(
block_scope: &BlockScope, symbols: &HashMap<Rc<str>, Rc<FunctionSymbol>>,
name: &str, name: &str,
) -> Option<Rc<RefCell<dyn Symbol>>> { ) -> Option<Symbol> {
block_scope symbols
.variable_symbols()
.get(name) .get(name)
.map(|symbol| symbol.clone() as Rc<RefCell<dyn Symbol>>) .map(|symbol| Symbol::Function(symbol.clone()))
} }
fn find_parameter_symbol_ref(
symbols: &HashMap<Rc<str>, Rc<ParameterSymbol>>,
name: &str,
) -> Option<Symbol> {
symbols
.get(name)
.map(|symbol| Symbol::Parameter(symbol.clone()))
}
fn find_variable_symbol_ref(
symbols: &HashMap<Rc<str>, Rc<VariableSymbol>>,
name: &str,
) -> Option<Symbol> {
symbols
.get(name)
.map(|symbol| Symbol::Variable(symbol.clone()))
}
/* Expressible symbol refs */
fn find_class_expressible_symbol_ref(
symbols: &HashMap<Rc<str>, Rc<ClassSymbol>>,
name: &str,
) -> Option<ExpressibleSymbol> {
symbols
.get(name)
.map(|symbol| ExpressibleSymbol::Class(symbol.clone()))
}
fn find_field_expressible_symbol_ref(
symbols: &HashMap<Rc<str>, Rc<FieldSymbol>>,
name: &str,
) -> Option<ExpressibleSymbol> {
symbols
.get(name)
.map(|symbol| ExpressibleSymbol::Field(symbol.clone()))
}
fn find_function_expressible_symbol_ref(
symbols: &HashMap<Rc<str>, Rc<FunctionSymbol>>,
name: &str,
) -> Option<ExpressibleSymbol> {
symbols
.get(name)
.map(|symbol| ExpressibleSymbol::Function(symbol.clone()))
}
fn find_parameter_expressible_symbol_ref(
symbols: &HashMap<Rc<str>, Rc<ParameterSymbol>>,
name: &str,
) -> Option<ExpressibleSymbol> {
symbols
.get(name)
.map(|symbol| ExpressibleSymbol::Parameter(symbol.clone()))
}
fn find_variable_expressible_symbol_ref(
symbols: &HashMap<Rc<str>, Rc<VariableSymbol>>,
name: &str,
) -> Option<ExpressibleSymbol> {
symbols
.get(name)
.map(|symbol| ExpressibleSymbol::Variable(symbol.clone()))
}
/* Find type symbols */
fn find_class_type_symbol_ref(
symbols: &HashMap<Rc<str>, Rc<ClassSymbol>>,
name: &str,
) -> Option<TypeSymbol> {
symbols
.get(name)
.map(|symbol| TypeSymbol::Class(symbol.clone()))
}
fn find_generic_parameter_type_symbol_ref(
symbols: &HashMap<Rc<str>, Rc<GenericParameterSymbol>>,
name: &str,
) -> Option<TypeSymbol> {
symbols
.get(name)
.map(|symbol| TypeSymbol::GenericParameter(symbol.clone()))
}
/* Public helper functions */
/* Various find in functions */
pub fn find_in_module_by_name(module_scope: &ModuleScope, name: &str) -> Option<Symbol> {
find_function_symbol_ref(&module_scope.function_symbols(), name)
.or_else(|| find_class_symbol_ref(&module_scope.class_symbols(), name))
}
pub fn find_in_class_by_name(class_scope: &ClassScope, name: &str) -> Option<Symbol> {
find_generic_parameter_symbol_ref(&class_scope.generic_parameter_symbols(), name)
}
pub fn find_in_class_body_by_name(class_body_scope: &ClassBodyScope, name: &str) -> Option<Symbol> {
find_class_symbol_ref(&class_body_scope.class_symbols(), name)
.or_else(|| find_function_symbol_ref(&class_body_scope.function_symbols(), name))
.or_else(|| find_field_symbol_ref(&class_body_scope.field_symbols(), name))
}
pub fn find_in_function_by_name(function_scope: &FunctionScope, name: &str) -> Option<Symbol> {
find_parameter_symbol_ref(&function_scope.parameter_symbols(), name)
}
pub fn find_in_block_by_name(block_scope: &BlockScope, name: &str) -> Option<Symbol> {
find_variable_symbol_ref(&block_scope.variable_symbols(), name)
}
/* Find expressible */
fn find_expressible_in_module_by_name( fn find_expressible_in_module_by_name(
module_scope: &ModuleScope, module_scope: &ModuleScope,
name: &str, name: &str,
) -> Option<ExpressibleSymbol> { ) -> Option<ExpressibleSymbol> {
module_scope find_class_expressible_symbol_ref(&module_scope.class_symbols(), name)
.class_symbols() .or_else(|| find_function_expressible_symbol_ref(&module_scope.function_symbols(), name))
.get(name)
.map(|class_symbol| ExpressibleSymbol::Class(class_symbol.clone()))
.or_else(|| {
module_scope
.function_symbols()
.get(name)
.map(|function_symbol| ExpressibleSymbol::Function(function_symbol.clone()))
})
} }
fn find_expressible_in_class_by_name( fn find_expressible_in_class_body_by_name(
class_scope: &ClassScope, class_body_scope: &ClassBodyScope,
name: &str, name: &str,
) -> Option<ExpressibleSymbol> { ) -> Option<ExpressibleSymbol> {
class_scope find_class_expressible_symbol_ref(&class_body_scope.class_symbols(), name)
.class_symbols()
.get(name)
.map(|symbol| ExpressibleSymbol::Class(symbol.clone()))
.or_else(|| { .or_else(|| {
class_scope find_function_expressible_symbol_ref(&class_body_scope.function_symbols(), name)
.function_symbols()
.get(name)
.map(|function_symbol| ExpressibleSymbol::Function(function_symbol.clone()))
})
.or_else(|| {
class_scope
.field_symbols()
.get(name)
.map(|field_symbol| ExpressibleSymbol::Field(field_symbol.clone()))
}) })
.or_else(|| find_field_expressible_symbol_ref(&class_body_scope.field_symbols(), name))
} }
fn find_expressible_in_function_by_name( fn find_expressible_in_function_by_name(
function_scope: &FunctionScope, function_scope: &FunctionScope,
name: &str, name: &str,
) -> Option<ExpressibleSymbol> { ) -> Option<ExpressibleSymbol> {
function_scope find_parameter_expressible_symbol_ref(function_scope.parameter_symbols(), name)
.parameter_symbols()
.get(name)
.map(|parameter_symbol| ExpressibleSymbol::Parameter(parameter_symbol.clone()))
} }
fn find_expressible_in_block_by_name( fn find_expressible_in_block_by_name(
block_scope: &BlockScope, block_scope: &BlockScope,
name: &str, name: &str,
) -> Option<ExpressibleSymbol> { ) -> Option<ExpressibleSymbol> {
block_scope find_variable_expressible_symbol_ref(block_scope.variable_symbols(), name)
.variable_symbols()
.get(name)
.map(|variable_symbol| ExpressibleSymbol::Variable(variable_symbol.clone()))
} }
pub fn find_expressible_symbol(scope: &Scope, name: &str) -> Option<ExpressibleSymbol> { pub fn find_expressible_symbol(scope: &Scope, name: &str) -> Option<ExpressibleSymbol> {
match scope { match scope {
Scope::Module(module_scope) => find_expressible_in_module_by_name(module_scope, name), Scope::Module(module_scope) => find_expressible_in_module_by_name(module_scope, name),
Scope::Class(class_scope) => find_expressible_in_class_by_name(class_scope, name), Scope::Class(_) => None,
Scope::ClassBody(class_body_scope) => {
find_expressible_in_class_body_by_name(class_body_scope, name)
}
Scope::Function(function_scope) => { Scope::Function(function_scope) => {
find_expressible_in_function_by_name(function_scope, name) find_expressible_in_function_by_name(function_scope, name)
} }
@ -136,32 +212,30 @@ pub fn find_expressible_symbol(scope: &Scope, name: &str) -> Option<ExpressibleS
} }
} }
/* Find type */
fn find_type_symbol_in_module(module_scope: &ModuleScope, name: &str) -> Option<TypeSymbol> { fn find_type_symbol_in_module(module_scope: &ModuleScope, name: &str) -> Option<TypeSymbol> {
module_scope find_class_type_symbol_ref(&module_scope.class_symbols(), name)
.class_symbols()
.get(name)
.map(|symbol| TypeSymbol::Class(symbol.clone()))
} }
pub fn find_type_symbol_in_class(class_scope: &ClassScope, name: &str) -> Option<TypeSymbol> { fn find_type_symbol_in_class_body(
class_scope class_body_scope: &ClassBodyScope,
.class_symbols() name: &str,
.get(name) ) -> Option<TypeSymbol> {
.map(|symbol| TypeSymbol::Class(symbol.clone())) find_class_type_symbol_ref(&class_body_scope.class_symbols(), name)
.or_else(|| { }
class_scope
.generic_parameter_symbols() fn find_type_symbol_in_class(class_scope: &ClassScope, name: &str) -> Option<TypeSymbol> {
.get(name) find_generic_parameter_type_symbol_ref(&class_scope.generic_parameter_symbols(), name)
.map(|generic_parameter_symbol| {
TypeSymbol::GenericParameter(generic_parameter_symbol.clone())
})
})
} }
pub fn find_type_symbol(scope: &Scope, name: &str) -> Option<TypeSymbol> { pub fn find_type_symbol(scope: &Scope, name: &str) -> Option<TypeSymbol> {
match scope { match scope {
Scope::Module(module_scope) => find_type_symbol_in_module(module_scope, name), Scope::Module(module_scope) => find_type_symbol_in_module(module_scope, name),
Scope::Class(class_scope) => find_type_symbol_in_class(class_scope, name), Scope::Class(class_scope) => find_type_symbol_in_class(class_scope, name),
Scope::ClassBody(class_body_scope) => {
find_type_symbol_in_class_body(class_body_scope, name)
}
_ => None, _ => None,
} }
} }

View File

@ -2,6 +2,7 @@ mod helpers;
use crate::scope::Scope; use crate::scope::Scope;
use crate::scope::block_scope::BlockScope; use crate::scope::block_scope::BlockScope;
use crate::scope::class_body_scope::ClassBodyScope;
use crate::scope::class_scope::ClassScope; use crate::scope::class_scope::ClassScope;
use crate::scope::function_scope::FunctionScope; use crate::scope::function_scope::FunctionScope;
use crate::scope::module_scope::ModuleScope; use crate::scope::module_scope::ModuleScope;
@ -16,10 +17,9 @@ use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol::type_symbol::TypeSymbol; use crate::symbol::type_symbol::TypeSymbol;
use crate::symbol::variable_symbol::VariableSymbol; use crate::symbol::variable_symbol::VariableSymbol;
use crate::symbol_table::helpers::{ use crate::symbol_table::helpers::{
find_expressible_symbol, find_in_block_by_name, find_in_class_by_name, find_expressible_symbol, find_in_block_by_name, find_in_class_body_by_name,
find_in_function_by_name, find_in_module_by_name, find_type_symbol, find_type_symbol_in_class, find_in_class_by_name, find_in_function_by_name, find_in_module_by_name, find_type_symbol,
}; };
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub struct SymbolTable { pub struct SymbolTable {
@ -39,282 +39,213 @@ impl SymbolTable {
self.scopes.len() self.scopes.len()
} }
fn push_scope(&mut self, scope: Scope) { fn push_scope(&mut self, scope: Scope) -> usize {
let scope_id = self.new_scope_id(); let scope_id = self.new_scope_id();
self.scopes.push(scope); self.scopes.push(scope);
self.current_scope_id = Some(scope_id); self.current_scope_id = Some(scope_id);
scope_id
} }
pub fn push_module_scope(&mut self, debug_name: &str) { pub fn push_module_scope(&mut self, debug_name: &str) -> usize {
self.push_scope(Scope::Module(ModuleScope::new( self.push_scope(Scope::Module(ModuleScope::new(
debug_name, debug_name,
self.current_scope_id, self.current_scope_id,
))); )))
} }
pub fn push_class_scope(&mut self, debug_name: &str) { pub fn push_class_scope(&mut self, debug_name: &str) -> usize {
self.push_scope(Scope::Class(ClassScope::new( self.push_scope(Scope::Class(ClassScope::new(
debug_name, debug_name,
self.current_scope_id.unwrap(), self.current_scope_id.unwrap(),
))); )))
} }
pub fn push_function_scope(&mut self, debug_name: &str) { pub fn push_class_body_scope(&mut self, debug_name: &str) -> usize {
self.push_scope(Scope::ClassBody(ClassBodyScope::new(
debug_name,
self.current_scope_id.unwrap(),
)))
}
pub fn push_function_scope(&mut self, debug_name: &str) -> usize {
self.push_scope(Scope::Function(FunctionScope::new( self.push_scope(Scope::Function(FunctionScope::new(
debug_name, debug_name,
self.current_scope_id.unwrap(), self.current_scope_id.unwrap(),
))); )))
} }
pub fn push_block_scope(&mut self, debug_name: &str) { pub fn push_block_scope(&mut self, debug_name: &str) -> usize {
self.push_scope(Scope::Block(BlockScope::new( self.push_scope(Scope::Block(BlockScope::new(
debug_name, debug_name,
self.current_scope_id.unwrap(), self.current_scope_id.unwrap(),
))); )))
} }
pub fn pop_scope(&mut self) { pub fn pop_scope(&mut self) {
self.current_scope_id = self.current_scope().parent_id(); self.current_scope_id = self.scopes[self.current_scope_id.unwrap()].parent_id();
} }
pub fn current_scope_id(&self) -> usize { pub fn current_scope_id(&self) -> usize {
self.current_scope_id.unwrap() self.current_scope_id.unwrap()
} }
fn current_scope(&self) -> &Scope { fn scope(&self, scope_id: usize) -> &Scope {
&self.scopes[self.current_scope_id.unwrap()] &self.scopes[scope_id]
} }
fn current_scope_mut(&mut self) -> &mut Scope { fn scope_mut(&mut self, scope_id: usize) -> &mut Scope {
&mut self.scopes[self.current_scope_id.unwrap()] &mut self.scopes[scope_id]
} }
pub fn insert_class_symbol( pub fn get_symbol(&self, scope_id: usize, name: &str) -> Option<Symbol> {
&mut self, match self.scope(scope_id) {
class_symbol: ClassSymbol, Scope::Module(module_scope) => find_in_module_by_name(module_scope, name),
) -> Result<Rc<RefCell<ClassSymbol>>, SymbolInsertError> { Scope::Class(class_scope) => find_in_class_by_name(class_scope, name),
let maybe_already_inserted = match self.current_scope() { Scope::ClassBody(class_body_scope) => {
Scope::Module(module_scope) => { find_in_class_body_by_name(class_body_scope, name)
find_in_module_by_name(module_scope, class_symbol.declared_name())
} }
Scope::Class(class_scope) => { Scope::Function(function_scope) => find_in_function_by_name(function_scope, name),
find_in_class_by_name(class_scope, class_symbol.declared_name()) Scope::Block(block_scope) => find_in_block_by_name(block_scope, name),
}
}
pub fn insert_symbol(&mut self, symbol: Symbol) {
match symbol {
Symbol::Class(class_symbol) => {
self.insert_class_symbol(class_symbol);
}
Symbol::GenericParameter(generic_parameter_symbol) => {
self.insert_generic_parameter_symbol(generic_parameter_symbol);
}
Symbol::Field(field_symbol) => {
self.insert_field_symbol(field_symbol);
}
Symbol::Constructor(constructor_symbol) => {
self.insert_constructor_symbol(constructor_symbol);
}
Symbol::Function(function_symbol) => {
self.insert_function_symbol(function_symbol);
}
Symbol::Parameter(parameter_symbol) => {
self.insert_parameter_symbol(parameter_symbol);
}
Symbol::Variable(variable_symbol) => {
self.insert_variable_symbol(variable_symbol);
}
}
}
pub fn insert_class_symbol(&mut self, class_symbol: Rc<ClassSymbol>) {
let name = class_symbol.declared_name_owned();
match self.scope_mut(class_symbol.scope_id()) {
Scope::Module(module_scope) => {
module_scope.class_symbols_mut().insert(name, class_symbol);
}
Scope::ClassBody(class_scope) => {
class_scope.class_symbols_mut().insert(name, class_symbol);
} }
_ => panic!("Attempt to insert ClassSymbol in incompatible scope"), _ => panic!("Attempt to insert ClassSymbol in incompatible scope"),
};
if let Some(already_inserted) = maybe_already_inserted {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
already_inserted,
)));
} }
let name = class_symbol.declared_name_owned();
let as_rc = Rc::new(RefCell::new(class_symbol));
let to_return = as_rc.clone();
match self.current_scope_mut() {
Scope::Module(module_scope) => {
module_scope.class_symbols_mut().insert(name, as_rc);
}
Scope::Class(class_scope) => {
class_scope.class_symbols_mut().insert(name, as_rc);
}
_ => unreachable!(),
}
Ok(to_return)
} }
pub fn insert_generic_parameter_symbol( pub fn insert_generic_parameter_symbol(
&mut self, &mut self,
generic_parameter_symbol: GenericParameterSymbol, generic_parameter_symbol: Rc<GenericParameterSymbol>,
) -> Result<Rc<RefCell<GenericParameterSymbol>>, SymbolInsertError> { ) {
let maybe_already_inserted = match self.current_scope() {
Scope::Class(class_scope) => {
find_type_symbol_in_class(class_scope, generic_parameter_symbol.declared_name())
}
_ => panic!("Attempt to insert GenericParameterSymbol in incompatible scope"),
};
if let Some(already_inserted) = maybe_already_inserted {
match already_inserted {
TypeSymbol::Class(class_symbol) => {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
class_symbol as Rc<RefCell<dyn Symbol>>,
)));
}
TypeSymbol::GenericParameter(generic_parameter_symbol) => {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
generic_parameter_symbol as Rc<RefCell<dyn Symbol>>,
)));
}
}
}
let name = generic_parameter_symbol.declared_name_owned(); let name = generic_parameter_symbol.declared_name_owned();
let as_rc = Rc::new(RefCell::new(generic_parameter_symbol)); match self.scope_mut(generic_parameter_symbol.scope_id()) {
let to_return = as_rc.clone();
match self.current_scope_mut() {
Scope::Class(class_scope) => { Scope::Class(class_scope) => {
class_scope class_scope
.generic_parameter_symbols_mut() .generic_parameter_symbols_mut()
.insert(name, as_rc); .insert(name, generic_parameter_symbol);
} }
_ => unreachable!(), _ => panic!("Attempt to insert GenericParameterSymbol in incompatible scope"),
} }
Ok(to_return)
} }
pub fn insert_constructor_symbol( pub fn insert_constructor_symbol(&mut self, constructor_symbol: Rc<ConstructorSymbol>) {
&mut self, match self.scope_mut(constructor_symbol.scope_id()) {
constructor_symbol: ConstructorSymbol, Scope::ClassBody(class_body_scope) => {
) -> Result<Rc<RefCell<ConstructorSymbol>>, SymbolInsertError> { class_body_scope
let maybe_already_inserted = match self.current_scope() { .constructor_symbol_mut()
Scope::Class(class_scope) => class_scope.constructor_symbol(), .replace(constructor_symbol);
_ => panic!("Attempt to insert ConstructorSymbol in incompatible scope"),
};
if let Some(already_inserted) = maybe_already_inserted {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
already_inserted.clone() as Rc<RefCell<dyn Symbol>>,
)));
}
let as_rc = Rc::new(RefCell::new(constructor_symbol));
let to_return = as_rc.clone();
match self.current_scope_mut() {
Scope::Class(class_scope) => {
class_scope.constructor_symbol_mut().replace(as_rc);
} }
_ => unreachable!(), _ => panic!(
"Attempt to insert ConstructorSymbol in incompatible scope: {:?}",
self.scope(constructor_symbol.scope_id())
),
} }
Ok(to_return)
} }
pub fn insert_field_symbol( pub fn insert_field_symbol(&mut self, field_symbol: Rc<FieldSymbol>) {
&mut self, let name = field_symbol.declared_name_owned();
field_symbol: FieldSymbol, match self.scope_mut(field_symbol.scope_id()) {
) -> Result<Rc<RefCell<FieldSymbol>>, SymbolInsertError> { Scope::ClassBody(class_scope) => {
let maybe_already_inserted = match self.current_scope() { class_scope.field_symbols_mut().insert(name, field_symbol);
Scope::Class(class_scope) => {
find_in_class_by_name(class_scope, field_symbol.declared_name())
} }
_ => panic!("Attempt to insert FieldSymbol in incompatible scope"), _ => panic!("Attempt to insert FieldSymbol in incompatible scope"),
};
if let Some(already_inserted) = maybe_already_inserted {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
already_inserted,
)));
} }
let name = field_symbol.declared_name_owned();
let as_rc = Rc::new(RefCell::new(field_symbol));
let to_return = as_rc.clone();
match self.current_scope_mut() {
Scope::Class(class_scope) => {
class_scope.field_symbols_mut().insert(name, as_rc);
}
_ => unreachable!(),
}
Ok(to_return)
} }
pub fn insert_function_symbol( pub fn insert_function_symbol(&mut self, function_symbol: Rc<FunctionSymbol>) {
&mut self, let name = function_symbol.declared_name_owned();
function_symbol: FunctionSymbol, match self.scope_mut(function_symbol.scope_id()) {
) -> Result<Rc<RefCell<FunctionSymbol>>, SymbolInsertError> {
let maybe_already_inserted = match self.current_scope() {
Scope::Module(module_scope) => { Scope::Module(module_scope) => {
find_in_module_by_name(module_scope, function_symbol.declared_name()) module_scope
.function_symbols_mut()
.insert(name, function_symbol);
} }
Scope::Class(class_scope) => { Scope::ClassBody(class_scope) => {
find_in_class_by_name(class_scope, function_symbol.declared_name()) class_scope
.function_symbols_mut()
.insert(name, function_symbol);
} }
_ => panic!("Attempt to insert FunctionSymbol in incompatible scope"), _ => panic!("Attempt to insert FunctionSymbol in incompatible scope"),
};
if let Some(already_inserted) = maybe_already_inserted {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
already_inserted,
)));
} }
let name = function_symbol.declared_name_owned();
let as_rc = Rc::new(RefCell::new(function_symbol));
let to_return = as_rc.clone();
match self.current_scope_mut() {
Scope::Module(module_scope) => {
module_scope.function_symbols_mut().insert(name, as_rc);
}
Scope::Class(class_scope) => {
class_scope.function_symbols_mut().insert(name, as_rc);
}
_ => unreachable!(),
}
Ok(to_return)
} }
pub fn insert_parameter_symbol( pub fn insert_parameter_symbol(&mut self, parameter_symbol: Rc<ParameterSymbol>) {
&mut self,
parameter_symbol: ParameterSymbol,
) -> Result<Rc<RefCell<ParameterSymbol>>, SymbolInsertError> {
let maybe_already_inserted = match self.current_scope() {
Scope::Function(function_scope) => {
find_in_function_by_name(function_scope, parameter_symbol.declared_name())
}
_ => panic!("Attempt to insert ParameterSymbol in incompatible scope"),
};
if let Some(already_inserted) = maybe_already_inserted {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
already_inserted,
)));
}
let name = parameter_symbol.declared_name_owned(); let name = parameter_symbol.declared_name_owned();
let as_rc = Rc::new(RefCell::new(parameter_symbol)); match self.scope_mut(parameter_symbol.scope_id()) {
let to_return = as_rc.clone();
match self.current_scope_mut() {
Scope::Function(function_scope) => { Scope::Function(function_scope) => {
function_scope.parameter_symbols_mut().insert(name, as_rc); function_scope
.parameter_symbols_mut()
.insert(name, parameter_symbol);
} }
_ => panic!("Attempt to insert ParameterSymbol in incompatible scope"), _ => panic!("Attempt to insert ParameterSymbol in incompatible scope"),
} }
Ok(to_return)
} }
pub fn insert_variable_symbol( pub fn insert_variable_symbol(&mut self, variable_symbol: Rc<VariableSymbol>) {
&mut self, match self.scope_mut(variable_symbol.scope_id()) {
variable_symbol: VariableSymbol,
) -> Result<(), SymbolInsertError> {
let maybe_already_inserted = match self.current_scope() {
Scope::Block(block_scope) => { Scope::Block(block_scope) => {
find_in_block_by_name(block_scope, variable_symbol.declared_name()) block_scope
.variable_symbols_mut()
.insert(variable_symbol.declared_name_owned(), variable_symbol);
} }
_ => panic!("Attempt to insert VariableSymbol in incompatible scope"), _ => panic!("Attempt to insert VariableSymbol in incompatible scope"),
};
if let Some(already_inserted) = maybe_already_inserted {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
already_inserted,
)));
} }
}
match self.current_scope_mut() { fn find_symbol<S>(
Scope::Block(block_scope) => { &self,
block_scope.variable_symbols_mut().insert( scope_id: usize,
variable_symbol.declared_name_owned(), name: &str,
Rc::new(RefCell::new(variable_symbol)), f: impl Fn(&Scope, &str) -> Option<S>,
); ) -> Option<S> {
} let mut maybe_scope = self.scopes.get(scope_id);
_ => unreachable!(), if maybe_scope.is_none() {
panic!("Invalid scope_id: {}", scope_id);
} }
Ok(()) while let Some(scope) = maybe_scope {
let maybe_symbol = f(scope, name);
if maybe_symbol.is_some() {
return maybe_symbol;
} else {
maybe_scope = scope.parent_id().and_then(|id| self.scopes.get(id));
}
}
None
} }
pub fn find_expressible_symbol( pub fn find_expressible_symbol(
@ -353,28 +284,132 @@ impl SymbolTable {
None None
} }
pub fn get_variable_symbol(&self, scope_id: usize, name: &str) -> Rc<RefCell<VariableSymbol>> { pub fn get_class_symbol(&self, scope_id: usize, name: &str) -> Option<&Rc<ClassSymbol>> {
match self.scope(scope_id) {
Scope::Module(module_scope) => module_scope.class_symbols().get(name),
Scope::ClassBody(class_body_scope) => class_body_scope.class_symbols().get(name),
_ => panic!("scope_id {} cannot contain classes", scope_id),
}
}
pub fn get_generic_parameter_symbol_owned(
&self,
scope_id: usize,
name: &str,
) -> Option<Rc<GenericParameterSymbol>> {
match self.scope(scope_id) {
Scope::Class(class_scope) => class_scope.generic_parameter_symbols().get(name).cloned(),
_ => panic!("scope_id {} cannot contain generic types", scope_id),
}
}
pub fn get_field_symbol(&self, scope_id: usize, name: &str) -> Option<&FieldSymbol> {
match self.scope(scope_id) {
Scope::ClassBody(class_body_scope) => {
class_body_scope.field_symbols().get(name).map(Rc::as_ref)
}
_ => panic!("scope_id {} is not a ClassBodyScope", scope_id),
}
}
pub fn get_field_symbol_owned(&self, scope_id: usize, name: &str) -> Option<Rc<FieldSymbol>> {
match self.scope(scope_id) {
Scope::ClassBody(class_body_scope) => class_body_scope
.field_symbols()
.get(name)
.map(|s| Rc::clone(s)),
_ => panic!("scope_id {} is not a ClassBodyScope", scope_id),
}
}
pub fn get_constructor_symbol(&self, scope_id: usize) -> Option<&ConstructorSymbol> {
match self.scope(scope_id) {
Scope::ClassBody(class_body_scope) => {
class_body_scope.constructor_symbol().map(Rc::as_ref)
}
_ => panic!("scope_id {} is not a ClassBodyScope", scope_id),
}
}
pub fn get_constructor_symbol_owned(&self, scope_id: usize) -> Option<Rc<ConstructorSymbol>> {
match self.scope(scope_id) {
Scope::ClassBody(class_body_scope) => class_body_scope.constructor_symbol().cloned(),
_ => panic!(
"scope_id {} is not a ClassBodyScope: {:?}",
scope_id,
self.scope(scope_id)
),
}
}
pub fn get_function_symbol(&self, scope_id: usize, name: &str) -> Option<&FunctionSymbol> {
match self.scope(scope_id) {
Scope::Module(module_scope) => {
module_scope.function_symbols().get(name).map(Rc::as_ref)
}
Scope::ClassBody(class_body_scope) => class_body_scope
.function_symbols()
.get(name)
.map(Rc::as_ref),
_ => panic!("scope_id {} cannot contain Functions", scope_id),
}
}
pub fn get_function_symbol_owned(
&self,
scope_id: usize,
name: &str,
) -> Option<Rc<FunctionSymbol>> {
match self.scope(scope_id) {
Scope::Module(module_scope) => module_scope
.function_symbols()
.get(name)
.map(|s| Rc::clone(s)),
Scope::ClassBody(class_body_scope) => class_body_scope
.function_symbols()
.get(name)
.map(|s| Rc::clone(s)),
_ => panic!("scope_id {} cannot contain Functions", scope_id),
}
}
pub fn get_parameter_symbol(&self, scope_id: usize, name: &str) -> Option<&ParameterSymbol> {
match self.scope(scope_id) {
Scope::Function(function_scope) => {
function_scope.parameter_symbols().get(name).map(Rc::as_ref)
}
_ => panic!("scope_id {} cannot contain Parameters", scope_id),
}
}
pub fn get_parameter_symbol_owned(
&self,
scope_id: usize,
name: &str,
) -> Option<Rc<ParameterSymbol>> {
match self.scope(scope_id) {
Scope::Function(function_scope) => {
function_scope.parameter_symbols().get(name).cloned()
}
_ => panic!("scope_id {} cannot contain Parameters", scope_id),
}
}
pub fn get_variable_symbol(&self, scope_id: usize, name: &str) -> Option<&VariableSymbol> {
match &self.scopes[scope_id] { match &self.scopes[scope_id] {
Scope::Block(block_scope) => block_scope.variable_symbols().get(name).cloned().unwrap(), Scope::Block(block_scope) => block_scope.variable_symbols().get(name).map(Rc::as_ref),
_ => panic!("scope_id {} is not a BlockScope", scope_id),
}
}
pub fn get_variable_symbol_owned(
&self,
scope_id: usize,
name: &str,
) -> Option<Rc<VariableSymbol>> {
match self.scope(scope_id) {
Scope::Block(block_scope) => block_scope.variable_symbols().get(name).map(Rc::clone),
_ => panic!("scope_id {} is not a BlockScope", scope_id), _ => panic!("scope_id {} is not a BlockScope", scope_id),
} }
} }
} }
pub enum SymbolInsertError {
AlreadyDeclared(AlreadyDeclared),
}
pub struct AlreadyDeclared {
symbol: Rc<RefCell<dyn Symbol>>,
}
impl AlreadyDeclared {
pub fn new(symbol: Rc<RefCell<dyn Symbol>>) -> Self {
Self { symbol }
}
pub fn symbol(&self) -> &Rc<RefCell<dyn Symbol>> {
&self.symbol
}
}

View File

@ -1,10 +1,6 @@
use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol; use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol::type_symbol::TypeSymbol;
use crate::symbol_table::SymbolTable;
use std::cell::RefCell;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
@ -14,9 +10,9 @@ pub enum TypeInfo {
Integer, Integer,
Double, Double,
String, String,
Function(Rc<RefCell<FunctionSymbol>>), Function(Rc<FunctionSymbol>),
ClassInstance(Rc<RefCell<ClassSymbol>>), Class(Rc<ClassSymbol>),
GenericType(Rc<RefCell<GenericParameterSymbol>>), GenericType(Rc<GenericParameterSymbol>),
Void, Void,
} }
@ -29,16 +25,19 @@ impl Display for TypeInfo {
TypeInfo::String => write!(f, "String"), TypeInfo::String => write!(f, "String"),
TypeInfo::Function(function_symbol) => { TypeInfo::Function(function_symbol) => {
write!(f, "fn(")?; write!(f, "fn(")?;
for parameter in function_symbol.borrow().parameters() { for (i, parameter) in function_symbol.parameters().iter().enumerate() {
parameter.borrow().type_info().fmt(f)?; parameter.declared_name().fmt(f)?;
if i < function_symbol.parameters().len() - 1 {
f.write_str(", ")?;
}
} }
write!(f, ")") write!(f, ")")
} }
TypeInfo::ClassInstance(class_symbol) => { TypeInfo::Class(class_symbol) => {
write!(f, "{}", class_symbol.borrow().declared_name()) write!(f, "Class({:?})", class_symbol)
} }
TypeInfo::GenericType(generic_parameter_symbol) => { TypeInfo::GenericType(generic_parameter_symbol) => {
write!(f, "{}", generic_parameter_symbol.borrow().declared_name()) write!(f, "{}", generic_parameter_symbol.declared_name())
} }
TypeInfo::Void => write!(f, "Void"), TypeInfo::Void => write!(f, "Void"),
} }
@ -54,31 +53,6 @@ fn are_numbers(left: &TypeInfo, right: &TypeInfo) -> bool {
} }
impl TypeInfo { impl TypeInfo {
pub fn from_declared_name(
declared_name: &str,
scope_id: usize,
symbol_table: &SymbolTable,
class_context: Option<&Rc<RefCell<ClassSymbol>>>,
) -> Option<Self> {
match declared_name {
"Any" => Some(TypeInfo::Any),
"Int" => Some(TypeInfo::Integer),
"Double" => Some(TypeInfo::Double),
"String" => Some(TypeInfo::String),
"Void" => Some(TypeInfo::Void),
"Self" => Some(TypeInfo::ClassInstance(class_context.unwrap().clone())),
_ => match symbol_table.find_type_symbol(scope_id, declared_name) {
None => None,
Some(type_symbol) => match type_symbol {
TypeSymbol::Class(class_symbol) => Some(TypeInfo::ClassInstance(class_symbol)),
TypeSymbol::GenericParameter(generic_parameter_symbol) => {
Some(TypeInfo::GenericType(generic_parameter_symbol))
}
},
},
}
}
pub fn is_assignable_from(&self, other: &TypeInfo) -> bool { pub fn is_assignable_from(&self, other: &TypeInfo) -> bool {
match self { match self {
TypeInfo::Any => true, TypeInfo::Any => true,
@ -94,21 +68,16 @@ impl TypeInfo {
TypeInfo::Function(_) => { TypeInfo::Function(_) => {
unimplemented!("Type matching on Functions not yet supported.") unimplemented!("Type matching on Functions not yet supported.")
} }
TypeInfo::ClassInstance(class_symbol) => { TypeInfo::Class(class_symbol) => match other {
match other { TypeInfo::Class(other_class_symbol) => class_symbol == other_class_symbol,
TypeInfo::ClassInstance(other_class_symbol) => { _ => false,
class_symbol.borrow().declared_name() },
== other_class_symbol.borrow().declared_name() // good enough for now
}
_ => false,
}
}
TypeInfo::GenericType(generic_parameter_symbol) => { TypeInfo::GenericType(generic_parameter_symbol) => {
if generic_parameter_symbol.borrow().extends().len() > 0 { // if generic_parameter_symbol.extends().len() > 0 {
unimplemented!( // unimplemented!(
"Assigning to generic parameter type with extends type uses not yet supported." // "Assigning to generic parameter type with extends type uses not yet supported."
); // );
} // }
true true
} }
TypeInfo::Void => { TypeInfo::Void => {

118
dmc-lib/src/types_table.rs Normal file
View File

@ -0,0 +1,118 @@
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol::variable_symbol::VariableSymbol;
use crate::type_info::TypeInfo;
use std::collections::HashMap;
use std::rc::Rc;
pub struct TypesTable {
class_types: HashMap<Rc<ClassSymbol>, TypeInfo>,
class_instance_types: HashMap<Rc<ClassSymbol>, TypeInfo>,
generic_parameter_types: HashMap<Rc<GenericParameterSymbol>, TypeInfo>,
field_types: HashMap<Rc<FieldSymbol>, TypeInfo>,
constructor_return_types: HashMap<Rc<ConstructorSymbol>, TypeInfo>,
function_types: HashMap<Rc<FunctionSymbol>, TypeInfo>,
function_return_types: HashMap<Rc<FunctionSymbol>, TypeInfo>,
parameter_types: HashMap<Rc<ParameterSymbol>, TypeInfo>,
variable_types: HashMap<Rc<VariableSymbol>, TypeInfo>,
}
impl TypesTable {
pub fn new() -> Self {
Self {
class_types: HashMap::new(),
class_instance_types: HashMap::new(),
generic_parameter_types: HashMap::new(),
field_types: HashMap::new(),
parameter_types: HashMap::new(),
variable_types: HashMap::new(),
function_types: HashMap::new(),
function_return_types: HashMap::new(),
constructor_return_types: HashMap::new(),
}
}
pub fn class_types(&self) -> &HashMap<Rc<ClassSymbol>, TypeInfo> {
&self.class_types
}
pub fn class_types_mut(&mut self) -> &mut HashMap<Rc<ClassSymbol>, TypeInfo> {
&mut self.class_types
}
pub fn class_instance_types(&self) -> &HashMap<Rc<ClassSymbol>, TypeInfo> {
&self.class_instance_types
}
pub fn class_instance_types_mut(&mut self) -> &mut HashMap<Rc<ClassSymbol>, TypeInfo> {
&mut self.class_instance_types
}
pub fn generic_parameter_types(&self) -> &HashMap<Rc<GenericParameterSymbol>, TypeInfo> {
&self.generic_parameter_types
}
pub fn generic_parameter_types_mut(
&mut self,
) -> &mut HashMap<Rc<GenericParameterSymbol>, TypeInfo> {
&mut self.generic_parameter_types
}
pub fn field_types(&self) -> &HashMap<Rc<FieldSymbol>, TypeInfo> {
&self.field_types
}
pub fn field_types_mut(&mut self) -> &mut HashMap<Rc<FieldSymbol>, TypeInfo> {
&mut self.field_types
}
pub fn parameter_types(&self) -> &HashMap<Rc<ParameterSymbol>, TypeInfo> {
&self.parameter_types
}
pub fn parameter_types_mut(&mut self) -> &mut HashMap<Rc<ParameterSymbol>, TypeInfo> {
&mut self.parameter_types
}
pub fn variable_types(&self) -> &HashMap<Rc<VariableSymbol>, TypeInfo> {
&self.variable_types
}
pub fn variable_types_mut(&mut self) -> &mut HashMap<Rc<VariableSymbol>, TypeInfo> {
&mut self.variable_types
}
pub fn function_types(&self) -> &HashMap<Rc<FunctionSymbol>, TypeInfo> {
&self.function_types
}
pub fn function_types_mut(&mut self) -> &mut HashMap<Rc<FunctionSymbol>, TypeInfo> {
&mut self.function_types
}
#[deprecated]
pub fn function_return_types(&self) -> &HashMap<Rc<FunctionSymbol>, TypeInfo> {
&self.function_return_types
}
#[deprecated]
pub fn function_return_types_mut(&mut self) -> &mut HashMap<Rc<FunctionSymbol>, TypeInfo> {
&mut self.function_return_types
}
#[deprecated]
pub fn constructor_return_types(&self) -> &HashMap<Rc<ConstructorSymbol>, TypeInfo> {
&self.constructor_return_types
}
#[deprecated]
pub fn constructor_return_types_mut(
&mut self,
) -> &mut HashMap<Rc<ConstructorSymbol>, TypeInfo> {
&mut self.constructor_return_types
}
}

View File

@ -2,8 +2,10 @@
mod e2e_tests { mod e2e_tests {
use dmc_lib::constants_table::ConstantsTable; use dmc_lib::constants_table::ConstantsTable;
use dmc_lib::diagnostic::Diagnostic; use dmc_lib::diagnostic::Diagnostic;
use dmc_lib::intrinsics::{insert_intrinsic_symbols, insert_intrinsic_types};
use dmc_lib::parser::parse_compilation_unit; use dmc_lib::parser::parse_compilation_unit;
use dmc_lib::symbol_table::SymbolTable; use dmc_lib::symbol_table::SymbolTable;
use dmc_lib::types_table::TypesTable;
use dvm_lib::vm::class::Class; use dvm_lib::vm::class::Class;
use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::constant::{Constant, StringConstant};
use dvm_lib::vm::function::Function; use dvm_lib::vm::function::Function;
@ -26,7 +28,7 @@ mod e2e_tests {
panic!("There were diagnostics."); panic!("There were diagnostics.");
} }
fn prepare_context(input: &str) -> DvmContext { fn prepare_context(input: &str) -> Result<DvmContext, Vec<Diagnostic>> {
let parse_result = parse_compilation_unit(input); let parse_result = parse_compilation_unit(input);
let mut compilation_unit = match parse_result { let mut compilation_unit = match parse_result {
Ok(compilation_unit) => compilation_unit, Ok(compilation_unit) => compilation_unit,
@ -36,29 +38,19 @@ mod e2e_tests {
}; };
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
symbol_table.push_module_scope("global_scope");
insert_intrinsic_symbols(&mut symbol_table);
match compilation_unit.gather_declared_names(&mut symbol_table) { let mut types_table = TypesTable::new();
Ok(_) => {} insert_intrinsic_types(&symbol_table, &mut types_table);
Err(diagnostics) => {
report_diagnostics(&diagnostics);
}
}
match compilation_unit.check_name_usages(&symbol_table) { compilation_unit.init_scopes(&mut symbol_table);
Ok(_) => {} compilation_unit.gather_symbols_into(&mut symbol_table)?;
Err(diagnostics) => { compilation_unit.check_names(&mut symbol_table)?;
report_diagnostics(&diagnostics); compilation_unit.gather_types_into(&symbol_table, &mut types_table)?;
} compilation_unit.type_check(&symbol_table, &mut types_table)?;
}
match compilation_unit.type_check(&symbol_table) { let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table, &types_table);
Ok(_) => {}
Err(diagnostics) => {
report_diagnostics(&diagnostics);
}
}
let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table);
let mut functions: Vec<Function> = vec![]; let mut functions: Vec<Function> = vec![];
let mut constants_table = ConstantsTable::new(); let mut constants_table = ConstantsTable::new();
@ -95,7 +87,7 @@ mod e2e_tests {
); );
} }
dvm_context Ok(dvm_context)
} }
fn get_result( fn get_result(
@ -116,7 +108,10 @@ mod e2e_tests {
} }
fn assert_result(input: &str, function_name: &str, arguments: &[Value], expected_value: Value) { fn assert_result(input: &str, function_name: &str, arguments: &[Value], expected_value: Value) {
let context = prepare_context(input); let context = match prepare_context(input) {
Ok(context) => context,
Err(diagnostics) => report_diagnostics(&diagnostics),
};
match get_result(&context, function_name, arguments) { match get_result(&context, function_name, arguments) {
None => panic!("Call returned no value"), None => panic!("Call returned no value"),
Some(result_value) => { Some(result_value) => {
@ -214,41 +209,6 @@ mod e2e_tests {
) )
} }
#[test]
fn two_classes() {
let context = prepare_context(
"
class Foo
mut bar: Int = 42
ctor(_bar: Int)
end
fn baz() -> Int
bar
end
end
class Qux
fn foo() -> Foo
Foo(42)
end
end
fn foo(n: Int) -> Foo
Foo(n)
end
",
);
let result = get_result(&context, "foo", &[Value::Int(42)]);
assert!(result.is_some());
let value = result.unwrap();
assert!(matches!(value, Value::Object(_)));
let o = value.unwrap_object().borrow();
assert_eq!(o.fields().len(), 1);
assert_eq!(o.fields()[0].unwrap_int(), 42);
}
#[test] #[test]
fn simple_assign() { fn simple_assign() {
assert_result( assert_result(
@ -266,7 +226,7 @@ mod e2e_tests {
} }
#[test] #[test]
fn assign_field() { fn assign_field() -> Result<(), Vec<Diagnostic>> {
let context = prepare_context( let context = prepare_context(
" "
class Foo class Foo
@ -281,7 +241,7 @@ mod e2e_tests {
Foo(42) Foo(42)
end end
", ",
); )?;
let result = get_result(&context, "foo", &vec![]); let result = get_result(&context, "foo", &vec![]);
assert!(result.is_some()); assert!(result.is_some());
let value = result.unwrap(); let value = result.unwrap();
@ -289,10 +249,11 @@ mod e2e_tests {
let o = value.unwrap_object().borrow(); let o = value.unwrap_object().borrow();
assert_eq!(o.fields().len(), 1); assert_eq!(o.fields().len(), 1);
assert_eq!(o.fields()[0].unwrap_int(), 42); assert_eq!(o.fields()[0].unwrap_int(), 42);
Ok(())
} }
#[test] #[test]
fn see_what_happens() { fn generic_field_and_ctor_param() -> Result<(), Vec<Diagnostic>> {
let context = prepare_context( let context = prepare_context(
" "
class Foo<T> class Foo<T>
@ -306,7 +267,7 @@ mod e2e_tests {
Foo(42) Foo(42)
end end
", ",
); )?;
let result = get_result(&context, "main", &vec![]); let result = get_result(&context, "main", &vec![]);
assert!(result.is_some()); assert!(result.is_some());
let value = result.unwrap(); let value = result.unwrap();
@ -314,49 +275,6 @@ mod e2e_tests {
let o = value.unwrap_object().borrow(); let o = value.unwrap_object().borrow();
assert_eq!(o.fields().len(), 1); assert_eq!(o.fields().len(), 1);
assert_eq!(o.fields()[0].unwrap_int(), 42); assert_eq!(o.fields()[0].unwrap_int(), 42);
} Ok(())
}
#[cfg(test)]
mod diagnostic_tests {
use dmc_lib::diagnostic::Diagnostic;
use dmc_lib::parser::parse_compilation_unit;
use dmc_lib::symbol_table::SymbolTable;
fn get_diagnostics(input: &str) -> Vec<Diagnostic> {
let parse_result = parse_compilation_unit(input);
let mut compilation_unit = match parse_result {
Ok(compilation_unit) => compilation_unit,
Err(diagnostics) => {
return diagnostics;
}
};
let mut symbol_table = SymbolTable::new();
match compilation_unit.gather_declared_names(&mut symbol_table) {
Ok(_) => {}
Err(diagnostics) => {
return diagnostics;
}
}
match compilation_unit.check_name_usages(&symbol_table) {
Ok(_) => {}
Err(diagnostics) => {
return diagnostics;
}
}
match compilation_unit.type_check(&symbol_table) {
Ok(_) => vec![],
Err(diagnostics) => diagnostics,
}
}
#[test]
fn wrong_return_type() {
let diagnostics = get_diagnostics("fn main() -> String 42 end");
assert_eq!(diagnostics.len(), 1);
} }
} }

View File

@ -0,0 +1,13 @@
class Class
pub type Target
pub declaredName: String
pub fqn: String
pub extern fn constructor() -> Constructor<Self::Target>
end
class Constructor<T>
pub type Args : []
pub fn new(...args: Self::Args) -> T
end

View File

@ -0,0 +1,47 @@
trait Add<R = Self>
type Output = Self
fn add(other: R) -> Self::Output
end
intrinsic class Int end
intrinsic class Double end
impl Add for Int
fn add(other: Self) -> Self
self + other
end
end
impl Add<Double> for Int
type Output = Double
fn add(other: Double) -> Double
self + other
end
end
impl Add for Double
fn add(other: Self) -> Self
self + other
end
end
impl Add<Int> for Double
fn add(other: Int) -> Self
self + other
end
end
fn add_rough_pi(a: impl Add<Double>) -> Double = a + 3.14
fn concat<T: impl Add<U>, U>(a: T, b: U) -> T::Output
a + b
end
fn main()
let x = add_rough_pi(3)
println(x) // 6.14
let y = concat(1, 2) // 3
let z = concat("Hello, ", "World!") // "Hello, World!"
end