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, } 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 { // 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 { // 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 { 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 { 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_local_name( &self, symbol_table: &SymbolTable, context_class_symbol: &ClassSymbol, ) -> Option { 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 { 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 { 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 variable_symbol = symbol_table .get_variable_symbol(self.scope_id.unwrap(), &self.name) .unwrap(); 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(¶meter_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()) } } } }