Add ctors, most name-analysis for classes/fields/constructors.
This commit is contained in:
parent
75dcca0002
commit
ad821ce6a7
@ -1,3 +1,4 @@
|
|||||||
|
use crate::ast::constructor::Constructor;
|
||||||
use crate::ast::field::Field;
|
use crate::ast::field::Field;
|
||||||
use crate::ast::function::Function;
|
use crate::ast::function::Function;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
@ -9,6 +10,7 @@ use std::rc::Rc;
|
|||||||
pub struct Class {
|
pub struct Class {
|
||||||
declared_name: Rc<str>,
|
declared_name: Rc<str>,
|
||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
|
constructor: Option<Constructor>,
|
||||||
fields: Vec<Field>,
|
fields: Vec<Field>,
|
||||||
functions: Vec<Function>,
|
functions: Vec<Function>,
|
||||||
}
|
}
|
||||||
@ -17,12 +19,14 @@ impl Class {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
declared_name: &str,
|
declared_name: &str,
|
||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
|
constructor: Option<Constructor>,
|
||||||
fields: Vec<Field>,
|
fields: Vec<Field>,
|
||||||
functions: Vec<Function>,
|
functions: Vec<Function>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Class {
|
Class {
|
||||||
declared_name: declared_name.into(),
|
declared_name: declared_name.into(),
|
||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
|
constructor,
|
||||||
fields,
|
fields,
|
||||||
functions,
|
functions,
|
||||||
}
|
}
|
||||||
@ -73,7 +77,12 @@ impl Class {
|
|||||||
return Err(fields_diagnostics);
|
return Err(fields_diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. gather functions
|
// 4. gather constructor
|
||||||
|
if let Some(constructor) = &mut self.constructor {
|
||||||
|
constructor.gather_declared_names(symbol_table)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. gather functions
|
||||||
let functions_diagnostics: Vec<Diagnostic> = self
|
let functions_diagnostics: Vec<Diagnostic> = self
|
||||||
.functions
|
.functions
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
@ -86,13 +95,18 @@ impl Class {
|
|||||||
return Err(functions_diagnostics);
|
return Err(functions_diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. pop scope
|
// 6. pop scope
|
||||||
symbol_table.pop_scope();
|
symbol_table.pop_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
self.constructor
|
||||||
|
.as_mut()
|
||||||
|
.map(|constructor| constructor.check_name_usages(symbol_table))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
let fields_diagnostics: Vec<Diagnostic> = self
|
let fields_diagnostics: Vec<Diagnostic> = self
|
||||||
.fields
|
.fields
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
@ -104,6 +118,10 @@ impl Class {
|
|||||||
return Err(fields_diagnostics);
|
return Err(fields_diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(constructor) = &mut self.constructor {
|
||||||
|
constructor.check_name_usages(symbol_table)?;
|
||||||
|
}
|
||||||
|
|
||||||
let functions_diagnostics: Vec<Diagnostic> = self
|
let functions_diagnostics: Vec<Diagnostic> = self
|
||||||
.functions
|
.functions
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
@ -120,7 +138,26 @@ impl Class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
todo!()
|
// 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 mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
|
self.fields
|
||||||
|
.iter_mut()
|
||||||
|
.map(|field| field.type_check(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.for_each(|diagnostic| diagnostics.push(diagnostic));
|
||||||
|
|
||||||
|
// todo check constructor and functions and re-check fields
|
||||||
|
|
||||||
|
if diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(diagnostics)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +171,10 @@ mod tests {
|
|||||||
let parse_result = parse_compilation_unit(
|
let parse_result = parse_compilation_unit(
|
||||||
"
|
"
|
||||||
class Foo
|
class Foo
|
||||||
mut bar = 42
|
mut bar: Int
|
||||||
|
|
||||||
|
ctor(_bar: Int)
|
||||||
|
end
|
||||||
|
|
||||||
fn baz() -> Int
|
fn baz() -> Int
|
||||||
bar
|
bar
|
||||||
@ -143,7 +183,7 @@ mod tests {
|
|||||||
|
|
||||||
class Qux
|
class Qux
|
||||||
fn foo() -> Foo
|
fn foo() -> Foo
|
||||||
Foo()
|
Foo(42)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
",
|
",
|
||||||
|
|||||||
127
dmc-lib/src/ast/constructor.rs
Normal file
127
dmc-lib/src/ast/constructor.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::statement::Statement;
|
||||||
|
use crate::diagnostic::Diagnostic;
|
||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct Constructor {
|
||||||
|
is_public: bool,
|
||||||
|
ctor_keyword_source_range: SourceRange,
|
||||||
|
parameters: Vec<Parameter>,
|
||||||
|
statements: Vec<Statement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Constructor {
|
||||||
|
pub fn new(
|
||||||
|
is_public: bool,
|
||||||
|
ctor_keyword_source_range: SourceRange,
|
||||||
|
parameters: Vec<Parameter>,
|
||||||
|
statements: Vec<Statement>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
is_public,
|
||||||
|
ctor_keyword_source_range,
|
||||||
|
parameters,
|
||||||
|
statements,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gather_declared_names(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
// insert constructor symbol
|
||||||
|
let to_insert = ConstructorSymbol::new(self.ctor_keyword_source_range.clone(), false);
|
||||||
|
let constructor_symbol =
|
||||||
|
symbol_table
|
||||||
|
.insert_constructor_symbol(to_insert)
|
||||||
|
.map_err(|symbol_insert_error| match symbol_insert_error {
|
||||||
|
SymbolInsertError::AlreadyDeclared(_) => {
|
||||||
|
vec![
|
||||||
|
Diagnostic::new(
|
||||||
|
"Cannot declare more than one constructor.",
|
||||||
|
self.ctor_keyword_source_range.start(),
|
||||||
|
self.ctor_keyword_source_range.end(),
|
||||||
|
)
|
||||||
|
.with_reporter(file!(), line!()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
symbol_table.push_function_scope("ctor_scope");
|
||||||
|
|
||||||
|
let mut parameter_symbols: Vec<Rc<RefCell<ParameterSymbol>>> = vec![];
|
||||||
|
let mut parameters_diagnostics = vec![];
|
||||||
|
|
||||||
|
for parameter in &mut self.parameters {
|
||||||
|
match parameter.gather_declared_names(symbol_table) {
|
||||||
|
Ok(parameter_symbol) => {
|
||||||
|
parameter_symbols.push(parameter_symbol);
|
||||||
|
}
|
||||||
|
Err(mut ds) => {
|
||||||
|
parameters_diagnostics.append(&mut ds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !parameters_diagnostics.is_empty() {
|
||||||
|
symbol_table.pop_scope();
|
||||||
|
return Err(parameters_diagnostics);
|
||||||
|
} else {
|
||||||
|
constructor_symbol
|
||||||
|
.borrow_mut()
|
||||||
|
.set_parameters(parameter_symbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol_table.push_block_scope("ctor_main_block");
|
||||||
|
|
||||||
|
let statements_diagnostics = self
|
||||||
|
.statements
|
||||||
|
.iter_mut()
|
||||||
|
.map(|stmt| stmt.gather_declared_names(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
symbol_table.pop_scope(); // block
|
||||||
|
symbol_table.pop_scope(); // function
|
||||||
|
|
||||||
|
if statements_diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(statements_diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let parameters_diagnostics: Vec<Diagnostic> = self
|
||||||
|
.parameters
|
||||||
|
.iter_mut()
|
||||||
|
.map(|param| param.check_name_usages(symbol_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.check_name_usages(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if statements_diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(statements_diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ use crate::diagnostic::Diagnostic;
|
|||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol::field_symbol::FieldSymbol;
|
use crate::symbol::field_symbol::FieldSymbol;
|
||||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Field {
|
pub struct Field {
|
||||||
@ -13,6 +14,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>>,
|
||||||
|
field_symbol: Option<Rc<RefCell<FieldSymbol>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Field {
|
impl Field {
|
||||||
@ -31,6 +33,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),
|
||||||
|
field_symbol: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +45,7 @@ impl Field {
|
|||||||
let to_insert =
|
let to_insert =
|
||||||
FieldSymbol::new(&self.declared_name, self.declared_name_source_range.clone());
|
FieldSymbol::new(&self.declared_name, self.declared_name_source_range.clone());
|
||||||
|
|
||||||
symbol_table
|
let field_symbol = symbol_table
|
||||||
.insert_field_symbol(to_insert)
|
.insert_field_symbol(to_insert)
|
||||||
.map_err(|e| match e {
|
.map_err(|e| match e {
|
||||||
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
||||||
@ -57,7 +60,14 @@ impl Field {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// 2. gather initializer, if present
|
// save for later
|
||||||
|
self.field_symbol = Some(field_symbol);
|
||||||
|
|
||||||
|
// 2. gather type_use and initializer, if present
|
||||||
|
if let Some(type_use) = &mut self.declared_type {
|
||||||
|
type_use.gather_declared_names(symbol_table)?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(initializer) = &mut self.initializer {
|
if let Some(initializer) = &mut self.initializer {
|
||||||
initializer.gather_declared_names(symbol_table)?;
|
initializer.gather_declared_names(symbol_table)?;
|
||||||
}
|
}
|
||||||
@ -80,4 +90,83 @@ impl Field {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> 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).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).err() {
|
||||||
|
diagnostics.append(&mut initializer_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !diagnostics.is_empty() {
|
||||||
|
return Err(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check that types are assignable, and update field symbol's type info
|
||||||
|
let field_type_info =
|
||||||
|
match self.declared_type.as_ref() {
|
||||||
|
Some(type_use) => {
|
||||||
|
match self.initializer.as_ref() {
|
||||||
|
Some(initializer) => {
|
||||||
|
let initializer_type_info = initializer.type_info();
|
||||||
|
let declared_type_info = type_use.to_type_info();
|
||||||
|
if declared_type_info.is_assignable_from(initializer_type_info) {
|
||||||
|
declared_type_info
|
||||||
|
} else {
|
||||||
|
return 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 => {
|
||||||
|
// easy: the declared type
|
||||||
|
type_use.to_type_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// type is the initializer
|
||||||
|
match self.initializer.as_ref() {
|
||||||
|
Some(initializer) => initializer.type_info().clone(),
|
||||||
|
None => {
|
||||||
|
// this is an error
|
||||||
|
return Err(vec![Diagnostic::new(
|
||||||
|
"Field must have either a declared type or initializer, or both.",
|
||||||
|
self.declared_name_source_range.start(),
|
||||||
|
self.declared_name_source_range.end(),
|
||||||
|
).with_reporter(file!(), line!())]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// update field symbol's type info
|
||||||
|
self.field_symbol
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut()
|
||||||
|
.set_type_info(field_type_info);
|
||||||
|
|
||||||
|
if diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -169,9 +169,11 @@ impl Function {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// we don't have a given return type, so it's void
|
// we don't have a given return type, so it's void
|
||||||
self.function_symbol.as_mut().unwrap().borrow_mut().set_return_type_info(
|
self.function_symbol
|
||||||
TypeInfo::Void
|
.as_mut()
|
||||||
);
|
.unwrap()
|
||||||
|
.borrow_mut()
|
||||||
|
.set_return_type_info(TypeInfo::Void);
|
||||||
}
|
}
|
||||||
|
|
||||||
// statements
|
// statements
|
||||||
|
|||||||
@ -2,6 +2,7 @@ pub mod add_expression;
|
|||||||
pub mod call;
|
pub mod call;
|
||||||
pub mod class;
|
pub mod class;
|
||||||
pub mod compilation_unit;
|
pub mod compilation_unit;
|
||||||
|
pub mod constructor;
|
||||||
pub mod double_literal;
|
pub mod double_literal;
|
||||||
pub mod expression;
|
pub mod expression;
|
||||||
pub mod expression_statement;
|
pub mod expression_statement;
|
||||||
|
|||||||
@ -56,6 +56,10 @@ impl TypeUse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
Ok(()) // no-op, for now
|
||||||
|
}
|
||||||
|
|
||||||
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::Class(class_symbol.clone()),
|
||||||
|
|||||||
@ -136,6 +136,7 @@ impl<'a> Lexer<'a> {
|
|||||||
"self" => TokenKind::SelfKw,
|
"self" => TokenKind::SelfKw,
|
||||||
"pub" => TokenKind::Public,
|
"pub" => TokenKind::Public,
|
||||||
"mut" => TokenKind::Mut,
|
"mut" => TokenKind::Mut,
|
||||||
|
"ctor" => TokenKind::Ctor,
|
||||||
_ => TokenKind::Identifier,
|
_ => TokenKind::Identifier,
|
||||||
};
|
};
|
||||||
Token::new(self.position, self.position + prefix.len(), token_kind)
|
Token::new(self.position, self.position + prefix.len(), token_kind)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use crate::ast::add_expression::AddExpression;
|
|||||||
use crate::ast::call::Call;
|
use crate::ast::call::Call;
|
||||||
use crate::ast::class::Class;
|
use crate::ast::class::Class;
|
||||||
use crate::ast::compilation_unit::CompilationUnit;
|
use crate::ast::compilation_unit::CompilationUnit;
|
||||||
|
use crate::ast::constructor::Constructor;
|
||||||
use crate::ast::double_literal::DoubleLiteral;
|
use crate::ast::double_literal::DoubleLiteral;
|
||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
use crate::ast::expression_statement::ExpressionStatement;
|
use crate::ast::expression_statement::ExpressionStatement;
|
||||||
@ -49,6 +50,12 @@ macro_rules! matches_expression_first {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! matches_statement_first {
|
||||||
|
( $token_kind : expr ) => {
|
||||||
|
matches!($token_kind, TokenKind::Let) || matches_expression_first!($token_kind)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
struct Parser<'a> {
|
struct Parser<'a> {
|
||||||
input: &'a str,
|
input: &'a str,
|
||||||
lexer: Lexer<'a>,
|
lexer: Lexer<'a>,
|
||||||
@ -382,12 +389,17 @@ impl<'a> Parser<'a> {
|
|||||||
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
||||||
let mut fields = vec![];
|
let mut fields = vec![];
|
||||||
let mut functions = vec![];
|
let mut functions = vec![];
|
||||||
|
let mut maybe_constructor: Option<Constructor> = None;
|
||||||
|
|
||||||
let mut diagnostics = vec![];
|
let mut diagnostics = vec![];
|
||||||
|
|
||||||
while self.current.is_some() && !self.peek_current(TokenKind::End) {
|
while self.current.is_some() && !self.peek_current(TokenKind::End) {
|
||||||
match self.get_current().kind() {
|
match self.get_current().kind() {
|
||||||
TokenKind::Public => match self.public_class_member(&mut fields, &mut functions) {
|
TokenKind::Public => match self.public_class_member(
|
||||||
|
&mut fields,
|
||||||
|
&mut functions,
|
||||||
|
&mut maybe_constructor,
|
||||||
|
) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(mut member_diagnostics) => diagnostics.append(&mut member_diagnostics),
|
Err(mut member_diagnostics) => diagnostics.append(&mut member_diagnostics),
|
||||||
},
|
},
|
||||||
@ -399,6 +411,12 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(function) => functions.push(function),
|
Ok(function) => functions.push(function),
|
||||||
Err(mut function_diagnostics) => diagnostics.append(&mut function_diagnostics),
|
Err(mut function_diagnostics) => diagnostics.append(&mut function_diagnostics),
|
||||||
},
|
},
|
||||||
|
TokenKind::Ctor => match self.constructor() {
|
||||||
|
Ok(constructor) => {
|
||||||
|
maybe_constructor = Some(constructor);
|
||||||
|
}
|
||||||
|
Err(mut ctor_diagnostics) => diagnostics.append(&mut ctor_diagnostics),
|
||||||
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -409,6 +427,7 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(Class::new(
|
Ok(Class::new(
|
||||||
self.token_text(&identifier_token),
|
self.token_text(&identifier_token),
|
||||||
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||||
|
maybe_constructor,
|
||||||
fields,
|
fields,
|
||||||
functions,
|
functions,
|
||||||
))
|
))
|
||||||
@ -469,16 +488,18 @@ impl<'a> Parser<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
fields: &mut Vec<Field>,
|
fields: &mut Vec<Field>,
|
||||||
functions: &mut Vec<Function>,
|
functions: &mut Vec<Function>,
|
||||||
|
maybe_ctor: &mut Option<Constructor>,
|
||||||
) -> Result<(), Vec<Diagnostic>> {
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
if self.lookahead.is_some() {
|
if self.lookahead.is_some() {
|
||||||
if matches!(
|
match self.lookahead.as_ref().unwrap().kind() {
|
||||||
self.lookahead.as_ref().unwrap().kind(),
|
TokenKind::Mut | TokenKind::Identifier => {
|
||||||
TokenKind::Mut | TokenKind::Identifier
|
|
||||||
) {
|
|
||||||
fields.push(self.field()?);
|
fields.push(self.field()?);
|
||||||
} else if matches!(self.lookahead.as_ref().unwrap().kind(), TokenKind::Fn) {
|
}
|
||||||
functions.push(self.function()?);
|
TokenKind::Fn => functions.push(self.function()?),
|
||||||
} else {
|
TokenKind::Ctor => {
|
||||||
|
maybe_ctor.replace(self.constructor()?);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
let lookahead = self.lookahead.as_ref().unwrap();
|
let lookahead = self.lookahead.as_ref().unwrap();
|
||||||
return Err(vec![Diagnostic::new(
|
return Err(vec![Diagnostic::new(
|
||||||
&format!(
|
&format!(
|
||||||
@ -490,6 +511,7 @@ impl<'a> Parser<'a> {
|
|||||||
lookahead.end(),
|
lookahead.end(),
|
||||||
)]);
|
)]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let current = self.current.as_ref().unwrap();
|
let current = self.current.as_ref().unwrap();
|
||||||
@ -504,6 +526,56 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn constructor(&mut self) -> Result<Constructor, Vec<Diagnostic>> {
|
||||||
|
let is_public = if self.current.is_some() && self.peek_current(TokenKind::Public) {
|
||||||
|
self.advance();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctor_keyword = self.expect_advance(TokenKind::Ctor)?;
|
||||||
|
self.expect_advance(TokenKind::LeftParentheses)?;
|
||||||
|
|
||||||
|
let parameters = if self.current.is_some() && self.peek_current(TokenKind::Identifier) {
|
||||||
|
self.parameter_list()?
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
self.expect_advance(TokenKind::RightParentheses)?;
|
||||||
|
|
||||||
|
// statements
|
||||||
|
let mut statements: Vec<Statement> = vec![];
|
||||||
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
|
while self.current.is_some()
|
||||||
|
&& matches_statement_first!(self.current.as_ref().unwrap().kind())
|
||||||
|
{
|
||||||
|
match self.statement() {
|
||||||
|
Ok(statement) => {
|
||||||
|
statements.push(statement);
|
||||||
|
}
|
||||||
|
Err(mut statement_diagnostics) => {
|
||||||
|
diagnostics.append(&mut statement_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expect_advance(TokenKind::End)?;
|
||||||
|
|
||||||
|
if diagnostics.is_empty() {
|
||||||
|
Ok(Constructor::new(
|
||||||
|
is_public,
|
||||||
|
SourceRange::new(ctor_keyword.start(), ctor_keyword.end()),
|
||||||
|
parameters,
|
||||||
|
statements,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn field(&mut self) -> Result<Field, Vec<Diagnostic>> {
|
fn field(&mut self) -> Result<Field, Vec<Diagnostic>> {
|
||||||
let is_public = if self.current.is_some() && self.peek_current(TokenKind::Public) {
|
let is_public = if self.current.is_some() && self.peek_current(TokenKind::Public) {
|
||||||
self.advance();
|
self.advance();
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use crate::symbol::class_symbol::ClassSymbol;
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
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;
|
||||||
@ -11,6 +12,7 @@ pub struct ClassScope {
|
|||||||
class_symbols: HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>,
|
class_symbols: HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>,
|
||||||
field_symbols: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>,
|
field_symbols: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>,
|
||||||
function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
|
function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
|
||||||
|
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClassScope {
|
impl ClassScope {
|
||||||
@ -21,6 +23,7 @@ impl ClassScope {
|
|||||||
class_symbols: HashMap::new(),
|
class_symbols: HashMap::new(),
|
||||||
field_symbols: HashMap::new(),
|
field_symbols: HashMap::new(),
|
||||||
function_symbols: HashMap::new(),
|
function_symbols: HashMap::new(),
|
||||||
|
constructor_symbol: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +51,14 @@ impl ClassScope {
|
|||||||
&mut self.function_symbols
|
&mut self.function_symbols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn constructor_symbol(&self) -> Option<&Rc<RefCell<ConstructorSymbol>>> {
|
||||||
|
self.constructor_symbol.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constructor_symbol_mut(&mut self) -> &mut Option<Rc<RefCell<ConstructorSymbol>>> {
|
||||||
|
&mut self.constructor_symbol
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parent_id(&self) -> usize {
|
pub fn parent_id(&self) -> usize {
|
||||||
self.parent_id
|
self.parent_id
|
||||||
}
|
}
|
||||||
|
|||||||
47
dmc-lib/src/symbol/constructor_symbol.rs
Normal file
47
dmc-lib/src/symbol/constructor_symbol.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct ConstructorSymbol {
|
||||||
|
ctor_keyword_source_range: SourceRange,
|
||||||
|
is_extern: bool,
|
||||||
|
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstructorSymbol {
|
||||||
|
pub fn new(ctor_keyword_source_range: SourceRange, is_extern: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
ctor_keyword_source_range,
|
||||||
|
is_extern,
|
||||||
|
parameters: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_parameters(&mut self, parameters: Vec<Rc<RefCell<ParameterSymbol>>>) {
|
||||||
|
self.parameters = Some(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parameters(&self) -> &[Rc<RefCell<ParameterSymbol>>] {
|
||||||
|
self.parameters.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_extern(&self) -> bool {
|
||||||
|
self.is_extern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Symbol for ConstructorSymbol {
|
||||||
|
fn declared_name(&self) -> &str {
|
||||||
|
"ctor"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declared_name_owned(&self) -> Rc<str> {
|
||||||
|
Rc::from(self.declared_name())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declared_name_source_range(&self) -> &SourceRange {
|
||||||
|
&self.ctor_keyword_source_range
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,12 +19,8 @@ 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) => {
|
ExpressibleSymbol::Class(class_symbol) => TypeInfo::Class(class_symbol.clone()),
|
||||||
todo!()
|
ExpressibleSymbol::Field(field_symbol) => field_symbol.borrow().type_info().clone(),
|
||||||
}
|
|
||||||
ExpressibleSymbol::Field(field_symbol) => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
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!
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,14 @@ impl FieldSymbol {
|
|||||||
type_info: None,
|
type_info: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_type_info(&mut self, type_info: TypeInfo) {
|
||||||
|
self.type_info = Some(type_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_info(&self) -> &TypeInfo {
|
||||||
|
self.type_info.as_ref().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Symbol for FieldSymbol {
|
impl Symbol for FieldSymbol {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use crate::source_range::SourceRange;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub mod class_symbol;
|
pub mod class_symbol;
|
||||||
|
pub mod constructor_symbol;
|
||||||
pub mod expressible_symbol;
|
pub mod expressible_symbol;
|
||||||
pub mod field_symbol;
|
pub mod field_symbol;
|
||||||
pub mod function_symbol;
|
pub mod function_symbol;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use crate::scope::function_scope::FunctionScope;
|
|||||||
use crate::scope::module_scope::ModuleScope;
|
use crate::scope::module_scope::ModuleScope;
|
||||||
use crate::symbol::Symbol;
|
use crate::symbol::Symbol;
|
||||||
use crate::symbol::class_symbol::ClassSymbol;
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||||
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
||||||
use crate::symbol::field_symbol::FieldSymbol;
|
use crate::symbol::field_symbol::FieldSymbol;
|
||||||
use crate::symbol::function_symbol::FunctionSymbol;
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
@ -123,6 +124,32 @@ impl SymbolTable {
|
|||||||
Ok(to_return)
|
Ok(to_return)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_constructor_symbol(
|
||||||
|
&mut self,
|
||||||
|
constructor_symbol: ConstructorSymbol,
|
||||||
|
) -> Result<Rc<RefCell<ConstructorSymbol>>, SymbolInsertError> {
|
||||||
|
let maybe_already_inserted = match self.current_scope() {
|
||||||
|
Scope::Class(class_scope) => class_scope.constructor_symbol(),
|
||||||
|
_ => panic!("Attempt to insert ConstructorSymbol in incompatible scope"),
|
||||||
|
};
|
||||||
|
if let Some(already_inserted) = maybe_already_inserted {
|
||||||
|
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||||
|
already_inserted.clone() as Rc<RefCell<dyn Symbol>>,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let as_rc = Rc::new(RefCell::new(constructor_symbol));
|
||||||
|
let to_return = as_rc.clone();
|
||||||
|
|
||||||
|
match self.current_scope_mut() {
|
||||||
|
Scope::Class(class_scope) => {
|
||||||
|
class_scope.constructor_symbol_mut().replace(as_rc);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
Ok(to_return)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_field_symbol(
|
pub fn insert_field_symbol(
|
||||||
&mut self,
|
&mut self,
|
||||||
field_symbol: FieldSymbol,
|
field_symbol: FieldSymbol,
|
||||||
|
|||||||
@ -47,4 +47,5 @@ pub enum TokenKind {
|
|||||||
SelfKw,
|
SelfKw,
|
||||||
Public,
|
Public,
|
||||||
Mut,
|
Mut,
|
||||||
|
Ctor,
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user