Compare commits
8 Commits
75dcca0002
...
320cdcf805
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
320cdcf805 | ||
|
|
51a80bb5ed | ||
|
|
940671822c | ||
|
|
75802e6ee4 | ||
|
|
93eb5eb204 | ||
|
|
8082f4c2e6 | ||
|
|
3c0bf948ac | ||
|
|
ad821ce6a7 |
@ -6,18 +6,14 @@ use dmc_lib::ir::ir_return::IrReturn;
|
|||||||
use dmc_lib::ir::ir_statement::IrStatement;
|
use dmc_lib::ir::ir_statement::IrStatement;
|
||||||
use dmc_lib::lexer::Lexer;
|
use dmc_lib::lexer::Lexer;
|
||||||
use dmc_lib::parser::parse_expression;
|
use dmc_lib::parser::parse_expression;
|
||||||
use dmc_lib::source_range::SourceRange;
|
|
||||||
use dmc_lib::symbol::function_symbol::FunctionSymbol;
|
|
||||||
use dmc_lib::symbol_table::SymbolTable;
|
use dmc_lib::symbol_table::SymbolTable;
|
||||||
use dmc_lib::token::TokenKind;
|
use dmc_lib::token::TokenKind;
|
||||||
use dvm_lib::vm::constant::{Constant, StringConstant};
|
use dvm_lib::vm::constant::{Constant, StringConstant};
|
||||||
use dvm_lib::vm::function::Function;
|
use dvm_lib::vm::function::Function;
|
||||||
use dvm_lib::vm::value::Value;
|
use dvm_lib::vm::value::Value;
|
||||||
use dvm_lib::vm::{CallStack, DvmContext, call};
|
use dvm_lib::vm::{CallStack, DvmContext, call};
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub fn repl(register_count: usize) {
|
pub fn repl(register_count: usize) {
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
@ -128,19 +124,8 @@ fn compile_expression(
|
|||||||
ir_builder.finish_block();
|
ir_builder.finish_block();
|
||||||
let entry_block = ir_builder.get_block(entry_block_id);
|
let entry_block = ir_builder.get_block(entry_block_id);
|
||||||
|
|
||||||
// synthesize symbol
|
|
||||||
let fake_function_symbol = Rc::new(RefCell::new(FunctionSymbol::new(
|
|
||||||
"__repl",
|
|
||||||
SourceRange::new(0, 0),
|
|
||||||
false,
|
|
||||||
)));
|
|
||||||
|
|
||||||
fake_function_symbol
|
|
||||||
.borrow_mut()
|
|
||||||
.set_return_type_info(expression.type_info().clone());
|
|
||||||
|
|
||||||
let mut ir_function = IrFunction::new(
|
let mut ir_function = IrFunction::new(
|
||||||
fake_function_symbol,
|
"__repl".into(),
|
||||||
&[],
|
&[],
|
||||||
expression.type_info(),
|
expression.type_info(),
|
||||||
entry_block.clone(),
|
entry_block.clone(),
|
||||||
|
|||||||
@ -125,12 +125,42 @@ fn report_and_exit(
|
|||||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||||
let config = term::Config::default();
|
let config = term::Config::default();
|
||||||
for diagnostic in diagnostics {
|
for diagnostic in diagnostics {
|
||||||
let csr_diagnostic = codespan_reporting::diagnostic::Diagnostic::error()
|
let mut primary_label =
|
||||||
|
Label::primary(script_file_id, diagnostic.start()..diagnostic.end());
|
||||||
|
if let Some(primary_label_message) = diagnostic.primary_label_message() {
|
||||||
|
primary_label = primary_label.with_message(primary_label_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
let secondary_labels: Vec<Label<usize>> = diagnostic
|
||||||
|
.secondary_labels()
|
||||||
|
.iter()
|
||||||
|
.map(|secondary_label| {
|
||||||
|
let mut label = Label::secondary(
|
||||||
|
script_file_id,
|
||||||
|
secondary_label.start()..secondary_label.end(),
|
||||||
|
);
|
||||||
|
if let Some(message) = secondary_label.message() {
|
||||||
|
label = label.with_message(message);
|
||||||
|
}
|
||||||
|
label
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut csr_diagnostic = codespan_reporting::diagnostic::Diagnostic::error()
|
||||||
.with_message(diagnostic.message())
|
.with_message(diagnostic.message())
|
||||||
.with_label(Label::primary(
|
.with_label(primary_label)
|
||||||
script_file_id,
|
.with_labels(secondary_labels);
|
||||||
diagnostic.start()..diagnostic.end(),
|
|
||||||
|
if let Some(error_code) = diagnostic.error_code() {
|
||||||
|
csr_diagnostic = csr_diagnostic.with_code(format!("E{}", error_code));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((reporter_file, reporter_line)) = diagnostic.reporter() {
|
||||||
|
csr_diagnostic = csr_diagnostic.with_note(format!(
|
||||||
|
"Reported by (Rust) source: {}, line {}",
|
||||||
|
reporter_file, reporter_line
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
term::emit_to_write_style(&mut writer.lock(), &config, files, &csr_diagnostic).unwrap();
|
term::emit_to_write_style(&mut writer.lock(), &config, files, &csr_diagnostic).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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!(
|
||||||
|
|||||||
@ -1,30 +1,39 @@
|
|||||||
|
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, SecondaryLabel};
|
||||||
|
use crate::ir::ir_function::IrFunction;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol::class_symbol::ClassSymbol;
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
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>,
|
||||||
|
class_symbol: Option<Rc<RefCell<ClassSymbol>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Class {
|
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,
|
||||||
|
class_symbol: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,10 +48,11 @@ 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) => {
|
||||||
|
let symbol = already_declared.symbol().borrow();
|
||||||
vec![
|
vec![
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
&format!(
|
&format!(
|
||||||
@ -52,11 +62,19 @@ impl Class {
|
|||||||
self.declared_name_source_range.start(),
|
self.declared_name_source_range.start(),
|
||||||
self.declared_name_source_range.end(),
|
self.declared_name_source_range.end(),
|
||||||
)
|
)
|
||||||
|
.with_secondary_labels(&[SecondaryLabel::new(
|
||||||
|
symbol.declared_name_source_range().start(),
|
||||||
|
symbol.declared_name_source_range().end(),
|
||||||
|
Some("Symbol declared here.".to_string()),
|
||||||
|
)])
|
||||||
.with_reporter(file!(), line!()),
|
.with_reporter(file!(), line!()),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// save symbol for later
|
||||||
|
self.class_symbol = Some(class_symbol);
|
||||||
|
|
||||||
// 2. push scope
|
// 2. push scope
|
||||||
symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
|
symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
|
||||||
|
|
||||||
@ -64,7 +82,8 @@ impl Class {
|
|||||||
let fields_diagnostics: Vec<Diagnostic> = self
|
let fields_diagnostics: Vec<Diagnostic> = self
|
||||||
.fields
|
.fields
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|field| field.gather_declared_names(symbol_table))
|
.enumerate()
|
||||||
|
.map(|(field_index, field)| field.gather_declared_names(symbol_table, field_index))
|
||||||
.filter_map(Result::err)
|
.filter_map(Result::err)
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
@ -73,7 +92,17 @@ impl Class {
|
|||||||
return Err(fields_diagnostics);
|
return Err(fields_diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. gather functions
|
// 4. gather constructor
|
||||||
|
if let Some(constructor) = &mut self.constructor {
|
||||||
|
let constructor_symbol = constructor.gather_declared_names(symbol_table)?;
|
||||||
|
self.class_symbol
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut()
|
||||||
|
.set_constructor_symbol(Some(constructor_symbol));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. gather functions
|
||||||
let functions_diagnostics: Vec<Diagnostic> = self
|
let functions_diagnostics: Vec<Diagnostic> = self
|
||||||
.functions
|
.functions
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
@ -86,13 +115,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 +138,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,21 +158,123 @@ 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!()
|
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));
|
||||||
|
|
||||||
|
if !diagnostics.is_empty() {
|
||||||
|
return Err(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(constructor) = &mut self.constructor {
|
||||||
|
match constructor.type_check(symbol_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mut constructor_diagnostics) => {
|
||||||
|
diagnostics.append(&mut constructor_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !diagnostics.is_empty() {
|
||||||
|
return Err(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.functions
|
||||||
|
.iter_mut()
|
||||||
|
.map(|function| function.type_check(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.for_each(|diagnostic| diagnostics.push(diagnostic));
|
||||||
|
|
||||||
|
if !diagnostics.is_empty() {
|
||||||
|
return Err(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 initialized_field_names: HashSet<&str> = HashSet::new();
|
||||||
|
|
||||||
|
for field in &self.fields {
|
||||||
|
if field.initializer().is_some() {
|
||||||
|
initialized_field_names.insert(field.declared_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check constructor
|
||||||
|
if let Some(constructor) = &self.constructor {
|
||||||
|
for statement in constructor.statements() {
|
||||||
|
match statement {
|
||||||
|
_ => {
|
||||||
|
// no-op for now, because we don't yet have assign statements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that all fields are present in the hash set
|
||||||
|
for field in &self.fields {
|
||||||
|
if !initialized_field_names.contains(field.declared_name()) {
|
||||||
|
diagnostics.push(
|
||||||
|
Diagnostic::new(
|
||||||
|
&format!("Field {} is not initialized.", field.declared_name()),
|
||||||
|
field.declared_name_source_range().start(),
|
||||||
|
field.declared_name_source_range().end(),
|
||||||
|
)
|
||||||
|
.with_primary_label_message(
|
||||||
|
"Must be initialized in declaration or constructor.",
|
||||||
|
)
|
||||||
|
.with_reporter(file!(), line!()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec<IrFunction> {
|
||||||
|
let mut ir_functions: Vec<IrFunction> = vec![];
|
||||||
|
if let Some(constructor) = &self.constructor {
|
||||||
|
ir_functions.push(constructor.to_ir(
|
||||||
|
self.class_symbol.as_ref().unwrap(),
|
||||||
|
&self.fields,
|
||||||
|
symbol_table,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
for function in &self.functions {
|
||||||
|
ir_functions.push(function.to_ir(symbol_table));
|
||||||
|
}
|
||||||
|
|
||||||
|
ir_functions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::constants_table::ConstantsTable;
|
||||||
use crate::parser::parse_compilation_unit;
|
use crate::parser::parse_compilation_unit;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn name_analysis_no_diagnostics() {
|
fn complete_example_no_diagnostics() {
|
||||||
let parse_result = parse_compilation_unit(
|
let parse_result = parse_compilation_unit(
|
||||||
"
|
"
|
||||||
class Foo
|
class Foo
|
||||||
mut bar = 42
|
mut bar: Int = 42
|
||||||
|
|
||||||
|
ctor(_bar: Int)
|
||||||
|
end
|
||||||
|
|
||||||
fn baz() -> Int
|
fn baz() -> Int
|
||||||
bar
|
bar
|
||||||
@ -143,7 +283,7 @@ mod tests {
|
|||||||
|
|
||||||
class Qux
|
class Qux
|
||||||
fn foo() -> Foo
|
fn foo() -> Foo
|
||||||
Foo()
|
Foo(42)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
",
|
",
|
||||||
@ -170,5 +310,20 @@ mod tests {
|
|||||||
panic!("{:?}", diagnostics);
|
panic!("{:?}", diagnostics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match compilation_unit.type_check(&symbol_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(diagnostics) => {
|
||||||
|
panic!("{:?}", diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ir_functions = compilation_unit.to_ir(&symbol_table);
|
||||||
|
let mut constants_table = ConstantsTable::new();
|
||||||
|
for ir_function in &mut ir_functions {
|
||||||
|
let (_, stack_size) = ir_function.assign_registers(8);
|
||||||
|
let vm_function = ir_function.assemble(stack_size, &mut constants_table);
|
||||||
|
println!("{}", vm_function)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -136,9 +136,15 @@ impl CompilationUnit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec<IrFunction> {
|
pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec<IrFunction> {
|
||||||
|
let mut functions: Vec<IrFunction> = vec![];
|
||||||
self.functions
|
self.functions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| f.to_ir(symbol_table))
|
.map(|f| f.to_ir(symbol_table))
|
||||||
.collect()
|
.for_each(|f| functions.push(f));
|
||||||
|
self.classes
|
||||||
|
.iter()
|
||||||
|
.flat_map(|c| c.to_ir(symbol_table))
|
||||||
|
.for_each(|f| functions.push(f));
|
||||||
|
functions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
241
dmc-lib/src/ast/constructor.rs
Normal file
241
dmc-lib/src/ast/constructor.rs
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
use crate::ast::field::Field;
|
||||||
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::statement::Statement;
|
||||||
|
use crate::diagnostic::Diagnostic;
|
||||||
|
use crate::ir::ir_allocate::IrAllocate;
|
||||||
|
use crate::ir::ir_assign::IrAssign;
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
|
use crate::ir::ir_function::IrFunction;
|
||||||
|
use crate::ir::ir_operation::IrOperation;
|
||||||
|
use crate::ir::ir_parameter::IrParameter;
|
||||||
|
use crate::ir::ir_set_field::IrSetField;
|
||||||
|
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::constructor_symbol::ConstructorSymbol;
|
||||||
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
|
use crate::type_info::TypeInfo;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::ops::Neg;
|
||||||
|
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 statements(&self) -> &[Statement] {
|
||||||
|
&self.statements
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gather_declared_names(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Result<Rc<RefCell<ConstructorSymbol>>, 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(constructor_symbol)
|
||||||
|
} 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let parameters_diagnostics: Vec<Diagnostic> = self
|
||||||
|
.parameters
|
||||||
|
.iter_mut()
|
||||||
|
.map(|param| param.type_check(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.type_check(symbol_table, None))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if statements_diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(statements_diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ir(
|
||||||
|
&self,
|
||||||
|
class_symbol: &Rc<RefCell<ClassSymbol>>,
|
||||||
|
fields: &[Field],
|
||||||
|
symbol_table: &SymbolTable,
|
||||||
|
) -> IrFunction {
|
||||||
|
let mut ir_builder = IrBuilder::new();
|
||||||
|
|
||||||
|
let parameters_count = self.parameters.len();
|
||||||
|
let ir_parameters = self
|
||||||
|
.parameters
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, parameter)| {
|
||||||
|
let parameter_symbol = parameter.parameter_symbol().borrow();
|
||||||
|
let offset = (parameters_count as isize).neg() + i as isize;
|
||||||
|
Rc::new(IrParameter::new(
|
||||||
|
parameter_symbol.declared_name(),
|
||||||
|
parameter_symbol.type_info().clone(),
|
||||||
|
offset,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let entry_block_id = ir_builder.new_block();
|
||||||
|
|
||||||
|
// first, allocate the object into a t var
|
||||||
|
let alloc_assign_destination = IrVariable::new_vr(
|
||||||
|
ir_builder.new_t_var().into(),
|
||||||
|
ir_builder.current_block().id(),
|
||||||
|
&TypeInfo::ClassInstance(class_symbol.clone()),
|
||||||
|
);
|
||||||
|
let self_variable = Rc::new(RefCell::new(alloc_assign_destination));
|
||||||
|
let alloc_assign = IrAssign::new(
|
||||||
|
self_variable.clone(),
|
||||||
|
IrOperation::Allocate(IrAllocate::new(class_symbol.borrow().declared_name_owned())),
|
||||||
|
);
|
||||||
|
ir_builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::Assign(alloc_assign));
|
||||||
|
|
||||||
|
// next, initialize fields that have an initializer in their declaration
|
||||||
|
for field in fields {
|
||||||
|
if let Some(initializer) = field.initializer() {
|
||||||
|
let ir_expression = initializer.to_ir(&mut ir_builder, symbol_table).unwrap();
|
||||||
|
let ir_set_field = IrSetField::new(
|
||||||
|
&self_variable.clone(),
|
||||||
|
field.field_symbol().borrow().field_index(),
|
||||||
|
ir_expression,
|
||||||
|
);
|
||||||
|
ir_builder
|
||||||
|
.current_block_mut()
|
||||||
|
.add_statement(IrStatement::SetField(ir_set_field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ir_builder.finish_block();
|
||||||
|
let entry_block = ir_builder.get_block(entry_block_id);
|
||||||
|
|
||||||
|
IrFunction::new(
|
||||||
|
format!("{}::ctor", class_symbol.borrow().declared_name()).into(), // fake function symbol
|
||||||
|
&ir_parameters, // make params
|
||||||
|
&TypeInfo::ClassInstance(class_symbol.clone()),
|
||||||
|
entry_block.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,10 +3,8 @@ use crate::ast::ir_builder::IrBuilder;
|
|||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::ir::ir_return::IrReturn;
|
use crate::ir::ir_return::IrReturn;
|
||||||
use crate::ir::ir_statement::IrStatement;
|
use crate::ir::ir_statement::IrStatement;
|
||||||
use crate::symbol::function_symbol::FunctionSymbol;
|
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
use std::cell::RefCell;
|
use crate::type_info::TypeInfo;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub struct ExpressionStatement {
|
pub struct ExpressionStatement {
|
||||||
expression: Box<Expression>,
|
expression: Box<Expression>,
|
||||||
@ -37,15 +35,13 @@ impl ExpressionStatement {
|
|||||||
pub fn type_check(
|
pub fn type_check(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
is_last: bool,
|
must_return_type_info: Option<&TypeInfo>,
|
||||||
function_symbol: &Rc<RefCell<FunctionSymbol>>,
|
|
||||||
) -> Result<(), Vec<Diagnostic>> {
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
self.expression.type_check(symbol_table)?;
|
self.expression.type_check(symbol_table)?;
|
||||||
|
|
||||||
if is_last {
|
if must_return_type_info.is_some() {
|
||||||
let expression_type = self.expression.type_info();
|
let expression_type = self.expression.type_info();
|
||||||
let borrowed_symbol = function_symbol.borrow();
|
let return_type = must_return_type_info.unwrap();
|
||||||
let return_type = borrowed_symbol.return_type_info();
|
|
||||||
if !return_type.is_assignable_from(expression_type) {
|
if !return_type.is_assignable_from(expression_type) {
|
||||||
return Err(vec![Diagnostic::new(
|
return Err(vec![Diagnostic::new(
|
||||||
&format!(
|
&format!(
|
||||||
|
|||||||
@ -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,18 +33,32 @@ 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declared_name(&self) -> &str {
|
||||||
|
&self.declared_name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declared_name_source_range(&self) -> &SourceRange {
|
||||||
|
&self.declared_name_source_range
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initializer(&self) -> Option<&Expression> {
|
||||||
|
self.initializer.as_ref().map(Box::as_ref)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gather_declared_names(
|
pub fn gather_declared_names(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
|
field_index: usize,
|
||||||
) -> Result<(), Vec<Diagnostic>> {
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
// 1. insert field symbol
|
// 1. insert field symbol
|
||||||
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 +73,21 @@ impl Field {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// 2. gather initializer, if present
|
// save for later
|
||||||
|
self.field_symbol = Some(field_symbol);
|
||||||
|
|
||||||
|
// set field index on symbol
|
||||||
|
self.field_symbol
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut()
|
||||||
|
.set_field_index(field_index);
|
||||||
|
|
||||||
|
// 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 +110,87 @@ 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field_symbol(&self) -> &Rc<RefCell<FieldSymbol>> {
|
||||||
|
self.field_symbol.as_ref().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
@ -206,6 +208,7 @@ impl Function {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let function_symbol = self.function_symbol.as_ref().unwrap();
|
let function_symbol = self.function_symbol.as_ref().unwrap();
|
||||||
|
let return_type_info = function_symbol.borrow().return_type_info().clone();
|
||||||
let statements_len = self.statements.len();
|
let statements_len = self.statements.len();
|
||||||
|
|
||||||
// statements
|
// statements
|
||||||
@ -216,7 +219,11 @@ impl Function {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, statement)| {
|
.map(|(i, statement)| {
|
||||||
let is_last = i == statements_len - 1;
|
let is_last = i == statements_len - 1;
|
||||||
statement.type_check(symbol_table, is_last, function_symbol)
|
if is_last {
|
||||||
|
statement.type_check(symbol_table, Some(&return_type_info))
|
||||||
|
} else {
|
||||||
|
statement.type_check(symbol_table, None)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.filter_map(Result::err)
|
.filter_map(Result::err)
|
||||||
.flatten()
|
.flatten()
|
||||||
@ -262,7 +269,11 @@ impl Function {
|
|||||||
|
|
||||||
let entry_block = builder.get_block(entry_block_id).clone();
|
let entry_block = builder.get_block(entry_block_id).clone();
|
||||||
IrFunction::new(
|
IrFunction::new(
|
||||||
self.function_symbol.as_ref().unwrap().clone(),
|
self.function_symbol
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.borrow()
|
||||||
|
.declared_name_owned(), // ok for now... but we need to start using the fqn
|
||||||
builder.parameters(),
|
builder.parameters(),
|
||||||
return_type_info,
|
return_type_info,
|
||||||
entry_block,
|
entry_block,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -74,6 +74,9 @@ impl NegativeExpression {
|
|||||||
.expect("Attempt to negate non-value expression");
|
.expect("Attempt to negate non-value expression");
|
||||||
|
|
||||||
match operand_as_ir {
|
match operand_as_ir {
|
||||||
|
IrExpression::Field(self_variable, _) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
IrExpression::Parameter(parameter) => {
|
IrExpression::Parameter(parameter) => {
|
||||||
let destination = Rc::new(RefCell::new(IrVariable::new_vr(
|
let destination = Rc::new(RefCell::new(IrVariable::new_vr(
|
||||||
builder.new_t_var().into(),
|
builder.new_t_var().into(),
|
||||||
|
|||||||
@ -36,7 +36,7 @@ impl Parameter {
|
|||||||
let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new(
|
let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new(
|
||||||
&self.declared_name,
|
&self.declared_name,
|
||||||
self.declared_name_source_range.clone(),
|
self.declared_name_source_range.clone(),
|
||||||
TypeInfo::from_declared_name(self.type_use.declared_name()),
|
TypeInfo::from_declared_name(self.type_use.declared_name()), // todo: this will blow up if type is a Class
|
||||||
));
|
));
|
||||||
match insert_result {
|
match insert_result {
|
||||||
Ok(parameter_symbol) => {
|
Ok(parameter_symbol) => {
|
||||||
|
|||||||
@ -2,10 +2,8 @@ use crate::ast::expression_statement::ExpressionStatement;
|
|||||||
use crate::ast::ir_builder::IrBuilder;
|
use crate::ast::ir_builder::IrBuilder;
|
||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::symbol::function_symbol::FunctionSymbol;
|
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
use std::cell::RefCell;
|
use crate::type_info::TypeInfo;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Let(LetStatement),
|
Let(LetStatement),
|
||||||
@ -37,13 +35,12 @@ impl Statement {
|
|||||||
pub fn type_check(
|
pub fn type_check(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
is_last: bool,
|
must_return_type_info: Option<&TypeInfo>,
|
||||||
function_symbol: &Rc<RefCell<FunctionSymbol>>,
|
|
||||||
) -> Result<(), Vec<Diagnostic>> {
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
match self {
|
match self {
|
||||||
Statement::Let(let_statement) => let_statement.type_check(symbol_table),
|
Statement::Let(let_statement) => let_statement.type_check(symbol_table),
|
||||||
Statement::Expression(expression_statement) => {
|
Statement::Expression(expression_statement) => {
|
||||||
expression_statement.type_check(symbol_table, is_last, function_symbol)
|
expression_statement.type_check(symbol_table, must_return_type_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,9 +56,13 @@ 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::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,
|
||||||
|
|||||||
@ -1,20 +1,26 @@
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Diagnostic {
|
pub struct Diagnostic {
|
||||||
message: String,
|
message: String,
|
||||||
|
primary_label_message: Option<String>,
|
||||||
|
error_code: Option<usize>,
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
reporter_file: Option<&'static str>,
|
reporter_file: Option<&'static str>,
|
||||||
reporter_line: Option<u32>,
|
reporter_line: Option<u32>,
|
||||||
|
secondary_labels: Vec<SecondaryLabel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diagnostic {
|
impl Diagnostic {
|
||||||
pub fn new(message: &str, start: usize, end: usize) -> Self {
|
pub fn new(message: &str, start: usize, end: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
message: message.into(),
|
message: message.into(),
|
||||||
|
primary_label_message: None,
|
||||||
|
error_code: None,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
reporter_line: None,
|
reporter_line: None,
|
||||||
reporter_file: None,
|
reporter_file: None,
|
||||||
|
secondary_labels: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,6 +28,14 @@ impl Diagnostic {
|
|||||||
&self.message
|
&self.message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn primary_label_message(&self) -> Option<&str> {
|
||||||
|
self.primary_label_message.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error_code(&self) -> Option<usize> {
|
||||||
|
self.error_code
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start(&self) -> usize {
|
pub fn start(&self) -> usize {
|
||||||
self.start
|
self.start
|
||||||
}
|
}
|
||||||
@ -30,13 +44,65 @@ impl Diagnostic {
|
|||||||
self.end
|
self.end
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_reporter(&self, file: &'static str, line: u32) -> Self {
|
pub fn reporter(&self) -> Option<(&'static str, u32)> {
|
||||||
Self {
|
if let Some(file) = self.reporter_file {
|
||||||
message: self.message.clone(),
|
Some((file, self.reporter_line.unwrap()))
|
||||||
start: self.start,
|
} else {
|
||||||
end: self.end,
|
None
|
||||||
reporter_file: Some(file),
|
|
||||||
reporter_line: Some(line),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn secondary_labels(&self) -> &[SecondaryLabel] {
|
||||||
|
&self.secondary_labels
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_error_code(mut self, code: usize) -> Self {
|
||||||
|
self.error_code = Some(code);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_primary_label_message(mut self, message: &str) -> Self {
|
||||||
|
self.primary_label_message = Some(message.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_reporter(mut self, file: &'static str, line: u32) -> Self {
|
||||||
|
self.reporter_file = Some(file);
|
||||||
|
self.reporter_line = Some(line);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_secondary_labels(mut self, labels: &[SecondaryLabel]) -> Self {
|
||||||
|
self.secondary_labels.extend_from_slice(labels);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SecondaryLabel {
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
message: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecondaryLabel {
|
||||||
|
pub fn new(start: usize, end: usize, message: Option<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&self) -> usize {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(&self) -> usize {
|
||||||
|
self.end
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message(&self) -> Option<&str> {
|
||||||
|
self.message.as_deref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
dmc-lib/src/ir/ir_allocate.rs
Normal file
26
dmc-lib/src/ir/ir_allocate.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct IrAllocate {
|
||||||
|
class_fqn: Rc<str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IrAllocate {
|
||||||
|
pub fn new(class_fqn: Rc<str>) -> Self {
|
||||||
|
Self { class_fqn }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn class_fqn(&self) -> &str {
|
||||||
|
&self.class_fqn
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn class_fqn_owned(&self) -> Rc<str> {
|
||||||
|
self.class_fqn.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IrAllocate {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "alloc {}", self.class_fqn)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -109,6 +109,10 @@ impl Assemble for IrAssign {
|
|||||||
ir_call.assemble(builder, constants_table);
|
ir_call.assemble(builder, constants_table);
|
||||||
builder.push(Instruction::Pop(Some(destination)));
|
builder.push(Instruction::Pop(Some(destination)));
|
||||||
}
|
}
|
||||||
|
IrOperation::Allocate(ir_allocate) => builder.push(Instruction::Allocate(
|
||||||
|
ir_allocate.class_fqn_owned(),
|
||||||
|
destination,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,8 @@ use crate::ir::ir_variable::{
|
|||||||
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
||||||
use crate::type_info::TypeInfo;
|
use crate::type_info::TypeInfo;
|
||||||
use dvm_lib::instruction::{
|
use dvm_lib::instruction::{
|
||||||
AddOperand, Location, MoveOperand, MultiplyOperand, PushOperand, ReturnOperand, SubtractOperand,
|
AddOperand, Location, MoveOperand, MultiplyOperand, PushOperand, ReturnOperand,
|
||||||
|
SetFieldOperand, SubtractOperand,
|
||||||
};
|
};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
@ -14,6 +15,7 @@ use std::fmt::{Display, Formatter};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub enum IrExpression {
|
pub enum IrExpression {
|
||||||
|
Field(Rc<RefCell<IrVariable>>, usize),
|
||||||
Parameter(Rc<IrParameter>),
|
Parameter(Rc<IrParameter>),
|
||||||
Variable(Rc<RefCell<IrVariable>>),
|
Variable(Rc<RefCell<IrVariable>>),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
@ -22,18 +24,11 @@ pub enum IrExpression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IrExpression {
|
impl IrExpression {
|
||||||
pub fn type_info(&self) -> TypeInfo {
|
|
||||||
match self {
|
|
||||||
IrExpression::Parameter(ir_parameter) => ir_parameter.type_info().clone(),
|
|
||||||
IrExpression::Variable(ir_variable) => ir_variable.borrow().type_info().clone(),
|
|
||||||
IrExpression::Int(_) => TypeInfo::Integer,
|
|
||||||
IrExpression::Double(_) => TypeInfo::Double,
|
|
||||||
IrExpression::String(_) => TypeInfo::String,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn move_operand(&self, constants_table: &mut ConstantsTable) -> MoveOperand {
|
pub fn move_operand(&self, constants_table: &mut ConstantsTable) -> MoveOperand {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, _) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
MoveOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
MoveOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
}
|
}
|
||||||
@ -56,6 +51,9 @@ impl IrExpression {
|
|||||||
|
|
||||||
pub fn push_operand(&self, constants_table: &mut ConstantsTable) -> PushOperand {
|
pub fn push_operand(&self, constants_table: &mut ConstantsTable) -> PushOperand {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, _) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
PushOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
PushOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
}
|
}
|
||||||
@ -78,6 +76,9 @@ impl IrExpression {
|
|||||||
|
|
||||||
pub fn add_operand(&self, constants_table: &mut ConstantsTable) -> AddOperand {
|
pub fn add_operand(&self, constants_table: &mut ConstantsTable) -> AddOperand {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, _) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
|
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
|
||||||
TypeInfo::Integer | TypeInfo::Double | TypeInfo::String => {
|
TypeInfo::Integer | TypeInfo::Double | TypeInfo::String => {
|
||||||
AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
@ -116,6 +117,9 @@ impl IrExpression {
|
|||||||
|
|
||||||
pub fn subtract_operand(&self) -> SubtractOperand {
|
pub fn subtract_operand(&self) -> SubtractOperand {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, _) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
|
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
|
||||||
TypeInfo::Integer | TypeInfo::Double => SubtractOperand::Location(
|
TypeInfo::Integer | TypeInfo::Double => SubtractOperand::Location(
|
||||||
Location::StackFrameOffset(ir_parameter.stack_offset()),
|
Location::StackFrameOffset(ir_parameter.stack_offset()),
|
||||||
@ -151,6 +155,9 @@ impl IrExpression {
|
|||||||
|
|
||||||
pub fn multiply_operand(&self) -> MultiplyOperand {
|
pub fn multiply_operand(&self) -> MultiplyOperand {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, _) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
MultiplyOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
MultiplyOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
}
|
}
|
||||||
@ -172,6 +179,9 @@ impl IrExpression {
|
|||||||
|
|
||||||
pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand {
|
pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, _) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
ReturnOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
ReturnOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
}
|
}
|
||||||
@ -193,11 +203,39 @@ impl IrExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_field_operand(&self, constants_table: &mut ConstantsTable) -> SetFieldOperand {
|
||||||
|
match self {
|
||||||
|
IrExpression::Field(self_variable, _) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
|
SetFieldOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
|
}
|
||||||
|
IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() {
|
||||||
|
IrVariableDescriptor::VirtualRegister(vr_variable) => {
|
||||||
|
SetFieldOperand::Location(Location::Register(vr_variable.assigned_register()))
|
||||||
|
}
|
||||||
|
IrVariableDescriptor::Stack(stack_variable) => {
|
||||||
|
SetFieldOperand::Location(Location::StackFrameOffset(stack_variable.offset()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
IrExpression::Int(i) => SetFieldOperand::Int(*i),
|
||||||
|
IrExpression::Double(d) => SetFieldOperand::Double(*d),
|
||||||
|
IrExpression::String(s) => {
|
||||||
|
let constant_name = constants_table.get_or_insert(s);
|
||||||
|
SetFieldOperand::String(constant_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for IrExpression {
|
impl Display for IrExpression {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, field_index) => {
|
||||||
|
write!(f, "{}.{}", self_variable.borrow(), field_index)
|
||||||
|
}
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
write!(f, "{}", ir_parameter)
|
write!(f, "{}", ir_parameter)
|
||||||
}
|
}
|
||||||
@ -219,11 +257,21 @@ impl Display for IrExpression {
|
|||||||
|
|
||||||
impl VrUser for IrExpression {
|
impl VrUser for IrExpression {
|
||||||
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||||
|
// no defs for an expression
|
||||||
HashSet::new()
|
HashSet::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, field_index) => {
|
||||||
|
if let IrVariableDescriptor::VirtualRegister(vr_variable) =
|
||||||
|
self_variable.borrow().descriptor()
|
||||||
|
{
|
||||||
|
HashSet::from([vr_variable.clone()])
|
||||||
|
} else {
|
||||||
|
HashSet::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
IrExpression::Parameter(_) => HashSet::new(),
|
IrExpression::Parameter(_) => HashSet::new(),
|
||||||
IrExpression::Variable(ir_variable) => {
|
IrExpression::Variable(ir_variable) => {
|
||||||
if let IrVariableDescriptor::VirtualRegister(vr_variable) =
|
if let IrVariableDescriptor::VirtualRegister(vr_variable) =
|
||||||
@ -242,6 +290,9 @@ impl VrUser for IrExpression {
|
|||||||
|
|
||||||
fn propagate_spills(&mut self, spills: &HashSet<IrVrVariableDescriptor>) {
|
fn propagate_spills(&mut self, spills: &HashSet<IrVrVariableDescriptor>) {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, field_index) => {
|
||||||
|
// no-op, no defs
|
||||||
|
}
|
||||||
IrExpression::Parameter(_) => {
|
IrExpression::Parameter(_) => {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
@ -278,6 +329,9 @@ impl VrUser for IrExpression {
|
|||||||
assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
|
IrExpression::Field(self_variable, field_index) => {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
IrExpression::Parameter(_) => {
|
IrExpression::Parameter(_) => {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,6 @@ use crate::ir::ir_block::IrBlock;
|
|||||||
use crate::ir::ir_parameter::IrParameter;
|
use crate::ir::ir_parameter::IrParameter;
|
||||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||||
use crate::ir::register_allocation::HasVrUsers;
|
use crate::ir::register_allocation::HasVrUsers;
|
||||||
use crate::symbol::Symbol;
|
|
||||||
use crate::symbol::function_symbol::FunctionSymbol;
|
|
||||||
use crate::type_info::TypeInfo;
|
use crate::type_info::TypeInfo;
|
||||||
use dvm_lib::vm::function::Function;
|
use dvm_lib::vm::function::Function;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@ -14,7 +12,7 @@ use std::fmt::Display;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct IrFunction {
|
pub struct IrFunction {
|
||||||
function_symbol: Rc<RefCell<FunctionSymbol>>,
|
fqn: Rc<str>,
|
||||||
parameters: Vec<Rc<IrParameter>>,
|
parameters: Vec<Rc<IrParameter>>,
|
||||||
return_type_info: TypeInfo,
|
return_type_info: TypeInfo,
|
||||||
entry: Rc<RefCell<IrBlock>>,
|
entry: Rc<RefCell<IrBlock>>,
|
||||||
@ -22,13 +20,13 @@ pub struct IrFunction {
|
|||||||
|
|
||||||
impl IrFunction {
|
impl IrFunction {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
function_symbol: Rc<RefCell<FunctionSymbol>>,
|
fqn: Rc<str>,
|
||||||
parameters: &[Rc<IrParameter>],
|
parameters: &[Rc<IrParameter>],
|
||||||
return_type_info: &TypeInfo,
|
return_type_info: &TypeInfo,
|
||||||
entry: Rc<RefCell<IrBlock>>,
|
entry: Rc<RefCell<IrBlock>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
function_symbol,
|
fqn,
|
||||||
parameters: parameters.to_vec(),
|
parameters: parameters.to_vec(),
|
||||||
return_type_info: return_type_info.clone(),
|
return_type_info: return_type_info.clone(),
|
||||||
entry,
|
entry,
|
||||||
@ -47,7 +45,7 @@ impl IrFunction {
|
|||||||
self.entry.borrow().assemble(&mut builder, constants_table);
|
self.entry.borrow().assemble(&mut builder, constants_table);
|
||||||
let instructions = builder.take_instructions();
|
let instructions = builder.take_instructions();
|
||||||
Function::new(
|
Function::new(
|
||||||
self.function_symbol.borrow().declared_name_owned(),
|
self.fqn.clone(),
|
||||||
self.parameters.len(),
|
self.parameters.len(),
|
||||||
stack_size,
|
stack_size,
|
||||||
instructions,
|
instructions,
|
||||||
@ -57,7 +55,7 @@ impl IrFunction {
|
|||||||
|
|
||||||
impl Display for IrFunction {
|
impl Display for IrFunction {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "fn {}(", self.function_symbol.borrow().declared_name())?;
|
write!(f, "fn {}(", self.fqn)?;
|
||||||
for (i, parameter) in self.parameters.iter().enumerate() {
|
for (i, parameter) in self.parameters.iter().enumerate() {
|
||||||
write!(f, "{}: {}", parameter, parameter.type_info())?;
|
write!(f, "{}: {}", parameter, parameter.type_info())?;
|
||||||
if i < self.parameters.len() - 1 {
|
if i < self.parameters.len() - 1 {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use crate::ir::ir_add::IrAdd;
|
use crate::ir::ir_add::IrAdd;
|
||||||
|
use crate::ir::ir_allocate::IrAllocate;
|
||||||
use crate::ir::ir_call::IrCall;
|
use crate::ir::ir_call::IrCall;
|
||||||
use crate::ir::ir_expression::IrExpression;
|
use crate::ir::ir_expression::IrExpression;
|
||||||
use crate::ir::ir_multiply::IrMultiply;
|
use crate::ir::ir_multiply::IrMultiply;
|
||||||
@ -14,6 +15,7 @@ pub enum IrOperation {
|
|||||||
Subtract(IrSubtract),
|
Subtract(IrSubtract),
|
||||||
Multiply(IrMultiply),
|
Multiply(IrMultiply),
|
||||||
Call(IrCall),
|
Call(IrCall),
|
||||||
|
Allocate(IrAllocate),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for IrOperation {
|
impl Display for IrOperation {
|
||||||
@ -34,6 +36,9 @@ impl Display for IrOperation {
|
|||||||
IrOperation::Call(ir_call) => {
|
IrOperation::Call(ir_call) => {
|
||||||
write!(f, "{}", ir_call)
|
write!(f, "{}", ir_call)
|
||||||
}
|
}
|
||||||
|
IrOperation::Allocate(ir_allocate) => {
|
||||||
|
write!(f, "{}", ir_allocate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,6 +51,7 @@ impl VrUser for IrOperation {
|
|||||||
IrOperation::Subtract(ir_subtract) => ir_subtract.vr_definitions(),
|
IrOperation::Subtract(ir_subtract) => ir_subtract.vr_definitions(),
|
||||||
IrOperation::Multiply(ir_multiply) => ir_multiply.vr_definitions(),
|
IrOperation::Multiply(ir_multiply) => ir_multiply.vr_definitions(),
|
||||||
IrOperation::Call(ir_call) => ir_call.vr_definitions(),
|
IrOperation::Call(ir_call) => ir_call.vr_definitions(),
|
||||||
|
IrOperation::Allocate(_) => HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +62,7 @@ impl VrUser for IrOperation {
|
|||||||
IrOperation::Subtract(ir_subtract) => ir_subtract.vr_uses(),
|
IrOperation::Subtract(ir_subtract) => ir_subtract.vr_uses(),
|
||||||
IrOperation::Multiply(ir_multiply) => ir_multiply.vr_uses(),
|
IrOperation::Multiply(ir_multiply) => ir_multiply.vr_uses(),
|
||||||
IrOperation::Call(ir_call) => ir_call.vr_uses(),
|
IrOperation::Call(ir_call) => ir_call.vr_uses(),
|
||||||
|
IrOperation::Allocate(_) => HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +83,7 @@ impl VrUser for IrOperation {
|
|||||||
IrOperation::Call(ir_call) => {
|
IrOperation::Call(ir_call) => {
|
||||||
ir_call.propagate_spills(spills);
|
ir_call.propagate_spills(spills);
|
||||||
}
|
}
|
||||||
|
IrOperation::Allocate(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +107,7 @@ impl VrUser for IrOperation {
|
|||||||
IrOperation::Call(ir_call) => {
|
IrOperation::Call(ir_call) => {
|
||||||
ir_call.propagate_register_assignments(assignments);
|
ir_call.propagate_register_assignments(assignments);
|
||||||
}
|
}
|
||||||
|
IrOperation::Allocate(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
98
dmc-lib/src/ir/ir_set_field.rs
Normal file
98
dmc-lib/src/ir/ir_set_field.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use crate::constants_table::ConstantsTable;
|
||||||
|
use crate::ir::assemble::{Assemble, InstructionsBuilder};
|
||||||
|
use crate::ir::ir_expression::IrExpression;
|
||||||
|
use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor};
|
||||||
|
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
||||||
|
use crate::ir::util::propagate_spills;
|
||||||
|
use dvm_lib::instruction::{Instruction, Location};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct IrSetField {
|
||||||
|
target_object: Rc<RefCell<IrVariable>>,
|
||||||
|
field_index: usize,
|
||||||
|
initializer: Box<IrExpression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IrSetField {
|
||||||
|
pub fn new(
|
||||||
|
target_object: &Rc<RefCell<IrVariable>>,
|
||||||
|
field_index: usize,
|
||||||
|
initializer: IrExpression,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
target_object: target_object.clone(),
|
||||||
|
field_index,
|
||||||
|
initializer: initializer.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VrUser for IrSetField {
|
||||||
|
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||||
|
// only this, because the target_object is technically not a definition
|
||||||
|
self.initializer.vr_definitions()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||||
|
let mut set = HashSet::new();
|
||||||
|
match self.target_object.borrow().descriptor() {
|
||||||
|
IrVariableDescriptor::VirtualRegister(vr_variable) => {
|
||||||
|
set.insert(vr_variable.clone());
|
||||||
|
}
|
||||||
|
IrVariableDescriptor::Stack(_) => {}
|
||||||
|
}
|
||||||
|
set.extend(self.initializer.vr_uses());
|
||||||
|
set
|
||||||
|
}
|
||||||
|
|
||||||
|
fn propagate_spills(&mut self, spills: &HashSet<IrVrVariableDescriptor>) {
|
||||||
|
propagate_spills(&mut self.target_object, spills);
|
||||||
|
self.initializer.propagate_spills(spills);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn propagate_register_assignments(
|
||||||
|
&mut self,
|
||||||
|
_assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
||||||
|
) {
|
||||||
|
// no definitions
|
||||||
|
}
|
||||||
|
|
||||||
|
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
|
||||||
|
// no definitions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Assemble for IrSetField {
|
||||||
|
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) {
|
||||||
|
let destination = match self.target_object.borrow().descriptor() {
|
||||||
|
IrVariableDescriptor::VirtualRegister(vr_variable) => {
|
||||||
|
Location::Register(vr_variable.assigned_register())
|
||||||
|
}
|
||||||
|
IrVariableDescriptor::Stack(stack_variable) => {
|
||||||
|
Location::StackFrameOffset(stack_variable.offset())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let set_field_operand = self.initializer.set_field_operand(constants_table);
|
||||||
|
builder.push(Instruction::SetField(
|
||||||
|
destination,
|
||||||
|
self.field_index,
|
||||||
|
set_field_operand,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IrSetField {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}.{} = {}",
|
||||||
|
self.target_object.borrow(),
|
||||||
|
self.field_index,
|
||||||
|
self.initializer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ use crate::ir::assemble::{Assemble, InstructionsBuilder};
|
|||||||
use crate::ir::ir_assign::IrAssign;
|
use crate::ir::ir_assign::IrAssign;
|
||||||
use crate::ir::ir_call::IrCall;
|
use crate::ir::ir_call::IrCall;
|
||||||
use crate::ir::ir_return::IrReturn;
|
use crate::ir::ir_return::IrReturn;
|
||||||
|
use crate::ir::ir_set_field::IrSetField;
|
||||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||||
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
@ -12,6 +13,7 @@ pub enum IrStatement {
|
|||||||
Assign(IrAssign),
|
Assign(IrAssign),
|
||||||
Call(IrCall),
|
Call(IrCall),
|
||||||
Return(IrReturn),
|
Return(IrReturn),
|
||||||
|
SetField(IrSetField),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrStatement {
|
impl VrUser for IrStatement {
|
||||||
@ -20,6 +22,7 @@ impl VrUser for IrStatement {
|
|||||||
IrStatement::Assign(ir_assign) => ir_assign.vr_definitions(),
|
IrStatement::Assign(ir_assign) => ir_assign.vr_definitions(),
|
||||||
IrStatement::Call(ir_call) => ir_call.vr_definitions(),
|
IrStatement::Call(ir_call) => ir_call.vr_definitions(),
|
||||||
IrStatement::Return(ir_return) => ir_return.vr_definitions(),
|
IrStatement::Return(ir_return) => ir_return.vr_definitions(),
|
||||||
|
IrStatement::SetField(ir_set_field) => ir_set_field.vr_definitions(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,6 +31,7 @@ impl VrUser for IrStatement {
|
|||||||
IrStatement::Assign(ir_assign) => ir_assign.vr_uses(),
|
IrStatement::Assign(ir_assign) => ir_assign.vr_uses(),
|
||||||
IrStatement::Call(ir_call) => ir_call.vr_uses(),
|
IrStatement::Call(ir_call) => ir_call.vr_uses(),
|
||||||
IrStatement::Return(ir_return) => ir_return.vr_uses(),
|
IrStatement::Return(ir_return) => ir_return.vr_uses(),
|
||||||
|
IrStatement::SetField(ir_set_field) => ir_set_field.vr_uses(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +46,9 @@ impl VrUser for IrStatement {
|
|||||||
IrStatement::Return(ir_return) => {
|
IrStatement::Return(ir_return) => {
|
||||||
ir_return.propagate_spills(spills);
|
ir_return.propagate_spills(spills);
|
||||||
}
|
}
|
||||||
|
IrStatement::SetField(ir_set_field) => {
|
||||||
|
ir_set_field.propagate_spills(spills);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +66,9 @@ impl VrUser for IrStatement {
|
|||||||
IrStatement::Return(ir_return) => {
|
IrStatement::Return(ir_return) => {
|
||||||
ir_return.propagate_register_assignments(assignments);
|
ir_return.propagate_register_assignments(assignments);
|
||||||
}
|
}
|
||||||
|
IrStatement::SetField(ir_set_field) => {
|
||||||
|
ir_set_field.propagate_register_assignments(assignments);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +83,9 @@ impl VrUser for IrStatement {
|
|||||||
IrStatement::Return(ir_return) => {
|
IrStatement::Return(ir_return) => {
|
||||||
ir_return.propagate_stack_offsets(counter);
|
ir_return.propagate_stack_offsets(counter);
|
||||||
}
|
}
|
||||||
|
IrStatement::SetField(ir_set_field) => {
|
||||||
|
ir_set_field.propagate_stack_offsets(counter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,6 +102,9 @@ impl Assemble for IrStatement {
|
|||||||
IrStatement::Return(ir_return) => {
|
IrStatement::Return(ir_return) => {
|
||||||
ir_return.assemble(builder, constants_table);
|
ir_return.assemble(builder, constants_table);
|
||||||
}
|
}
|
||||||
|
IrStatement::SetField(ir_set_field) => {
|
||||||
|
ir_set_field.assemble(builder, constants_table);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,6 +121,9 @@ impl Display for IrStatement {
|
|||||||
IrStatement::Return(ir_return) => {
|
IrStatement::Return(ir_return) => {
|
||||||
write!(f, "{}", ir_return)
|
write!(f, "{}", ir_return)
|
||||||
}
|
}
|
||||||
|
IrStatement::SetField(ir_set_field) => {
|
||||||
|
write!(f, "{}", ir_set_field)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,6 +73,13 @@ impl IrVariableDescriptor {
|
|||||||
IrVariableDescriptor::Stack(stack_variable) => stack_variable.name(),
|
IrVariableDescriptor::Stack(stack_variable) => stack_variable.name(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn name_owned(&self) -> Rc<str> {
|
||||||
|
match self {
|
||||||
|
IrVariableDescriptor::VirtualRegister(vr_variable) => vr_variable.name_owned(),
|
||||||
|
IrVariableDescriptor::Stack(stack_variable) => stack_variable.name_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
@ -95,6 +102,10 @@ impl IrVrVariableDescriptor {
|
|||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn name_owned(&self) -> Rc<str> {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn block_id(&self) -> usize {
|
pub fn block_id(&self) -> usize {
|
||||||
self.block_id
|
self.block_id
|
||||||
}
|
}
|
||||||
@ -139,6 +150,10 @@ impl IrStackVariableDescriptor {
|
|||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn name_owned(&self) -> Rc<str> {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_offset(&mut self, offset: isize) {
|
pub fn set_offset(&mut self, offset: isize) {
|
||||||
self.offset = Some(offset);
|
self.offset = Some(offset);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
mod assemble;
|
mod assemble;
|
||||||
pub mod ir_add;
|
pub mod ir_add;
|
||||||
|
pub mod ir_allocate;
|
||||||
pub mod ir_assign;
|
pub mod ir_assign;
|
||||||
pub mod ir_block;
|
pub mod ir_block;
|
||||||
pub mod ir_call;
|
pub mod ir_call;
|
||||||
@ -9,7 +10,9 @@ pub mod ir_multiply;
|
|||||||
pub mod ir_operation;
|
pub mod ir_operation;
|
||||||
pub mod ir_parameter;
|
pub mod ir_parameter;
|
||||||
pub mod ir_return;
|
pub mod ir_return;
|
||||||
|
pub mod ir_set_field;
|
||||||
pub mod ir_statement;
|
pub mod ir_statement;
|
||||||
pub mod ir_subtract;
|
pub mod ir_subtract;
|
||||||
pub mod ir_variable;
|
pub mod ir_variable;
|
||||||
mod register_allocation;
|
mod register_allocation;
|
||||||
|
mod util;
|
||||||
|
|||||||
22
dmc-lib/src/ir/util.rs
Normal file
22
dmc-lib/src/ir/util.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use crate::ir::ir_variable::{
|
||||||
|
IrStackVariableDescriptor, IrVariable, IrVariableDescriptor, IrVrVariableDescriptor,
|
||||||
|
};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub fn propagate_spills(
|
||||||
|
ir_variable: &mut Rc<RefCell<IrVariable>>,
|
||||||
|
spills: &HashSet<IrVrVariableDescriptor>,
|
||||||
|
) {
|
||||||
|
let mut borrowed_ir_variable = ir_variable.borrow_mut();
|
||||||
|
if let IrVariableDescriptor::VirtualRegister(vr_variable) = borrowed_ir_variable.descriptor() {
|
||||||
|
if spills.contains(vr_variable) {
|
||||||
|
let name = vr_variable.name_owned();
|
||||||
|
let block_id = vr_variable.block_id();
|
||||||
|
borrowed_ir_variable.set_descriptor(IrVariableDescriptor::Stack(
|
||||||
|
IrStackVariableDescriptor::new(name, block_id),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,26 +488,29 @@ 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()?);
|
TokenKind::Fn => functions.push(self.function()?),
|
||||||
} else if matches!(self.lookahead.as_ref().unwrap().kind(), TokenKind::Fn) {
|
TokenKind::Ctor => {
|
||||||
functions.push(self.function()?);
|
maybe_ctor.replace(self.constructor()?);
|
||||||
} else {
|
}
|
||||||
let lookahead = self.lookahead.as_ref().unwrap();
|
_ => {
|
||||||
return Err(vec![Diagnostic::new(
|
let lookahead = self.lookahead.as_ref().unwrap();
|
||||||
&format!(
|
return Err(vec![Diagnostic::new(
|
||||||
"Expected any of {:?}; found {:?}",
|
&format!(
|
||||||
[TokenKind::Mut, TokenKind::Identifier, TokenKind::Fn],
|
"Expected any of {:?}; found {:?}",
|
||||||
lookahead.kind()
|
[TokenKind::Mut, TokenKind::Identifier, TokenKind::Fn],
|
||||||
),
|
lookahead.kind()
|
||||||
lookahead.start(),
|
),
|
||||||
lookahead.end(),
|
lookahead.start(),
|
||||||
)]);
|
lookahead.end(),
|
||||||
|
)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
38
dmc-lib/src/symbol/callable_symbol.rs
Normal file
38
dmc-lib/src/symbol/callable_symbol.rs
Normal 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![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,27 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fields(&self) -> &HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> {
|
||||||
|
&self.fields
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
|||||||
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::ClassInstance(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!
|
||||||
}
|
}
|
||||||
@ -43,6 +39,8 @@ impl ExpressibleSymbol {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
ExpressibleSymbol::Field(field_symbol) => {
|
ExpressibleSymbol::Field(field_symbol) => {
|
||||||
|
// this, unfortunately, is going to need more context, because fields live longer
|
||||||
|
// than function calls
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
ExpressibleSymbol::Function(_) => {
|
ExpressibleSymbol::Function(_) => {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ pub struct FieldSymbol {
|
|||||||
declared_name: Rc<str>,
|
declared_name: Rc<str>,
|
||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
type_info: Option<TypeInfo>,
|
type_info: Option<TypeInfo>,
|
||||||
|
field_index: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldSymbol {
|
impl FieldSymbol {
|
||||||
@ -15,8 +16,25 @@ impl FieldSymbol {
|
|||||||
declared_name: declared_name.clone(),
|
declared_name: declared_name.clone(),
|
||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
type_info: None,
|
type_info: None,
|
||||||
|
field_index: 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_field_index(&mut self, field_index: usize) {
|
||||||
|
self.field_index = Some(field_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field_index(&self) -> usize {
|
||||||
|
self.field_index.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Symbol for FieldSymbol {
|
impl Symbol for FieldSymbol {
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
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 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,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
148
doc/deimos.asciidoc
Normal file
148
doc/deimos.asciidoc
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
= Deimos
|
||||||
|
Jesse Brault <jesse@jessebrault.com>
|
||||||
|
0.1.0
|
||||||
|
:toc:
|
||||||
|
|
||||||
|
== Language
|
||||||
|
|
||||||
|
=== Records
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
pub record Foo(bar: Int, baz: String) end
|
||||||
|
|
||||||
|
fn main()
|
||||||
|
let foo = Foo(1, "Hello")
|
||||||
|
println(foo.bar()) // 1
|
||||||
|
println(foo.baz()) // Hello
|
||||||
|
|
||||||
|
let foo2 = Foo(1, "Hello")
|
||||||
|
println(foo == foo2) // true
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
Records are essentially classes whose data members are all shallowly immutable. The data members of the constructor are
|
||||||
|
always inherently public; it is a syntax error to include the `pub` keyword.
|
||||||
|
|
||||||
|
Records inherently have methods named after the data fields to get the fields' values. It is an error to redeclare a
|
||||||
|
data field's method. Additionally, records inherently implement `Eq` and `Hash`, but each only if the constituent
|
||||||
|
members also implement those traits. *Records that do _not_ automatically implement `Eq` _and_ `Hash` must manually
|
||||||
|
implement the missing traits; it is an error to not do so.*
|
||||||
|
|
||||||
|
==== DVM/Performance Considerations
|
||||||
|
|
||||||
|
Records are interned and compared based on their data content, much like Strings (however, while Strings are primitive,
|
||||||
|
records are not). References to the record's shared data are cloned in the VM for all same-valued instances. To
|
||||||
|
illustrate:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
class Cat(pub sound: String) end
|
||||||
|
|
||||||
|
record Dog(sound: String) end
|
||||||
|
|
||||||
|
fn main()
|
||||||
|
let cats: List<Cat> = []
|
||||||
|
for i in 0..100_000 do
|
||||||
|
cats << Cat("meow") // creates a new Cat object in memory each iteration
|
||||||
|
end
|
||||||
|
let dogs: List<Dog> = []
|
||||||
|
for i in 0..100_000 do
|
||||||
|
dogs << Dog("meow") // creates one Dog object in memory on first iteration,
|
||||||
|
// with 100_000 references stored in dogs list
|
||||||
|
end
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
==== Engineering Considerations
|
||||||
|
|
||||||
|
Records should be preferred against classes in most cases where the data of the class is all immutable, as this saves
|
||||||
|
memory and should result in faster execution times for data-heavy workloads.
|
||||||
|
|
||||||
|
==== Extern Records
|
||||||
|
|
||||||
|
Extern records signify native (Rust or otherwise) objects. They are declared with the `extern` keyword. However, unlike
|
||||||
|
plain Deimos records, all data-access methods, as well as the `Eq` and `Hash` traits' methods, must be implemented
|
||||||
|
natively, as the Deimos compiler has no method of knowing how to actually access the inner data.
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
extern record GameCharacter(name: String) end
|
||||||
|
----
|
||||||
|
|
||||||
|
== Compile Error Explanations
|
||||||
|
|
||||||
|
=== Not more than one constructor
|
||||||
|
|
||||||
|
Classes may have up to only one constructor. The following does not compile:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
class Foo
|
||||||
|
ctor(bar: Int) end
|
||||||
|
ctor(baz: String) end // Error
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Field must be initialized
|
||||||
|
|
||||||
|
All class fields must be initialized in their declaration, or in the constructor. It
|
||||||
|
is an error to leave a field uninitialized.
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
class Foo
|
||||||
|
bar: Int // Error: no initializer
|
||||||
|
end
|
||||||
|
|
||||||
|
class Bar
|
||||||
|
baz: Int // Error: no initializer
|
||||||
|
|
||||||
|
ctor()
|
||||||
|
// some other stuff
|
||||||
|
end
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Use of field before initialization
|
||||||
|
|
||||||
|
A field _without_ a declared initializer (i.e., one present at its declaration, not the constructor) may not be read in
|
||||||
|
the initializer of any other field or constructor variable, as the language does not guarantee a specific ordering of
|
||||||
|
field initialization and cannot statically analyze field/variable dependencies.
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
class Foo
|
||||||
|
bar: Int
|
||||||
|
baz = bar // Error
|
||||||
|
end
|
||||||
|
|
||||||
|
class Bar
|
||||||
|
foo: Int
|
||||||
|
baz = fnWithArg(foo) // Error
|
||||||
|
end
|
||||||
|
|
||||||
|
class Baz
|
||||||
|
foo: Int
|
||||||
|
qux: String
|
||||||
|
|
||||||
|
ctor(baz: String)
|
||||||
|
qux = foo + baz // Error: attempt to read uninitialized foo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
The exception to this is reading the field inside another field's initializer *only* when that expression is itself a
|
||||||
|
closure.
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
class Foo
|
||||||
|
bar: Int
|
||||||
|
baz = { bar } // OK
|
||||||
|
|
||||||
|
ctor(bar: Int)
|
||||||
|
self.bar = bar
|
||||||
|
end
|
||||||
|
end
|
||||||
|
----
|
||||||
@ -7,6 +7,8 @@ pub type ConstantName = Rc<str>;
|
|||||||
pub type FunctionName = Rc<str>;
|
pub type FunctionName = Rc<str>;
|
||||||
pub type ArgCount = usize;
|
pub type ArgCount = usize;
|
||||||
pub type CallerPopCount = usize;
|
pub type CallerPopCount = usize;
|
||||||
|
pub type ClassFqn = Rc<str>;
|
||||||
|
pub type FieldIndex = usize;
|
||||||
|
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
Move(MoveOperand, Location),
|
Move(MoveOperand, Location),
|
||||||
@ -15,6 +17,10 @@ pub enum Instruction {
|
|||||||
InvokeStatic(FunctionName, ArgCount),
|
InvokeStatic(FunctionName, ArgCount),
|
||||||
InvokePlatformStatic(FunctionName, ArgCount),
|
InvokePlatformStatic(FunctionName, ArgCount),
|
||||||
|
|
||||||
|
Allocate(ClassFqn, Location),
|
||||||
|
GetField(Location, FieldIndex, Location),
|
||||||
|
SetField(Location, FieldIndex, SetFieldOperand),
|
||||||
|
|
||||||
Add(AddOperand, AddOperand, Location),
|
Add(AddOperand, AddOperand, Location),
|
||||||
Subtract(SubtractOperand, SubtractOperand, Location),
|
Subtract(SubtractOperand, SubtractOperand, Location),
|
||||||
Multiply(MultiplyOperand, MultiplyOperand, Location),
|
Multiply(MultiplyOperand, MultiplyOperand, Location),
|
||||||
@ -64,6 +70,15 @@ impl Display for Instruction {
|
|||||||
Instruction::Return => {
|
Instruction::Return => {
|
||||||
write!(f, "ret")
|
write!(f, "ret")
|
||||||
}
|
}
|
||||||
|
Instruction::Allocate(class_fqn, location) => {
|
||||||
|
write!(f, "alloc {}, {}", class_fqn, location)
|
||||||
|
}
|
||||||
|
Instruction::GetField(source, field_index, destination) => {
|
||||||
|
write!(f, "getf {}.{}, {}", source, field_index, destination)
|
||||||
|
}
|
||||||
|
Instruction::SetField(target_location, field_index, operand) => {
|
||||||
|
write!(f, "setf {}.{}, {}", target_location, field_index, operand)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,3 +252,29 @@ impl Display for ReturnOperand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum SetFieldOperand {
|
||||||
|
Location(Location),
|
||||||
|
Int(i32),
|
||||||
|
Double(f64),
|
||||||
|
String(Rc<str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SetFieldOperand {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
SetFieldOperand::Location(location) => {
|
||||||
|
write!(f, "{}", location)
|
||||||
|
}
|
||||||
|
SetFieldOperand::Int(i) => {
|
||||||
|
write!(f, "{}", i)
|
||||||
|
}
|
||||||
|
SetFieldOperand::Double(d) => {
|
||||||
|
write!(f, "{}", d)
|
||||||
|
}
|
||||||
|
SetFieldOperand::String(s) => {
|
||||||
|
write!(f, "{}", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -318,6 +318,29 @@ pub fn call<'a>(
|
|||||||
call_stack.top_mut().increment_ip();
|
call_stack.top_mut().increment_ip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Object instructions */
|
||||||
|
Instruction::Allocate(class_fqn, destination) => {
|
||||||
|
// let class = context.classes().get(class_fqn).expect("No such class loaded: {}")
|
||||||
|
// let object: Gc<GcCell<Object>> = Object::new(class, heap)
|
||||||
|
// let value = Value::Object(object)
|
||||||
|
// put_value(registers, call_stack.top_mut(), destination, result)
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction::GetField(source, field_index, destination) => {
|
||||||
|
// let object = load_value(...).unwrap_object();
|
||||||
|
// let field_value = object.fields()[field_index];
|
||||||
|
// put_value(..., destination, field_value.clone());
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction::SetField(target, field_index, operand) => {
|
||||||
|
// let new_value = to_value(operand);
|
||||||
|
// let object = load_value(... target).unwrap_object();
|
||||||
|
// object.fields_mut()[field_index] = new_value;
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
/* Add instructions */
|
/* Add instructions */
|
||||||
Instruction::Add(left_operand, right_operand, destination) => {
|
Instruction::Add(left_operand, right_operand, destination) => {
|
||||||
let left_value = add_operand_to_value(
|
let left_value = add_operand_to_value(
|
||||||
|
|||||||
20
examples/basic_classes.dm
Normal file
20
examples/basic_classes.dm
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
class Foo
|
||||||
|
mut bar: Int = 42
|
||||||
|
|
||||||
|
ctor(_bar: Int)
|
||||||
|
end
|
||||||
|
|
||||||
|
fn baz() -> Int
|
||||||
|
bar
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Qux
|
||||||
|
fn foo() -> Foo
|
||||||
|
Foo(42)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fn main()
|
||||||
|
let crash = Qux()
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue
Block a user