deimos-lang/dmc-lib/src/ast/identifier.rs
2026-03-21 18:18:28 -05:00

335 lines
12 KiB
Rust

use crate::ast::diagnostic_factories::{
outer_class_field_usage, outer_class_method_usage, self_constructor_used_in_init,
self_field_used_in_init, self_method_used_in_init, symbol_not_found,
};
use crate::ast::ir_builder::IrBuilder;
use crate::ast::ir_util::get_or_init_field_pointer_variable;
use crate::diagnostic::Diagnostic;
use crate::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::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::rc::Rc;
pub struct Identifier {
name: String,
source_range: SourceRange,
scope_id: Option<usize>,
}
impl Identifier {
pub fn new(name: &str, source_range: SourceRange) -> Self {
Self {
name: name.into(),
source_range,
scope_id: None,
}
}
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()
}
/// Check against recursively constructing this class.
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.
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.
fn check_self_or_outer_method_use(
&self,
context_class_symbol: &ClassSymbol,
function_symbol: &FunctionSymbol,
) -> Option<Diagnostic> {
if context_class_symbol
.functions()
.contains_key(function_symbol.declared_name())
{
// Can only use Self static functions, which we don't have yet
Some(self_method_used_in_init(&self.source_range))
} else if function_symbol.is_method() {
// Can only use outer class static functions, which we don't have yet
Some(outer_class_method_usage(&self.source_range))
} else {
None
}
}
pub fn 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))
}
}
pub fn check_constructor_destination_name(
&self,
symbol_table: &SymbolTable,
class_symbol: &ClassSymbol,
) -> Option<Diagnostic> {
let expressible_symbol =
symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
if let Some(expressible_symbol) = expressible_symbol {
match expressible_symbol {
ExpressibleSymbol::Class(_) => {
panic!("Class is not an L value")
}
ExpressibleSymbol::Field(field_symbol) => {
// This is just a stop-gap for now. We need to decide if we are going to do
// field assignment analysis (whether it's initialized already, if it's mut,
// etc.) during name checking or during type checking.
None
}
ExpressibleSymbol::Function(_) => {
panic!("Function is not an L value")
}
ExpressibleSymbol::Parameter(_) => {
panic!("Parameter is not an L value")
}
ExpressibleSymbol::Variable(variable_symbol) => {
// Again, a stop-gap.
None
}
}
} else {
Some(symbol_not_found(&self.name, &self.source_range))
}
}
pub fn check_constructor_local_name(
&self,
symbol_table: &SymbolTable,
context_class_symbol: &ClassSymbol,
) -> Option<Diagnostic> {
let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
if let Some(symbol) = symbol {
match symbol {
ExpressibleSymbol::Class(class_symbol) => {
self.check_self_constructor_use(context_class_symbol, &class_symbol)
}
ExpressibleSymbol::Field(field_symbol) => {
self.check_self_or_outer_field_use(context_class_symbol, &field_symbol)
}
ExpressibleSymbol::Function(function_symbol) => {
self.check_self_or_outer_method_use(context_class_symbol, &function_symbol)
}
ExpressibleSymbol::Parameter(_) => None,
ExpressibleSymbol::Variable(_) => None,
}
} else {
Some(symbol_not_found(&self.name, &self.source_range))
}
}
pub fn check_method_local_name(
&self,
symbol_table: &SymbolTable,
context_class_symbol: &ClassSymbol,
) -> Option<Diagnostic> {
let symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
if let Some(symbol) = symbol {
match symbol {
ExpressibleSymbol::Class(_) => {
None // all class usages should be ok
}
ExpressibleSymbol::Field(field_symbol) => {
// Must be a reference to a field in this class
if context_class_symbol
.fields()
.contains_key(field_symbol.declared_name())
{
None
} else {
Some(outer_class_field_usage(&self.source_range))
}
}
ExpressibleSymbol::Function(function_symbol) => {
// Must be a method in this class
if function_symbol.is_method()
&& !context_class_symbol
.functions()
.contains_key(function_symbol.declared_name())
{
Some(outer_class_method_usage(&self.source_range))
} else {
None
}
}
ExpressibleSymbol::Parameter(_) => {
None // ok
}
ExpressibleSymbol::Variable(_) => {
None // ok
}
}
} else {
Some(symbol_not_found(&self.name, &self.source_range))
}
}
/// WARNING: this is not appropriate (yet) for class static functions.
pub fn check_static_fn_local_name(&self, symbol_table: &SymbolTable) -> Option<Diagnostic> {
if symbol_table
.find_expressible_symbol(self.scope_id.unwrap(), &self.name)
.is_some()
{
None
} else {
Some(symbol_not_found(&self.name, &self.source_range))
}
}
pub fn source_range(&self) -> &SourceRange {
&self.source_range
}
pub fn type_info<'a>(
&self,
symbol_table: &SymbolTable,
types_table: &'a TypesTable,
) -> &'a TypeInfo {
let expressible_symbol = symbol_table
.find_expressible_symbol(self.scope_id.unwrap(), &self.name)
.unwrap();
match expressible_symbol {
ExpressibleSymbol::Class(class_symbol) => {
types_table.class_types().get(&class_symbol).unwrap()
}
ExpressibleSymbol::Field(field_symbol) => {
types_table.field_types().get(&field_symbol).unwrap()
}
ExpressibleSymbol::Function(function_symbol) => types_table
.function_types()
.get(&function_symbol)
.expect(&format!(
"Unable to get function type for {:?}",
function_symbol
)),
ExpressibleSymbol::Parameter(parameter_symbol) => types_table
.parameter_types()
.get(&parameter_symbol)
.unwrap(),
ExpressibleSymbol::Variable(variable_symbol) => {
types_table.variable_types().get(&variable_symbol).unwrap()
}
}
}
pub fn ir_expression(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
types_table: &TypesTable,
) -> IrExpression {
let expressible_symbol = symbol_table
.find_expressible_symbol(self.scope_id.unwrap(), &self.name)
.unwrap();
match expressible_symbol {
ExpressibleSymbol::Class(class_symbol) => {
todo!()
}
ExpressibleSymbol::Field(field_symbol) => {
let field_type = types_table.field_types().get(&field_symbol).unwrap();
let read_destination = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
field_type,
);
let read_destination_as_rc = Rc::new(RefCell::new(read_destination));
let ir_read_field = IrReadField::new(
get_or_init_field_pointer_variable(builder, &field_symbol, field_type).clone(),
);
builder
.current_block_mut()
.add_statement(IrStatement::Assign(IrAssign::new(
read_destination_as_rc.clone(),
IrOperation::ReadField(ir_read_field),
)));
IrExpression::Variable(read_destination_as_rc)
}
ExpressibleSymbol::Function(_) => {
panic!("Cannot yet get ir-variable for FunctionSymbol")
}
ExpressibleSymbol::Parameter(parameter_symbol) => {
let parameters_map = builder.parameters_map();
let ir_parameter = parameters_map.get(&parameter_symbol).unwrap();
IrExpression::Parameter(ir_parameter.clone())
}
ExpressibleSymbol::Variable(variable_symbol) => {
let ir_variable = builder.local_variables().get(&variable_symbol).unwrap();
IrExpression::Variable(ir_variable.clone())
}
}
}
}