deimos-lang/dmc-lib/src/ast/identifier.rs
2026-04-22 10:35:56 -04:00

653 lines
24 KiB
Rust

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::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;
pub struct Identifier {
node_id: NodeId,
name: Rc<str>,
source_range: SourceRange,
scope_id: Option<usize>,
}
impl Identifier {
pub fn new(node_id: NodeId, name: &str, source_range: SourceRange) -> Self {
Self {
node_id,
name: name.into(),
source_range,
scope_id: None,
}
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn name(&self) -> &str {
&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)
}
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)
}
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)
}
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 {
&self.source_range
}
pub fn resolve_type(
&self,
resolved_symbols: &NodesToSymbols,
resolved_symbol_type_infos: &SymbolsToTypes,
) -> (NodesToTypes, Diagnostics) {
let self_symbol = resolved_symbols.get(&self.node_id).unwrap();
let type_info = resolved_symbol_type_infos.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(&parameter_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(IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
field_type,
)));
let ir_read_field = IrReadField::new(
get_or_init_field_pointer_variable(builder, field_symbol, field_type).clone(),
);
builder
.current_block_mut()
.add_statement(IrStatement::Assign(IrAssign::new(
read_destination.clone(),
IrOperation::ReadField(ir_read_field),
)));
IrExpression::Variable(read_destination)
}
ExpressibleSymbol::Function(_function_symbol) => {
todo!()
}
ExpressibleSymbol::Parameter(parameter_symbol) => IrExpression::Parameter(
builder
.parameters_map()
.get(parameter_symbol)
.unwrap()
.clone(),
),
ExpressibleSymbol::Variable(variable_symbol) => IrExpression::Variable(
builder
.local_variables()
.get(variable_symbol)
.unwrap()
.clone(),
),
}
}
pub fn ir_expression(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrExpression {
let expressible_symbol = symbol_table
.find_expressible_symbol(self.scope_id.unwrap(), &self.name)
.unwrap();
match expressible_symbol {
ExpressibleSymbol::Class(class_symbol) => {
todo!()
}
ExpressibleSymbol::Field(field_symbol) => {
let field_type = types_table.field_types().get(&field_symbol).unwrap();
let read_destination = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
field_type,
);
let read_destination_as_rc = Rc::new(RefCell::new(read_destination));
let ir_read_field = IrReadField::new(
get_or_init_field_pointer_variable(builder, &field_symbol, field_type).clone(),
);
builder
.current_block_mut()
.add_statement(IrStatement::Assign(IrAssign::new(
read_destination_as_rc.clone(),
IrOperation::ReadField(ir_read_field),
)));
IrExpression::Variable(read_destination_as_rc)
}
ExpressibleSymbol::Function(_) => {
panic!("Cannot yet get ir-variable for FunctionSymbol")
}
ExpressibleSymbol::Parameter(parameter_symbol) => {
let parameters_map = builder.parameters_map();
let ir_parameter = parameters_map.get(&parameter_symbol).unwrap();
IrExpression::Parameter(ir_parameter.clone())
}
ExpressibleSymbol::Variable(variable_symbol) => {
let ir_variable = builder.local_variables().get(&variable_symbol).unwrap();
IrExpression::Variable(ir_variable.clone())
}
}
}
}