Compare commits
No commits in common. "dbbcda638f9ea28aa1b84a144aab377dbebf6f3f" and "826c9fb2b75513eddca62bd2db1ebdd18c14d9ce" have entirely different histories.
dbbcda638f
...
826c9fb2b7
@ -1,4 +1,24 @@
|
|||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::helpers::{insert_resolved_names_into, insert_resolved_types_into};
|
||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
|
use crate::ast::ir_util::get_or_init_mut_field_pointer_variable;
|
||||||
|
use crate::ast::{NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics, SecondaryLabel};
|
||||||
|
use crate::diagnostic_factories::{
|
||||||
|
destination_must_be_mutable, mismatched_assign_types, must_be_l_value,
|
||||||
|
};
|
||||||
|
use crate::error_codes::{ASSIGN_LHS_IMMUTABLE, ASSIGN_MISMATCHED_TYPES, ASSIGN_NO_L_VALUE};
|
||||||
|
use crate::ir::ir_assign::IrAssign;
|
||||||
|
use crate::ir::ir_set_field::IrSetField;
|
||||||
|
use crate::ir::ir_statement::IrStatement;
|
||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct AssignStatement {
|
pub struct AssignStatement {
|
||||||
destination: Box<Expression>,
|
destination: Box<Expression>,
|
||||||
@ -20,4 +40,513 @@ impl AssignStatement {
|
|||||||
pub fn value(&self) -> &Expression {
|
pub fn value(&self) -> &Expression {
|
||||||
&self.value
|
&self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for expression in [self.destination.as_ref(), self.value.as_ref()] {
|
||||||
|
let (ns, mut ds) = expression.resolve_names_static(symbol_table);
|
||||||
|
for (node_id, symbol) in ns {
|
||||||
|
names_table.insert(node_id, symbol);
|
||||||
|
}
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_ctor(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
initialized_fields: &mut HashSet<Rc<str>>,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (ns, mut ds) = self
|
||||||
|
.value
|
||||||
|
.resolve_names_ctor(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.destination.as_ref() {
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
let (ns, mut ds) =
|
||||||
|
identifier.resolve_name_ctor_destination(symbol_table, initialized_fields);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// no-op, because this is a non-L-Value and will be caught during type checking.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for expression in [self.destination.as_ref(), self.value.as_ref()] {
|
||||||
|
let (ns, mut ds) = expression.resolve_names_method(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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 resolve_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (NodesToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut nodes_to_types = NodesToTypes::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (nts, mut ds) = self.value.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
insert_resolved_types_into(nts, &mut nodes_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let (nts, mut ds) = self
|
||||||
|
.destination
|
||||||
|
.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
insert_resolved_types_into(nts, &mut nodes_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that destination is L value, mutable, and assignable
|
||||||
|
match &*self.destination {
|
||||||
|
// must be identifier (for now)
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
let expressible_symbol = nodes_to_symbols
|
||||||
|
.get(&identifier.node_id())
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_expressible_symbol();
|
||||||
|
let is_mut = match &expressible_symbol {
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => field_symbol.is_mut(),
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => variable_symbol.is_mut(),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// check mut
|
||||||
|
if !is_mut {
|
||||||
|
diagnostics.push(destination_must_be_mutable(
|
||||||
|
self.destination.source_range(),
|
||||||
|
expressible_symbol.source_range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_expressible_symbol_source_range =
|
||||||
|
expressible_symbol.source_range().cloned();
|
||||||
|
|
||||||
|
let lhs_type = symbols_to_types
|
||||||
|
.get(&expressible_symbol.into_symbol())
|
||||||
|
.unwrap();
|
||||||
|
let rhs_type = nodes_to_types.get(&self.value.node_id()).unwrap();
|
||||||
|
|
||||||
|
// check assignable
|
||||||
|
if !lhs_type.is_assignable_from(rhs_type) {
|
||||||
|
diagnostics.push(mismatched_assign_types(
|
||||||
|
rhs_type,
|
||||||
|
lhs_type,
|
||||||
|
&SourceRange::new(
|
||||||
|
self.destination.source_range().start(),
|
||||||
|
self.value.source_range().end(),
|
||||||
|
),
|
||||||
|
maybe_expressible_symbol_source_range.as_ref(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
diagnostics.push(must_be_l_value(self.destination.source_range()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
|
match self.value.type_check(symbol_table, types_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mut value_diagnostics) => {
|
||||||
|
diagnostics.append(&mut value_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.destination.type_check(symbol_table, types_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mut destination_diagnostics) => {
|
||||||
|
diagnostics.append(&mut destination_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check destination is l value
|
||||||
|
match &*self.destination {
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
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
|
||||||
|
if !is_mut {
|
||||||
|
let secondary_label =
|
||||||
|
if let Some(source_range) = expressible_symbol.source_range() {
|
||||||
|
Some(SecondaryLabel::new(
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
Some("Destination (declared here) is immutable.".to_string()),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut diagnostic = Diagnostic::new(
|
||||||
|
"Destination is immutable and not re-assignable.",
|
||||||
|
self.destination.source_range().start(),
|
||||||
|
self.destination.source_range().end(),
|
||||||
|
)
|
||||||
|
.with_primary_label_message("Attempt to mutate immutable destination.")
|
||||||
|
.with_reporter(file!(), line!())
|
||||||
|
.with_error_code(ASSIGN_LHS_IMMUTABLE);
|
||||||
|
|
||||||
|
if let Some(secondary_label) = secondary_label {
|
||||||
|
diagnostic = diagnostic.with_secondary_labels(&[secondary_label]);
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check assignable
|
||||||
|
let lhs_type = match &expressible_symbol {
|
||||||
|
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) {
|
||||||
|
let secondary_label =
|
||||||
|
if let Some(source_range) = expressible_symbol.source_range() {
|
||||||
|
Some(SecondaryLabel::new(
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
Some(format!(
|
||||||
|
"Destination declared here is of type {}.",
|
||||||
|
lhs_type
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut diagnostic = Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Mismatched types: right-hand side {} is not assignable to left {}.",
|
||||||
|
rhs_type, lhs_type
|
||||||
|
),
|
||||||
|
self.destination.source_range().start(),
|
||||||
|
self.value.source_range().end(),
|
||||||
|
)
|
||||||
|
.with_primary_label_message(&format!(
|
||||||
|
"Attempt to assign {} to {}.",
|
||||||
|
rhs_type, lhs_type
|
||||||
|
))
|
||||||
|
.with_error_code(ASSIGN_MISMATCHED_TYPES)
|
||||||
|
.with_reporter(file!(), line!());
|
||||||
|
|
||||||
|
if let Some(secondary_label) = secondary_label {
|
||||||
|
diagnostic = diagnostic.with_secondary_labels(&[secondary_label]);
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let diagnostic = Diagnostic::new(
|
||||||
|
"Left-hand side of assign must be an L value.",
|
||||||
|
self.destination.source_range().start(),
|
||||||
|
self.destination.source_range().end(),
|
||||||
|
)
|
||||||
|
.with_primary_label_message("Must be L value.")
|
||||||
|
.with_reporter(file!(), line!())
|
||||||
|
.with_error_code(ASSIGN_NO_L_VALUE);
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) {
|
||||||
|
let destination_symbol = match &*self.destination {
|
||||||
|
Expression::Identifier(identifier) => symbol_table
|
||||||
|
.find_expressible_symbol(identifier.scope_id(), identifier.name())
|
||||||
|
.unwrap(),
|
||||||
|
_ => unreachable!("Destination must be a mutable L value"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ir_statement = match destination_symbol {
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => {
|
||||||
|
let field_type = types_table.field_types().get(&field_symbol).unwrap();
|
||||||
|
let mut_field_pointer_variable =
|
||||||
|
get_or_init_mut_field_pointer_variable(builder, &field_symbol, field_type)
|
||||||
|
.clone();
|
||||||
|
let ir_set_field = IrSetField::new(
|
||||||
|
todo!(),
|
||||||
|
self.value
|
||||||
|
.to_ir_expression(builder, symbol_table, types_table)
|
||||||
|
.expect("Attempt to convert non-value to value"),
|
||||||
|
);
|
||||||
|
IrStatement::SetField(ir_set_field)
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||||
|
let vr_variable = builder.local_variables().get(&variable_symbol).unwrap();
|
||||||
|
let ir_assign = IrAssign::new(
|
||||||
|
todo!(),
|
||||||
|
self.value
|
||||||
|
.to_ir_operation(builder, symbol_table, types_table),
|
||||||
|
);
|
||||||
|
IrStatement::Assign(ir_assign)
|
||||||
|
}
|
||||||
|
_ => unreachable!("Destination must be a mutable L value"),
|
||||||
|
};
|
||||||
|
|
||||||
|
builder.current_block_mut().add_statement(ir_statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) {
|
||||||
|
let destination_expressible_symbol = match self.destination.as_ref() {
|
||||||
|
Expression::Identifier(identifier) => nodes_to_symbols
|
||||||
|
.get(&identifier.scope_id())
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_expressible_symbol(),
|
||||||
|
_ => unreachable!("Destination must be a mutable L value"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ir_statement = match destination_expressible_symbol {
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => {
|
||||||
|
let field_type = symbols_to_types
|
||||||
|
.get(&Symbol::Field(field_symbol.clone()))
|
||||||
|
.unwrap();
|
||||||
|
let mut_field_pointer_variable =
|
||||||
|
get_or_init_mut_field_pointer_variable(builder, &field_symbol, field_type)
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let ir_set_field = IrSetField::new(
|
||||||
|
todo!(),
|
||||||
|
self.value.lower_to_ir_expression(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
IrStatement::SetField(ir_set_field)
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||||
|
let ir_variable = builder.local_variables().get(&variable_symbol).unwrap();
|
||||||
|
let ir_assign = IrAssign::new(
|
||||||
|
todo!(),
|
||||||
|
self.value.lower_to_ir_operation(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
IrStatement::Assign(ir_assign)
|
||||||
|
}
|
||||||
|
_ => unreachable!("Destination must be a mutable L value"),
|
||||||
|
};
|
||||||
|
|
||||||
|
builder.current_block_mut().add_statement(ir_statement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
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::parser::get_compilation_unit;
|
||||||
|
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]
|
||||||
|
fn finds_mismatched_types() -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut compilation_unit = get_compilation_unit(
|
||||||
|
"
|
||||||
|
fn main()
|
||||||
|
let mut x = 4
|
||||||
|
x = \"Hello\"
|
||||||
|
end
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
let mut types_table = TypesTable::new();
|
||||||
|
compile_up_to_type_check(&mut compilation_unit, &mut symbol_table, &mut types_table)?;
|
||||||
|
let diagnostics = compilation_unit
|
||||||
|
.type_check(&symbol_table, &mut types_table)
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(diagnostics.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
diagnostics[0].error_code().unwrap(),
|
||||||
|
ASSIGN_MISMATCHED_TYPES
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_no_l_value() -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut compilation_unit = get_compilation_unit(
|
||||||
|
"
|
||||||
|
fn main()
|
||||||
|
42 = 42
|
||||||
|
end
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
let mut types_table = TypesTable::new();
|
||||||
|
compile_up_to_type_check(&mut compilation_unit, &mut symbol_table, &mut types_table)?;
|
||||||
|
let diagnostics = compilation_unit
|
||||||
|
.type_check(&symbol_table, &mut types_table)
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(diagnostics.len(), 1);
|
||||||
|
assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_NO_L_VALUE);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finds_immutable_destination() -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut compilation_unit = get_compilation_unit(
|
||||||
|
"
|
||||||
|
fn main()
|
||||||
|
let x = 42
|
||||||
|
x = 43
|
||||||
|
end
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
let mut types_table = TypesTable::new();
|
||||||
|
compile_up_to_type_check(&mut compilation_unit, &mut symbol_table, &mut types_table)?;
|
||||||
|
let diagnostics = compilation_unit
|
||||||
|
.type_check(&symbol_table, &mut types_table)
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(diagnostics.len(), 1);
|
||||||
|
assert_eq!(diagnostics[0].error_code().unwrap(), ASSIGN_LHS_IMMUTABLE);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,23 @@
|
|||||||
use crate::ast::NodeId;
|
|
||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::helpers::{insert_resolved_names_into, insert_resolved_types_into};
|
||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
|
use crate::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::error_codes::BINARY_INCOMPATIBLE_TYPES;
|
||||||
|
use crate::ir::ir_assign::IrAssign;
|
||||||
|
use crate::ir::ir_binary_operation::{IrBinaryOperation, IrBinaryOperator};
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
|
use crate::ir::ir_operation::IrOperation;
|
||||||
|
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_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use crate::{diagnostics_result, handle_diagnostic, handle_diagnostics, maybe_return_diagnostics};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub enum BinaryOperation {
|
pub enum BinaryOperation {
|
||||||
Multiply,
|
Multiply,
|
||||||
@ -21,6 +38,7 @@ pub struct BinaryExpression {
|
|||||||
rhs: Box<Expression>,
|
rhs: Box<Expression>,
|
||||||
op: BinaryOperation,
|
op: BinaryOperation,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
type_info: Option<TypeInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinaryExpression {
|
impl BinaryExpression {
|
||||||
@ -37,6 +55,7 @@ impl BinaryExpression {
|
|||||||
rhs: rhs.into(),
|
rhs: rhs.into(),
|
||||||
op,
|
op,
|
||||||
source_range,
|
source_range,
|
||||||
|
type_info: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,4 +78,558 @@ impl BinaryExpression {
|
|||||||
pub fn source_range(&self) -> &SourceRange {
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
&self.source_range
|
&self.source_range
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_info(&self) -> &TypeInfo {
|
||||||
|
self.type_info.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.lhs.init_scopes(symbol_table, container_scope);
|
||||||
|
self.rhs.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for expression in [self.lhs.as_ref(), self.rhs.as_ref()] {
|
||||||
|
let (ns, mut ds) = expression.resolve_names_static(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_field_init(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for expression in [self.lhs.as_ref(), self.rhs.as_ref()] {
|
||||||
|
let (ns, mut ds) = expression.resolve_names_field_init(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_ctor(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for expression in [self.lhs.as_ref(), self.rhs.as_ref()] {
|
||||||
|
let (ns, mut ds) = expression.resolve_names_ctor(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for expression in [self.lhs.as_ref(), self.rhs.as_ref()] {
|
||||||
|
let (ns, mut ds) = expression.resolve_names_method(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_field_initializer_names(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
class_symbol: &ClassSymbol,
|
||||||
|
) -> Vec<Diagnostic> {
|
||||||
|
[
|
||||||
|
self.lhs
|
||||||
|
.check_field_initializer_names(symbol_table, class_symbol),
|
||||||
|
self.rhs
|
||||||
|
.check_field_initializer_names(symbol_table, class_symbol),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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 resolve_op_result(
|
||||||
|
&self,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
op_compatibility_check: impl Fn(&TypeInfo, &TypeInfo) -> bool,
|
||||||
|
get_op_result: impl Fn(&TypeInfo, &TypeInfo) -> TypeInfo,
|
||||||
|
lazy_diagnostic_message: impl Fn(&TypeInfo, &TypeInfo) -> String,
|
||||||
|
) -> (TypeInfo, Diagnostics) {
|
||||||
|
let lhs_type_info = nodes_to_types.get(&self.lhs.node_id()).unwrap();
|
||||||
|
let rhs_type_info = nodes_to_types.get(&self.rhs.node_id()).unwrap();
|
||||||
|
|
||||||
|
if op_compatibility_check(lhs_type_info, rhs_type_info) {
|
||||||
|
let op_result = get_op_result(lhs_type_info, rhs_type_info);
|
||||||
|
(op_result, Diagnostics::new())
|
||||||
|
} else {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
diagnostics.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
&lazy_diagnostic_message(lhs_type_info, rhs_type_info),
|
||||||
|
self.source_range.start(),
|
||||||
|
self.source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(BINARY_INCOMPATIBLE_TYPES),
|
||||||
|
);
|
||||||
|
(TypeInfo::PlaceholderError, diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (NodesToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut nodes_to_types = NodesToTypes::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (nts, mut ds) = self.lhs.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
insert_resolved_types_into(nts, &mut nodes_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let (nts, mut ds) = self.rhs.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
insert_resolved_types_into(nts, &mut nodes_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let (result_type_info, mut ds) = match &self.op {
|
||||||
|
BinaryOperation::Multiply => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_multiply(rhs),
|
||||||
|
|lhs, rhs| lhs.multiply_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot multiply {} by {}", lhs, rhs),
|
||||||
|
),
|
||||||
|
BinaryOperation::Divide => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_divide(rhs),
|
||||||
|
|lhs, rhs| lhs.divide_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot divide {} by {}", lhs, rhs),
|
||||||
|
),
|
||||||
|
BinaryOperation::Modulo => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_modulo(rhs),
|
||||||
|
|lhs, rhs| lhs.modulo_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot modulo {} by {}", lhs, rhs),
|
||||||
|
),
|
||||||
|
BinaryOperation::Add => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_add(rhs),
|
||||||
|
|lhs, rhs| lhs.add_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot add {} and {}", lhs, rhs),
|
||||||
|
),
|
||||||
|
BinaryOperation::Subtract => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_subtract(rhs),
|
||||||
|
|lhs, rhs| lhs.subtract_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot subtract {} from {}", rhs, lhs), // n.b. order
|
||||||
|
),
|
||||||
|
BinaryOperation::LeftShift => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_left_shift(rhs),
|
||||||
|
|lhs, rhs| lhs.left_shift_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot left shift {} by {}", lhs, rhs),
|
||||||
|
),
|
||||||
|
BinaryOperation::RightShift => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_right_shift(rhs),
|
||||||
|
|lhs, rhs| lhs.right_shift_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot right shift {} by {}", lhs, rhs),
|
||||||
|
),
|
||||||
|
BinaryOperation::BitwiseAnd => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_bitwise_and(rhs),
|
||||||
|
|lhs, rhs| lhs.bitwise_and_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot bitwise-and {} by {}", lhs, rhs),
|
||||||
|
),
|
||||||
|
BinaryOperation::BitwiseXor => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_bitwise_xor(rhs),
|
||||||
|
|lhs, rhs| lhs.bitwise_xor_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot bitwise-xor {} by {}", lhs, rhs),
|
||||||
|
),
|
||||||
|
BinaryOperation::BitwiseOr => self.resolve_op_result(
|
||||||
|
&nodes_to_types,
|
||||||
|
|lhs, rhs| lhs.can_bitwise_or(rhs),
|
||||||
|
|lhs, rhs| lhs.bitwise_or_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot bitwise-or {} by {}", lhs, rhs),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes_to_types.insert(self.node_id, result_type_info);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_op(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
check: impl Fn(&TypeInfo, &TypeInfo) -> bool,
|
||||||
|
op_result: impl Fn(&TypeInfo, &TypeInfo) -> TypeInfo,
|
||||||
|
lazy_diagnostic_message: impl Fn(&TypeInfo, &TypeInfo) -> String,
|
||||||
|
) -> Result<(), Diagnostic> {
|
||||||
|
let lhs_type_info = self.lhs.type_info(symbol_table, types_table);
|
||||||
|
let rhs_type_info = self.rhs.type_info(symbol_table, types_table);
|
||||||
|
|
||||||
|
if check(lhs_type_info, rhs_type_info) {
|
||||||
|
self.type_info = Some(op_result(lhs_type_info, rhs_type_info));
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let diagnostic = Diagnostic::new(
|
||||||
|
&lazy_diagnostic_message(lhs_type_info, rhs_type_info),
|
||||||
|
self.source_range.start(),
|
||||||
|
self.source_range.end(),
|
||||||
|
)
|
||||||
|
.with_primary_label_message("Incompatible types for addition.")
|
||||||
|
.with_reporter(file!(), line!())
|
||||||
|
.with_error_code(BINARY_INCOMPATIBLE_TYPES);
|
||||||
|
Err(diagnostic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
|
handle_diagnostics!(self.lhs.type_check(symbol_table, types_table), diagnostics);
|
||||||
|
handle_diagnostics!(self.rhs.type_check(symbol_table, types_table), diagnostics);
|
||||||
|
|
||||||
|
maybe_return_diagnostics!(diagnostics);
|
||||||
|
|
||||||
|
match &self.op {
|
||||||
|
BinaryOperation::Multiply => {
|
||||||
|
handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_multiply(rhs),
|
||||||
|
|lhs, rhs| lhs.multiply_result(rhs),
|
||||||
|
|lhs, rhs| format!(
|
||||||
|
"Incompatible types: cannot multiply {} by {}",
|
||||||
|
lhs, rhs
|
||||||
|
)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BinaryOperation::Divide => {
|
||||||
|
handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_divide(rhs),
|
||||||
|
|lhs, rhs| lhs.divide_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot divide {} by {}", lhs, rhs)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BinaryOperation::Modulo => {
|
||||||
|
handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_modulo(rhs),
|
||||||
|
|lhs, rhs| lhs.modulo_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot modulo {} by {}", lhs, rhs)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BinaryOperation::Add => {
|
||||||
|
handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_add(rhs),
|
||||||
|
|lhs, rhs| lhs.add_result(&rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot add {} to {}.", rhs, lhs)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BinaryOperation::Subtract => {
|
||||||
|
handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_subtract(rhs),
|
||||||
|
|lhs, rhs| lhs.subtract_result(rhs),
|
||||||
|
|lhs, rhs| format!(
|
||||||
|
"Incompatible types: cannot subtract {} from {}.",
|
||||||
|
rhs, lhs
|
||||||
|
)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
)
|
||||||
|
}
|
||||||
|
BinaryOperation::LeftShift => {
|
||||||
|
handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_left_shift(rhs),
|
||||||
|
|lhs, rhs| lhs.left_shift_result(rhs),
|
||||||
|
|lhs, rhs| format!(
|
||||||
|
"Incompatible types: cannot left shift {} by {}",
|
||||||
|
lhs, rhs
|
||||||
|
)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BinaryOperation::RightShift => {
|
||||||
|
handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_right_shift(rhs),
|
||||||
|
|lhs, rhs| lhs.right_shift_result(rhs),
|
||||||
|
|lhs, rhs| format!(
|
||||||
|
"Incompatible types: cannot right shift {} by {}",
|
||||||
|
lhs, rhs
|
||||||
|
)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BinaryOperation::BitwiseAnd => {
|
||||||
|
handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_bitwise_and(rhs),
|
||||||
|
|lhs, rhs| lhs.bitwise_and_result(rhs),
|
||||||
|
|lhs, rhs| format!(
|
||||||
|
"Incompatible types: cannot bitwise and {} by {}",
|
||||||
|
lhs, rhs
|
||||||
|
)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BinaryOperation::BitwiseXor => handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_bitwise_xor(rhs),
|
||||||
|
|lhs, rhs| lhs.bitwise_xor_result(rhs),
|
||||||
|
|lhs, rhs| format!("Incompatible types: cannot bitwise xor {} by {}", lhs, rhs)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
),
|
||||||
|
BinaryOperation::BitwiseOr => {
|
||||||
|
handle_diagnostic!(
|
||||||
|
self.check_op(
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
|lhs, rhs| lhs.can_bitwise_or(rhs),
|
||||||
|
|lhs, rhs| lhs.bitwise_or_result(rhs),
|
||||||
|
|lhs, rhs| format!(
|
||||||
|
"Incompatible types: cannot bitwise or {} by {}",
|
||||||
|
lhs, rhs
|
||||||
|
)
|
||||||
|
),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics_result!(diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir_operation(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> IrOperation {
|
||||||
|
let lhs = self.lhs.lower_to_ir_expression(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
);
|
||||||
|
let rhs = self.rhs.lower_to_ir_expression(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
);
|
||||||
|
|
||||||
|
let ir_binary_operator = match &self.op {
|
||||||
|
BinaryOperation::Multiply => IrBinaryOperator::Multiply,
|
||||||
|
BinaryOperation::Divide => IrBinaryOperator::Divide,
|
||||||
|
BinaryOperation::Modulo => IrBinaryOperator::Modulo,
|
||||||
|
BinaryOperation::Add => IrBinaryOperator::Add,
|
||||||
|
BinaryOperation::Subtract => IrBinaryOperator::Subtract,
|
||||||
|
BinaryOperation::LeftShift => IrBinaryOperator::LeftShift,
|
||||||
|
BinaryOperation::RightShift => IrBinaryOperator::RightShift,
|
||||||
|
BinaryOperation::BitwiseAnd => IrBinaryOperator::BitwiseAnd,
|
||||||
|
BinaryOperation::BitwiseXor => IrBinaryOperator::BitwiseXor,
|
||||||
|
BinaryOperation::BitwiseOr => IrBinaryOperator::BitwiseOr,
|
||||||
|
};
|
||||||
|
IrOperation::Binary(IrBinaryOperation::new(lhs, rhs, ir_binary_operator))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn to_ir_operation(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> IrOperation {
|
||||||
|
let lhs = self
|
||||||
|
.lhs
|
||||||
|
.to_ir_expression(builder, symbol_table, types_table)
|
||||||
|
.expect("Attempt to use a non-value expression in binary expression.");
|
||||||
|
let rhs = self
|
||||||
|
.rhs
|
||||||
|
.to_ir_expression(builder, symbol_table, types_table)
|
||||||
|
.expect("Attempt to use a non-value expression in binary expression.");
|
||||||
|
let ir_binary_operation = match self.op {
|
||||||
|
BinaryOperation::Multiply => {
|
||||||
|
IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::Multiply)
|
||||||
|
}
|
||||||
|
BinaryOperation::Divide => IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::Divide),
|
||||||
|
BinaryOperation::Modulo => IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::Modulo),
|
||||||
|
BinaryOperation::Add => IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::Add),
|
||||||
|
BinaryOperation::Subtract => {
|
||||||
|
IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::Subtract)
|
||||||
|
}
|
||||||
|
BinaryOperation::LeftShift => {
|
||||||
|
IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::LeftShift)
|
||||||
|
}
|
||||||
|
BinaryOperation::RightShift => {
|
||||||
|
IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::RightShift)
|
||||||
|
}
|
||||||
|
BinaryOperation::BitwiseAnd => {
|
||||||
|
IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::BitwiseAnd)
|
||||||
|
}
|
||||||
|
BinaryOperation::BitwiseXor => {
|
||||||
|
IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::BitwiseXor)
|
||||||
|
}
|
||||||
|
BinaryOperation::BitwiseOr => {
|
||||||
|
IrBinaryOperation::new(lhs, rhs, IrBinaryOperator::BitwiseOr)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
IrOperation::Binary(ir_binary_operation)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn to_ir_expression(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> IrExpression {
|
||||||
|
let ir_operation = self.to_ir_operation(builder, symbol_table, types_table);
|
||||||
|
let t_var = todo!();
|
||||||
|
let as_rc = Rc::new(RefCell::new(t_var));
|
||||||
|
let ir_assign = IrAssign::new(todo!(), ir_operation);
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(ir_assign));
|
||||||
|
IrExpression::Variable(todo!())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir_expression(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> IrExpression {
|
||||||
|
let ir_operation =
|
||||||
|
self.lower_to_ir_operation(builder, nodes_to_symbols, symbols_to_types, nodes_to_types);
|
||||||
|
|
||||||
|
let type_info = nodes_to_types.get(&self.node_id).unwrap();
|
||||||
|
|
||||||
|
let t_var = Rc::new(RefCell::new(todo!()));
|
||||||
|
|
||||||
|
let ir_assign = IrAssign::new(todo!(), ir_operation);
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(ir_assign));
|
||||||
|
IrExpression::Variable(todo!())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,22 @@
|
|||||||
use crate::ast::NodeId;
|
|
||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::fqn_util::fqn_parts_to_string;
|
||||||
|
use crate::ast::helpers::{insert_resolved_names_into, insert_resolved_types_into};
|
||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
|
use crate::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::diagnostic_factories::{
|
||||||
|
class_has_no_constructor, mismatched_types, receiver_not_callable, wrong_number_of_arguments,
|
||||||
|
};
|
||||||
|
use crate::ir::ir_call::IrCall;
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::callable_symbol::CallableSymbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
@ -36,6 +52,470 @@ impl Call {
|
|||||||
&self.arguments
|
&self.arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (ns, mut ds) = self.callee.resolve_names_static(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for argument in &self.arguments {
|
||||||
|
let (ns, mut ds) = argument.resolve_names_static(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_field_init(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (ns, mut ds) = self
|
||||||
|
.callee
|
||||||
|
.resolve_names_field_init(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for argument in &self.arguments {
|
||||||
|
let (ns, mut ds) = argument.resolve_names_field_init(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_ctor(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (ns, mut ds) = self
|
||||||
|
.callee
|
||||||
|
.resolve_names_ctor(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for argument in &self.arguments {
|
||||||
|
let (ns, mut ds) = argument.resolve_names_ctor(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut nodes_to_symbols = NodesToSymbols::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (ns, mut ds) = self
|
||||||
|
.callee
|
||||||
|
.resolve_names_method(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut nodes_to_symbols);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for argument in &self.arguments {
|
||||||
|
let (ns, mut ds) = argument.resolve_names_method(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut nodes_to_symbols);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes_to_symbols, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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 resolve_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (NodesToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut nodes_to_types = NodesToTypes::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (nts, mut ds) = self
|
||||||
|
.callee
|
||||||
|
.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
insert_resolved_types_into(nts, &mut nodes_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for argument in &self.arguments {
|
||||||
|
let (nts, mut ds) = argument.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
insert_resolved_types_into(nts, &mut nodes_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get callee symbol and check that its callable
|
||||||
|
let callee_symbol = nodes_to_symbols.get(&self.callee.node_id()).unwrap();
|
||||||
|
let callable_symbol = match callee_symbol {
|
||||||
|
Symbol::Class(class_symbol) => match class_symbol.constructor_symbol_owned() {
|
||||||
|
None => {
|
||||||
|
diagnostics.push(class_has_no_constructor(
|
||||||
|
class_symbol.declared_name(),
|
||||||
|
self.callee.source_range(),
|
||||||
|
));
|
||||||
|
CallableSymbol::ErrorPlaceholder
|
||||||
|
}
|
||||||
|
Some(constructor_symbol) => CallableSymbol::Constructor(constructor_symbol),
|
||||||
|
},
|
||||||
|
Symbol::Function(function_symbol) => CallableSymbol::Function(function_symbol.clone()),
|
||||||
|
_ => {
|
||||||
|
diagnostics.push(receiver_not_callable(
|
||||||
|
symbols_to_types.get(&callee_symbol).unwrap(),
|
||||||
|
self.callee.source_range(),
|
||||||
|
));
|
||||||
|
CallableSymbol::ErrorPlaceholder
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let parameter_symbols = callable_symbol.parameters();
|
||||||
|
|
||||||
|
// check args length
|
||||||
|
if parameter_symbols.len() != self.arguments().len() {
|
||||||
|
diagnostics.push(wrong_number_of_arguments(
|
||||||
|
self.source_range(),
|
||||||
|
parameter_symbols.len(),
|
||||||
|
self.arguments().len(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check arg types
|
||||||
|
for i in 0..parameter_symbols.len() {
|
||||||
|
let parameter_symbol = parameter_symbols[i].clone();
|
||||||
|
let argument = if i < self.arguments.len() {
|
||||||
|
&self.arguments[i]
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let parameter_type_info = symbols_to_types
|
||||||
|
.get(&Symbol::Parameter(parameter_symbol))
|
||||||
|
.unwrap();
|
||||||
|
let argument_type_info = nodes_to_types.get(&argument.node_id()).unwrap();
|
||||||
|
if !parameter_type_info.is_assignable_from(argument_type_info) {
|
||||||
|
diagnostics.push(mismatched_types(
|
||||||
|
parameter_type_info,
|
||||||
|
argument_type_info,
|
||||||
|
argument.source_range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: insert self type (i.e., the return type of the call)
|
||||||
|
|
||||||
|
(nodes_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
self.callee.as_mut().type_check(symbol_table, types_table)?;
|
||||||
|
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = self
|
||||||
|
.arguments
|
||||||
|
.iter_mut()
|
||||||
|
.map(|argument| argument.type_check(symbol_table, types_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// check that callee is callable
|
||||||
|
let callable_symbol = match self.callee.type_info(symbol_table, types_table) {
|
||||||
|
TypeInfo::Function(function_symbol) => {
|
||||||
|
CallableSymbol::Function(function_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(
|
||||||
|
&format!(
|
||||||
|
"Receiver of type {} is not callable.",
|
||||||
|
self.callee.type_info(symbol_table, types_table)
|
||||||
|
),
|
||||||
|
self.callee.source_range().start(),
|
||||||
|
self.callee.source_range().end(),
|
||||||
|
));
|
||||||
|
return Err(diagnostics);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// check arguments length
|
||||||
|
let parameters = callable_symbol.parameters();
|
||||||
|
if parameters.len() != self.arguments.len() {
|
||||||
|
diagnostics.push(Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Wrong number of arguments; expected {} but found {}",
|
||||||
|
parameters.len(),
|
||||||
|
self.arguments.len()
|
||||||
|
),
|
||||||
|
self.source_range.start(),
|
||||||
|
self.source_range.end(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !diagnostics.is_empty() {
|
||||||
|
return Err(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check argument types
|
||||||
|
for i in 0..parameters.len() {
|
||||||
|
let parameter = ¶meters[i];
|
||||||
|
let argument = &self.arguments[i];
|
||||||
|
let parameter_type_info = types_table.parameter_types().get(parameter).unwrap();
|
||||||
|
let argument_type_info = argument.type_info(symbol_table, types_table);
|
||||||
|
if !parameter_type_info.is_assignable_from(argument_type_info) {
|
||||||
|
diagnostics.push(Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Mismatched types: expected {} but found {}",
|
||||||
|
parameter_type_info, argument_type_info
|
||||||
|
),
|
||||||
|
argument.source_range().start(),
|
||||||
|
argument.source_range().end(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
fn get_callee_symbol(&self, symbol_table: &SymbolTable) -> CallableSymbol {
|
||||||
|
match self.callee() {
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
let expressible_symbol = symbol_table
|
||||||
|
.find_expressible_symbol(identifier.scope_id(), identifier.name())
|
||||||
|
.unwrap();
|
||||||
|
match expressible_symbol {
|
||||||
|
ExpressibleSymbol::Function(function_symbol) => {
|
||||||
|
CallableSymbol::Function(function_symbol.clone())
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Class(class_symbol) => {
|
||||||
|
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 identifiers not yet supported."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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(),
|
||||||
|
CallableSymbol::ErrorPlaceholder => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> IrCall {
|
||||||
|
let arguments = self
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|expression| {
|
||||||
|
expression.lower_to_ir_expression(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let callee_symbol = nodes_to_symbols.get(&self.callee().node_id()).unwrap();
|
||||||
|
let callable_symbol = match callee_symbol {
|
||||||
|
Symbol::Class(class_symbol) => match class_symbol.constructor_symbol_owned() {
|
||||||
|
None => panic!(),
|
||||||
|
Some(constructor_symbol) => CallableSymbol::Constructor(constructor_symbol),
|
||||||
|
},
|
||||||
|
Symbol::Function(function_symbol) => CallableSymbol::Function(function_symbol.clone()),
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match callable_symbol {
|
||||||
|
CallableSymbol::Function(function_symbol) => IrCall::new(
|
||||||
|
fqn_parts_to_string(function_symbol.fqn_parts()),
|
||||||
|
arguments,
|
||||||
|
function_symbol.is_extern(),
|
||||||
|
),
|
||||||
|
CallableSymbol::Constructor(constructor_symbol) => IrCall::new(
|
||||||
|
fqn_parts_to_string(constructor_symbol.fqn_parts()),
|
||||||
|
arguments,
|
||||||
|
constructor_symbol.is_extern(),
|
||||||
|
),
|
||||||
|
CallableSymbol::ErrorPlaceholder => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> IrCall {
|
||||||
|
let arguments: Vec<IrExpression> = self
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|argument| argument.to_ir_expression(builder, symbol_table, types_table))
|
||||||
|
.inspect(|expression| {
|
||||||
|
if expression.is_none() {
|
||||||
|
panic!("Attempt to pass non-expression")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(Option::unwrap)
|
||||||
|
.collect();
|
||||||
|
let callable_symbol = self.get_callee_symbol(symbol_table);
|
||||||
|
match callable_symbol {
|
||||||
|
CallableSymbol::Function(function_symbol) => IrCall::new(
|
||||||
|
fqn_parts_to_string(function_symbol.fqn_parts()),
|
||||||
|
arguments,
|
||||||
|
function_symbol.is_extern(),
|
||||||
|
),
|
||||||
|
CallableSymbol::Constructor(constructor_symbol) => IrCall::new(
|
||||||
|
fqn_parts_to_string(constructor_symbol.fqn_parts()),
|
||||||
|
arguments,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
CallableSymbol::ErrorPlaceholder => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn source_range(&self) -> &SourceRange {
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
&self.source_range
|
&self.source_range
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,30 @@
|
|||||||
use crate::ast::NodeId;
|
use crate::ast::assign_statement::AssignStatement;
|
||||||
use crate::ast::constructor::Constructor;
|
use crate::ast::constructor::Constructor;
|
||||||
|
use crate::ast::expression::Expression;
|
||||||
use crate::ast::field::Field;
|
use crate::ast::field::Field;
|
||||||
|
use crate::ast::fqn_context::FqnContext;
|
||||||
|
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, insert_resolved_names_into,
|
||||||
|
resolve_ctor_name,
|
||||||
|
};
|
||||||
|
use crate::ast::statement::Statement;
|
||||||
|
use crate::ast::{FunctionReturnTypes, NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::error_codes::{FIELD_MULTIPLE_INIT, FIELD_UNINIT};
|
||||||
|
use crate::ir::ir_class::{IrClass, IrField};
|
||||||
|
use crate::ir::ir_function::IrFunction;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use crate::{diagnostics_result, handle_diagnostics, ok_or_err_diagnostics};
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Class {
|
pub struct Class {
|
||||||
@ -14,6 +35,9 @@ pub struct Class {
|
|||||||
constructor: Option<Constructor>,
|
constructor: Option<Constructor>,
|
||||||
fields: Vec<Field>,
|
fields: Vec<Field>,
|
||||||
functions: Vec<Function>,
|
functions: Vec<Function>,
|
||||||
|
scope_id: Option<usize>,
|
||||||
|
self_class_scope_id: Option<usize>,
|
||||||
|
self_class_body_scope_id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Class {
|
impl Class {
|
||||||
@ -34,6 +58,523 @@ impl Class {
|
|||||||
constructor,
|
constructor,
|
||||||
fields,
|
fields,
|
||||||
functions,
|
functions,
|
||||||
|
scope_id: None,
|
||||||
|
self_class_scope_id: None,
|
||||||
|
self_class_body_scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.scope_id = Some(container_scope);
|
||||||
|
|
||||||
|
let class_scope_id =
|
||||||
|
symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
|
||||||
|
self.self_class_scope_id = Some(class_scope_id);
|
||||||
|
|
||||||
|
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 declared_symbols(&self, fqn_context: &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);
|
||||||
|
}
|
||||||
|
|
||||||
|
let class_body_fqn_context = fqn_context.with_part(&self.declared_name);
|
||||||
|
|
||||||
|
let constructor_symbol = if let Some(constructor) = &self.constructor {
|
||||||
|
let (constructor_symbol, mut symbols) =
|
||||||
|
constructor.make_symbols(&class_body_fqn_context);
|
||||||
|
all_symbols.append(&mut symbols);
|
||||||
|
constructor_symbol
|
||||||
|
} else {
|
||||||
|
Rc::new(ConstructorSymbol::new(
|
||||||
|
&self.declared_name_source_range,
|
||||||
|
resolve_ctor_name(&class_body_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.declared_symbols(&class_body_fqn_context, true);
|
||||||
|
all_symbols.append(&mut symbols);
|
||||||
|
function_symbols.push(function_symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
let class_symbol = Rc::new(ClassSymbol::new(
|
||||||
|
&self.declared_name,
|
||||||
|
Some(self.declared_name_source_range.clone()),
|
||||||
|
fqn_context.resolve(&self.declared_name), // not class body!
|
||||||
|
false,
|
||||||
|
self.scope_id.unwrap(),
|
||||||
|
generic_parameter_symbols,
|
||||||
|
Some(constructor_symbol),
|
||||||
|
field_symbols,
|
||||||
|
function_symbols,
|
||||||
|
));
|
||||||
|
all_symbols.push(Symbol::Class(class_symbol.clone()));
|
||||||
|
|
||||||
|
all_symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names(&self, symbol_table: &mut SymbolTable) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for generic_parameter in &self.generic_parameters {
|
||||||
|
let (ns, mut ds) = generic_parameter.resolve_names(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
let self_class_symbol = self.get_class_symbol_owned(symbol_table);
|
||||||
|
let mut initialized_fields = HashSet::new();
|
||||||
|
|
||||||
|
for field in &self.fields {
|
||||||
|
let (ns, mut ds) = field.resolve_names(symbol_table, &self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
initialized_fields.insert(field.declared_name_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(constructor) = &self.constructor {
|
||||||
|
let (ns, mut ds) = constructor.resolve_names(
|
||||||
|
symbol_table,
|
||||||
|
self_class_symbol.as_ref(),
|
||||||
|
&mut initialized_fields,
|
||||||
|
);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||||
|
|
||||||
|
for generic_parameter in &self.generic_parameters {
|
||||||
|
diagnostics.append(&mut generic_parameter.check_names(symbol_table));
|
||||||
|
}
|
||||||
|
|
||||||
|
for field in &self.fields {
|
||||||
|
diagnostics.append(&mut field.check_names(symbol_table));
|
||||||
|
}
|
||||||
|
|
||||||
|
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_self_class_symbol<'a>(&self, symbol_table: &'a SymbolTable) -> &'a ClassSymbol {
|
||||||
|
symbol_table
|
||||||
|
.get_class_symbol(self.scope_id.unwrap(), &self.declared_name)
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_field_initializer_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
let class_symbol = self.get_class_symbol_owned(symbol_table);
|
||||||
|
self.fields
|
||||||
|
.iter()
|
||||||
|
.flat_map(|field| field.check_field_initializer_names(symbol_table, &class_symbol))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn analyze_local_names(&self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
let class_symbol = self.get_class_symbol_owned(symbol_table);
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||||
|
if let Some(constructor) = &self.constructor {
|
||||||
|
diagnostics.append(&mut constructor.analyze_local_names(symbol_table, &class_symbol));
|
||||||
|
}
|
||||||
|
for function in &self.functions {
|
||||||
|
diagnostics
|
||||||
|
.append(&mut function.analyze_method_local_names(symbol_table, &class_symbol));
|
||||||
|
}
|
||||||
|
diagnostics
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// field types
|
||||||
|
for field in &self.fields {
|
||||||
|
handle_diagnostics!(field.gather_types(symbol_table, types_table), diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
constructor.type_check(symbol_table, types_table)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_check_functions(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &mut TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
collect_diagnostics_mut(&mut self.functions, |f| {
|
||||||
|
f.type_check(symbol_table, types_table)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all field names with declared initializers.
|
||||||
|
fn field_names_with_initializers(&self) -> HashSet<&str> {
|
||||||
|
let mut set: HashSet<&str> = HashSet::new();
|
||||||
|
for field in &self.fields {
|
||||||
|
if field.initializer().is_some() {
|
||||||
|
set.insert(field.declared_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the destination of the given [AssignStatement] matches a field, returns an
|
||||||
|
/// `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)`.
|
||||||
|
fn check_ctor_assign_statement<'a>(
|
||||||
|
&self,
|
||||||
|
assign_statement: &'a AssignStatement,
|
||||||
|
fields_already_init: &HashSet<&&str>,
|
||||||
|
class_symbol: &ClassSymbol,
|
||||||
|
) -> Result<Option<&'a str>, Diagnostic> {
|
||||||
|
match assign_statement.destination() {
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
// find matching field symbol, if there is one
|
||||||
|
if let Some(field_symbol) = class_symbol.fields().get(identifier.name()) {
|
||||||
|
// check that we don't init more than once IF field is immutable
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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![];
|
||||||
|
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() {
|
||||||
|
match statement {
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
let fields_init_so_far = constructor_inits
|
||||||
|
.union(fields_with_declared_initializers)
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
match self.check_ctor_assign_statement(
|
||||||
|
assign_statement,
|
||||||
|
&fields_init_so_far,
|
||||||
|
&class_symbol,
|
||||||
|
) {
|
||||||
|
Ok(maybe_init_field) => match maybe_init_field {
|
||||||
|
None => {}
|
||||||
|
Some(init_field) => {
|
||||||
|
constructor_inits.insert(init_field);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(diagnostic) => {
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok_or_err_diagnostics!(constructor_inits, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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![];
|
||||||
|
if let Some(constructor) = &self.constructor {
|
||||||
|
ir_functions.push(constructor.to_ir(
|
||||||
|
self_class_symbol,
|
||||||
|
&self.fields,
|
||||||
|
symbol_table,
|
||||||
|
types_table,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
ir_functions.push(function.to_ir(symbol_table, types_table, Some(self_class_symbol)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ir_class = IrClass::new(
|
||||||
|
self_class_symbol.declared_name_owned(),
|
||||||
|
fqn_parts_to_string(self_class_symbol.fqn_parts()).into(),
|
||||||
|
self.fields
|
||||||
|
.iter()
|
||||||
|
.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(
|
||||||
|
field.declared_name().into(),
|
||||||
|
field_symbol.field_index(),
|
||||||
|
field_type.clone(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
|
(ir_class, ir_functions)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> (IrClass, Vec<IrFunction>) {
|
||||||
|
let mut ir_functions = Vec::new();
|
||||||
|
|
||||||
|
let self_class_symbol = nodes_to_symbols
|
||||||
|
.get(&self.node_id)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_class_symbol();
|
||||||
|
|
||||||
|
if let Some(constructor) = &self.constructor {
|
||||||
|
ir_functions.push(constructor.lower_to_ir(
|
||||||
|
self_class_symbol,
|
||||||
|
&self.fields,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
ir_functions.push(function.lower_to_ir_static(
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ir_class = IrClass::new(
|
||||||
|
self_class_symbol.declared_name_owned(),
|
||||||
|
fqn_parts_to_string(self_class_symbol.fqn_parts()).into(),
|
||||||
|
self.fields
|
||||||
|
.iter()
|
||||||
|
.map(|field| field.lower_to_ir_field(nodes_to_symbols, symbols_to_types))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
|
(ir_class, ir_functions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,20 @@
|
|||||||
use crate::FileId;
|
|
||||||
use crate::ast::class::Class;
|
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::function::Function;
|
use crate::ast::function::Function;
|
||||||
|
use crate::ast::helpers::{
|
||||||
|
collect_diagnostics_into_mut, insert_declared_types_into, insert_resolved_types_into,
|
||||||
|
};
|
||||||
|
use crate::ast::{NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::compile_pipeline::FileId;
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::ir::ir_class::IrClass;
|
||||||
|
use crate::ir::ir_function::IrFunction;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::symbol_table::util::try_insert_symbols_into;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use crate::{diagnostics_result, handle_diagnostics};
|
||||||
|
|
||||||
pub struct CompilationUnit {
|
pub struct CompilationUnit {
|
||||||
file_id: Option<FileId>,
|
file_id: Option<FileId>,
|
||||||
@ -36,4 +49,258 @@ impl CompilationUnit {
|
|||||||
pub fn classes(&self) -> &[Class] {
|
pub fn classes(&self) -> &[Class] {
|
||||||
&self.classes
|
&self.classes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable) {
|
||||||
|
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 declared_symbols(&self) -> Vec<Symbol> {
|
||||||
|
let fqn_context = FqnContext::new();
|
||||||
|
[
|
||||||
|
self.classes
|
||||||
|
.iter()
|
||||||
|
.flat_map(|class| class.declared_symbols(&fqn_context))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
self.extern_functions
|
||||||
|
.iter()
|
||||||
|
.flat_map(|function| function.declared_symbols(&fqn_context).1)
|
||||||
|
.collect(),
|
||||||
|
self.functions
|
||||||
|
.iter()
|
||||||
|
.flat_map(|function| function.declared_symbols(&fqn_context, false).1)
|
||||||
|
.collect(),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn gather_symbols_into(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut diagnostics = vec![];
|
||||||
|
let fqn_context = FqnContext::new();
|
||||||
|
for class in &self.classes {
|
||||||
|
handle_diagnostics!(
|
||||||
|
try_insert_symbols_into(class.declared_symbols(&fqn_context), symbol_table),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for function in &self.functions {
|
||||||
|
let (_, symbols) = function.declared_symbols(&fqn_context, false);
|
||||||
|
handle_diagnostics!(try_insert_symbols_into(symbols, symbol_table), diagnostics);
|
||||||
|
}
|
||||||
|
for extern_function in &self.extern_functions {
|
||||||
|
let (_, symbols) = extern_function.declared_symbols(&fqn_context);
|
||||||
|
handle_diagnostics!(try_insert_symbols_into(symbols, symbol_table), diagnostics);
|
||||||
|
}
|
||||||
|
diagnostics_result!(diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names(&self, symbol_table: &mut SymbolTable) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
let (ns, mut ds) = function.resolve_names_static(symbol_table);
|
||||||
|
for (node_id, symbol) in ns.into_iter() {
|
||||||
|
names_table.insert(node_id, symbol);
|
||||||
|
}
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for extern_function in &self.extern_functions {
|
||||||
|
let (ns, mut ds) = extern_function.resolve_names_static(symbol_table);
|
||||||
|
for (node_id, symbol) in ns {
|
||||||
|
names_table.insert(node_id, symbol);
|
||||||
|
}
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for class in &self.classes {
|
||||||
|
let (ns, mut ds) = class.resolve_names(symbol_table);
|
||||||
|
for (node_id, symbol) in ns {
|
||||||
|
names_table.insert(node_id, symbol);
|
||||||
|
}
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_names(&self, symbol_table: &mut SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut diagnostics = vec![];
|
||||||
|
for class in &self.classes {
|
||||||
|
diagnostics.append(&mut class.check_names(symbol_table));
|
||||||
|
diagnostics.append(&mut class.check_field_initializer_names(symbol_table));
|
||||||
|
diagnostics.append(&mut class.analyze_local_names(symbol_table));
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associate each declared symbol with a TypeInfo.
|
||||||
|
pub fn declared_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
) -> (SymbolsToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut symbols_to_types = SymbolsToTypes::new();
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
let (sts, mut ds) = function.declared_types(nodes_to_symbols);
|
||||||
|
insert_declared_types_into(sts, &mut symbols_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for extern_function in &self.extern_functions {
|
||||||
|
let (sts, mut ds) = extern_function.declared_types(nodes_to_symbols);
|
||||||
|
insert_declared_types_into(sts, &mut symbols_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(symbols_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve types of all nodes that have an implicit (perhaps not declared) type, checking that
|
||||||
|
/// things are assignable, etc., along the way.
|
||||||
|
pub fn resolve_types(
|
||||||
|
&self,
|
||||||
|
names_table: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (SymbolsToTypes, NodesToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut resolved_types = NodesToTypes::new();
|
||||||
|
let mut symbols_to_types = symbols_to_types.clone();
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
let (sts, nts, mut ds) = function.resolve_types(names_table, &symbols_to_types);
|
||||||
|
insert_declared_types_into(sts, &mut symbols_to_types);
|
||||||
|
insert_resolved_types_into(nts, &mut resolved_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(symbols_to_types, resolved_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn gather_types_into(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &mut TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut diagnostics = Vec::new();
|
||||||
|
|
||||||
|
for class in &self.classes {
|
||||||
|
handle_diagnostics!(class.gather_types(symbol_table, types_table), 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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 lower_to_ir(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> (Vec<IrClass>, Vec<IrFunction>) {
|
||||||
|
let mut ir_classes = Vec::new();
|
||||||
|
let mut ir_functions = Vec::new();
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
ir_functions.push(function.lower_to_ir_static(
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for class in &self.classes {
|
||||||
|
let (ir_class, mut class_ir_functions) =
|
||||||
|
class.lower_to_ir(nodes_to_symbols, symbols_to_types, nodes_to_types);
|
||||||
|
ir_classes.push(ir_class);
|
||||||
|
ir_functions.append(&mut class_ir_functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
(ir_classes, ir_functions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn to_ir(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> (Vec<IrClass>, Vec<IrFunction>) {
|
||||||
|
let mut functions: Vec<IrFunction> = vec![];
|
||||||
|
let mut classes: Vec<IrClass> = vec![];
|
||||||
|
|
||||||
|
self.functions
|
||||||
|
.iter()
|
||||||
|
.map(|f| f.to_ir(symbol_table, types_table, None))
|
||||||
|
.for_each(|f| functions.push(f));
|
||||||
|
|
||||||
|
for class in &self.classes {
|
||||||
|
let (class, mut class_functions) = class.to_ir(symbol_table, types_table);
|
||||||
|
functions.append(&mut class_functions);
|
||||||
|
classes.push(class);
|
||||||
|
}
|
||||||
|
|
||||||
|
(classes, functions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,37 @@
|
|||||||
use crate::ast::NodeId;
|
use crate::ast::field::Field;
|
||||||
|
use crate::ast::fqn_context::FqnContext;
|
||||||
|
use crate::ast::fqn_util::fqn_parts_to_string;
|
||||||
|
use crate::ast::helpers::{
|
||||||
|
collect_parameter_symbols_into, insert_resolved_names_into, resolve_ctor_name,
|
||||||
|
};
|
||||||
|
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::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::ir::ir_allocate::IrAllocate;
|
||||||
|
use crate::ir::ir_assign::IrAssign;
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
|
use crate::ir::ir_function::IrFunction;
|
||||||
|
use crate::ir::ir_get_field_ref_mut::IrGetFieldRefMut;
|
||||||
|
use crate::ir::ir_operation::IrOperation;
|
||||||
|
use crate::ir::ir_parameter::IrParameter;
|
||||||
|
use crate::ir::ir_parameter_or_variable::IrParameterOrVariable;
|
||||||
|
use crate::ir::ir_return::IrReturn;
|
||||||
|
use crate::ir::ir_set_field::IrSetField;
|
||||||
|
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::constructor_symbol::ConstructorSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::ops::Neg;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Constructor {
|
pub struct Constructor {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
@ -33,4 +63,375 @@ impl Constructor {
|
|||||||
pub fn statements(&self) -> &[Statement] {
|
pub fn statements(&self) -> &[Statement] {
|
||||||
&self.statements
|
&self.statements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.scope_id = Some(container_scope);
|
||||||
|
|
||||||
|
let function_scope = symbol_table.push_function_scope("constructor_scope");
|
||||||
|
|
||||||
|
for parameter in &mut self.parameters {
|
||||||
|
parameter.init_scopes(symbol_table, function_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body_scope = symbol_table.push_block_scope("body_scope");
|
||||||
|
for statement in &mut self.statements {
|
||||||
|
statement.init_scopes(symbol_table, body_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol_table.pop_scope();
|
||||||
|
symbol_table.pop_scope();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_symbols(&self, fqn_context: &FqnContext) -> (Rc<ConstructorSymbol>, 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 constructor_symbol = Rc::new(ConstructorSymbol::new(
|
||||||
|
&self.ctor_keyword_source_range,
|
||||||
|
resolve_ctor_name(fqn_context),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
self.scope_id.unwrap(),
|
||||||
|
parameter_symbols,
|
||||||
|
));
|
||||||
|
all_symbols.push(Symbol::Constructor(constructor_symbol.clone()));
|
||||||
|
|
||||||
|
(constructor_symbol, all_symbols)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
initialized_fields: &mut HashSet<Rc<str>>,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for parameter in &self.parameters {
|
||||||
|
let (ns, mut ds) = parameter.resolve_names(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for statement in &self.statements {
|
||||||
|
let (ns, mut ds) =
|
||||||
|
statement.resolve_names_ctor(symbol_table, self_class_symbol, initialized_fields);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &mut TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let parameters_diagnostics: Vec<Diagnostic> = self
|
||||||
|
.parameters
|
||||||
|
.iter_mut()
|
||||||
|
.map(|param| param.type_check(symbol_table, types_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, types_table, None))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if statements_diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(statements_diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn to_ir(
|
||||||
|
&self,
|
||||||
|
class_symbol: &Rc<ClassSymbol>,
|
||||||
|
fields: &[Field],
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> IrFunction {
|
||||||
|
let mut ir_builder = IrBuilder::new();
|
||||||
|
|
||||||
|
let parameters_count = self.parameters.len();
|
||||||
|
let ir_parameters = self
|
||||||
|
.parameters
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, parameter)| {
|
||||||
|
let parameter_symbol = symbol_table
|
||||||
|
.get_parameter_symbol_owned(parameter.scope_id(), parameter.declared_name())
|
||||||
|
.unwrap();
|
||||||
|
let parameter_type = types_table
|
||||||
|
.parameter_types()
|
||||||
|
.get(¶meter_symbol)
|
||||||
|
.unwrap();
|
||||||
|
let offset = (parameters_count as isize).neg() + i as isize;
|
||||||
|
let ir_parameter = Rc::new(IrParameter::new(
|
||||||
|
parameter_symbol.declared_name(),
|
||||||
|
todo!(),
|
||||||
|
offset,
|
||||||
|
));
|
||||||
|
|
||||||
|
// make sure to save ir_parameter to symbol so others can access it
|
||||||
|
ir_builder.push_parameter(¶meter_symbol, ir_parameter.clone());
|
||||||
|
|
||||||
|
ir_parameter
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let entry_block_id = ir_builder.new_block();
|
||||||
|
|
||||||
|
// first, allocate the object into a t var
|
||||||
|
let alloc_assign_destination = todo!();
|
||||||
|
let self_variable = Rc::new(RefCell::new(alloc_assign_destination));
|
||||||
|
|
||||||
|
// save self variable so statements can assign stuff to self's fields
|
||||||
|
ir_builder.set_self_parameter_or_variable(IrParameterOrVariable::Variable(todo!()));
|
||||||
|
|
||||||
|
let alloc_assign = IrAssign::new(
|
||||||
|
todo!(),
|
||||||
|
IrOperation::Allocate(IrAllocate::new(class_symbol.declared_name_owned())),
|
||||||
|
);
|
||||||
|
ir_builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(alloc_assign));
|
||||||
|
|
||||||
|
// next, initialize fields that have an initializer in their declaration
|
||||||
|
for field in fields {
|
||||||
|
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
|
||||||
|
let ir_get_field_ref_mut = IrGetFieldRefMut::new(
|
||||||
|
IrParameterOrVariable::Variable(todo!()),
|
||||||
|
field_symbol.field_index(),
|
||||||
|
);
|
||||||
|
let field_mut_ref_variable_name: Rc<str> = ir_builder.new_t_var().into();
|
||||||
|
let field_mut_ref_variable = Rc::new(RefCell::new(todo!()));
|
||||||
|
let field_mut_ref_assign =
|
||||||
|
IrAssign::new(todo!(), IrOperation::GetFieldRefMut(ir_get_field_ref_mut));
|
||||||
|
ir_builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(field_mut_ref_assign));
|
||||||
|
|
||||||
|
// save the mut ref to the builder for other uses if needed
|
||||||
|
ir_builder
|
||||||
|
.field_mut_pointer_variables_mut()
|
||||||
|
.insert(field.declared_name_owned(), field_mut_ref_variable); // n.b. field name, not t var name
|
||||||
|
|
||||||
|
// now write the initializer result to the field
|
||||||
|
let field_mut_ref_variable = ir_builder
|
||||||
|
.field_mut_pointer_variables()
|
||||||
|
.get(field.declared_name())
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let ir_expression = initializer
|
||||||
|
.to_ir_expression(&mut ir_builder, symbol_table, types_table)
|
||||||
|
.unwrap();
|
||||||
|
let ir_set_field = IrSetField::new(
|
||||||
|
todo!(), // dumb that we clone it and then ref it
|
||||||
|
ir_expression,
|
||||||
|
);
|
||||||
|
ir_builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::SetField(ir_set_field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do "declared" statements of constructor
|
||||||
|
for statement in &self.statements {
|
||||||
|
statement.to_ir(&mut ir_builder, symbol_table, types_table, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return complete self object
|
||||||
|
let ir_return_statement =
|
||||||
|
IrStatement::Return(IrReturn::new(Some(IrExpression::Variable(todo!()))));
|
||||||
|
ir_builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(ir_return_statement);
|
||||||
|
|
||||||
|
ir_builder.finish_block();
|
||||||
|
let entry_block = ir_builder.get_block(entry_block_id);
|
||||||
|
|
||||||
|
let constructor_symbol = symbol_table
|
||||||
|
.get_constructor_symbol(self.scope_id.unwrap())
|
||||||
|
.unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir(
|
||||||
|
&self,
|
||||||
|
class_symbol: &Rc<ClassSymbol>,
|
||||||
|
fields: &[Field],
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> IrFunction {
|
||||||
|
let mut ir_builder = IrBuilder::new();
|
||||||
|
|
||||||
|
// gather ir_parameters
|
||||||
|
let mut ir_parameters = Vec::new();
|
||||||
|
let base_offset = (self.parameters.len() as isize).neg();
|
||||||
|
for (i, parameter) in self.parameters.iter().enumerate() {
|
||||||
|
let symbol = nodes_to_symbols.get(¶meter.node_id()).unwrap();
|
||||||
|
let parameter_type = symbols_to_types.get(symbol).unwrap();
|
||||||
|
let offset = base_offset + i as isize;
|
||||||
|
let ir_parameter = Rc::new(IrParameter::new(symbol.declared_name(), todo!(), offset));
|
||||||
|
|
||||||
|
// save in builder
|
||||||
|
ir_builder.push_parameter(symbol.unwrap_parameter_symbol(), ir_parameter.clone());
|
||||||
|
|
||||||
|
// push for saving to IrFunction
|
||||||
|
ir_parameters.push(ir_parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// entry-block
|
||||||
|
let entry_block_id = ir_builder.new_block();
|
||||||
|
|
||||||
|
// PART 1: Make self object
|
||||||
|
let self_variable = Rc::new(RefCell::new(todo!()));
|
||||||
|
//
|
||||||
|
// // save self variable in builder
|
||||||
|
// ir_builder
|
||||||
|
// .set_self_parameter_or_variable(IrParameterOrVariable::Variable(self_variable.clone()));
|
||||||
|
//
|
||||||
|
// // allocate the self object
|
||||||
|
// let ir_assign = IrAssign::new(
|
||||||
|
// self_variable.clone(),
|
||||||
|
// IrOperation::Allocate(IrAllocate::new(class_symbol.declared_name_owned())),
|
||||||
|
// );
|
||||||
|
// ir_builder
|
||||||
|
// .current_block_mut()
|
||||||
|
// .add_statement(IrStatement::Assign(ir_assign));
|
||||||
|
//
|
||||||
|
// // PART 2: Initialize fields that are initialized OUTSIDE the constructor
|
||||||
|
// for field in fields {
|
||||||
|
// if let Some(initializer) = field.initializer() {
|
||||||
|
// let symbol = nodes_to_symbols.get(&field.node_id()).unwrap();
|
||||||
|
// let field_type = symbols_to_types.get(symbol).unwrap();
|
||||||
|
// let field_symbol = symbol.unwrap_field_symbol();
|
||||||
|
//
|
||||||
|
// // 1. Get a mut ref to the field
|
||||||
|
// // mut t_var: Type = &mut self.x
|
||||||
|
// let ir_get_field_ref_mut = IrGetFieldRefMut::new(
|
||||||
|
// IrParameterOrVariable::Variable(self_variable.clone()),
|
||||||
|
// field_symbol.field_index(),
|
||||||
|
// );
|
||||||
|
// let field_ref_mut_ir_variable = Rc::new(RefCell::new(IrVariable::new_vr(
|
||||||
|
// ir_builder.new_t_var().into(),
|
||||||
|
// ir_builder.current_block().id(),
|
||||||
|
// field_type,
|
||||||
|
// )));
|
||||||
|
// let ir_assign = IrAssign::new(
|
||||||
|
// field_ref_mut_ir_variable.clone(),
|
||||||
|
// IrOperation::GetFieldRefMut(ir_get_field_ref_mut),
|
||||||
|
// );
|
||||||
|
// ir_builder
|
||||||
|
// .current_block_mut()
|
||||||
|
// .add_statement(IrStatement::Assign(ir_assign));
|
||||||
|
//
|
||||||
|
// // save the mut ref for later uses if needed
|
||||||
|
// ir_builder.field_mut_pointer_variables_mut().insert(
|
||||||
|
// field.declared_name_owned(),
|
||||||
|
// field_ref_mut_ir_variable.clone(),
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// // 2. Evaluate initializer and save result to mut field ref
|
||||||
|
// let ir_expression = initializer.lower_to_ir_expression(
|
||||||
|
// &mut ir_builder,
|
||||||
|
// nodes_to_symbols,
|
||||||
|
// symbols_to_types,
|
||||||
|
// nodes_to_types,
|
||||||
|
// );
|
||||||
|
// let ir_set_field = IrSetField::new(&field_ref_mut_ir_variable, ir_expression);
|
||||||
|
// ir_builder
|
||||||
|
// .current_block_mut()
|
||||||
|
// .add_statement(IrStatement::SetField(ir_set_field));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // PART 3. Constructor statements
|
||||||
|
// for statement in &self.statements {
|
||||||
|
// statement.lower_to_ir(
|
||||||
|
// &mut ir_builder,
|
||||||
|
// nodes_to_symbols,
|
||||||
|
// symbols_to_types,
|
||||||
|
// nodes_to_types,
|
||||||
|
// false,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // PART 4. Return finished self object
|
||||||
|
// let ir_return_statement = IrStatement::Return(IrReturn::new(Some(IrExpression::Variable(
|
||||||
|
// self_variable.clone(),
|
||||||
|
// ))));
|
||||||
|
// ir_builder
|
||||||
|
// .current_block_mut()
|
||||||
|
// .add_statement(ir_return_statement);
|
||||||
|
//
|
||||||
|
// // Finish up the builder and return IrFunction
|
||||||
|
// ir_builder.finish_block();
|
||||||
|
// let entry_block = ir_builder.get_block(entry_block_id);
|
||||||
|
//
|
||||||
|
// let constructor_symbol = nodes_to_symbols
|
||||||
|
// .get(&self.node_id)
|
||||||
|
// .unwrap()
|
||||||
|
// .unwrap_constructor_symbol();
|
||||||
|
|
||||||
|
// IrFunction::new(
|
||||||
|
// fqn_parts_to_string(constructor_symbol.fqn_parts()),
|
||||||
|
// ir_parameters,
|
||||||
|
// &TypeInfo::Class(class_symbol.clone()), // TODO
|
||||||
|
// entry_block.clone(),
|
||||||
|
// )
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,22 @@
|
|||||||
use crate::ast::NodeId;
|
use crate::ast::NodeId;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
|
||||||
pub struct DoubleLiteral {
|
pub struct DoubleLiteral {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
value: f64,
|
value: f64,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
type_info: &'static TypeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DoubleLiteral {
|
impl DoubleLiteral {
|
||||||
pub fn new(node_id: NodeId, value: f64, source_range: SourceRange) -> Self {
|
pub fn new(node_id: NodeId, value: f64, source_range: SourceRange) -> Self {
|
||||||
|
const TYPE_INFO: TypeInfo = TypeInfo::Double;
|
||||||
Self {
|
Self {
|
||||||
node_id,
|
node_id,
|
||||||
value,
|
value,
|
||||||
source_range,
|
source_range,
|
||||||
|
type_info: &TYPE_INFO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +28,10 @@ impl DoubleLiteral {
|
|||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_info(&self) -> &TypeInfo {
|
||||||
|
&self.type_info
|
||||||
|
}
|
||||||
|
|
||||||
pub fn source_range(&self) -> &SourceRange {
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
&self.source_range
|
&self.source_range
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,25 @@
|
|||||||
use crate::ast::NodeId;
|
|
||||||
use crate::ast::binary_expression::BinaryExpression;
|
use crate::ast::binary_expression::BinaryExpression;
|
||||||
use crate::ast::call::Call;
|
use crate::ast::call::Call;
|
||||||
use crate::ast::double_literal::DoubleLiteral;
|
use crate::ast::double_literal::DoubleLiteral;
|
||||||
use crate::ast::identifier::Identifier;
|
use crate::ast::identifier::Identifier;
|
||||||
use crate::ast::integer_literal::IntegerLiteral;
|
use crate::ast::integer_literal::IntegerLiteral;
|
||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
use crate::ast::string_literal::StringLiteral;
|
use crate::ast::string_literal::StringLiteral;
|
||||||
|
use crate::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::ir::ir_assign::IrAssign;
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
|
use crate::ir::ir_operation::IrOperation;
|
||||||
|
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_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Binary(BinaryExpression),
|
Binary(BinaryExpression),
|
||||||
@ -31,6 +44,321 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
binary_expression.resolve_names_static(symbol_table)
|
||||||
|
}
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
negative_expression.resolve_names_static(symbol_table)
|
||||||
|
}
|
||||||
|
Expression::Call(call) => call.resolve_names_static(symbol_table),
|
||||||
|
Expression::Identifier(identifier) => identifier.resolve_name_static(symbol_table),
|
||||||
|
_ => (NodesToSymbols::new(), Diagnostics::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_field_init(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
binary_expression.resolve_names_field_init(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
negative_expression.resolve_names_field_init(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Expression::Call(call) => {
|
||||||
|
call.resolve_names_field_init(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
identifier.resolve_name_field_init(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
_ => (NodesToSymbols::new(), Diagnostics::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_ctor(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
binary_expression.resolve_names_ctor(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
negative_expression.resolve_names_ctor(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Expression::Call(call) => call.resolve_names_ctor(symbol_table, self_class_symbol),
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
identifier.resolve_name_ctor(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
_ => (NodesToSymbols::new(), Diagnostics::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
binary_expression.resolve_names_method(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
negative_expression.resolve_names_method(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Expression::Call(call) => call.resolve_names_method(symbol_table, self_class_symbol),
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
identifier.resolve_name_method(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
_ => (NodesToSymbols::new(), Diagnostics::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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 resolve_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (NodesToTypes, Diagnostics) {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
binary_expression.resolve_types(nodes_to_symbols, symbols_to_types)
|
||||||
|
}
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
negative_expression.resolve_types(nodes_to_symbols, symbols_to_types)
|
||||||
|
}
|
||||||
|
Expression::Call(call) => call.resolve_types(nodes_to_symbols, symbols_to_types),
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
identifier.resolve_type(nodes_to_symbols, symbols_to_types)
|
||||||
|
}
|
||||||
|
Expression::Integer(integer_literal) => {
|
||||||
|
let mut resolved_types = NodesToTypes::new();
|
||||||
|
resolved_types.insert(
|
||||||
|
integer_literal.node_id(),
|
||||||
|
integer_literal.type_info().clone(),
|
||||||
|
);
|
||||||
|
(resolved_types, Diagnostics::new())
|
||||||
|
}
|
||||||
|
Expression::Double(double_literal) => {
|
||||||
|
let mut resolved_types = NodesToTypes::new();
|
||||||
|
resolved_types.insert(double_literal.node_id(), double_literal.type_info().clone());
|
||||||
|
(resolved_types, Diagnostics::new())
|
||||||
|
}
|
||||||
|
Expression::String(string_literal) => {
|
||||||
|
let mut resolved_types = NodesToTypes::new();
|
||||||
|
resolved_types.insert(string_literal.node_id(), string_literal.type_info().clone());
|
||||||
|
(resolved_types, Diagnostics::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
binary_expression.type_check(symbol_table, types_table)
|
||||||
|
}
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
negative_expression.type_check(symbol_table, types_table)
|
||||||
|
}
|
||||||
|
Expression::Call(call) => call.type_check(symbol_table, types_table),
|
||||||
|
Expression::Identifier(_) => Ok(()),
|
||||||
|
Expression::Integer(_) => Ok(()),
|
||||||
|
Expression::Double(_) => Ok(()),
|
||||||
|
Expression::String(_) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn type_info<'a>(
|
||||||
|
&'a self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &'a TypesTable,
|
||||||
|
) -> &'a TypeInfo {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => binary_expression.type_info(),
|
||||||
|
Expression::Negative(negative_expression) => negative_expression.type_info(),
|
||||||
|
Expression::Call(call) => call.return_type_info(symbol_table, types_table),
|
||||||
|
Expression::Identifier(identifier) => identifier.type_info(symbol_table, types_table),
|
||||||
|
Expression::Integer(integer_literal) => integer_literal.type_info(),
|
||||||
|
Expression::Double(double_literal) => double_literal.type_info(),
|
||||||
|
Expression::String(string_literal) => string_literal.type_info(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn source_range(&self) -> &SourceRange {
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
match self {
|
match self {
|
||||||
Expression::Binary(binary_expression) => binary_expression.source_range(),
|
Expression::Binary(binary_expression) => binary_expression.source_range(),
|
||||||
@ -42,4 +370,136 @@ impl Expression {
|
|||||||
Expression::String(string_literal) => string_literal.source_range(),
|
Expression::String(string_literal) => string_literal.source_range(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir_operation(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> IrOperation {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => binary_expression.lower_to_ir_operation(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
),
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
IrOperation::Load(negative_expression.lower_to_ir_expression(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Expression::Call(call) => IrOperation::Call(call.lower_to_ir(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
)),
|
||||||
|
Expression::Identifier(identifier) => IrOperation::Load(
|
||||||
|
identifier.lower_to_ir_expression(builder, nodes_to_symbols, symbols_to_types),
|
||||||
|
),
|
||||||
|
Expression::Integer(integer_literal) => {
|
||||||
|
IrOperation::Load(IrExpression::Int(integer_literal.value()))
|
||||||
|
}
|
||||||
|
Expression::Double(double_literal) => {
|
||||||
|
IrOperation::Load(IrExpression::Double(double_literal.value()))
|
||||||
|
}
|
||||||
|
Expression::String(string_literal) => {
|
||||||
|
IrOperation::Load(IrExpression::String(string_literal.content().into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir_expression(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> IrExpression {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ir_operation(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> IrOperation {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
binary_expression.to_ir_operation(builder, symbol_table, types_table)
|
||||||
|
}
|
||||||
|
Expression::Call(call) => {
|
||||||
|
IrOperation::Call(call.to_ir(builder, symbol_table, types_table))
|
||||||
|
}
|
||||||
|
Expression::Integer(integer_literal) => {
|
||||||
|
IrOperation::Load(IrExpression::Int(integer_literal.value()))
|
||||||
|
}
|
||||||
|
Expression::Double(double_literal) => {
|
||||||
|
IrOperation::Load(IrExpression::Double(double_literal.value()))
|
||||||
|
}
|
||||||
|
Expression::String(string_literal) => {
|
||||||
|
IrOperation::Load(IrExpression::String(string_literal.content().into()))
|
||||||
|
}
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
IrOperation::Load(identifier.ir_expression(builder, symbol_table, types_table))
|
||||||
|
}
|
||||||
|
Expression::Negative(negative_expression) => {
|
||||||
|
IrOperation::Load(negative_expression.to_ir(builder, symbol_table, types_table))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ir_expression(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> Option<IrExpression> {
|
||||||
|
match self {
|
||||||
|
Expression::Binary(binary_expression) => {
|
||||||
|
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) => {
|
||||||
|
let ir_call = call.to_ir(builder, symbol_table, types_table);
|
||||||
|
if matches!(
|
||||||
|
call.return_type_info(symbol_table, types_table),
|
||||||
|
TypeInfo::Void
|
||||||
|
) {
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Call(ir_call));
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let t_var = todo!();
|
||||||
|
let as_rc = Rc::new(RefCell::new(t_var));
|
||||||
|
let assign = IrAssign::new(todo!(), IrOperation::Call(ir_call));
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(assign));
|
||||||
|
Some(IrExpression::Variable(todo!()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
Some(identifier.ir_expression(builder, symbol_table, types_table))
|
||||||
|
}
|
||||||
|
Expression::Integer(integer_literal) => {
|
||||||
|
Some(IrExpression::Int(integer_literal.value()))
|
||||||
|
}
|
||||||
|
Expression::Double(double_literal) => {
|
||||||
|
Some(IrExpression::Double(double_literal.value()))
|
||||||
|
}
|
||||||
|
Expression::String(string_literal) => {
|
||||||
|
Some(IrExpression::String(string_literal.content().into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,23 @@
|
|||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
|
use crate::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::ir::ir_return::IrReturn;
|
||||||
|
use crate::ir::ir_statement::IrStatement;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
|
||||||
pub struct ExpressionStatement {
|
pub struct ExpressionStatement {
|
||||||
|
node_id: NodeId,
|
||||||
expression: Box<Expression>,
|
expression: Box<Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpressionStatement {
|
impl ExpressionStatement {
|
||||||
pub fn new(expression: Expression) -> Self {
|
pub fn new(node_id: NodeId, expression: Expression) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
node_id,
|
||||||
expression: expression.into(),
|
expression: expression.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -14,4 +25,140 @@ impl ExpressionStatement {
|
|||||||
pub fn expression(&self) -> &Expression {
|
pub fn expression(&self) -> &Expression {
|
||||||
&self.expression
|
&self.expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.expression.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
self.expression.resolve_names_static(symbol_table)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_ctor(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
self.expression
|
||||||
|
.resolve_names_ctor(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
self.expression
|
||||||
|
.resolve_names_method(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_constructor_local_names(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
class_symbol: &ClassSymbol,
|
||||||
|
) -> Vec<Diagnostic> {
|
||||||
|
self.expression
|
||||||
|
.check_constructor_local_names(symbol_table, class_symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
self.expression.check_static_fn_local_names(symbol_table)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (NodesToTypes, Diagnostics) {
|
||||||
|
let (mut nodes_to_types, diagnostics) = self
|
||||||
|
.expression
|
||||||
|
.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
|
||||||
|
// add self for last-statement type checking
|
||||||
|
let expression_type_info = nodes_to_types
|
||||||
|
.get(&self.expression.node_id())
|
||||||
|
.cloned()
|
||||||
|
.unwrap();
|
||||||
|
nodes_to_types.insert(self.node_id, expression_type_info);
|
||||||
|
|
||||||
|
(nodes_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
must_return_type_info: Option<&TypeInfo>,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
self.expression.type_check(symbol_table, types_table)?;
|
||||||
|
|
||||||
|
if must_return_type_info.is_some() {
|
||||||
|
let expression_type = self.expression.type_info(symbol_table, types_table);
|
||||||
|
let return_type = must_return_type_info.unwrap();
|
||||||
|
if !return_type.is_assignable_from(expression_type) {
|
||||||
|
return Err(vec![Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Incompatible type on return expression: expected {} but found {}",
|
||||||
|
return_type, expression_type
|
||||||
|
),
|
||||||
|
self.expression.source_range().start(),
|
||||||
|
self.expression.source_range().end(),
|
||||||
|
)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
should_return_value: bool,
|
||||||
|
) {
|
||||||
|
let ir_expression = self
|
||||||
|
.expression
|
||||||
|
.to_ir_expression(builder, symbol_table, types_table);
|
||||||
|
if ir_expression.is_some() && should_return_value {
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Return(IrReturn::new(ir_expression)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
is_return_statement: bool,
|
||||||
|
) {
|
||||||
|
let ir_expression = self.expression.lower_to_ir_expression(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
);
|
||||||
|
if is_return_statement {
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Return(IrReturn::new(Some(ir_expression))));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,18 @@
|
|||||||
use crate::ast::NodeId;
|
use crate::ast::fqn_context::FqnContext;
|
||||||
|
use crate::ast::helpers::{
|
||||||
|
collect_diagnostics_into_mut, collect_parameter_symbols_into, resolve_parameter_names_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::ast::{NodeId, NodesToSymbols, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
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 {
|
||||||
@ -10,6 +21,7 @@ pub struct ExternFunction {
|
|||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
parameters: Vec<Parameter>,
|
parameters: Vec<Parameter>,
|
||||||
return_type: TypeUse,
|
return_type: TypeUse,
|
||||||
|
scope_id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExternFunction {
|
impl ExternFunction {
|
||||||
@ -26,6 +38,7 @@ impl ExternFunction {
|
|||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
parameters,
|
parameters,
|
||||||
return_type,
|
return_type,
|
||||||
|
scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,4 +65,153 @@ impl ExternFunction {
|
|||||||
pub fn return_type(&self) -> &TypeUse {
|
pub fn return_type(&self) -> &TypeUse {
|
||||||
&self.return_type
|
&self.return_type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.scope_id = Some(container_scope);
|
||||||
|
|
||||||
|
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 declared_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_source_range.clone(),
|
||||||
|
fqn_context.resolve(self.declared_name()),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
self.scope_id.unwrap(),
|
||||||
|
parameter_symbols,
|
||||||
|
));
|
||||||
|
all_symbols.push(Symbol::Function(function_symbol.clone()));
|
||||||
|
|
||||||
|
(function_symbol, all_symbols)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
resolve_parameter_names_into(
|
||||||
|
&self.parameters,
|
||||||
|
symbol_table,
|
||||||
|
&mut names_table,
|
||||||
|
&mut diagnostics,
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
let (ns, mut ds) = self.return_type.resolve_names(symbol_table);
|
||||||
|
for (node_id, symbol) in ns {
|
||||||
|
names_table.insert(node_id, symbol);
|
||||||
|
}
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
) -> (SymbolsToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut symbols_to_types = SymbolsToTypes::new();
|
||||||
|
|
||||||
|
for parameter in &self.parameters {
|
||||||
|
let (type_info, mut ds) = parameter.declared_type(nodes_to_symbols);
|
||||||
|
let parameter_symbol = nodes_to_symbols.get(¶meter.node_id()).unwrap();
|
||||||
|
symbols_to_types.insert(parameter_symbol.clone(), type_info);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(symbols_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.append(&mut self.return_type.check_names(symbol_table));
|
||||||
|
diagnostics
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// self function type
|
||||||
|
types_table.function_types_mut().insert(
|
||||||
|
function_symbol.clone(),
|
||||||
|
TypeInfo::Function(function_symbol.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// parameters
|
||||||
|
for parameter in &self.parameters {
|
||||||
|
parameter.gather_types_into(symbol_table, types_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_check_parameters(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
|
) {
|
||||||
|
collect_diagnostics_into_mut(
|
||||||
|
&mut self.parameters,
|
||||||
|
|p| p.type_check(symbol_table, types_table),
|
||||||
|
diagnostics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_check_return_type(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
|
) {
|
||||||
|
handle_diagnostics!(
|
||||||
|
self.return_type.type_check(symbol_table, types_table),
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,15 @@
|
|||||||
use crate::ast::NodeId;
|
|
||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::helpers::insert_resolved_names_into;
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
|
use crate::ast::{NodeId, NodesToSymbols, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::diagnostic_factories::field_has_no_type_or_init;
|
||||||
|
use crate::ir::ir_class::IrField;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::field_symbol::FieldSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Field {
|
pub struct Field {
|
||||||
@ -12,6 +20,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>>,
|
||||||
|
scope_id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Field {
|
impl Field {
|
||||||
@ -32,6 +41,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),
|
||||||
|
scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,4 +64,177 @@ impl Field {
|
|||||||
pub fn initializer(&self) -> Option<&Expression> {
|
pub fn initializer(&self) -> Option<&Expression> {
|
||||||
self.initializer.as_ref().map(Box::as_ref)
|
self.initializer.as_ref().map(Box::as_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.scope_id = Some(container_scope);
|
||||||
|
if let Some(type_use) = &mut self.declared_type {
|
||||||
|
type_use.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
if let Some(expression) = &mut self.initializer {
|
||||||
|
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_source_range.clone(),
|
||||||
|
self.is_mut,
|
||||||
|
self.scope_id.unwrap(),
|
||||||
|
field_index,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
|
||||||
|
if let Some(type_use) = &self.declared_type {
|
||||||
|
let (ns, mut ds) = type_use.resolve_names(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(initializer) = &self.initializer {
|
||||||
|
let (ns, mut ds) = initializer.resolve_names_field_init(symbol_table, class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||||
|
if let Some(type_use) = &self.declared_type {
|
||||||
|
diagnostics.append(&mut type_use.check_names(symbol_table));
|
||||||
|
}
|
||||||
|
diagnostics
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_field_initializer_names(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
class_symbol: &ClassSymbol,
|
||||||
|
) -> Vec<Diagnostic> {
|
||||||
|
if let Some(initializer) = &self.initializer {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
|
if let Some(type_use) = &mut self.declared_type {
|
||||||
|
if let Some(mut type_use_diagnostics) =
|
||||||
|
type_use.type_check(symbol_table, types_table).err()
|
||||||
|
{
|
||||||
|
diagnostics.append(&mut type_use_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(initializer) = &mut self.initializer {
|
||||||
|
if let Some(mut initializer_diagnostics) =
|
||||||
|
initializer.type_check(symbol_table, types_table).err()
|
||||||
|
{
|
||||||
|
diagnostics.append(&mut initializer_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !diagnostics.is_empty() {
|
||||||
|
return Err(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check that types are assignable
|
||||||
|
match self.declared_type.as_ref() {
|
||||||
|
Some(type_use) => match self.initializer.as_ref() {
|
||||||
|
Some(initializer) => {
|
||||||
|
let initializer_type_info = initializer.type_info(symbol_table, types_table);
|
||||||
|
let declared_type_info = type_use.type_info(symbol_table, types_table);
|
||||||
|
if declared_type_info.is_assignable_from(initializer_type_info) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(vec![
|
||||||
|
Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Mismatched types: {} is not assignable to {}",
|
||||||
|
initializer_type_info, declared_type_info
|
||||||
|
),
|
||||||
|
initializer.source_range().start(),
|
||||||
|
initializer.source_range().end(),
|
||||||
|
)
|
||||||
|
.with_reporter(file!(), line!()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Ok(()),
|
||||||
|
},
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir_field(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> IrField {
|
||||||
|
let symbol = nodes_to_symbols.get(&self.node_id).unwrap();
|
||||||
|
let field_type = symbols_to_types.get(symbol).unwrap();
|
||||||
|
IrField::new(
|
||||||
|
self.declared_name.clone(),
|
||||||
|
symbol.unwrap_field_symbol().field_index(), // todo: this needs to be stored NOT in the symbol
|
||||||
|
field_type.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
dmc-lib/src/ast/fqn.rs
Normal file
15
dmc-lib/src/ast/fqn.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
pub struct Fqn {
|
||||||
|
parts: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fqn {
|
||||||
|
pub fn new(parts: &[&str]) -> Self {
|
||||||
|
Self {
|
||||||
|
parts: parts.iter().map(|s| s.to_string()).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parts(&self) -> &[String] {
|
||||||
|
self.parts.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
23
dmc-lib/src/ast/fqn_context.rs
Normal file
23
dmc-lib/src/ast/fqn_context.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct FqnContext {
|
||||||
|
parts: Vec<Rc<str>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FqnContext {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { parts: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_part(&self, part: &Rc<str>) -> Self {
|
||||||
|
let mut new_parts = self.parts.clone();
|
||||||
|
new_parts.push(part.clone());
|
||||||
|
Self { parts: new_parts }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(&self, name: &str) -> Vec<Rc<str>> {
|
||||||
|
let mut result = self.parts.clone();
|
||||||
|
result.push(name.into());
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
6
dmc-lib/src/ast/fqn_util.rs
Normal file
6
dmc-lib/src/ast/fqn_util.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn fqn_parts_to_string(parts: &[Rc<str>]) -> Rc<str> {
|
||||||
|
parts.join("::").into()
|
||||||
|
}
|
||||||
@ -1,8 +1,29 @@
|
|||||||
use crate::ast::NodeId;
|
use crate::ast::fqn_context::FqnContext;
|
||||||
|
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, insert_declared_types_into, insert_resolved_names_into,
|
||||||
|
insert_resolved_types_into, resolve_parameter_names_into,
|
||||||
|
};
|
||||||
|
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::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::ir::ir_function::IrFunction;
|
||||||
|
use crate::ir::ir_parameter::IrParameter;
|
||||||
|
use crate::ir::ir_parameter_or_variable::IrParameterOrVariable;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use crate::{diagnostics_result, handle_diagnostics};
|
||||||
|
use std::ops::Neg;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
@ -13,6 +34,8 @@ pub struct Function {
|
|||||||
parameters: Vec<Parameter>,
|
parameters: Vec<Parameter>,
|
||||||
return_type: Option<TypeUse>,
|
return_type: Option<TypeUse>,
|
||||||
statements: Vec<Statement>,
|
statements: Vec<Statement>,
|
||||||
|
container_scope_id: Option<usize>,
|
||||||
|
function_scope_id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
@ -33,6 +56,8 @@ impl Function {
|
|||||||
parameters,
|
parameters,
|
||||||
return_type,
|
return_type,
|
||||||
statements,
|
statements,
|
||||||
|
container_scope_id: None,
|
||||||
|
function_scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,4 +88,466 @@ impl Function {
|
|||||||
pub fn statements(&self) -> Vec<&Statement> {
|
pub fn statements(&self) -> Vec<&Statement> {
|
||||||
self.statements.iter().collect()
|
self.statements.iter().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.container_scope_id = Some(container_scope);
|
||||||
|
let function_scope =
|
||||||
|
symbol_table.push_function_scope(&format!("function_scope({})", self.declared_name));
|
||||||
|
self.function_scope_id = Some(function_scope);
|
||||||
|
|
||||||
|
for parameter in &mut self.parameters {
|
||||||
|
parameter.init_scopes(symbol_table, function_scope);
|
||||||
|
}
|
||||||
|
if let Some(type_use) = &mut self.return_type {
|
||||||
|
type_use.init_scopes(symbol_table, function_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body_scope =
|
||||||
|
symbol_table.push_block_scope(&format!("body_scope({})", 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return value contains self FunctionSymbol followed by all symbols (including self symbol).
|
||||||
|
pub fn declared_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();
|
||||||
|
|
||||||
|
if is_method {
|
||||||
|
let self_parameter_symbol = Rc::new(ParameterSymbol::new(
|
||||||
|
&Rc::from("self"),
|
||||||
|
None,
|
||||||
|
self.function_scope_id.unwrap(),
|
||||||
|
));
|
||||||
|
parameter_symbols.push(self_parameter_symbol.clone());
|
||||||
|
all_symbols.push(Symbol::Parameter(self_parameter_symbol))
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
fqn_context.resolve(self.declared_name()),
|
||||||
|
false,
|
||||||
|
is_method,
|
||||||
|
self.container_scope_id.unwrap(),
|
||||||
|
parameter_symbols,
|
||||||
|
));
|
||||||
|
all_symbols.push(Symbol::Function(function_symbol.clone()));
|
||||||
|
|
||||||
|
(function_symbol, all_symbols)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_names_common(&self, symbol_table: &SymbolTable) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut nodes_to_symbols = NodesToSymbols::new();
|
||||||
|
|
||||||
|
resolve_parameter_names_into(
|
||||||
|
&self.parameters,
|
||||||
|
symbol_table,
|
||||||
|
&mut nodes_to_symbols,
|
||||||
|
&mut diagnostics,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(type_use) = &self.return_type {
|
||||||
|
let (ns, mut ds) = type_use.resolve_names(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut nodes_to_symbols);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert self function symbol with this node
|
||||||
|
let function_symbol = symbol_table
|
||||||
|
.get_function_symbol_owned(self.container_scope_id.unwrap(), &self.declared_name)
|
||||||
|
.unwrap();
|
||||||
|
nodes_to_symbols.insert(self.node_id, Symbol::Function(function_symbol));
|
||||||
|
|
||||||
|
(nodes_to_symbols, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let (mut nodes_to_symbols, mut diagnostics) = self.resolve_names_common(symbol_table);
|
||||||
|
|
||||||
|
for statement in &self.statements {
|
||||||
|
let (ns, mut ds) = statement.resolve_names_static(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut nodes_to_symbols);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes_to_symbols, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let (mut nodes_to_symbols, mut diagnostics) = self.resolve_names_common(symbol_table);
|
||||||
|
|
||||||
|
for statement in &self.statements {
|
||||||
|
let (ns, mut ds) = statement.resolve_names_method(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut nodes_to_symbols);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes_to_symbols, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
let mut diagnostics = Vec::new();
|
||||||
|
for parameter in &self.parameters {
|
||||||
|
diagnostics.append(&mut parameter.check_names(symbol_table));
|
||||||
|
}
|
||||||
|
if let Some(type_use) = &self.return_type {
|
||||||
|
diagnostics.append(&mut type_use.check_names(symbol_table));
|
||||||
|
}
|
||||||
|
diagnostics
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn analyze_method_local_names(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
class_symbol: &ClassSymbol,
|
||||||
|
) -> Vec<Diagnostic> {
|
||||||
|
self.statements
|
||||||
|
.iter()
|
||||||
|
.flat_map(|statement| statement.analyze_method_local_names(symbol_table, class_symbol))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn analyze_static_fn_local_names(&self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
self.statements
|
||||||
|
.iter()
|
||||||
|
.flat_map(|statement| statement.analyze_static_fn_local_names(symbol_table))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
) -> (SymbolsToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut symbols_to_types = SymbolsToTypes::new();
|
||||||
|
|
||||||
|
for parameter in &self.parameters {
|
||||||
|
let symbol = nodes_to_symbols.get(¶meter.node_id()).unwrap();
|
||||||
|
let (type_info, mut ds) = parameter.declared_type(nodes_to_symbols);
|
||||||
|
symbols_to_types.insert(symbol.clone(), type_info);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert return type as the type of this function symbol
|
||||||
|
let symbol = nodes_to_symbols.get(&self.node_id).unwrap().clone();
|
||||||
|
let type_info = match &self.return_type {
|
||||||
|
None => TypeInfo::Void,
|
||||||
|
Some(type_use) => {
|
||||||
|
let (type_info, mut ds) = type_use.declared_type(nodes_to_symbols);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
type_info
|
||||||
|
}
|
||||||
|
};
|
||||||
|
symbols_to_types.insert(symbol, type_info);
|
||||||
|
|
||||||
|
(symbols_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (SymbolsToTypes, NodesToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut nodes_to_types = NodesToTypes::new();
|
||||||
|
let mut symbols_to_types = symbols_to_types.clone();
|
||||||
|
|
||||||
|
for statement in &self.statements {
|
||||||
|
let (sts, nts, mut ds) = statement.resolve_types(nodes_to_symbols, &symbols_to_types);
|
||||||
|
insert_declared_types_into(sts, &mut symbols_to_types); // merge!
|
||||||
|
insert_resolved_types_into(nts, &mut nodes_to_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: check last statement for return type
|
||||||
|
|
||||||
|
(symbols_to_types, nodes_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn gather_types(&self, symbol_table: &SymbolTable, types_table: &mut TypesTable) {
|
||||||
|
let function_symbol = symbol_table
|
||||||
|
.get_function_symbol_owned(self.container_scope_id.unwrap(), self.declared_name())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// self type (the signature)
|
||||||
|
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 {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
|
) {
|
||||||
|
collect_diagnostics_into_mut(
|
||||||
|
&mut self.parameters,
|
||||||
|
|p| p.type_check(symbol_table, types_table),
|
||||||
|
diagnostics,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type checks return type.
|
||||||
|
fn type_check_return_type(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
|
) {
|
||||||
|
if let Some(type_use) = &mut self.return_type {
|
||||||
|
handle_diagnostics!(type_use.type_check(symbol_table, types_table), diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type checks statements, making sure the last statement matches return type, if necessary.
|
||||||
|
fn type_check_statements(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &mut TypesTable,
|
||||||
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
|
function_symbol: &FunctionSymbol,
|
||||||
|
) {
|
||||||
|
let return_type_info = Self::get_return_type_info(types_table, function_symbol);
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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.container_scope_id.unwrap(), self.declared_name())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// parameters
|
||||||
|
self.type_check_parameters(symbol_table, types_table, &mut diagnostics);
|
||||||
|
|
||||||
|
// return type
|
||||||
|
self.type_check_return_type(symbol_table, types_table, &mut diagnostics);
|
||||||
|
|
||||||
|
// statements
|
||||||
|
self.type_check_statements(symbol_table, types_table, &mut diagnostics, function_symbol);
|
||||||
|
|
||||||
|
diagnostics_result!(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(¶meter_symbol)
|
||||||
|
.unwrap();
|
||||||
|
let stack_offset = (self.parameters.len() as isize).neg() + (i as isize);
|
||||||
|
let ir_parameter =
|
||||||
|
IrParameter::new(parameter_symbol.declared_name(), todo!(), stack_offset);
|
||||||
|
let as_rc = Rc::new(ir_parameter);
|
||||||
|
builder.push_parameter(¶meter_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::Parameter(todo!()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn to_ir(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
class_context: Option<&ClassSymbol>,
|
||||||
|
) -> IrFunction {
|
||||||
|
let mut builder = IrBuilder::new();
|
||||||
|
let function_symbol = symbol_table
|
||||||
|
.get_function_symbol(self.container_scope_id.unwrap(), self.declared_name())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// parameters
|
||||||
|
self.parameters_to_ir(&mut builder, symbol_table, types_table);
|
||||||
|
|
||||||
|
let entry_block_id = builder.new_block();
|
||||||
|
|
||||||
|
// preamble
|
||||||
|
self.handle_method_case(&mut builder, class_context);
|
||||||
|
|
||||||
|
// body
|
||||||
|
self.statements_to_ir(&mut builder, symbol_table, types_table, function_symbol);
|
||||||
|
|
||||||
|
builder.finish_block();
|
||||||
|
|
||||||
|
let entry_block = builder.get_block(entry_block_id).clone();
|
||||||
|
IrFunction::new(
|
||||||
|
fqn_parts_to_string(function_symbol.fqn_parts()),
|
||||||
|
todo!(),
|
||||||
|
todo!(),
|
||||||
|
todo!(),
|
||||||
|
todo!(),
|
||||||
|
todo!(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir_static(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> IrFunction {
|
||||||
|
let mut builder = IrBuilder::new();
|
||||||
|
|
||||||
|
let function_symbol = nodes_to_symbols
|
||||||
|
.get(&self.node_id)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_function_symbol();
|
||||||
|
|
||||||
|
// put parameters in builder
|
||||||
|
for (i, parameter_symbol) in function_symbol.parameters().iter().enumerate() {
|
||||||
|
let parameter_type_info = symbols_to_types
|
||||||
|
.get(&Symbol::Parameter(parameter_symbol.clone()))
|
||||||
|
.unwrap();
|
||||||
|
let stack_offset = (function_symbol.parameters().len() as isize).neg() + (i as isize);
|
||||||
|
let ir_parameter = Rc::new(IrParameter::new(
|
||||||
|
parameter_symbol.declared_name(),
|
||||||
|
todo!(),
|
||||||
|
stack_offset,
|
||||||
|
));
|
||||||
|
builder.push_parameter(parameter_symbol, ir_parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry_block_id = builder.new_block();
|
||||||
|
|
||||||
|
// lower statements
|
||||||
|
let return_type_info = symbols_to_types
|
||||||
|
.get(&Symbol::Function(function_symbol.clone()))
|
||||||
|
.unwrap();
|
||||||
|
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.lower_to_ir(
|
||||||
|
&mut builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
should_return_value && is_last,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.finish_block();
|
||||||
|
|
||||||
|
let entry_block = builder.get_block(entry_block_id).clone();
|
||||||
|
IrFunction::new(
|
||||||
|
fqn_parts_to_string(function_symbol.fqn_parts()),
|
||||||
|
todo!(),
|
||||||
|
todo!(),
|
||||||
|
todo!(),
|
||||||
|
todo!(),
|
||||||
|
todo!(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,22 @@
|
|||||||
|
use crate::ast::NodesToSymbols;
|
||||||
|
use crate::ast::helpers::insert_resolved_names_into;
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use crate::{diagnostics_result, handle_diagnostics};
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct GenericParameter {
|
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>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenericParameter {
|
impl GenericParameter {
|
||||||
@ -18,6 +29,82 @@ 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.scope_id = Some(container_scope);
|
||||||
|
for type_use in &mut self.extends {
|
||||||
|
type_use.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_symbol(&self) -> GenericParameterSymbol {
|
||||||
|
GenericParameterSymbol::new(
|
||||||
|
&self.declared_name,
|
||||||
|
&self.declared_name_source_range,
|
||||||
|
self.scope_id.unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names(&self, symbol_table: &SymbolTable) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
for type_use in &self.extends {
|
||||||
|
let (ns, mut ds) = type_use.resolve_names(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
types_table: &mut TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
// self param
|
||||||
|
let generic_parameter_symbol = symbol_table
|
||||||
|
.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!(
|
||||||
|
type_use.gather_types(symbol_table, types_table),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics_result!(diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
// check extends type uses
|
||||||
|
for type_use in &mut self.extends {
|
||||||
|
handle_diagnostics!(type_use.type_check(symbol_table, types_table), diagnostics);
|
||||||
|
}
|
||||||
|
diagnostics_result!(diagnostics)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
141
dmc-lib/src/ast/helpers.rs
Normal file
141
dmc-lib/src/ast/helpers.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
use crate::ast::fqn_context::FqnContext;
|
||||||
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::{NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_parameter_names_into(
|
||||||
|
parameters: &[Parameter],
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
nodes_to_symbols: &mut NodesToSymbols,
|
||||||
|
diagnostics: &mut Diagnostics,
|
||||||
|
) {
|
||||||
|
for parameter in parameters {
|
||||||
|
let (ns, mut ds) = parameter.resolve_names(symbol_table);
|
||||||
|
for (node_id, symbol) in ns {
|
||||||
|
nodes_to_symbols.insert(node_id, symbol);
|
||||||
|
}
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_resolved_names_into(source: NodesToSymbols, destination: &mut NodesToSymbols) {
|
||||||
|
for (node_id, symbol) in source {
|
||||||
|
destination.insert(node_id, symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_declared_types_into(source: SymbolsToTypes, destination: &mut SymbolsToTypes) {
|
||||||
|
for (symbol, type_info) in source {
|
||||||
|
destination.insert(symbol, type_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_resolved_types_into(source: NodesToTypes, destination: &mut NodesToTypes) {
|
||||||
|
for (node_id, type_info) in source {
|
||||||
|
destination.insert(node_id, type_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,36 @@
|
|||||||
use crate::ast::NodeId;
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
|
use crate::ast::ir_util::get_or_init_field_pointer_variable;
|
||||||
|
use crate::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::diagnostic_factories::{
|
||||||
|
cannot_reassign_immutable_field, not_assignable, 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::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::expressible_symbol::ExpressibleSymbol;
|
||||||
|
use crate::symbol::field_symbol::FieldSymbol;
|
||||||
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Identifier {
|
pub struct Identifier {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
name: Rc<str>,
|
name: Rc<str>,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
scope_id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Identifier {
|
impl Identifier {
|
||||||
@ -14,6 +39,7 @@ impl Identifier {
|
|||||||
node_id,
|
node_id,
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
source_range,
|
source_range,
|
||||||
|
scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +51,629 @@ impl Identifier {
|
|||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scope_id(&mut self, container_scope: usize) {
|
||||||
|
self.scope_id = Some(container_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope_id(&self) -> usize {
|
||||||
|
self.scope_id.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_name_static(&self, symbol_table: &SymbolTable) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
match symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name) {
|
||||||
|
None => {
|
||||||
|
diagnostics.push(symbol_not_found(&self.name, &self.source_range));
|
||||||
|
}
|
||||||
|
Some(expressible_symbol) => {
|
||||||
|
names_table.insert(self.node_id, expressible_symbol.into_symbol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the name inside an initializer for a field *outside* a constructor.
|
||||||
|
pub fn resolve_name_field_init(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
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.init_referring_to_class(
|
||||||
|
self_class_symbol,
|
||||||
|
&class_symbol,
|
||||||
|
&mut names_table,
|
||||||
|
&mut diagnostics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => {
|
||||||
|
self.init_referring_to_field(
|
||||||
|
self_class_symbol,
|
||||||
|
&field_symbol,
|
||||||
|
&mut diagnostics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Function(function_symbol) => {
|
||||||
|
self.init_referring_to_function(
|
||||||
|
self_class_symbol,
|
||||||
|
&function_symbol,
|
||||||
|
&mut names_table,
|
||||||
|
&mut diagnostics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
diagnostics.push(symbol_not_found(&self.name, &self.source_range));
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the name as used in a rhs expression in a constructor.
|
||||||
|
pub fn resolve_name_ctor(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
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.init_referring_to_class(
|
||||||
|
self_class_symbol,
|
||||||
|
&class_symbol,
|
||||||
|
&mut names_table,
|
||||||
|
&mut diagnostics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => {
|
||||||
|
self.init_referring_to_field(
|
||||||
|
self_class_symbol,
|
||||||
|
&field_symbol,
|
||||||
|
&mut diagnostics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Function(function_symbol) => {
|
||||||
|
self.init_referring_to_function(
|
||||||
|
self_class_symbol,
|
||||||
|
&function_symbol,
|
||||||
|
&mut names_table,
|
||||||
|
&mut diagnostics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Parameter(parameter_symbol) => {
|
||||||
|
names_table.insert(self.node_id, Symbol::Parameter(parameter_symbol));
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||||
|
names_table.insert(self.node_id, Symbol::Variable(variable_symbol));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
diagnostics.push(symbol_not_found(&self.name, &self.source_range));
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the name as an LValue in a constructor.
|
||||||
|
pub fn resolve_name_ctor_destination(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
initialized_fields: &mut HashSet<Rc<str>>,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
|
||||||
|
let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
|
||||||
|
if let Some(symbol) = symbol {
|
||||||
|
match symbol {
|
||||||
|
ExpressibleSymbol::Class(_) => {
|
||||||
|
// error
|
||||||
|
diagnostics.push(not_assignable(&self.name, &self.source_range));
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => {
|
||||||
|
// ok if field has not been initialized yet OR field is mutable
|
||||||
|
if !initialized_fields.contains(&self.name) {
|
||||||
|
initialized_fields.insert(self.name.clone());
|
||||||
|
names_table.insert(self.node_id, Symbol::Field(field_symbol));
|
||||||
|
} else if !field_symbol.is_mut() {
|
||||||
|
// error since we are trying to reassign an immutable field
|
||||||
|
diagnostics.push(cannot_reassign_immutable_field(&self.source_range));
|
||||||
|
} else {
|
||||||
|
// mut is ok
|
||||||
|
names_table.insert(self.node_id, Symbol::Field(field_symbol));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Function(_) => {
|
||||||
|
diagnostics.push(not_assignable(&self.name, &self.source_range));
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Parameter(_) => {
|
||||||
|
// assigning to parameter is an error
|
||||||
|
// we may in the future allow mut on parameters, but it's probably pointless
|
||||||
|
diagnostics.push(not_assignable(&self.name, &self.source_range));
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||||
|
// ok
|
||||||
|
names_table.insert(self.node_id, Symbol::Variable(variable_symbol));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
diagnostics.push(symbol_not_found(&self.name, &self.source_range));
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_name_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
_self_class_symbol: &ClassSymbol, // for future when we have paths?
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut nodes_to_symbols = NodesToSymbols::new();
|
||||||
|
|
||||||
|
let maybe_expressible_symbol =
|
||||||
|
symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
|
||||||
|
match maybe_expressible_symbol {
|
||||||
|
None => {
|
||||||
|
diagnostics.push(symbol_not_found(&self.name, &self.source_range));
|
||||||
|
}
|
||||||
|
Some(expressible_symbol) => {
|
||||||
|
nodes_to_symbols.insert(self.node_id, expressible_symbol.into_symbol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes_to_symbols, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_referring_to_class(
|
||||||
|
&self,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
class_symbol: &Rc<ClassSymbol>,
|
||||||
|
names_table: &mut NodesToSymbols,
|
||||||
|
diagnostics: &mut Diagnostics,
|
||||||
|
) {
|
||||||
|
// Check against recursively constructing this class.
|
||||||
|
// This is not future-proof, as we will eventually allow reference to the self class, which
|
||||||
|
// would (theoretically) be assigned to an instance field.
|
||||||
|
if self_class_symbol == class_symbol.as_ref() {
|
||||||
|
diagnostics.push(self_constructor_used_in_init(&self.source_range));
|
||||||
|
} else {
|
||||||
|
names_table.insert(self.node_id, Symbol::Class(class_symbol.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_referring_to_field(
|
||||||
|
&self,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
field_symbol: &FieldSymbol,
|
||||||
|
diagnostics: &mut Diagnostics,
|
||||||
|
) {
|
||||||
|
if self_class_symbol
|
||||||
|
.fields()
|
||||||
|
.contains_key(field_symbol.declared_name())
|
||||||
|
{
|
||||||
|
diagnostics.push(self_field_used_in_init(&self.source_range));
|
||||||
|
} else {
|
||||||
|
diagnostics.push(outer_class_field_usage(&self.source_range));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_referring_to_function(
|
||||||
|
&self,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
function_symbol: &Rc<FunctionSymbol>,
|
||||||
|
names_table: &mut NodesToSymbols,
|
||||||
|
diagnostics: &mut Diagnostics,
|
||||||
|
) {
|
||||||
|
if self_class_symbol
|
||||||
|
.functions()
|
||||||
|
.contains_key(function_symbol.declared_name())
|
||||||
|
{
|
||||||
|
diagnostics.push(self_method_used_in_init(&self.source_range));
|
||||||
|
} else if function_symbol.is_method() {
|
||||||
|
diagnostics.push(outer_class_method_usage(&self.source_range));
|
||||||
|
} else {
|
||||||
|
names_table.insert(self.node_id, Symbol::Function(function_symbol.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check against recursively constructing this class.
|
||||||
|
#[deprecated]
|
||||||
|
fn check_self_constructor_use(
|
||||||
|
&self,
|
||||||
|
context_class_symbol: &ClassSymbol,
|
||||||
|
class_symbol: &ClassSymbol,
|
||||||
|
) -> Option<Diagnostic> {
|
||||||
|
// this is not future-proof, as we will eventually allow reference to the self class, which
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check against using this or outer class' bare fields.
|
||||||
|
#[deprecated]
|
||||||
|
fn check_self_or_outer_field_use(
|
||||||
|
&self,
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check against using self or outer class methods.
|
||||||
|
#[deprecated]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_name_as_field_initializer(
|
||||||
|
&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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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.
|
||||||
|
#[deprecated]
|
||||||
|
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 resolve_type(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (NodesToTypes, Diagnostics) {
|
||||||
|
let self_symbol = nodes_to_symbols.get(&self.node_id).unwrap();
|
||||||
|
let type_info = symbols_to_types.get(self_symbol).unwrap();
|
||||||
|
|
||||||
|
let mut resolved_types = NodesToTypes::new();
|
||||||
|
resolved_types.insert(self.node_id, type_info.clone());
|
||||||
|
|
||||||
|
(resolved_types, Diagnostics::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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(¶meter_symbol)
|
||||||
|
.unwrap(),
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||||
|
types_table.variable_types().get(&variable_symbol).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir_expression(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> IrExpression {
|
||||||
|
let symbol = nodes_to_symbols.get(&self.node_id).unwrap();
|
||||||
|
match &symbol.unwrap_expressible_symbol() {
|
||||||
|
ExpressibleSymbol::Class(_class_symbol) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => {
|
||||||
|
let field_type = symbols_to_types.get(symbol).unwrap();
|
||||||
|
let read_destination = Rc::new(RefCell::new(todo!()));
|
||||||
|
let ir_read_field = IrReadField::new(todo!());
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(IrAssign::new(
|
||||||
|
todo!(),
|
||||||
|
IrOperation::ReadField(ir_read_field),
|
||||||
|
)));
|
||||||
|
IrExpression::Variable(todo!())
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Function(_function_symbol) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Parameter(parameter_symbol) => IrExpression::Parameter(todo!()),
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => IrExpression::Variable(todo!()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = todo!();
|
||||||
|
let read_destination_as_rc = Rc::new(RefCell::new(read_destination));
|
||||||
|
let ir_read_field = IrReadField::new(todo!());
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(IrAssign::new(
|
||||||
|
todo!(),
|
||||||
|
IrOperation::ReadField(ir_read_field),
|
||||||
|
)));
|
||||||
|
IrExpression::Variable(todo!())
|
||||||
|
}
|
||||||
|
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(¶meter_symbol).unwrap();
|
||||||
|
IrExpression::Parameter(todo!())
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||||
|
let ir_variable = builder.local_variables().get(&variable_symbol).unwrap();
|
||||||
|
IrExpression::Variable(todo!())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::ast::identifier::Identifier;
|
||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::variable_symbol::VariableSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inits_scope_id() {
|
||||||
|
let mut identifier = Identifier::new(0, "foo", SourceRange::new(0, 0));
|
||||||
|
identifier.init_scope_id(42);
|
||||||
|
assert_eq!(identifier.scope_id(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolves_static_foo() {
|
||||||
|
let mut identifier = Identifier::new(0, "foo", SourceRange::new(0, 0));
|
||||||
|
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
symbol_table.push_module_scope("foo module");
|
||||||
|
symbol_table.push_function_scope("foo function");
|
||||||
|
let scope_id = symbol_table.push_block_scope("foo block");
|
||||||
|
identifier.init_scope_id(scope_id);
|
||||||
|
|
||||||
|
let variable_symbol = Rc::new(VariableSymbol::new(
|
||||||
|
&"foo".into(),
|
||||||
|
&SourceRange::new(0, 0),
|
||||||
|
false,
|
||||||
|
scope_id,
|
||||||
|
));
|
||||||
|
symbol_table.insert_variable_symbol(variable_symbol.clone());
|
||||||
|
|
||||||
|
let (nodes_to_symbols, diagnostics) = identifier.resolve_name_static(&symbol_table);
|
||||||
|
|
||||||
|
assert_eq!(diagnostics.len(), 0);
|
||||||
|
assert_eq!(nodes_to_symbols.len(), 1);
|
||||||
|
let symbol = nodes_to_symbols.get(&identifier.node_id()).unwrap();
|
||||||
|
match symbol {
|
||||||
|
Symbol::Variable(matched_variable_symbol) => {
|
||||||
|
assert_eq!(&variable_symbol, matched_variable_symbol);
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,22 @@
|
|||||||
use crate::ast::NodeId;
|
use crate::ast::NodeId;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
|
||||||
pub struct IntegerLiteral {
|
pub struct IntegerLiteral {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
value: i32,
|
value: i32,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
type_info: &'static TypeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntegerLiteral {
|
impl IntegerLiteral {
|
||||||
pub fn new(node_id: NodeId, value: i32, source_range: SourceRange) -> Self {
|
pub fn new(node_id: NodeId, value: i32, source_range: SourceRange) -> Self {
|
||||||
|
const TYPE_INFO: TypeInfo = TypeInfo::Integer;
|
||||||
Self {
|
Self {
|
||||||
node_id,
|
node_id,
|
||||||
value,
|
value,
|
||||||
source_range,
|
source_range,
|
||||||
|
type_info: &TYPE_INFO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +28,10 @@ impl IntegerLiteral {
|
|||||||
self.node_id
|
self.node_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_info(&self) -> &TypeInfo {
|
||||||
|
&self.type_info
|
||||||
|
}
|
||||||
|
|
||||||
pub fn source_range(&self) -> &SourceRange {
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
&self.source_range
|
&self.source_range
|
||||||
}
|
}
|
||||||
|
|||||||
169
dmc-lib/src/ast/ir_builder.rs
Normal file
169
dmc-lib/src/ast/ir_builder.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
use crate::ir::ir_block::IrBlock;
|
||||||
|
use crate::ir::ir_parameter::IrParameter;
|
||||||
|
use crate::ir::ir_parameter_or_variable::IrParameterOrVariable;
|
||||||
|
use crate::ir::ir_statement::IrStatement;
|
||||||
|
use crate::ir::ir_variable::IrVariable;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use crate::symbol::variable_symbol::VariableSymbol;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct IrBuilder {
|
||||||
|
parameters: Vec<(Rc<ParameterSymbol>, Rc<IrParameter>)>,
|
||||||
|
local_variables: HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>>,
|
||||||
|
block_counter: usize,
|
||||||
|
t_var_counter: usize,
|
||||||
|
blocks: HashMap<usize, Rc<RefCell<IrBlock>>>,
|
||||||
|
current_block_builder: Option<IrBlockBuilder>,
|
||||||
|
self_parameter_or_variable: Option<IrParameterOrVariable>,
|
||||||
|
field_variables: HashMap<Rc<str>, Rc<RefCell<IrVariable>>>,
|
||||||
|
mut_field_variables: HashMap<Rc<str>, Rc<RefCell<IrVariable>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IrBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
parameters: vec![],
|
||||||
|
local_variables: HashMap::new(),
|
||||||
|
block_counter: 0,
|
||||||
|
t_var_counter: 0,
|
||||||
|
blocks: HashMap::new(),
|
||||||
|
current_block_builder: None,
|
||||||
|
self_parameter_or_variable: None,
|
||||||
|
field_variables: HashMap::new(),
|
||||||
|
mut_field_variables: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_variables(&self) -> &HashMap<Rc<VariableSymbol>, Rc<RefCell<IrVariable>>> {
|
||||||
|
&self.local_variables
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_variables_mut(
|
||||||
|
&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 {
|
||||||
|
let block_id = self.block_counter;
|
||||||
|
self.block_counter += 1;
|
||||||
|
let block_builder = IrBlockBuilder::new(block_id);
|
||||||
|
self.current_block_builder = Some(block_builder);
|
||||||
|
block_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_block(&mut self, block_id: usize) -> &Rc<RefCell<IrBlock>> {
|
||||||
|
self.blocks
|
||||||
|
.get(&block_id)
|
||||||
|
.expect(&format!("Block {} not found", block_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_block_mut(&mut self) -> &mut IrBlockBuilder {
|
||||||
|
self.current_block_builder
|
||||||
|
.as_mut()
|
||||||
|
.expect("No current block builder")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_block(&self) -> &IrBlockBuilder {
|
||||||
|
self.current_block_builder
|
||||||
|
.as_ref()
|
||||||
|
.expect("No current block")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish_block(&mut self) {
|
||||||
|
let builder = self
|
||||||
|
.current_block_builder
|
||||||
|
.take()
|
||||||
|
.expect("No current block builder");
|
||||||
|
let block = builder.build();
|
||||||
|
self.blocks.insert(block.id(), Rc::new(RefCell::new(block)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_t_var(&mut self) -> String {
|
||||||
|
let id = self.t_var_counter;
|
||||||
|
self.t_var_counter += 1;
|
||||||
|
format!("t{}", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_self_parameter_or_variable(
|
||||||
|
&mut self,
|
||||||
|
self_parameter_or_variable: IrParameterOrVariable,
|
||||||
|
) {
|
||||||
|
self.self_parameter_or_variable = Some(self_parameter_or_variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn self_parameter_or_variable(&self) -> &IrParameterOrVariable {
|
||||||
|
self.self_parameter_or_variable.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field_pointer_variables(&self) -> &HashMap<Rc<str>, Rc<RefCell<IrVariable>>> {
|
||||||
|
&self.field_variables
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field_pointer_variables_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> &mut HashMap<Rc<str>, Rc<RefCell<IrVariable>>> {
|
||||||
|
&mut self.field_variables
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field_mut_pointer_variables(&self) -> &HashMap<Rc<str>, Rc<RefCell<IrVariable>>> {
|
||||||
|
&self.mut_field_variables
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field_mut_pointer_variables_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> &mut HashMap<Rc<str>, Rc<RefCell<IrVariable>>> {
|
||||||
|
&mut self.mut_field_variables
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IrBlockBuilder {
|
||||||
|
id: usize,
|
||||||
|
statements: Vec<IrStatement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IrBlockBuilder {
|
||||||
|
pub fn new(id: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
statements: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> usize {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_statement(&mut self, statement: IrStatement) {
|
||||||
|
self.statements.push(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> IrBlock {
|
||||||
|
IrBlock::new(self.id, &format!("b{}", self.id), self.statements)
|
||||||
|
}
|
||||||
|
}
|
||||||
77
dmc-lib/src/ast/ir_util.rs
Normal file
77
dmc-lib/src/ast/ir_util.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
|
use crate::ir::ir_assign::IrAssign;
|
||||||
|
use crate::ir::ir_get_field_ref::IrGetFieldRef;
|
||||||
|
use crate::ir::ir_get_field_ref_mut::IrGetFieldRefMut;
|
||||||
|
use crate::ir::ir_operation::IrOperation;
|
||||||
|
use crate::ir::ir_statement::IrStatement;
|
||||||
|
use crate::ir::ir_variable::IrVariable;
|
||||||
|
use crate::symbol::field_symbol::FieldSymbol;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub fn get_or_init_field_pointer_variable<'a>(
|
||||||
|
builder: &'a mut IrBuilder,
|
||||||
|
field_symbol: &Rc<FieldSymbol>,
|
||||||
|
field_type: &TypeInfo,
|
||||||
|
) -> &'a Rc<RefCell<IrVariable>> {
|
||||||
|
// 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.
|
||||||
|
if !builder
|
||||||
|
.field_pointer_variables()
|
||||||
|
.contains_key(field_symbol.declared_name())
|
||||||
|
{
|
||||||
|
let field_ref_variable = todo!();
|
||||||
|
let as_rc = Rc::new(RefCell::new(field_ref_variable));
|
||||||
|
let to_insert = as_rc.clone();
|
||||||
|
let self_parameter_or_variable = builder.self_parameter_or_variable().clone();
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(IrAssign::new(
|
||||||
|
todo!(),
|
||||||
|
IrOperation::GetFieldRef(IrGetFieldRef::new(
|
||||||
|
self_parameter_or_variable.clone(),
|
||||||
|
field_symbol.field_index(),
|
||||||
|
)),
|
||||||
|
)));
|
||||||
|
builder
|
||||||
|
.field_pointer_variables_mut()
|
||||||
|
.insert(field_symbol.declared_name_owned(), to_insert);
|
||||||
|
}
|
||||||
|
builder
|
||||||
|
.field_pointer_variables()
|
||||||
|
.get(field_symbol.declared_name())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_init_mut_field_pointer_variable<'a>(
|
||||||
|
builder: &'a mut IrBuilder,
|
||||||
|
field_symbol: &Rc<FieldSymbol>,
|
||||||
|
field_type: &TypeInfo,
|
||||||
|
) -> &'a Rc<RefCell<IrVariable>> {
|
||||||
|
if !builder
|
||||||
|
.field_mut_pointer_variables()
|
||||||
|
.contains_key(field_symbol.declared_name())
|
||||||
|
{
|
||||||
|
let mut_field_pointer_variable = todo!();
|
||||||
|
let as_rc = Rc::new(RefCell::new(mut_field_pointer_variable));
|
||||||
|
let to_insert = as_rc.clone();
|
||||||
|
let self_parameter_or_variable = builder.self_parameter_or_variable().clone();
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(IrAssign::new(
|
||||||
|
todo!(),
|
||||||
|
IrOperation::GetFieldRefMut(IrGetFieldRefMut::new(
|
||||||
|
self_parameter_or_variable.clone(),
|
||||||
|
field_symbol.field_index(),
|
||||||
|
)),
|
||||||
|
)));
|
||||||
|
builder
|
||||||
|
.field_mut_pointer_variables_mut()
|
||||||
|
.insert(field_symbol.declared_name_owned(), to_insert);
|
||||||
|
}
|
||||||
|
builder
|
||||||
|
.field_mut_pointer_variables()
|
||||||
|
.get(field_symbol.declared_name())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
@ -1,6 +1,20 @@
|
|||||||
use crate::ast::NodeId;
|
|
||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::helpers::{insert_resolved_names_into, insert_resolved_types_into};
|
||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
|
use crate::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::ir::ir_assign::IrAssign;
|
||||||
|
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::variable_symbol::VariableSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::symbol_table::util::try_insert_symbol_into;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct LetStatement {
|
pub struct LetStatement {
|
||||||
@ -9,6 +23,7 @@ pub struct LetStatement {
|
|||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
is_mut: bool,
|
is_mut: bool,
|
||||||
initializer: Box<Expression>,
|
initializer: Box<Expression>,
|
||||||
|
scope_id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LetStatement {
|
impl LetStatement {
|
||||||
@ -25,6 +40,7 @@ impl LetStatement {
|
|||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
is_mut,
|
is_mut,
|
||||||
initializer: initializer.into(),
|
initializer: initializer.into(),
|
||||||
|
scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,4 +67,295 @@ impl LetStatement {
|
|||||||
pub fn initializer(&self) -> &Expression {
|
pub fn initializer(&self) -> &Expression {
|
||||||
&self.initializer
|
&self.initializer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn initializer_mut(&mut self) -> &mut Expression {
|
||||||
|
&mut self.initializer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.scope_id = Some(container_scope);
|
||||||
|
self.initializer.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope_id(&self) -> usize {
|
||||||
|
self.scope_id.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_and_insert_variable_symbol(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Option<Diagnostic> {
|
||||||
|
let variable_symbol = Rc::new(VariableSymbol::new(
|
||||||
|
&self.declared_name,
|
||||||
|
&self.declared_name_source_range,
|
||||||
|
self.is_mut,
|
||||||
|
self.scope_id.unwrap(),
|
||||||
|
));
|
||||||
|
try_insert_symbol_into(Symbol::Variable(variable_symbol), symbol_table).err()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut names_table = NodesToSymbols::new();
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (ns, mut ds) = self.initializer.resolve_names_static(symbol_table);
|
||||||
|
insert_resolved_names_into(ns, &mut names_table);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) {
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
(names_table, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_ctor(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut nodes_to_symbols = NodesToSymbols::new();
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (ns, mut ds) = self
|
||||||
|
.initializer
|
||||||
|
.resolve_names_ctor(symbol_table, self_class_symbol);
|
||||||
|
insert_resolved_names_into(ns, &mut nodes_to_symbols);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) {
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes_to_symbols, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut nodes_to_symbols = NodesToSymbols::new();
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (ns, mut ds) = self
|
||||||
|
.initializer
|
||||||
|
.resolve_names_method(symbol_table, self_class_symbol); // todo
|
||||||
|
insert_resolved_names_into(ns, &mut nodes_to_symbols);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(diagnostic) = self.make_and_insert_variable_symbol(symbol_table) {
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes_to_symbols, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn analyze_constructor_local_names(
|
||||||
|
&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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn analyze_method_local_names(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
class_symbol: &ClassSymbol,
|
||||||
|
) -> Vec<Diagnostic> {
|
||||||
|
let mut diagnostics = Vec::new();
|
||||||
|
diagnostics.append(
|
||||||
|
&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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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 resolve_types(
|
||||||
|
&self,
|
||||||
|
resolved_symbols: &NodesToSymbols,
|
||||||
|
resolved_symbol_type_infos: &SymbolsToTypes,
|
||||||
|
) -> (SymbolsToTypes, NodesToTypes, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
let mut resolved_types = NodesToTypes::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (rts, mut ds) = self
|
||||||
|
.initializer
|
||||||
|
.resolve_types(resolved_symbols, resolved_symbol_type_infos);
|
||||||
|
insert_resolved_types_into(rts, &mut resolved_types);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
let initializer_resolved_type = resolved_types.get(&self.initializer.node_id()).unwrap();
|
||||||
|
let self_symbol = resolved_symbols.get(&self.node_id).unwrap();
|
||||||
|
|
||||||
|
let mut resolved_symbol_type_infos = resolved_symbol_type_infos.clone();
|
||||||
|
resolved_symbol_type_infos.insert(self_symbol.clone(), initializer_resolved_type.clone());
|
||||||
|
|
||||||
|
(resolved_symbol_type_infos, resolved_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &mut TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
self.initializer.type_check(symbol_table, types_table)?;
|
||||||
|
// TODO: this is wrong. We need to check assignability
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_vr_variable(&self, builder: &mut IrBuilder, destination_type: &TypeInfo) -> IrVariable {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_stack_variable(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
destination_type: &TypeInfo,
|
||||||
|
offset: isize,
|
||||||
|
) -> IrVariable {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_destination_symbol(&self, symbol_table: &SymbolTable) -> Rc<VariableSymbol> {
|
||||||
|
symbol_table
|
||||||
|
.get_variable_symbol_owned(self.scope_id.unwrap(), &self.declared_name)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ir(
|
||||||
|
&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 = self.get_destination_symbol(symbol_table);
|
||||||
|
|
||||||
|
let destination_type = types_table
|
||||||
|
.variable_types()
|
||||||
|
.get(&destination_symbol)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let destination_vr_variable = self.make_vr_variable(builder, destination_type);
|
||||||
|
|
||||||
|
let as_rc = Rc::new(RefCell::new(destination_vr_variable));
|
||||||
|
let ir_assign = IrAssign::new(todo!(), init_operation);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.local_variables_mut()
|
||||||
|
.insert(destination_symbol, as_rc.clone());
|
||||||
|
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(ir_assign));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_repl_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
destination_stack_offset: isize,
|
||||||
|
) -> Rc<RefCell<IrVariable>> {
|
||||||
|
let init_operation = self
|
||||||
|
.initializer
|
||||||
|
.to_ir_operation(builder, symbol_table, types_table);
|
||||||
|
let destination_symbol = self.get_destination_symbol(symbol_table);
|
||||||
|
let destination_type = types_table
|
||||||
|
.variable_types()
|
||||||
|
.get(&destination_symbol)
|
||||||
|
.unwrap();
|
||||||
|
let destination_stack_variable =
|
||||||
|
self.make_stack_variable(builder, destination_type, destination_stack_offset);
|
||||||
|
let as_rc = Rc::new(RefCell::new(destination_stack_variable));
|
||||||
|
let ir_assign = IrAssign::new(todo!(), init_operation);
|
||||||
|
// do not need to save variable to builder as a new one is created for each repl function
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(ir_assign));
|
||||||
|
as_rc
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) {
|
||||||
|
let init_operation = self.initializer.lower_to_ir_operation(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
);
|
||||||
|
|
||||||
|
let destination_symbol = nodes_to_symbols.get(&self.node_id).unwrap();
|
||||||
|
let destination_type_info = symbols_to_types.get(destination_symbol).unwrap();
|
||||||
|
let vr_variable = Rc::new(RefCell::new(todo!()));
|
||||||
|
|
||||||
|
// save local variable to builder
|
||||||
|
builder.local_variables_mut().insert(
|
||||||
|
destination_symbol.unwrap_variable_symbol().clone(),
|
||||||
|
vr_variable.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let ir_assign = IrAssign::new(todo!(), init_operation);
|
||||||
|
builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(ir_assign));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub mod assign_statement;
|
pub mod assign_statement;
|
||||||
pub mod binary_expression;
|
pub mod binary_expression;
|
||||||
pub mod call;
|
pub mod call;
|
||||||
@ -9,10 +15,16 @@ pub mod expression;
|
|||||||
pub mod expression_statement;
|
pub mod expression_statement;
|
||||||
pub mod extern_function;
|
pub mod extern_function;
|
||||||
pub mod field;
|
pub mod field;
|
||||||
|
pub mod fqn;
|
||||||
|
pub mod fqn_context;
|
||||||
|
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(crate) mod ir_util;
|
||||||
pub mod let_statement;
|
pub mod let_statement;
|
||||||
pub mod negative_expression;
|
pub mod negative_expression;
|
||||||
pub mod parameter;
|
pub mod parameter;
|
||||||
@ -21,3 +33,7 @@ pub mod string_literal;
|
|||||||
pub mod type_use;
|
pub mod type_use;
|
||||||
|
|
||||||
pub type NodeId = usize;
|
pub type NodeId = usize;
|
||||||
|
pub type NodesToSymbols = HashMap<NodeId, Symbol>;
|
||||||
|
pub type SymbolsToTypes = HashMap<Symbol, TypeInfo>;
|
||||||
|
pub type NodesToTypes = HashMap<NodeId, TypeInfo>;
|
||||||
|
pub type FunctionReturnTypes = HashMap<Rc<FunctionSymbol>, TypeInfo>;
|
||||||
|
|||||||
@ -1,11 +1,27 @@
|
|||||||
use crate::ast::NodeId;
|
|
||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
|
use crate::ast::{NodeId, NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::diagnostic_factories::unary_incompatible_type;
|
||||||
|
use crate::ir::ir_assign::IrAssign;
|
||||||
|
use crate::ir::ir_binary_operation::{IrBinaryOperation, IrBinaryOperator};
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
|
use crate::ir::ir_operation::IrOperation;
|
||||||
|
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_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct NegativeExpression {
|
pub struct NegativeExpression {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
operand: Box<Expression>,
|
operand: Box<Expression>,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
type_info: Option<TypeInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NegativeExpression {
|
impl NegativeExpression {
|
||||||
@ -14,6 +30,7 @@ impl NegativeExpression {
|
|||||||
node_id,
|
node_id,
|
||||||
operand: operand.into(),
|
operand: operand.into(),
|
||||||
source_range,
|
source_range,
|
||||||
|
type_info: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,4 +45,261 @@ impl NegativeExpression {
|
|||||||
pub fn operand(&self) -> &Expression {
|
pub fn operand(&self) -> &Expression {
|
||||||
&self.operand
|
&self.operand
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn operand_mut(&mut self) -> &mut Expression {
|
||||||
|
&mut self.operand
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.operand.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
self.operand.resolve_names_static(symbol_table)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_field_init(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
self.operand
|
||||||
|
.resolve_names_field_init(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_ctor(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
self.operand
|
||||||
|
.resolve_names_ctor(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
self.operand
|
||||||
|
.resolve_names_method(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_static_fn_local_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
self.operand.check_static_fn_local_names(symbol_table)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (NodesToTypes, Diagnostics) {
|
||||||
|
let (mut nodes_to_types, mut diagnostics) = self
|
||||||
|
.operand
|
||||||
|
.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
|
||||||
|
let type_info = nodes_to_types.get(&self.operand.node_id()).unwrap();
|
||||||
|
if type_info.can_negate() {
|
||||||
|
nodes_to_types.insert(self.node_id, type_info.negate_result());
|
||||||
|
} else {
|
||||||
|
diagnostics.push(unary_incompatible_type(
|
||||||
|
&self.source_range,
|
||||||
|
"negation",
|
||||||
|
type_info,
|
||||||
|
));
|
||||||
|
nodes_to_types.insert(self.node_id, TypeInfo::PlaceholderError);
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes_to_types, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
self.operand.type_check(symbol_table, types_table)?;
|
||||||
|
|
||||||
|
let type_info = self.operand.type_info(symbol_table, types_table);
|
||||||
|
if type_info.can_negate() {
|
||||||
|
self.type_info = Some(type_info.negate_result());
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(vec![Diagnostic::new(
|
||||||
|
&format!("Cannot negate {}", type_info),
|
||||||
|
self.source_range.start(),
|
||||||
|
self.source_range.end(),
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir_expression(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
) -> IrExpression {
|
||||||
|
let base_ir_expression = self.operand.lower_to_ir_expression(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
);
|
||||||
|
|
||||||
|
match base_ir_expression {
|
||||||
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
|
let destination = Rc::new(RefCell::new(todo!()));
|
||||||
|
todo!()
|
||||||
|
// let rhs = match todo!() {
|
||||||
|
// TypeInfo::Integer => IrExpression::Int(-1),
|
||||||
|
// TypeInfo::Double => IrExpression::Double(-1.0),
|
||||||
|
// _ => panic!(),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let operation = IrOperation::Binary(IrBinaryOperation::new(
|
||||||
|
// IrExpression::Parameter(ir_parameter),
|
||||||
|
// rhs,
|
||||||
|
// IrBinaryOperator::Multiply,
|
||||||
|
// ));
|
||||||
|
//
|
||||||
|
// let ir_assign = IrAssign::new(destination.clone(), operation);
|
||||||
|
// builder
|
||||||
|
// .current_block_mut()
|
||||||
|
// .add_statement(IrStatement::Assign(ir_assign));
|
||||||
|
//
|
||||||
|
// IrExpression::Variable(destination)
|
||||||
|
}
|
||||||
|
IrExpression::Variable(ir_variable) => {
|
||||||
|
let destination = Rc::new(RefCell::new(todo!()));
|
||||||
|
todo!()
|
||||||
|
// let rhs = match ir_variable.borrow().type_info() {
|
||||||
|
// TypeInfo::Integer => IrExpression::Int(-1),
|
||||||
|
// TypeInfo::Double => IrExpression::Double(-1.0),
|
||||||
|
// _ => panic!(),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let operation = IrOperation::Binary(IrBinaryOperation::new(
|
||||||
|
// IrExpression::Variable(ir_variable),
|
||||||
|
// rhs,
|
||||||
|
// IrBinaryOperator::Multiply,
|
||||||
|
// ));
|
||||||
|
//
|
||||||
|
// let ir_assign = IrAssign::new(destination.clone(), operation);
|
||||||
|
// builder
|
||||||
|
// .current_block_mut()
|
||||||
|
// .add_statement(IrStatement::Assign(ir_assign));
|
||||||
|
// IrExpression::Variable(destination)
|
||||||
|
}
|
||||||
|
IrExpression::Int(i) => IrExpression::Int(i * -1),
|
||||||
|
IrExpression::Double(d) => IrExpression::Double(d * -1.0),
|
||||||
|
IrExpression::String(_) => {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> IrExpression {
|
||||||
|
let operand_as_ir = self
|
||||||
|
.operand
|
||||||
|
.to_ir_expression(builder, symbol_table, types_table)
|
||||||
|
.expect("Attempt to negate non-value expression");
|
||||||
|
|
||||||
|
match operand_as_ir {
|
||||||
|
IrExpression::Parameter(parameter) => {
|
||||||
|
let destination = Rc::new(RefCell::new(todo!()));
|
||||||
|
todo!()
|
||||||
|
// let rhs = match todo!() {
|
||||||
|
// TypeInfo::Integer => IrExpression::Int(-1),
|
||||||
|
// TypeInfo::Double => IrExpression::Double(-1.0),
|
||||||
|
// _ => panic!("Trying to multiply with a non-integer/double"),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let operation = IrOperation::Binary(IrBinaryOperation::new(
|
||||||
|
// IrExpression::Parameter(parameter),
|
||||||
|
// rhs,
|
||||||
|
// IrBinaryOperator::Multiply,
|
||||||
|
// ));
|
||||||
|
//
|
||||||
|
// let assign = IrAssign::new(destination.clone(), operation);
|
||||||
|
// builder
|
||||||
|
// .current_block_mut()
|
||||||
|
// .add_statement(IrStatement::Assign(assign));
|
||||||
|
//
|
||||||
|
// IrExpression::Variable(destination)
|
||||||
|
}
|
||||||
|
IrExpression::Variable(variable) => {
|
||||||
|
let destination = Rc::new(RefCell::new(todo!()));
|
||||||
|
todo!()
|
||||||
|
// let rhs = match variable.borrow().type_info() {
|
||||||
|
// TypeInfo::Integer => IrExpression::Int(-1),
|
||||||
|
// TypeInfo::Double => IrExpression::Double(-1.0),
|
||||||
|
// _ => panic!("Trying to multiply with a non-integer/double"),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let operation = IrOperation::Binary(IrBinaryOperation::new(
|
||||||
|
// IrExpression::Variable(variable),
|
||||||
|
// rhs,
|
||||||
|
// IrBinaryOperator::Multiply,
|
||||||
|
// ));
|
||||||
|
//
|
||||||
|
// let assign = IrAssign::new(destination.clone(), operation);
|
||||||
|
// builder
|
||||||
|
// .current_block_mut()
|
||||||
|
// .add_statement(IrStatement::Assign(assign));
|
||||||
|
//
|
||||||
|
// IrExpression::Variable(destination)
|
||||||
|
}
|
||||||
|
IrExpression::Int(i) => IrExpression::Int(i * -1),
|
||||||
|
IrExpression::Double(d) => IrExpression::Double(d * -1.0),
|
||||||
|
IrExpression::String(_) => {
|
||||||
|
panic!("Attempt to negate IrExpression::String")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_info(&self) -> &TypeInfo {
|
||||||
|
self.type_info.as_ref().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
use crate::ast::NodeId;
|
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
|
use crate::ast::{NodeId, NodesToSymbols};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Parameter {
|
pub struct Parameter {
|
||||||
@ -8,6 +13,7 @@ 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,
|
||||||
|
scope_id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parameter {
|
impl Parameter {
|
||||||
@ -22,6 +28,7 @@ impl Parameter {
|
|||||||
declared_name: declared_name.into(),
|
declared_name: declared_name.into(),
|
||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
type_use,
|
type_use,
|
||||||
|
scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,4 +51,54 @@ impl Parameter {
|
|||||||
pub fn type_use(&self) -> &TypeUse {
|
pub fn type_use(&self) -> &TypeUse {
|
||||||
&self.type_use
|
&self.type_use
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scope_id(&self) -> usize {
|
||||||
|
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,
|
||||||
|
Some(self.declared_name_source_range.clone()),
|
||||||
|
self.scope_id.unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names(&self, symbol_table: &SymbolTable) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
self.type_use.resolve_names(symbol_table)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
self.type_use.check_names(symbol_table)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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 declared_type(&self, names_table: &NodesToSymbols) -> (TypeInfo, Diagnostics) {
|
||||||
|
self.type_use.declared_type(names_table)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
self.type_use.type_check(symbol_table, types_table)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,233 @@
|
|||||||
use crate::ast::assign_statement::AssignStatement;
|
use crate::ast::assign_statement::AssignStatement;
|
||||||
use crate::ast::expression_statement::ExpressionStatement;
|
use crate::ast::expression_statement::ExpressionStatement;
|
||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
|
use crate::ast::{NodesToSymbols, NodesToTypes, SymbolsToTypes};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Let(LetStatement),
|
Let(LetStatement),
|
||||||
Expression(ExpressionStatement),
|
Expression(ExpressionStatement),
|
||||||
Assign(AssignStatement),
|
Assign(AssignStatement),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Statement {
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
match self {
|
||||||
|
Statement::Let(let_statement) => {
|
||||||
|
let_statement.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
expression_statement.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
assign_statement.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_static(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
match self {
|
||||||
|
Statement::Let(let_statement) => let_statement.resolve_names_static(symbol_table),
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
expression_statement.resolve_names_static(symbol_table)
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
assign_statement.resolve_names_static(symbol_table)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_ctor(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
initialized_fields: &mut HashSet<Rc<str>>,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
match self {
|
||||||
|
Statement::Let(let_statement) => {
|
||||||
|
let_statement.resolve_names_ctor(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
expression_statement.resolve_names_ctor(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => assign_statement.resolve_names_ctor(
|
||||||
|
symbol_table,
|
||||||
|
self_class_symbol,
|
||||||
|
initialized_fields,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names_method(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
self_class_symbol: &ClassSymbol,
|
||||||
|
) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
match self {
|
||||||
|
Statement::Let(let_statement) => {
|
||||||
|
let_statement.resolve_names_method(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
expression_statement.resolve_names_method(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
assign_statement.resolve_names_method(symbol_table, self_class_symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn analyze_constructor_local_names(
|
||||||
|
&self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
class_symbol: &ClassSymbol,
|
||||||
|
) -> Vec<Diagnostic> {
|
||||||
|
match self {
|
||||||
|
Statement::Let(let_statement) => {
|
||||||
|
let_statement.analyze_constructor_local_names(symbol_table, class_symbol)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_types(
|
||||||
|
&self,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
) -> (SymbolsToTypes, NodesToTypes, Diagnostics) {
|
||||||
|
match self {
|
||||||
|
Statement::Let(let_statement) => {
|
||||||
|
let_statement.resolve_types(nodes_to_symbols, symbols_to_types)
|
||||||
|
}
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
let (nts, ds) =
|
||||||
|
expression_statement.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
(SymbolsToTypes::new(), nts, ds)
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
let (nts, ds) = assign_statement.resolve_types(nodes_to_symbols, symbols_to_types);
|
||||||
|
(SymbolsToTypes::new(), nts, ds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn type_check(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &mut TypesTable,
|
||||||
|
must_return_type_info: Option<&TypeInfo>,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
match self {
|
||||||
|
Statement::Let(let_statement) => let_statement.type_check(symbol_table, types_table),
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
expression_statement.type_check(symbol_table, types_table, must_return_type_info)
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
assign_statement.type_check(symbol_table, types_table)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
types_table: &TypesTable,
|
||||||
|
should_return_value: bool,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
Statement::Let(let_statement) => {
|
||||||
|
let_statement.to_ir(builder, symbol_table, types_table);
|
||||||
|
}
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
expression_statement.to_ir(builder, symbol_table, types_table, should_return_value);
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
assign_statement.to_ir(builder, symbol_table, types_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_to_ir(
|
||||||
|
&self,
|
||||||
|
builder: &mut IrBuilder,
|
||||||
|
nodes_to_symbols: &NodesToSymbols,
|
||||||
|
symbols_to_types: &SymbolsToTypes,
|
||||||
|
nodes_to_types: &NodesToTypes,
|
||||||
|
is_return_statement: bool,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
Statement::Let(let_statement) => {
|
||||||
|
let_statement.lower(builder, nodes_to_symbols, symbols_to_types, nodes_to_types);
|
||||||
|
}
|
||||||
|
Statement::Expression(expression_statement) => {
|
||||||
|
expression_statement.lower_to_ir(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
is_return_statement,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Statement::Assign(assign_statement) => {
|
||||||
|
assign_statement.lower_to_ir(
|
||||||
|
builder,
|
||||||
|
nodes_to_symbols,
|
||||||
|
symbols_to_types,
|
||||||
|
nodes_to_types,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,18 +1,22 @@
|
|||||||
use crate::ast::NodeId;
|
use crate::ast::NodeId;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
|
||||||
pub struct StringLiteral {
|
pub struct StringLiteral {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
content: String,
|
content: String,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
type_info: &'static TypeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StringLiteral {
|
impl StringLiteral {
|
||||||
pub fn new(node_id: NodeId, content: &str, source_range: SourceRange) -> Self {
|
pub fn new(node_id: NodeId, content: &str, source_range: SourceRange) -> Self {
|
||||||
|
const TYPE_INFO: TypeInfo = TypeInfo::String;
|
||||||
Self {
|
Self {
|
||||||
node_id,
|
node_id,
|
||||||
content: content.into(),
|
content: content.into(),
|
||||||
source_range,
|
source_range,
|
||||||
|
type_info: &TYPE_INFO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +28,10 @@ impl StringLiteral {
|
|||||||
&self.content
|
&self.content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_info(&self) -> &TypeInfo {
|
||||||
|
&self.type_info
|
||||||
|
}
|
||||||
|
|
||||||
pub fn source_range(&self) -> &SourceRange {
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
&self.source_range
|
&self.source_range
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,14 @@
|
|||||||
use crate::ast::NodeId;
|
use crate::ast::{NodeId, NodesToSymbols};
|
||||||
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
|
use crate::diagnostic_factories::{cannot_provide_generic_args_generic_type, symbol_not_found};
|
||||||
|
use crate::error_codes::INCORRECT_GENERIC_ARGUMENTS;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::type_symbol::TypeSymbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct TypeUse {
|
pub struct TypeUse {
|
||||||
@ -7,6 +16,7 @@ pub struct TypeUse {
|
|||||||
declared_name: Rc<str>,
|
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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeUse {
|
impl TypeUse {
|
||||||
@ -21,6 +31,7 @@ impl TypeUse {
|
|||||||
declared_name: declared_name.into(),
|
declared_name: declared_name.into(),
|
||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
generic_arguments,
|
generic_arguments,
|
||||||
|
scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,4 +42,236 @@ impl TypeUse {
|
|||||||
pub fn node_id(&self) -> NodeId {
|
pub fn node_id(&self) -> NodeId {
|
||||||
self.node_id
|
self.node_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
||||||
|
self.scope_id = Some(container_scope);
|
||||||
|
for type_use in &mut self.generic_arguments {
|
||||||
|
type_use.init_scopes(symbol_table, container_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_names(&self, symbol_table: &SymbolTable) -> (NodesToSymbols, Diagnostics) {
|
||||||
|
let mut diagnostics = Vec::new();
|
||||||
|
let mut resolved_names = HashMap::new();
|
||||||
|
|
||||||
|
// resolve this name
|
||||||
|
match symbol_table.find_type_symbol(self.scope_id.unwrap(), &self.declared_name) {
|
||||||
|
None => {
|
||||||
|
diagnostics.push(symbol_not_found(
|
||||||
|
&self.declared_name,
|
||||||
|
&self.declared_name_source_range,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Some(type_symbol) => {
|
||||||
|
resolved_names.insert(self.node_id, type_symbol.into_symbol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check generic args
|
||||||
|
for type_use in &self.generic_arguments {
|
||||||
|
let (ns, mut ds) = type_use.resolve_names(symbol_table);
|
||||||
|
for (node_id, symbol) in ns {
|
||||||
|
resolved_names.insert(node_id, symbol);
|
||||||
|
}
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
(resolved_names, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub fn check_names(&self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||||
|
|
||||||
|
// find this name
|
||||||
|
let maybe_type_symbol =
|
||||||
|
symbol_table.find_type_symbol(self.scope_id.unwrap(), &self.declared_name);
|
||||||
|
if maybe_type_symbol.is_none() {
|
||||||
|
diagnostics.push(symbol_not_found(
|
||||||
|
self.declared_name(),
|
||||||
|
&self.declared_name_source_range,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check generic args
|
||||||
|
for type_use in &self.generic_arguments {
|
||||||
|
diagnostics.append(&mut type_use.check_names(symbol_table));
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
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!(
|
||||||
|
type_use.gather_types(symbol_table, types_table),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
diagnostics_result!(diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_type(&self, names_table: &NodesToSymbols) -> (TypeInfo, Diagnostics) {
|
||||||
|
let mut diagnostics = Diagnostics::new();
|
||||||
|
|
||||||
|
let mut generic_argument_type_infos = Vec::new();
|
||||||
|
for type_use in &self.generic_arguments {
|
||||||
|
let (type_info, mut ds) = type_use.declared_type(names_table);
|
||||||
|
generic_argument_type_infos.push(type_info);
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
let base_type_symbol = names_table.get(&self.node_id).unwrap().unwrap_type_symbol();
|
||||||
|
|
||||||
|
match base_type_symbol {
|
||||||
|
TypeSymbol::Class(class_symbol) => (
|
||||||
|
TypeInfo::ParameterizedClass(class_symbol, generic_argument_type_infos),
|
||||||
|
diagnostics,
|
||||||
|
),
|
||||||
|
TypeSymbol::GenericParameter(generic_parameter_symbol) => {
|
||||||
|
if generic_argument_type_infos.is_empty() {
|
||||||
|
(TypeInfo::GenericType(generic_parameter_symbol), diagnostics)
|
||||||
|
} else {
|
||||||
|
diagnostics.push(cannot_provide_generic_args_generic_type(
|
||||||
|
&self.declared_name_source_range,
|
||||||
|
));
|
||||||
|
(TypeInfo::PlaceholderError, diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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![];
|
||||||
|
|
||||||
|
match self.type_info(symbol_table, types_table) {
|
||||||
|
TypeInfo::Class(class_symbol) => {
|
||||||
|
// check number of params/args match
|
||||||
|
let generic_parameters = class_symbol.generic_parameters();
|
||||||
|
if generic_parameters.len() != self.generic_arguments.len() {
|
||||||
|
let diagnostic = Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Expected {} generic arguments; found {}.",
|
||||||
|
generic_parameters.len(),
|
||||||
|
self.generic_arguments.len()
|
||||||
|
),
|
||||||
|
self.declared_name_source_range.start(),
|
||||||
|
self.declared_name_source_range.end(),
|
||||||
|
)
|
||||||
|
.with_reporter(file!(), line!())
|
||||||
|
.with_error_code(INCORRECT_GENERIC_ARGUMENTS);
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
maybe_return_diagnostics!(diagnostics);
|
||||||
|
|
||||||
|
// check that each arg is assignable to the param's extends
|
||||||
|
// for i in 0..self.generic_arguments.len() {
|
||||||
|
// let generic_parameter_symbol = &generic_parameters[i];
|
||||||
|
// if generic_parameter_symbol.extends().len() > 0 {
|
||||||
|
// unimplemented!("Generic extends not implemented yet.")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// cannot extend a non-class type (except for Any)
|
||||||
|
if self.generic_arguments.len() > 0 {
|
||||||
|
let diagnostic = Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Type {} does not accept generic arguments.",
|
||||||
|
self.type_info(symbol_table, types_table)
|
||||||
|
),
|
||||||
|
self.declared_name_source_range.start(),
|
||||||
|
self.declared_name_source_range.end(),
|
||||||
|
)
|
||||||
|
.with_reporter(file!(), line!())
|
||||||
|
.with_error_code(INCORRECT_GENERIC_ARGUMENTS);
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse on generic arguments
|
||||||
|
for generic_argument in &mut self.generic_arguments {
|
||||||
|
handle_diagnostics!(
|
||||||
|
generic_argument.type_check(symbol_table, types_table),
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostics_result!(diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::diagnostic::Diagnostic;
|
||||||
|
use crate::parser::get_compilation_unit;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::types_table::TypesTable;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn type_check_generics() -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let mut compilation_unit = get_compilation_unit(
|
||||||
|
"
|
||||||
|
class String end
|
||||||
|
|
||||||
|
class Foo<T>
|
||||||
|
ctor(t: T) end
|
||||||
|
end
|
||||||
|
|
||||||
|
fn useFoo(foo: Foo<String>) end
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
let mut types_table = TypesTable::new();
|
||||||
|
|
||||||
|
compilation_unit.init_scopes(&mut symbol_table);
|
||||||
|
compilation_unit.gather_symbols_into(&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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
146
dmc-lib/src/compile_pipeline.rs
Normal file
146
dmc-lib/src/compile_pipeline.rs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
use crate::ast::compilation_unit::CompilationUnit;
|
||||||
|
use crate::diagnostic::Diagnostics;
|
||||||
|
use crate::ir::ir_class::IrClass;
|
||||||
|
use crate::ir::ir_function::IrFunction;
|
||||||
|
use crate::parser::parse_compilation_unit;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::symbol_table::util::try_insert_symbols_into;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub type Filename = Rc<str>;
|
||||||
|
pub type FileId = usize;
|
||||||
|
|
||||||
|
fn parse_compilation_units(
|
||||||
|
inputs: &HashMap<FileId, &str>,
|
||||||
|
) -> Result<HashMap<FileId, CompilationUnit>, Diagnostics> {
|
||||||
|
let mut parse_diagnostics = Vec::new();
|
||||||
|
let mut compilation_units = HashMap::new();
|
||||||
|
for (file_id, source) in inputs {
|
||||||
|
let (compilation_unit, mut ds) = parse_compilation_unit(source, Some(*file_id));
|
||||||
|
parse_diagnostics.append(&mut ds);
|
||||||
|
compilation_units.insert(*file_id, compilation_unit);
|
||||||
|
}
|
||||||
|
if parse_diagnostics.is_empty() {
|
||||||
|
Ok(compilation_units)
|
||||||
|
} else {
|
||||||
|
Err(parse_diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile_compilation_units(
|
||||||
|
inputs: &HashMap<FileId, &str>,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Result<(Vec<IrClass>, Vec<IrFunction>), Diagnostics> {
|
||||||
|
let mut compilation_units = parse_compilation_units(inputs)?;
|
||||||
|
|
||||||
|
// init scopes
|
||||||
|
for compilation_unit in compilation_units.values_mut() {
|
||||||
|
compilation_unit.init_scopes(symbol_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
// gather unordered symbols
|
||||||
|
let all_symbols = compilation_units
|
||||||
|
.values()
|
||||||
|
.flat_map(|compilation_unit| compilation_unit.declared_symbols())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
try_insert_symbols_into(all_symbols, symbol_table)?;
|
||||||
|
|
||||||
|
// now we can just finish each compilation unit, since we have the symbols
|
||||||
|
let mut ir_classes = Vec::new();
|
||||||
|
let mut ir_functions = Vec::new();
|
||||||
|
let mut diagnostics = Vec::new();
|
||||||
|
for compilation_unit in compilation_units.values() {
|
||||||
|
let (nodes_to_symbols, mut ds) = compilation_unit.resolve_names(symbol_table);
|
||||||
|
|
||||||
|
// in the future, we'll ideally be able to *actually* continue with the following steps
|
||||||
|
// instead of aborting here, but this needs to be tested :)
|
||||||
|
if !ds.is_empty() {
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (mut symbols_to_types, mut ds) = compilation_unit.declared_types(&nodes_to_symbols);
|
||||||
|
if !ds.is_empty() {
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (sts, nodes_to_types, mut ds) =
|
||||||
|
compilation_unit.resolve_types(&nodes_to_symbols, &symbols_to_types);
|
||||||
|
if !ds.is_empty() {
|
||||||
|
diagnostics.append(&mut ds);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge
|
||||||
|
for (symbol, type_info) in sts {
|
||||||
|
symbols_to_types.insert(symbol, type_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (mut classes, mut functions) =
|
||||||
|
compilation_unit.lower_to_ir(&nodes_to_symbols, &symbols_to_types, &nodes_to_types);
|
||||||
|
ir_classes.append(&mut classes);
|
||||||
|
ir_functions.append(&mut functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if diagnostics.is_empty() {
|
||||||
|
Ok((ir_classes, ir_functions))
|
||||||
|
} else {
|
||||||
|
Err(diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
|
||||||
|
fn prepare_symbol_table(symbol_table: &mut SymbolTable) {
|
||||||
|
let global_scope = symbol_table.push_module_scope("global scope");
|
||||||
|
let any_symbol = ClassSymbol::new(
|
||||||
|
&"Any".into(),
|
||||||
|
None,
|
||||||
|
vec!["Any".into()],
|
||||||
|
false,
|
||||||
|
global_scope,
|
||||||
|
Vec::new(),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
symbol_table.insert_class_symbol(Rc::new(any_symbol));
|
||||||
|
|
||||||
|
let void_symbol = ClassSymbol::new(
|
||||||
|
&"Void".into(),
|
||||||
|
None,
|
||||||
|
vec!["Void".into()],
|
||||||
|
false,
|
||||||
|
global_scope,
|
||||||
|
Vec::new(),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
symbol_table.insert_class_symbol(Rc::new(void_symbol));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hello_world() -> Result<(), Diagnostics> {
|
||||||
|
let input = "
|
||||||
|
extern fn println(msg: Any) -> Void
|
||||||
|
|
||||||
|
fn main()
|
||||||
|
println(\"Hello, World!\")
|
||||||
|
end
|
||||||
|
";
|
||||||
|
let mut inputs = HashMap::new();
|
||||||
|
inputs.insert(0, input);
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
prepare_symbol_table(&mut symbol_table);
|
||||||
|
let (ir_classes, ir_functions) = compile_compilation_units(&inputs, &mut symbol_table)?;
|
||||||
|
assert_eq!(ir_classes.len(), 0);
|
||||||
|
assert_eq!(ir_functions.len(), 1);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,15 @@
|
|||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::{Diagnostic, SecondaryLabel};
|
||||||
use crate::error_codes::SYMBOL_NOT_FOUND;
|
use crate::error_codes::{
|
||||||
|
ASSIGN_LHS_IMMUTABLE, ASSIGN_MISMATCHED_TYPES, ASSIGN_NO_L_VALUE,
|
||||||
|
CANNOT_PROVIDE_GENERIC_ARGS_GENERIC_TYPE, CLASS_NO_CONSTRUCTOR, FIELD_NO_TYPE_OR_INIT,
|
||||||
|
MISMATCHED_TYPES, NOT_ASSIGNABLE, OUTER_CLASS_FIELD_USED_IN_INIT,
|
||||||
|
OUTER_CLASS_METHOD_USED_IN_INIT, RECEIVER_NOT_CALLABLE, SELF_CONSTRUCTOR_USED_IN_INIT,
|
||||||
|
SELF_FIELD_USED_IN_INIT, SELF_METHOD_USED_IN_INIT, SYMBOL_ALREADY_DECLARED, SYMBOL_NOT_FOUND,
|
||||||
|
UNARY_INCOMPATIBLE_TYPE, WRONG_NUMBER_OF_ARGUMENTS,
|
||||||
|
};
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
|
||||||
pub fn symbol_not_found(name: &str, source_range: &SourceRange) -> Diagnostic {
|
pub fn symbol_not_found(name: &str, source_range: &SourceRange) -> Diagnostic {
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
@ -10,3 +19,255 @@ pub fn symbol_not_found(name: &str, source_range: &SourceRange) -> Diagnostic {
|
|||||||
)
|
)
|
||||||
.with_error_code(SYMBOL_NOT_FOUND)
|
.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not_assignable(name: &str, source_range: &SourceRange) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
&format!("Symbol {} is not assignable.", name),
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(NOT_ASSIGNABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cannot_reassign_immutable_field(source_range: &SourceRange) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
"Cannot reassign an immutable field.",
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(ASSIGN_LHS_IMMUTABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cannot_provide_generic_args_generic_type(source_range: &SourceRange) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
"Cannot provide generic arguments to an already generic type.",
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(CANNOT_PROVIDE_GENERIC_ARGS_GENERIC_TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn must_be_l_value(source_range: &SourceRange) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
"Left-hand side of assign mut be an L value",
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(ASSIGN_NO_L_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destination_must_be_mutable(
|
||||||
|
source_range: &SourceRange,
|
||||||
|
secondary_source_range: Option<&SourceRange>,
|
||||||
|
) -> Diagnostic {
|
||||||
|
let secondary_label = secondary_source_range.map(|sr| {
|
||||||
|
SecondaryLabel::new(
|
||||||
|
sr.start(),
|
||||||
|
sr.end(),
|
||||||
|
Some("Destination declared here is immutable".to_string()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut diagnostic = Diagnostic::new(
|
||||||
|
"Destination is immutable and cannot be reassigned.",
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(ASSIGN_LHS_IMMUTABLE);
|
||||||
|
|
||||||
|
if let Some(secondary_label) = secondary_label {
|
||||||
|
diagnostic = diagnostic.with_secondary_labels(&[secondary_label])
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostic
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mismatched_assign_types(
|
||||||
|
rhs_type: &TypeInfo,
|
||||||
|
lhs_type: &TypeInfo,
|
||||||
|
source_range: &SourceRange,
|
||||||
|
secondary_source_range: Option<&SourceRange>,
|
||||||
|
) -> Diagnostic {
|
||||||
|
let secondary_label = secondary_source_range.map(|sr| {
|
||||||
|
SecondaryLabel::new(
|
||||||
|
sr.start(),
|
||||||
|
sr.end(),
|
||||||
|
Some(format!(
|
||||||
|
"Destination declared here is of type {}.",
|
||||||
|
lhs_type
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut diagnostic = Diagnostic::new(
|
||||||
|
&format!("Mismatched types: right-hand side of type {} is not assignable to left-hand side of type {}", rhs_type, lhs_type),
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(ASSIGN_MISMATCHED_TYPES);
|
||||||
|
|
||||||
|
if let Some(secondary_label) = secondary_label {
|
||||||
|
diagnostic = diagnostic.with_secondary_labels(&[secondary_label])
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostic
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receiver_not_callable(
|
||||||
|
receiver_type_info: &TypeInfo,
|
||||||
|
source_range: &SourceRange,
|
||||||
|
) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
&format!("Receiver of type {} is not callable.", receiver_type_info),
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(RECEIVER_NOT_CALLABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrong_number_of_arguments(
|
||||||
|
source_range: &SourceRange,
|
||||||
|
found: usize,
|
||||||
|
expected: usize,
|
||||||
|
) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Wrong number of arguments: expected {} but found {}",
|
||||||
|
expected, found
|
||||||
|
),
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(WRONG_NUMBER_OF_ARGUMENTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mismatched_types(
|
||||||
|
expected_type_info: &TypeInfo,
|
||||||
|
found_type_info: &TypeInfo,
|
||||||
|
source_range: &SourceRange,
|
||||||
|
) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
&format!(
|
||||||
|
"Mismatched types; expected {} but found {}",
|
||||||
|
expected_type_info, found_type_info
|
||||||
|
),
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(MISMATCHED_TYPES)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unary_incompatible_type(
|
||||||
|
source_range: &SourceRange,
|
||||||
|
operator_name: &str,
|
||||||
|
type_info: &TypeInfo,
|
||||||
|
) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
&format!("Cannot apply {} to {}", operator_name, type_info),
|
||||||
|
source_range.start(),
|
||||||
|
source_range.end(),
|
||||||
|
)
|
||||||
|
.with_error_code(UNARY_INCOMPATIBLE_TYPE)
|
||||||
|
}
|
||||||
|
|||||||
48
dmc-lib/src/intrinsics/mod.rs
Normal file
48
dmc-lib/src/intrinsics/mod.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,7 @@
|
|||||||
|
use crate::ast::fqn_util::fqn_parts_to_string;
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use dvm_lib::vm::class::{Class, Field};
|
||||||
|
use dvm_lib::vm::type_info::TypeInfo as VmTypeInfo;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct IrClass {
|
pub struct IrClass {
|
||||||
@ -14,18 +18,45 @@ impl IrClass {
|
|||||||
fields,
|
fields,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_vm_class(&self) -> Class {
|
||||||
|
Class::new(
|
||||||
|
self.declared_name.clone(),
|
||||||
|
self.fqn.clone(),
|
||||||
|
self.fields.iter().map(IrField::to_vm_field).collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IrField {
|
pub struct IrField {
|
||||||
debug_name: Rc<str>,
|
debug_name: Rc<str>,
|
||||||
field_index: usize,
|
field_index: usize,
|
||||||
|
type_info: TypeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrField {
|
impl IrField {
|
||||||
pub fn new(debug_name: Rc<str>, field_index: usize) -> Self {
|
pub fn new(debug_name: Rc<str>, field_index: usize, type_info: TypeInfo) -> Self {
|
||||||
Self {
|
Self {
|
||||||
debug_name,
|
debug_name,
|
||||||
field_index,
|
field_index,
|
||||||
|
type_info,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_vm_field(&self) -> Field {
|
||||||
|
Field::new(
|
||||||
|
self.debug_name.clone(),
|
||||||
|
self.field_index,
|
||||||
|
match &self.type_info {
|
||||||
|
TypeInfo::Integer => VmTypeInfo::Int,
|
||||||
|
TypeInfo::Double => VmTypeInfo::Double,
|
||||||
|
TypeInfo::String => VmTypeInfo::String,
|
||||||
|
TypeInfo::Class(class_symbol) => {
|
||||||
|
VmTypeInfo::ClassInstance(fqn_parts_to_string(class_symbol.fqn_parts()).into())
|
||||||
|
}
|
||||||
|
TypeInfo::GenericType(_) => VmTypeInfo::Any,
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,17 +14,26 @@ use std::collections::HashMap;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
pub mod compile_pipeline;
|
||||||
pub mod constants_table;
|
pub mod constants_table;
|
||||||
pub mod diagnostic;
|
pub mod diagnostic;
|
||||||
mod diagnostic_factories;
|
mod diagnostic_factories;
|
||||||
mod error_codes;
|
pub mod error_codes;
|
||||||
|
pub mod intrinsics;
|
||||||
pub mod ir;
|
pub mod ir;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod lowering;
|
pub mod lowering;
|
||||||
|
pub mod offset_counter;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
pub mod scope;
|
||||||
pub mod semantic_analysis;
|
pub mod semantic_analysis;
|
||||||
pub mod source_range;
|
pub mod source_range;
|
||||||
|
pub mod symbol;
|
||||||
|
pub mod symbol_table;
|
||||||
pub mod token;
|
pub mod token;
|
||||||
|
pub mod type_info;
|
||||||
|
pub mod types_table;
|
||||||
|
mod util;
|
||||||
|
|
||||||
pub type Filename = Rc<str>;
|
pub type Filename = Rc<str>;
|
||||||
pub type FileId = usize;
|
pub type FileId = usize;
|
||||||
|
|||||||
23
dmc-lib/src/offset_counter.rs
Normal file
23
dmc-lib/src/offset_counter.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
pub struct OffsetCounter {
|
||||||
|
counter: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OffsetCounter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { counter: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_base(base: usize) -> Self {
|
||||||
|
Self { counter: base }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) -> usize {
|
||||||
|
let offset = self.counter;
|
||||||
|
self.counter += 1;
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_count(&self) -> usize {
|
||||||
|
self.counter
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,3 @@
|
|||||||
use crate::FileId;
|
|
||||||
use crate::ast::assign_statement::AssignStatement;
|
use crate::ast::assign_statement::AssignStatement;
|
||||||
use crate::ast::binary_expression::{BinaryExpression, BinaryOperation};
|
use crate::ast::binary_expression::{BinaryExpression, BinaryOperation};
|
||||||
use crate::ast::call::Call;
|
use crate::ast::call::Call;
|
||||||
@ -20,6 +19,7 @@ use crate::ast::parameter::Parameter;
|
|||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::ast::string_literal::StringLiteral;
|
use crate::ast::string_literal::StringLiteral;
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
|
use crate::compile_pipeline::FileId;
|
||||||
use crate::diagnostic::{Diagnostic, Diagnostics};
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use crate::error_codes::{LEXER_ERROR, PARSE_ERROR};
|
use crate::error_codes::{LEXER_ERROR, PARSE_ERROR};
|
||||||
use crate::lexer::{Lexer, LexerErrorKind};
|
use crate::lexer::{Lexer, LexerErrorKind};
|
||||||
@ -988,7 +988,7 @@ impl<'a> Parser<'a> {
|
|||||||
(Statement::Assign(assign_statement), diagnostics)
|
(Statement::Assign(assign_statement), diagnostics)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
Statement::Expression(ExpressionStatement::new(base)),
|
Statement::Expression(ExpressionStatement::new(self.next_node_id(), base)),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
38
dmc-lib/src/scope/block_scope.rs
Normal file
38
dmc-lib/src/scope/block_scope.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use crate::symbol::variable_symbol::VariableSymbol;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct BlockScope {
|
||||||
|
debug_name: String,
|
||||||
|
parent_id: usize,
|
||||||
|
variable_symbols: HashMap<Rc<str>, Rc<VariableSymbol>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockScope {
|
||||||
|
pub fn new(debug_name: &str, parent_id: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
debug_name: debug_name.into(),
|
||||||
|
parent_id,
|
||||||
|
variable_symbols: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variable_symbols(&self) -> &HashMap<Rc<str>, Rc<VariableSymbol>> {
|
||||||
|
&self.variable_symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variable_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<VariableSymbol>> {
|
||||||
|
&mut self.variable_symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent_id(&self) -> usize {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
71
dmc-lib/src/scope/class_body_scope.rs
Normal file
71
dmc-lib/src/scope/class_body_scope.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
40
dmc-lib/src/scope/class_scope.rs
Normal file
40
dmc-lib/src/scope/class_scope.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct ClassScope {
|
||||||
|
debug_name: String,
|
||||||
|
parent_id: usize,
|
||||||
|
generic_parameter_symbols: HashMap<Rc<str>, Rc<GenericParameterSymbol>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClassScope {
|
||||||
|
pub fn new(debug_name: &str, parent_id: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
debug_name: debug_name.into(),
|
||||||
|
parent_id,
|
||||||
|
generic_parameter_symbols: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent_id(&self) -> usize {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
38
dmc-lib/src/scope/function_scope.rs
Normal file
38
dmc-lib/src/scope/function_scope.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct FunctionScope {
|
||||||
|
debug_name: String,
|
||||||
|
parent_id: usize,
|
||||||
|
parameter_symbols: HashMap<Rc<str>, Rc<ParameterSymbol>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionScope {
|
||||||
|
pub fn new(debug_name: &str, parent_id: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
debug_name: debug_name.into(),
|
||||||
|
parent_id,
|
||||||
|
parameter_symbols: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parameter_symbols(&self) -> &HashMap<Rc<str>, Rc<ParameterSymbol>> {
|
||||||
|
&self.parameter_symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parameter_symbols_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<ParameterSymbol>> {
|
||||||
|
&mut self.parameter_symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent_id(&self) -> usize {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
54
dmc-lib/src/scope/mod.rs
Normal file
54
dmc-lib/src/scope/mod.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use crate::scope::block_scope::BlockScope;
|
||||||
|
use crate::scope::class_body_scope::ClassBodyScope;
|
||||||
|
use crate::scope::class_scope::ClassScope;
|
||||||
|
use crate::scope::function_scope::FunctionScope;
|
||||||
|
use crate::scope::module_scope::ModuleScope;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
pub mod block_scope;
|
||||||
|
pub mod class_body_scope;
|
||||||
|
pub mod class_scope;
|
||||||
|
pub mod function_scope;
|
||||||
|
pub mod module_scope;
|
||||||
|
|
||||||
|
pub enum Scope {
|
||||||
|
Module(ModuleScope),
|
||||||
|
Class(ClassScope),
|
||||||
|
ClassBody(ClassBodyScope),
|
||||||
|
Function(FunctionScope),
|
||||||
|
Block(BlockScope),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
pub fn parent_id(&self) -> Option<usize> {
|
||||||
|
match self {
|
||||||
|
Scope::Module(module_scope) => module_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::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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
dmc-lib/src/scope/module_scope.rs
Normal file
49
dmc-lib/src/scope/module_scope.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct ModuleScope {
|
||||||
|
debug_name: String,
|
||||||
|
parent_id: Option<usize>,
|
||||||
|
class_symbols: HashMap<Rc<str>, Rc<ClassSymbol>>,
|
||||||
|
function_symbols: HashMap<Rc<str>, Rc<FunctionSymbol>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleScope {
|
||||||
|
pub fn new(debug_name: &str, parent_id: Option<usize>) -> Self {
|
||||||
|
Self {
|
||||||
|
debug_name: debug_name.into(),
|
||||||
|
parent_id,
|
||||||
|
class_symbols: HashMap::new(),
|
||||||
|
function_symbols: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 parent_id(&self) -> Option<usize> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
22
dmc-lib/src/symbol/callable_symbol.rs
Normal file
22
dmc-lib/src/symbol/callable_symbol.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||||
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub enum CallableSymbol {
|
||||||
|
Function(Rc<FunctionSymbol>),
|
||||||
|
Constructor(Rc<ConstructorSymbol>),
|
||||||
|
ErrorPlaceholder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallableSymbol {
|
||||||
|
pub fn parameters(&self) -> Vec<Rc<ParameterSymbol>> {
|
||||||
|
match self {
|
||||||
|
CallableSymbol::Function(function_symbol) => function_symbol.parameters().to_vec(),
|
||||||
|
CallableSymbol::Constructor(constructor_symbol) => {
|
||||||
|
constructor_symbol.parameters().to_vec()
|
||||||
|
}
|
||||||
|
CallableSymbol::ErrorPlaceholder => Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
115
dmc-lib/src/symbol/class_symbol.rs
Normal file
115
dmc-lib/src/symbol/class_symbol.rs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
use crate::ast::fqn_util::fqn_parts_to_string;
|
||||||
|
use crate::source_range::SourceRange;
|
||||||
|
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 std::collections::HashMap;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct ClassSymbol {
|
||||||
|
declared_name: Rc<str>,
|
||||||
|
declared_name_source_range: Option<SourceRange>,
|
||||||
|
fqn_parts: Vec<Rc<str>>,
|
||||||
|
is_extern: bool,
|
||||||
|
scope_id: usize,
|
||||||
|
generic_parameters: Vec<Rc<GenericParameterSymbol>>,
|
||||||
|
constructor_symbol: Option<Rc<ConstructorSymbol>>,
|
||||||
|
fields: HashMap<Rc<str>, Rc<FieldSymbol>>,
|
||||||
|
functions: HashMap<Rc<str>, Rc<FunctionSymbol>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClassSymbol {
|
||||||
|
pub fn new(
|
||||||
|
declared_name: &Rc<str>,
|
||||||
|
declared_name_source_range: Option<SourceRange>,
|
||||||
|
fqn_parts: Vec<Rc<str>>,
|
||||||
|
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 {
|
||||||
|
declared_name: declared_name.clone(),
|
||||||
|
declared_name_source_range,
|
||||||
|
fqn_parts,
|
||||||
|
is_extern,
|
||||||
|
scope_id,
|
||||||
|
generic_parameters,
|
||||||
|
constructor_symbol,
|
||||||
|
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>] {
|
||||||
|
&self.fqn_parts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope_id(&self) -> usize {
|
||||||
|
self.scope_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generic_parameters(&self) -> &[Rc<GenericParameterSymbol>] {
|
||||||
|
&self.generic_parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constructor_symbol(&self) -> Option<&ConstructorSymbol> {
|
||||||
|
self.constructor_symbol.as_ref().map(|s| s.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constructor_symbol_owned(&self) -> Option<Rc<ConstructorSymbol>> {
|
||||||
|
self.constructor_symbol.as_ref().cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fields(&self) -> &HashMap<Rc<str>, Rc<FieldSymbol>> {
|
||||||
|
&self.fields
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn functions(&self) -> &HashMap<Rc<str>, Rc<FunctionSymbol>> {
|
||||||
|
&self.functions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for ClassSymbol {}
|
||||||
|
|
||||||
|
impl PartialEq for ClassSymbol {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.declared_name == other.declared_name && self.scope_id == other.scope_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
75
dmc-lib/src/symbol/constructor_symbol.rs
Normal file
75
dmc-lib/src/symbol/constructor_symbol.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct ConstructorSymbol {
|
||||||
|
keyword_source_range: SourceRange,
|
||||||
|
fqn_parts: Vec<Rc<str>>,
|
||||||
|
is_extern: bool,
|
||||||
|
is_default: bool,
|
||||||
|
scope_id: usize,
|
||||||
|
parameters: Vec<Rc<ParameterSymbol>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstructorSymbol {
|
||||||
|
pub fn new(
|
||||||
|
keyword_source_range: &SourceRange,
|
||||||
|
fqn_parts: Vec<Rc<str>>,
|
||||||
|
is_extern: bool,
|
||||||
|
is_default: bool,
|
||||||
|
scope_id: usize,
|
||||||
|
parameters: Vec<Rc<ParameterSymbol>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
keyword_source_range: keyword_source_range.clone(),
|
||||||
|
fqn_parts,
|
||||||
|
is_extern,
|
||||||
|
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>] {
|
||||||
|
&self.fqn_parts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope_id(&self) -> usize {
|
||||||
|
self.scope_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parameters(&self) -> &[Rc<ParameterSymbol>] {
|
||||||
|
&self.parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_extern(&self) -> bool {
|
||||||
|
self.is_extern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for ConstructorSymbol {}
|
||||||
|
|
||||||
|
impl PartialEq for ConstructorSymbol {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.scope_id == other.scope_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for ConstructorSymbol {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.scope_id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
dmc-lib/src/symbol/expressible_symbol.rs
Normal file
46
dmc-lib/src/symbol/expressible_symbol.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::field_symbol::FieldSymbol;
|
||||||
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use crate::symbol::variable_symbol::VariableSymbol;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub enum ExpressibleSymbol {
|
||||||
|
Class(Rc<ClassSymbol>),
|
||||||
|
Field(Rc<FieldSymbol>),
|
||||||
|
Function(Rc<FunctionSymbol>),
|
||||||
|
Parameter(Rc<ParameterSymbol>),
|
||||||
|
Variable(Rc<VariableSymbol>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExpressibleSymbol {
|
||||||
|
pub fn into_symbol(self) -> Symbol {
|
||||||
|
match self {
|
||||||
|
ExpressibleSymbol::Class(class_symbol) => Symbol::Class(class_symbol),
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => Symbol::Field(field_symbol),
|
||||||
|
ExpressibleSymbol::Function(function_symbol) => Symbol::Function(function_symbol),
|
||||||
|
ExpressibleSymbol::Parameter(parameter_symbol) => Symbol::Parameter(parameter_symbol),
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => Symbol::Variable(variable_symbol),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_range(&self) -> Option<&SourceRange> {
|
||||||
|
match self {
|
||||||
|
ExpressibleSymbol::Class(class_symbol) => class_symbol.declared_name_source_range(),
|
||||||
|
ExpressibleSymbol::Field(field_symbol) => {
|
||||||
|
Some(field_symbol.declared_name_source_range())
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Function(function_symbol) => {
|
||||||
|
Some(function_symbol.declared_name_source_range())
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Parameter(parameter_symbol) => {
|
||||||
|
parameter_symbol.declared_name_source_range()
|
||||||
|
}
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol) => {
|
||||||
|
Some(variable_symbol.declared_name_source_range())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
dmc-lib/src/symbol/field_symbol.rs
Normal file
68
dmc-lib/src/symbol/field_symbol.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct FieldSymbol {
|
||||||
|
declared_name: Rc<str>,
|
||||||
|
declared_name_source_range: SourceRange,
|
||||||
|
is_mut: bool,
|
||||||
|
scope_id: usize,
|
||||||
|
field_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FieldSymbol {
|
||||||
|
pub fn new(
|
||||||
|
declared_name: &Rc<str>,
|
||||||
|
declared_name_source_range: SourceRange,
|
||||||
|
is_mut: bool,
|
||||||
|
scope_id: usize,
|
||||||
|
field_index: usize,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
declared_name: declared_name.clone(),
|
||||||
|
declared_name_source_range,
|
||||||
|
is_mut,
|
||||||
|
scope_id,
|
||||||
|
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 {
|
||||||
|
self.is_mut
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field_index(&self) -> usize {
|
||||||
|
self.field_index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for FieldSymbol {}
|
||||||
|
|
||||||
|
impl PartialEq for FieldSymbol {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.declared_name == other.declared_name && self.scope_id == other.scope_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for FieldSymbol {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.declared_name.hash(state);
|
||||||
|
self.scope_id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
94
dmc-lib/src/symbol/function_symbol.rs
Normal file
94
dmc-lib/src/symbol/function_symbol.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use crate::ast::fqn_util::fqn_parts_to_string;
|
||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct FunctionSymbol {
|
||||||
|
declared_name: Rc<str>,
|
||||||
|
declared_name_source_range: SourceRange,
|
||||||
|
fqn_parts: Vec<Rc<str>>,
|
||||||
|
is_extern: bool,
|
||||||
|
is_method: bool,
|
||||||
|
scope_id: usize,
|
||||||
|
parameters: Vec<Rc<ParameterSymbol>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionSymbol {
|
||||||
|
pub fn new(
|
||||||
|
declared_name: &Rc<str>,
|
||||||
|
declared_name_source_range: SourceRange,
|
||||||
|
fqn_parts: Vec<Rc<str>>,
|
||||||
|
is_extern: bool,
|
||||||
|
is_method: bool,
|
||||||
|
scope_id: usize,
|
||||||
|
parameters: Vec<Rc<ParameterSymbol>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
declared_name: declared_name.clone(),
|
||||||
|
declared_name_source_range,
|
||||||
|
fqn_parts,
|
||||||
|
is_extern,
|
||||||
|
is_method,
|
||||||
|
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>] {
|
||||||
|
&self.fqn_parts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_method(&self) -> bool {
|
||||||
|
self.is_method
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope_id(&self) -> usize {
|
||||||
|
self.scope_id
|
||||||
|
}
|
||||||
|
pub fn parameters(&self) -> &[Rc<ParameterSymbol>] {
|
||||||
|
&self.parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_extern(&self) -> bool {
|
||||||
|
self.is_extern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for FunctionSymbol {}
|
||||||
|
|
||||||
|
impl PartialEq for FunctionSymbol {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.declared_name == other.declared_name && self.scope_id == other.scope_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
54
dmc-lib/src/symbol/generic_parameter_symbol.rs
Normal file
54
dmc-lib/src/symbol/generic_parameter_symbol.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct GenericParameterSymbol {
|
||||||
|
declared_name: Rc<str>,
|
||||||
|
declared_name_source_range: SourceRange,
|
||||||
|
scope_id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericParameterSymbol {
|
||||||
|
pub fn new(
|
||||||
|
declared_name: &Rc<str>,
|
||||||
|
declared_name_source_range: &SourceRange,
|
||||||
|
scope_id: usize,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
declared_name: declared_name.clone(),
|
||||||
|
declared_name_source_range: declared_name_source_range.clone(),
|
||||||
|
scope_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
148
dmc-lib/src/symbol/mod.rs
Normal file
148
dmc-lib/src/symbol/mod.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||||
|
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::variable_symbol::VariableSymbol;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub mod callable_symbol;
|
||||||
|
pub mod class_symbol;
|
||||||
|
pub mod constructor_symbol;
|
||||||
|
pub mod expressible_symbol;
|
||||||
|
pub mod field_symbol;
|
||||||
|
pub mod function_symbol;
|
||||||
|
pub mod generic_parameter_symbol;
|
||||||
|
pub mod parameter_symbol;
|
||||||
|
pub mod type_symbol;
|
||||||
|
pub mod variable_symbol;
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum Symbol {
|
||||||
|
Class(Rc<ClassSymbol>),
|
||||||
|
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) => parameter_symbol.declared_name_source_range(),
|
||||||
|
Symbol::Variable(variable_symbol) => Some(variable_symbol.declared_name_source_range()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_type_symbol(&self) -> TypeSymbol {
|
||||||
|
match self {
|
||||||
|
Symbol::Class(class_symbol) => TypeSymbol::Class(class_symbol.clone()),
|
||||||
|
Symbol::GenericParameter(generic_parameter_symbol) => {
|
||||||
|
TypeSymbol::GenericParameter(generic_parameter_symbol.clone())
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_expressible_symbol(&self) -> ExpressibleSymbol {
|
||||||
|
match self {
|
||||||
|
Symbol::Class(class_symbol) => ExpressibleSymbol::Class(class_symbol.clone()),
|
||||||
|
Symbol::Field(field_symbol) => ExpressibleSymbol::Field(field_symbol.clone()),
|
||||||
|
Symbol::Function(function_symbol) => {
|
||||||
|
ExpressibleSymbol::Function(function_symbol.clone())
|
||||||
|
}
|
||||||
|
Symbol::Parameter(parameter_symbol) => {
|
||||||
|
ExpressibleSymbol::Parameter(parameter_symbol.clone())
|
||||||
|
}
|
||||||
|
Symbol::Variable(variable_symbol) => {
|
||||||
|
ExpressibleSymbol::Variable(variable_symbol.clone())
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_function_symbol(&self) -> &Rc<FunctionSymbol> {
|
||||||
|
match self {
|
||||||
|
Symbol::Function(function_symbol) => function_symbol,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_variable_symbol(&self) -> &Rc<VariableSymbol> {
|
||||||
|
match self {
|
||||||
|
Symbol::Variable(variable_symbol) => variable_symbol,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_class_symbol(&self) -> &Rc<ClassSymbol> {
|
||||||
|
match self {
|
||||||
|
Symbol::Class(class_symbol) => class_symbol,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_field_symbol(&self) -> &Rc<FieldSymbol> {
|
||||||
|
match self {
|
||||||
|
Symbol::Field(field_symbol) => field_symbol,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_parameter_symbol(&self) -> &Rc<ParameterSymbol> {
|
||||||
|
match self {
|
||||||
|
Symbol::Parameter(parameter_symbol) => parameter_symbol,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_constructor_symbol(&self) -> &Rc<ConstructorSymbol> {
|
||||||
|
match self {
|
||||||
|
Symbol::Constructor(constructor_symbol) => constructor_symbol,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
dmc-lib/src/symbol/parameter_symbol.rs
Normal file
54
dmc-lib/src/symbol/parameter_symbol.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct ParameterSymbol {
|
||||||
|
declared_name: Rc<str>,
|
||||||
|
declared_name_source_range: Option<SourceRange>,
|
||||||
|
scope_id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParameterSymbol {
|
||||||
|
pub fn new(
|
||||||
|
declared_name: &Rc<str>,
|
||||||
|
declared_name_source_range: Option<SourceRange>,
|
||||||
|
scope_id: usize,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
declared_name: declared_name.clone(),
|
||||||
|
declared_name_source_range,
|
||||||
|
scope_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
dmc-lib/src/symbol/type_symbol.rs
Normal file
20
dmc-lib/src/symbol/type_symbol.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub enum TypeSymbol {
|
||||||
|
Class(Rc<ClassSymbol>),
|
||||||
|
GenericParameter(Rc<GenericParameterSymbol>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeSymbol {
|
||||||
|
pub fn into_symbol(self) -> Symbol {
|
||||||
|
match self {
|
||||||
|
TypeSymbol::Class(class_symbol) => Symbol::Class(class_symbol),
|
||||||
|
TypeSymbol::GenericParameter(generic_parameter_symbol) => {
|
||||||
|
Symbol::GenericParameter(generic_parameter_symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
dmc-lib/src/symbol/variable_symbol.rs
Normal file
72
dmc-lib/src/symbol/variable_symbol.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct VariableSymbol {
|
||||||
|
declared_name: Rc<str>,
|
||||||
|
declared_name_source_range: SourceRange,
|
||||||
|
is_mut: bool,
|
||||||
|
scope_id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableSymbol {
|
||||||
|
pub fn new(
|
||||||
|
name: &Rc<str>,
|
||||||
|
declared_name_source_range: &SourceRange,
|
||||||
|
is_mut: bool,
|
||||||
|
scope_id: usize,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
declared_name: name.clone(),
|
||||||
|
declared_name_source_range: declared_name_source_range.clone(),
|
||||||
|
is_mut,
|
||||||
|
scope_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
self.is_mut
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope_id(&self) -> usize {
|
||||||
|
self.scope_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for VariableSymbol {}
|
||||||
|
|
||||||
|
impl PartialEq for VariableSymbol {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.declared_name == other.declared_name && self.scope_id == other.scope_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for VariableSymbol {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.declared_name.hash(state);
|
||||||
|
self.scope_id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for VariableSymbol {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"VariableSymbol({:?}, {})",
|
||||||
|
self.declared_name, self.scope_id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
241
dmc-lib/src/symbol_table/helpers.rs
Normal file
241
dmc-lib/src/symbol_table/helpers.rs
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
use crate::scope::Scope;
|
||||||
|
use crate::scope::block_scope::BlockScope;
|
||||||
|
use crate::scope::class_body_scope::ClassBodyScope;
|
||||||
|
use crate::scope::class_scope::ClassScope;
|
||||||
|
use crate::scope::function_scope::FunctionScope;
|
||||||
|
use crate::scope::module_scope::ModuleScope;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
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::variable_symbol::VariableSymbol;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
fn find_class_symbol_ref(
|
||||||
|
symbols: &HashMap<Rc<str>, Rc<ClassSymbol>>,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<Symbol> {
|
||||||
|
symbols
|
||||||
|
.get(name)
|
||||||
|
.map(|symbol| Symbol::Class(symbol.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_generic_parameter_symbol_ref(
|
||||||
|
symbols: &HashMap<Rc<str>, Rc<GenericParameterSymbol>>,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<Symbol> {
|
||||||
|
symbols
|
||||||
|
.get(name)
|
||||||
|
.map(|symbol| Symbol::GenericParameter(symbol.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_field_symbol_ref(
|
||||||
|
symbols: &HashMap<Rc<str>, Rc<FieldSymbol>>,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<Symbol> {
|
||||||
|
symbols
|
||||||
|
.get(name)
|
||||||
|
.map(|symbol| Symbol::Field(symbol.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_function_symbol_ref(
|
||||||
|
symbols: &HashMap<Rc<str>, Rc<FunctionSymbol>>,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<Symbol> {
|
||||||
|
symbols
|
||||||
|
.get(name)
|
||||||
|
.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(
|
||||||
|
module_scope: &ModuleScope,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<ExpressibleSymbol> {
|
||||||
|
find_class_expressible_symbol_ref(&module_scope.class_symbols(), name)
|
||||||
|
.or_else(|| find_function_expressible_symbol_ref(&module_scope.function_symbols(), name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_expressible_in_class_body_by_name(
|
||||||
|
class_body_scope: &ClassBodyScope,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<ExpressibleSymbol> {
|
||||||
|
find_class_expressible_symbol_ref(&class_body_scope.class_symbols(), name)
|
||||||
|
.or_else(|| {
|
||||||
|
find_function_expressible_symbol_ref(&class_body_scope.function_symbols(), name)
|
||||||
|
})
|
||||||
|
.or_else(|| find_field_expressible_symbol_ref(&class_body_scope.field_symbols(), name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_expressible_in_function_by_name(
|
||||||
|
function_scope: &FunctionScope,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<ExpressibleSymbol> {
|
||||||
|
find_parameter_expressible_symbol_ref(function_scope.parameter_symbols(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_expressible_in_block_by_name(
|
||||||
|
block_scope: &BlockScope,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<ExpressibleSymbol> {
|
||||||
|
find_variable_expressible_symbol_ref(block_scope.variable_symbols(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_expressible_symbol(scope: &Scope, name: &str) -> Option<ExpressibleSymbol> {
|
||||||
|
match scope {
|
||||||
|
Scope::Module(module_scope) => find_expressible_in_module_by_name(module_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) => {
|
||||||
|
find_expressible_in_function_by_name(function_scope, name)
|
||||||
|
}
|
||||||
|
Scope::Block(block_scope) => find_expressible_in_block_by_name(block_scope, name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find type */
|
||||||
|
|
||||||
|
fn find_type_symbol_in_module(module_scope: &ModuleScope, name: &str) -> Option<TypeSymbol> {
|
||||||
|
find_class_type_symbol_ref(&module_scope.class_symbols(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_type_symbol_in_class_body(
|
||||||
|
class_body_scope: &ClassBodyScope,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<TypeSymbol> {
|
||||||
|
find_class_type_symbol_ref(&class_body_scope.class_symbols(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_type_symbol_in_class(class_scope: &ClassScope, name: &str) -> Option<TypeSymbol> {
|
||||||
|
find_generic_parameter_type_symbol_ref(&class_scope.generic_parameter_symbols(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_type_symbol(scope: &Scope, name: &str) -> Option<TypeSymbol> {
|
||||||
|
match scope {
|
||||||
|
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::ClassBody(class_body_scope) => {
|
||||||
|
find_type_symbol_in_class_body(class_body_scope, name)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
420
dmc-lib/src/symbol_table/mod.rs
Normal file
420
dmc-lib/src/symbol_table/mod.rs
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
mod helpers;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
|
use crate::scope::Scope;
|
||||||
|
use crate::scope::block_scope::BlockScope;
|
||||||
|
use crate::scope::class_body_scope::ClassBodyScope;
|
||||||
|
use crate::scope::class_scope::ClassScope;
|
||||||
|
use crate::scope::function_scope::FunctionScope;
|
||||||
|
use crate::scope::module_scope::ModuleScope;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||||
|
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::variable_symbol::VariableSymbol;
|
||||||
|
use crate::symbol_table::helpers::{
|
||||||
|
find_expressible_symbol, find_in_block_by_name, find_in_class_body_by_name,
|
||||||
|
find_in_class_by_name, find_in_function_by_name, find_in_module_by_name, find_type_symbol,
|
||||||
|
};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct SymbolTable {
|
||||||
|
scopes: Vec<Scope>,
|
||||||
|
current_scope_id: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolTable {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
scopes: vec![],
|
||||||
|
current_scope_id: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_scope_id(&self) -> usize {
|
||||||
|
self.scopes.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_scope(&mut self, scope: Scope) -> usize {
|
||||||
|
let scope_id = self.new_scope_id();
|
||||||
|
self.scopes.push(scope);
|
||||||
|
self.current_scope_id = Some(scope_id);
|
||||||
|
scope_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_module_scope(&mut self, debug_name: &str) -> usize {
|
||||||
|
self.push_scope(Scope::Module(ModuleScope::new(
|
||||||
|
debug_name,
|
||||||
|
self.current_scope_id,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_class_scope(&mut self, debug_name: &str) -> usize {
|
||||||
|
self.push_scope(Scope::Class(ClassScope::new(
|
||||||
|
debug_name,
|
||||||
|
self.current_scope_id.unwrap(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
debug_name,
|
||||||
|
self.current_scope_id.unwrap(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_block_scope(&mut self, debug_name: &str) -> usize {
|
||||||
|
self.push_scope(Scope::Block(BlockScope::new(
|
||||||
|
debug_name,
|
||||||
|
self.current_scope_id.unwrap(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_scope(&mut self) {
|
||||||
|
self.current_scope_id = self.scopes[self.current_scope_id.unwrap()].parent_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_scope_id(&self) -> usize {
|
||||||
|
self.current_scope_id.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_scope(&mut self, scope_id: usize) {
|
||||||
|
self.current_scope_id = Some(scope_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scope(&self, scope_id: usize) -> &Scope {
|
||||||
|
&self.scopes[scope_id]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scope_mut(&mut self, scope_id: usize) -> &mut Scope {
|
||||||
|
&mut self.scopes[scope_id]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_symbol(&self, scope_id: usize, name: &str) -> Option<Symbol> {
|
||||||
|
match self.scope(scope_id) {
|
||||||
|
Scope::Module(module_scope) => find_in_module_by_name(module_scope, name),
|
||||||
|
Scope::Class(class_scope) => find_in_class_by_name(class_scope, name),
|
||||||
|
Scope::ClassBody(class_body_scope) => {
|
||||||
|
find_in_class_body_by_name(class_body_scope, name)
|
||||||
|
}
|
||||||
|
Scope::Function(function_scope) => find_in_function_by_name(function_scope, 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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_generic_parameter_symbol(
|
||||||
|
&mut self,
|
||||||
|
generic_parameter_symbol: Rc<GenericParameterSymbol>,
|
||||||
|
) {
|
||||||
|
let name = generic_parameter_symbol.declared_name_owned();
|
||||||
|
match self.scope_mut(generic_parameter_symbol.scope_id()) {
|
||||||
|
Scope::Class(class_scope) => {
|
||||||
|
class_scope
|
||||||
|
.generic_parameter_symbols_mut()
|
||||||
|
.insert(name, generic_parameter_symbol);
|
||||||
|
}
|
||||||
|
_ => panic!("Attempt to insert GenericParameterSymbol in incompatible scope"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_constructor_symbol(&mut self, constructor_symbol: Rc<ConstructorSymbol>) {
|
||||||
|
match self.scope_mut(constructor_symbol.scope_id()) {
|
||||||
|
Scope::ClassBody(class_body_scope) => {
|
||||||
|
class_body_scope
|
||||||
|
.constructor_symbol_mut()
|
||||||
|
.replace(constructor_symbol);
|
||||||
|
}
|
||||||
|
_ => panic!(
|
||||||
|
"Attempt to insert ConstructorSymbol in incompatible scope: {:?}",
|
||||||
|
self.scope(constructor_symbol.scope_id())
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_field_symbol(&mut self, field_symbol: Rc<FieldSymbol>) {
|
||||||
|
let name = field_symbol.declared_name_owned();
|
||||||
|
match self.scope_mut(field_symbol.scope_id()) {
|
||||||
|
Scope::ClassBody(class_scope) => {
|
||||||
|
class_scope.field_symbols_mut().insert(name, field_symbol);
|
||||||
|
}
|
||||||
|
_ => panic!("Attempt to insert FieldSymbol in incompatible scope"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_function_symbol(&mut self, function_symbol: Rc<FunctionSymbol>) {
|
||||||
|
let name = function_symbol.declared_name_owned();
|
||||||
|
match self.scope_mut(function_symbol.scope_id()) {
|
||||||
|
Scope::Module(module_scope) => {
|
||||||
|
module_scope
|
||||||
|
.function_symbols_mut()
|
||||||
|
.insert(name, function_symbol);
|
||||||
|
}
|
||||||
|
Scope::ClassBody(class_scope) => {
|
||||||
|
class_scope
|
||||||
|
.function_symbols_mut()
|
||||||
|
.insert(name, function_symbol);
|
||||||
|
}
|
||||||
|
_ => panic!("Attempt to insert FunctionSymbol in incompatible scope"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_parameter_symbol(&mut self, parameter_symbol: Rc<ParameterSymbol>) {
|
||||||
|
let name = parameter_symbol.declared_name_owned();
|
||||||
|
match self.scope_mut(parameter_symbol.scope_id()) {
|
||||||
|
Scope::Function(function_scope) => {
|
||||||
|
function_scope
|
||||||
|
.parameter_symbols_mut()
|
||||||
|
.insert(name, parameter_symbol);
|
||||||
|
}
|
||||||
|
_ => panic!("Attempt to insert ParameterSymbol in incompatible scope"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_variable_symbol(&mut self, variable_symbol: Rc<VariableSymbol>) {
|
||||||
|
match self.scope_mut(variable_symbol.scope_id()) {
|
||||||
|
Scope::Block(block_scope) => {
|
||||||
|
block_scope
|
||||||
|
.variable_symbols_mut()
|
||||||
|
.insert(variable_symbol.declared_name_owned(), variable_symbol);
|
||||||
|
}
|
||||||
|
_ => panic!("Attempt to insert VariableSymbol in incompatible scope"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_symbol<S>(
|
||||||
|
&self,
|
||||||
|
scope_id: usize,
|
||||||
|
name: &str,
|
||||||
|
f: impl Fn(&Scope, &str) -> Option<S>,
|
||||||
|
) -> Option<S> {
|
||||||
|
let mut maybe_scope = self.scopes.get(scope_id);
|
||||||
|
if maybe_scope.is_none() {
|
||||||
|
panic!("Invalid scope_id: {}", scope_id);
|
||||||
|
}
|
||||||
|
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(
|
||||||
|
&self,
|
||||||
|
scope_id: usize,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<ExpressibleSymbol> {
|
||||||
|
let mut maybe_scope = self.scopes.get(scope_id);
|
||||||
|
if maybe_scope.is_none() {
|
||||||
|
panic!("Invalid scope_id: {}", scope_id);
|
||||||
|
}
|
||||||
|
while let Some(scope) = maybe_scope {
|
||||||
|
let maybe_expressible_symbol = find_expressible_symbol(scope, name);
|
||||||
|
if maybe_expressible_symbol.is_some() {
|
||||||
|
return maybe_expressible_symbol;
|
||||||
|
} else {
|
||||||
|
maybe_scope = scope.parent_id().map(|id| &self.scopes[id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_type_symbol(&self, scope_id: usize, name: &str) -> Option<TypeSymbol> {
|
||||||
|
let mut maybe_scope = self.scopes.get(scope_id);
|
||||||
|
if maybe_scope.is_none() {
|
||||||
|
panic!("Invalid scope_id: {}", scope_id);
|
||||||
|
}
|
||||||
|
while let Some(scope) = maybe_scope {
|
||||||
|
let maybe_type_symbol = find_type_symbol(scope, name);
|
||||||
|
if maybe_type_symbol.is_some() {
|
||||||
|
return maybe_type_symbol;
|
||||||
|
} else {
|
||||||
|
maybe_scope = scope.parent_id().map(|id| &self.scopes[id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
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] {
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
dmc-lib/src/symbol_table/util.rs
Normal file
30
dmc-lib/src/symbol_table/util.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use crate::diagnostic::Diagnostic;
|
||||||
|
use crate::diagnostic_factories::symbol_already_declared;
|
||||||
|
use crate::diagnostics_result;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
265
dmc-lib/src/type_info.rs
Normal file
265
dmc-lib/src/type_info.rs
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
|
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum TypeInfo {
|
||||||
|
Any,
|
||||||
|
Integer,
|
||||||
|
Double,
|
||||||
|
String,
|
||||||
|
Function(Rc<FunctionSymbol>),
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
Class(Rc<ClassSymbol>),
|
||||||
|
|
||||||
|
ParameterizedClass(Rc<ClassSymbol>, Vec<TypeInfo>),
|
||||||
|
GenericType(Rc<GenericParameterSymbol>),
|
||||||
|
Void,
|
||||||
|
|
||||||
|
PlaceholderError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TypeInfo {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
TypeInfo::Any => write!(f, "Any"),
|
||||||
|
TypeInfo::Integer => write!(f, "Int"),
|
||||||
|
TypeInfo::Double => write!(f, "Double"),
|
||||||
|
TypeInfo::String => write!(f, "String"),
|
||||||
|
TypeInfo::Function(function_symbol) => {
|
||||||
|
write!(f, "fn(")?;
|
||||||
|
for (i, parameter) in function_symbol.parameters().iter().enumerate() {
|
||||||
|
parameter.declared_name().fmt(f)?;
|
||||||
|
if i < function_symbol.parameters().len() - 1 {
|
||||||
|
f.write_str(", ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
TypeInfo::Class(class_symbol) => {
|
||||||
|
write!(f, "Class({:?})", class_symbol)
|
||||||
|
}
|
||||||
|
TypeInfo::ParameterizedClass(class_symbol, arguments) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"ParameterizedClass({:?}<{}>)",
|
||||||
|
class_symbol,
|
||||||
|
arguments
|
||||||
|
.iter()
|
||||||
|
.map(|a| a.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TypeInfo::GenericType(generic_parameter_symbol) => {
|
||||||
|
write!(f, "{}", generic_parameter_symbol.declared_name())
|
||||||
|
}
|
||||||
|
TypeInfo::Void => write!(f, "Void"),
|
||||||
|
TypeInfo::PlaceholderError => write!(f, "<PlaceholderError>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_number(type_info: &TypeInfo) -> bool {
|
||||||
|
matches!(type_info, TypeInfo::Integer | TypeInfo::Double)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn are_numbers(left: &TypeInfo, right: &TypeInfo) -> bool {
|
||||||
|
is_number(left) && is_number(right)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeInfo {
|
||||||
|
pub fn is_assignable_from(&self, other: &TypeInfo) -> bool {
|
||||||
|
match self {
|
||||||
|
TypeInfo::Any => true,
|
||||||
|
TypeInfo::Integer => {
|
||||||
|
matches!(other, TypeInfo::Integer)
|
||||||
|
}
|
||||||
|
TypeInfo::Double => {
|
||||||
|
matches!(other, TypeInfo::Double)
|
||||||
|
}
|
||||||
|
TypeInfo::String => {
|
||||||
|
matches!(other, TypeInfo::String)
|
||||||
|
}
|
||||||
|
TypeInfo::Function(_) => {
|
||||||
|
unimplemented!("Type matching on Functions not yet supported.")
|
||||||
|
}
|
||||||
|
TypeInfo::Class(class_symbol) => match other {
|
||||||
|
TypeInfo::Class(other_class_symbol) => class_symbol == other_class_symbol,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
TypeInfo::ParameterizedClass(class_symbol, arguments) => match other {
|
||||||
|
TypeInfo::ParameterizedClass(other_class_symbol, other_arguments) => {
|
||||||
|
if arguments.is_empty() && other_arguments.is_empty() {
|
||||||
|
class_symbol == other_class_symbol
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
TypeInfo::GenericType(generic_parameter_symbol) => {
|
||||||
|
// if generic_parameter_symbol.extends().len() > 0 {
|
||||||
|
// unimplemented!(
|
||||||
|
// "Assigning to generic parameter type with extends type uses not yet supported."
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
true
|
||||||
|
}
|
||||||
|
TypeInfo::Void => {
|
||||||
|
matches!(other, TypeInfo::Void)
|
||||||
|
}
|
||||||
|
TypeInfo::PlaceholderError => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_negate(&self) -> bool {
|
||||||
|
is_number(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn negate_result(&self) -> TypeInfo {
|
||||||
|
match self {
|
||||||
|
TypeInfo::Integer => TypeInfo::Integer,
|
||||||
|
TypeInfo::Double => TypeInfo::Double,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_multiply(&self, rhs: &Self) -> bool {
|
||||||
|
are_numbers(self, rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multiply_result(&self, rhs: &Self) -> TypeInfo {
|
||||||
|
match self {
|
||||||
|
TypeInfo::Integer => match rhs {
|
||||||
|
TypeInfo::Integer => TypeInfo::Integer,
|
||||||
|
TypeInfo::Double => TypeInfo::Double,
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
TypeInfo::Double => TypeInfo::Double,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_divide(&self, rhs: &Self) -> bool {
|
||||||
|
are_numbers(self, rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn divide_result(&self, _rhs: &Self) -> TypeInfo {
|
||||||
|
TypeInfo::Double // ok for now
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_modulo(&self, rhs: &Self) -> bool {
|
||||||
|
are_numbers(self, rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modulo_result(&self, rhs: &Self) -> TypeInfo {
|
||||||
|
match self {
|
||||||
|
TypeInfo::Integer => match rhs {
|
||||||
|
TypeInfo::Integer => TypeInfo::Integer,
|
||||||
|
TypeInfo::Double => TypeInfo::Double,
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
TypeInfo::Double => match rhs {
|
||||||
|
TypeInfo::Integer | TypeInfo::Double => TypeInfo::Double,
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_add(&self, rhs: &Self) -> bool {
|
||||||
|
match self {
|
||||||
|
TypeInfo::Integer => {
|
||||||
|
matches!(rhs, TypeInfo::Integer | TypeInfo::Double | TypeInfo::String)
|
||||||
|
}
|
||||||
|
TypeInfo::Double => {
|
||||||
|
matches!(rhs, TypeInfo::Integer | TypeInfo::Double | TypeInfo::String)
|
||||||
|
}
|
||||||
|
TypeInfo::String => {
|
||||||
|
matches!(rhs, TypeInfo::Integer | TypeInfo::Double | TypeInfo::String)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_result(&self, rhs: &Self) -> TypeInfo {
|
||||||
|
match self {
|
||||||
|
TypeInfo::Integer => match rhs {
|
||||||
|
TypeInfo::Integer => TypeInfo::Integer,
|
||||||
|
TypeInfo::Double => TypeInfo::Double,
|
||||||
|
TypeInfo::String => TypeInfo::String,
|
||||||
|
_ => panic!("Unsupported add: {} + {}.", self, rhs),
|
||||||
|
},
|
||||||
|
TypeInfo::Double => match rhs {
|
||||||
|
TypeInfo::Integer | TypeInfo::Double => TypeInfo::Double,
|
||||||
|
TypeInfo::String => TypeInfo::String,
|
||||||
|
_ => panic!("Unsupported add: {} + {}.", self, rhs),
|
||||||
|
},
|
||||||
|
TypeInfo::String => TypeInfo::String,
|
||||||
|
_ => panic!("Unsupported add: {} + {}.", self, rhs),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_subtract(&self, rhs: &Self) -> bool {
|
||||||
|
are_numbers(self, rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subtract_result(&self, rhs: &Self) -> TypeInfo {
|
||||||
|
match self {
|
||||||
|
TypeInfo::Integer => match rhs {
|
||||||
|
TypeInfo::Integer => TypeInfo::Integer,
|
||||||
|
TypeInfo::Double => TypeInfo::Double,
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
TypeInfo::Double => match rhs {
|
||||||
|
TypeInfo::Integer | TypeInfo::Double => TypeInfo::Double,
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_left_shift(&self, rhs: &Self) -> bool {
|
||||||
|
matches!(self, TypeInfo::Integer) && matches!(rhs, TypeInfo::Integer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn left_shift_result(&self, _rhs: &Self) -> TypeInfo {
|
||||||
|
TypeInfo::Integer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_right_shift(&self, rhs: &Self) -> bool {
|
||||||
|
matches!(self, TypeInfo::Integer) && matches!(rhs, TypeInfo::Integer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn right_shift_result(&self, _rhs: &Self) -> TypeInfo {
|
||||||
|
TypeInfo::Integer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_bitwise_and(&self, rhs: &Self) -> bool {
|
||||||
|
matches!(self, TypeInfo::Integer) && matches!(rhs, TypeInfo::Integer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bitwise_and_result(&self, _rhs: &Self) -> TypeInfo {
|
||||||
|
TypeInfo::Integer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_bitwise_xor(&self, rhs: &Self) -> bool {
|
||||||
|
matches!(self, TypeInfo::Integer) && matches!(rhs, TypeInfo::Integer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bitwise_xor_result(&self, _rhs: &Self) -> TypeInfo {
|
||||||
|
TypeInfo::Integer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_bitwise_or(&self, rhs: &Self) -> bool {
|
||||||
|
matches!(self, TypeInfo::Integer) && matches!(rhs, TypeInfo::Integer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bitwise_or_result(&self, _rhs: &Self) -> TypeInfo {
|
||||||
|
TypeInfo::Integer
|
||||||
|
}
|
||||||
|
}
|
||||||
118
dmc-lib/src/types_table.rs
Normal file
118
dmc-lib/src/types_table.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
85
dmc-lib/src/util.rs
Normal file
85
dmc-lib/src/util.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! handle_diagnostic {
|
||||||
|
( $result: expr, $diagnostics: expr ) => {
|
||||||
|
match $result {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(diagnostic) => {
|
||||||
|
$diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! handle_diagnostics {
|
||||||
|
( $result: expr, $diagnostics: expr ) => {
|
||||||
|
match $result {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mut result_diagnostics) => {
|
||||||
|
$diagnostics.append(&mut result_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ok_or_return {
|
||||||
|
( $result: expr, $diagnostics: expr ) => {
|
||||||
|
match $result {
|
||||||
|
Ok(inner) => inner,
|
||||||
|
Err(mut diagnostics) => {
|
||||||
|
$diagnostics.append(&mut diagnostics);
|
||||||
|
return Err($diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
( $result: expr ) => {
|
||||||
|
match $result {
|
||||||
|
Ok(inner) => inner,
|
||||||
|
Err(diagnostics) => {
|
||||||
|
return Err(diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! maybe_return_diagnostics {
|
||||||
|
( $diagnostics: expr ) => {
|
||||||
|
if !$diagnostics.is_empty() {
|
||||||
|
return Err($diagnostics);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! diagnostics_result {
|
||||||
|
( $diagnostics: expr ) => {
|
||||||
|
if $diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err($diagnostics)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user