Add ability to call constructors.

This commit is contained in:
Jesse Brault 2026-03-12 15:55:15 -05:00
parent 8082f4c2e6
commit 93eb5eb204
9 changed files with 75 additions and 15 deletions

View File

@ -5,6 +5,7 @@ use crate::ir::ir_call::IrCall;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol; use crate::symbol::Symbol;
use crate::symbol::callable_symbol::CallableSymbol;
use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
@ -87,8 +88,11 @@ impl Call {
.collect(); .collect();
// check that callee is callable // check that callee is callable
let function_symbol = match self.callee.type_info() { let callable_symbol = match self.callee.type_info() {
TypeInfo::Function(function_symbol) => function_symbol, TypeInfo::Function(function_symbol) => {
CallableSymbol::Function(function_symbol.clone())
}
TypeInfo::ClassInstance(class_symbol) => CallableSymbol::Class(class_symbol.clone()),
_ => { _ => {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
&format!( &format!(
@ -103,11 +107,10 @@ impl Call {
}; };
// set return type // set return type
self.return_type_info = Some(function_symbol.borrow().return_type_info().clone()); self.return_type_info = Some(callable_symbol.return_type_info());
// check arguments length // check arguments length
let function_symbol_ref = function_symbol.borrow(); let parameters = callable_symbol.parameters();
let parameters = function_symbol_ref.parameters();
if parameters.len() != self.arguments.len() { if parameters.len() != self.arguments.len() {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
&format!( &format!(

View File

@ -44,7 +44,7 @@ impl Class {
false, false,
); );
symbol_table let class_symbol = symbol_table
.insert_class_symbol(to_insert) .insert_class_symbol(to_insert)
.map_err(|e| match e { .map_err(|e| match e {
SymbolInsertError::AlreadyDeclared(already_declared) => { SymbolInsertError::AlreadyDeclared(already_declared) => {
@ -80,7 +80,10 @@ impl Class {
// 4. gather constructor // 4. gather constructor
if let Some(constructor) = &mut self.constructor { if let Some(constructor) = &mut self.constructor {
constructor.gather_declared_names(symbol_table)?; let constructor_symbol = constructor.gather_declared_names(symbol_table)?;
class_symbol
.borrow_mut()
.set_constructor_symbol(Some(constructor_symbol));
} }
// 5. gather functions // 5. gather functions

View File

@ -37,7 +37,7 @@ impl Constructor {
pub fn gather_declared_names( pub fn gather_declared_names(
&mut self, &mut self,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<Rc<RefCell<ConstructorSymbol>>, Vec<Diagnostic>> {
// insert constructor symbol // insert constructor symbol
let to_insert = ConstructorSymbol::new(self.ctor_keyword_source_range.clone(), false); let to_insert = ConstructorSymbol::new(self.ctor_keyword_source_range.clone(), false);
let constructor_symbol = let constructor_symbol =
@ -95,7 +95,7 @@ impl Constructor {
symbol_table.pop_scope(); // function symbol_table.pop_scope(); // function
if statements_diagnostics.is_empty() { if statements_diagnostics.is_empty() {
Ok(()) Ok(constructor_symbol)
} else { } else {
Err(statements_diagnostics) Err(statements_diagnostics)
} }

View File

@ -62,7 +62,7 @@ impl TypeUse {
pub fn to_type_info(&self) -> TypeInfo { pub fn to_type_info(&self) -> TypeInfo {
match self.type_symbol.as_ref().unwrap() { match self.type_symbol.as_ref().unwrap() {
TypeSymbol::Class(class_symbol) => TypeInfo::Class(class_symbol.clone()), TypeSymbol::Class(class_symbol) => TypeInfo::ClassInstance(class_symbol.clone()),
TypeSymbol::Primitive(primitive_type) => match primitive_type { TypeSymbol::Primitive(primitive_type) => match primitive_type {
PrimitiveTypeSymbol::Any => TypeInfo::Any, PrimitiveTypeSymbol::Any => TypeInfo::Any,
PrimitiveTypeSymbol::Int => TypeInfo::Integer, PrimitiveTypeSymbol::Int => TypeInfo::Integer,

View File

@ -0,0 +1,38 @@
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc;
pub enum CallableSymbol {
Function(Rc<RefCell<FunctionSymbol>>),
Class(Rc<RefCell<ClassSymbol>>),
}
impl CallableSymbol {
pub fn return_type_info(&self) -> TypeInfo {
match self {
CallableSymbol::Function(function) => function.borrow().return_type_info().clone(),
CallableSymbol::Class(class_symbol) => {
// At the language level, constructors "return" an instance of their type
TypeInfo::ClassInstance(class_symbol.clone())
}
}
}
pub fn parameters(&self) -> Vec<Rc<RefCell<ParameterSymbol>>> {
match self {
CallableSymbol::Function(function_symbol) => {
function_symbol.borrow().parameters().to_vec()
}
CallableSymbol::Class(class_symbol) => {
if let Some(constructor_symbol) = class_symbol.borrow().constructor_symbol() {
constructor_symbol.borrow().parameters().to_vec()
} else {
vec![]
}
}
}
}
}

View File

@ -1,5 +1,6 @@
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol; use crate::symbol::Symbol;
use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use std::cell::RefCell; use std::cell::RefCell;
@ -10,6 +11,7 @@ pub struct ClassSymbol {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
is_extern: bool, is_extern: bool,
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>, fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>,
functions: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>, functions: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
} }
@ -24,10 +26,23 @@ impl ClassSymbol {
declared_name: declared_name.clone(), declared_name: declared_name.clone(),
declared_name_source_range, declared_name_source_range,
is_extern, is_extern,
constructor_symbol: None,
fields: HashMap::new(), fields: HashMap::new(),
functions: HashMap::new(), functions: HashMap::new(),
} }
} }
pub fn set_constructor_symbol(
&mut self,
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
) {
self.constructor_symbol = constructor_symbol;
}
/// A value of `None` indicates that there is no declared constructor for this class.
pub fn constructor_symbol(&self) -> Option<&Rc<RefCell<ConstructorSymbol>>> {
self.constructor_symbol.as_ref()
}
} }
impl Symbol for ClassSymbol { impl Symbol for ClassSymbol {

View File

@ -19,7 +19,7 @@ pub enum ExpressibleSymbol {
impl ExpressibleSymbol { impl ExpressibleSymbol {
pub fn type_info(&self) -> TypeInfo { pub fn type_info(&self) -> TypeInfo {
match self { match self {
ExpressibleSymbol::Class(class_symbol) => TypeInfo::Class(class_symbol.clone()), ExpressibleSymbol::Class(class_symbol) => TypeInfo::ClassInstance(class_symbol.clone()),
ExpressibleSymbol::Field(field_symbol) => field_symbol.borrow().type_info().clone(), ExpressibleSymbol::Field(field_symbol) => field_symbol.borrow().type_info().clone(),
ExpressibleSymbol::Function(function_symbol) => { ExpressibleSymbol::Function(function_symbol) => {
TypeInfo::Function(function_symbol.clone()) // n.b. not the return type! TypeInfo::Function(function_symbol.clone()) // n.b. not the return type!

View File

@ -1,6 +1,7 @@
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use std::rc::Rc; use std::rc::Rc;
pub mod callable_symbol;
pub mod class_symbol; pub mod class_symbol;
pub mod constructor_symbol; pub mod constructor_symbol;
pub mod expressible_symbol; pub mod expressible_symbol;

View File

@ -12,7 +12,7 @@ pub enum TypeInfo {
Double, Double,
String, String,
Function(Rc<RefCell<FunctionSymbol>>), Function(Rc<RefCell<FunctionSymbol>>),
Class(Rc<RefCell<ClassSymbol>>), ClassInstance(Rc<RefCell<ClassSymbol>>),
Void, Void,
} }
@ -30,7 +30,7 @@ impl Display for TypeInfo {
} }
write!(f, ")") write!(f, ")")
} }
TypeInfo::Class(class_symbol) => { TypeInfo::ClassInstance(class_symbol) => {
write!(f, "{}", class_symbol.borrow().declared_name()) write!(f, "{}", class_symbol.borrow().declared_name())
} }
TypeInfo::Void => write!(f, "Void"), TypeInfo::Void => write!(f, "Void"),
@ -67,9 +67,9 @@ impl TypeInfo {
TypeInfo::Function(_) => { TypeInfo::Function(_) => {
unimplemented!("Type matching on Functions not yet supported.") unimplemented!("Type matching on Functions not yet supported.")
} }
TypeInfo::Class(class_symbol) => { TypeInfo::ClassInstance(class_symbol) => {
match other { match other {
TypeInfo::Class(other_class_symbol) => { TypeInfo::ClassInstance(other_class_symbol) => {
class_symbol.borrow().declared_name() class_symbol.borrow().declared_name()
== other_class_symbol.borrow().declared_name() // good enough for now == other_class_symbol.borrow().declared_name() // good enough for now
} }