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::lexer::Lexer;
|
||||
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::token::TokenKind;
|
||||
use dvm_lib::vm::constant::{Constant, StringConstant};
|
||||
use dvm_lib::vm::function::Function;
|
||||
use dvm_lib::vm::value::Value;
|
||||
use dvm_lib::vm::{CallStack, DvmContext, call};
|
||||
use std::cell::RefCell;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn repl(register_count: usize) {
|
||||
let mut buffer = String::new();
|
||||
@ -128,19 +124,8 @@ fn compile_expression(
|
||||
ir_builder.finish_block();
|
||||
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(
|
||||
fake_function_symbol,
|
||||
"__repl".into(),
|
||||
&[],
|
||||
expression.type_info(),
|
||||
entry_block.clone(),
|
||||
|
||||
@ -125,12 +125,42 @@ fn report_and_exit(
|
||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||
let config = term::Config::default();
|
||||
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_label(Label::primary(
|
||||
script_file_id,
|
||||
diagnostic.start()..diagnostic.end(),
|
||||
.with_label(primary_label)
|
||||
.with_labels(secondary_labels);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::ir::ir_call::IrCall;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::Symbol;
|
||||
use crate::symbol::callable_symbol::CallableSymbol;
|
||||
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
||||
use crate::symbol::function_symbol::FunctionSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
@ -87,8 +88,11 @@ impl Call {
|
||||
.collect();
|
||||
|
||||
// check that callee is callable
|
||||
let function_symbol = match self.callee.type_info() {
|
||||
TypeInfo::Function(function_symbol) => function_symbol,
|
||||
let callable_symbol = match self.callee.type_info() {
|
||||
TypeInfo::Function(function_symbol) => {
|
||||
CallableSymbol::Function(function_symbol.clone())
|
||||
}
|
||||
TypeInfo::ClassInstance(class_symbol) => CallableSymbol::Class(class_symbol.clone()),
|
||||
_ => {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
&format!(
|
||||
@ -103,11 +107,10 @@ impl Call {
|
||||
};
|
||||
|
||||
// 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
|
||||
let function_symbol_ref = function_symbol.borrow();
|
||||
let parameters = function_symbol_ref.parameters();
|
||||
let parameters = callable_symbol.parameters();
|
||||
if parameters.len() != self.arguments.len() {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
&format!(
|
||||
|
||||
@ -1,30 +1,39 @@
|
||||
use crate::ast::constructor::Constructor;
|
||||
use crate::ast::field::Field;
|
||||
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::symbol::class_symbol::ClassSymbol;
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Class {
|
||||
declared_name: Rc<str>,
|
||||
declared_name_source_range: SourceRange,
|
||||
constructor: Option<Constructor>,
|
||||
fields: Vec<Field>,
|
||||
functions: Vec<Function>,
|
||||
class_symbol: Option<Rc<RefCell<ClassSymbol>>>,
|
||||
}
|
||||
|
||||
impl Class {
|
||||
pub fn new(
|
||||
declared_name: &str,
|
||||
declared_name_source_range: SourceRange,
|
||||
constructor: Option<Constructor>,
|
||||
fields: Vec<Field>,
|
||||
functions: Vec<Function>,
|
||||
) -> Self {
|
||||
Class {
|
||||
declared_name: declared_name.into(),
|
||||
declared_name_source_range,
|
||||
constructor,
|
||||
fields,
|
||||
functions,
|
||||
class_symbol: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,10 +48,11 @@ impl Class {
|
||||
false,
|
||||
);
|
||||
|
||||
symbol_table
|
||||
let class_symbol = symbol_table
|
||||
.insert_class_symbol(to_insert)
|
||||
.map_err(|e| match e {
|
||||
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
||||
let symbol = already_declared.symbol().borrow();
|
||||
vec![
|
||||
Diagnostic::new(
|
||||
&format!(
|
||||
@ -52,11 +62,19 @@ impl Class {
|
||||
self.declared_name_source_range.start(),
|
||||
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!()),
|
||||
]
|
||||
}
|
||||
})?;
|
||||
|
||||
// save symbol for later
|
||||
self.class_symbol = Some(class_symbol);
|
||||
|
||||
// 2. push scope
|
||||
symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
|
||||
|
||||
@ -64,7 +82,8 @@ impl Class {
|
||||
let fields_diagnostics: Vec<Diagnostic> = self
|
||||
.fields
|
||||
.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)
|
||||
.flatten()
|
||||
.collect();
|
||||
@ -73,7 +92,17 @@ impl Class {
|
||||
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
|
||||
.functions
|
||||
.iter_mut()
|
||||
@ -86,13 +115,18 @@ impl Class {
|
||||
return Err(functions_diagnostics);
|
||||
}
|
||||
|
||||
// 5. pop scope
|
||||
// 6. pop scope
|
||||
symbol_table.pop_scope();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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
|
||||
.fields
|
||||
.iter_mut()
|
||||
@ -104,6 +138,10 @@ impl Class {
|
||||
return Err(fields_diagnostics);
|
||||
}
|
||||
|
||||
if let Some(constructor) = &mut self.constructor {
|
||||
constructor.check_name_usages(symbol_table)?;
|
||||
}
|
||||
|
||||
let functions_diagnostics: Vec<Diagnostic> = self
|
||||
.functions
|
||||
.iter_mut()
|
||||
@ -120,21 +158,123 @@ impl Class {
|
||||
}
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::constants_table::ConstantsTable;
|
||||
use crate::parser::parse_compilation_unit;
|
||||
|
||||
#[test]
|
||||
fn name_analysis_no_diagnostics() {
|
||||
fn complete_example_no_diagnostics() {
|
||||
let parse_result = parse_compilation_unit(
|
||||
"
|
||||
class Foo
|
||||
mut bar = 42
|
||||
mut bar: Int = 42
|
||||
|
||||
ctor(_bar: Int)
|
||||
end
|
||||
|
||||
fn baz() -> Int
|
||||
bar
|
||||
@ -143,7 +283,7 @@ mod tests {
|
||||
|
||||
class Qux
|
||||
fn foo() -> Foo
|
||||
Foo()
|
||||
Foo(42)
|
||||
end
|
||||
end
|
||||
",
|
||||
@ -170,5 +310,20 @@ mod tests {
|
||||
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> {
|
||||
let mut functions: Vec<IrFunction> = vec![];
|
||||
self.functions
|
||||
.iter()
|
||||
.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::ir::ir_return::IrReturn;
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
use crate::symbol::function_symbol::FunctionSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use crate::type_info::TypeInfo;
|
||||
|
||||
pub struct ExpressionStatement {
|
||||
expression: Box<Expression>,
|
||||
@ -37,15 +35,13 @@ impl ExpressionStatement {
|
||||
pub fn type_check(
|
||||
&mut self,
|
||||
symbol_table: &SymbolTable,
|
||||
is_last: bool,
|
||||
function_symbol: &Rc<RefCell<FunctionSymbol>>,
|
||||
must_return_type_info: Option<&TypeInfo>,
|
||||
) -> Result<(), Vec<Diagnostic>> {
|
||||
self.expression.type_check(symbol_table)?;
|
||||
|
||||
if is_last {
|
||||
if must_return_type_info.is_some() {
|
||||
let expression_type = self.expression.type_info();
|
||||
let borrowed_symbol = function_symbol.borrow();
|
||||
let return_type = borrowed_symbol.return_type_info();
|
||||
let return_type = must_return_type_info.unwrap();
|
||||
if !return_type.is_assignable_from(expression_type) {
|
||||
return Err(vec![Diagnostic::new(
|
||||
&format!(
|
||||
|
||||
@ -4,6 +4,7 @@ use crate::diagnostic::Diagnostic;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol::field_symbol::FieldSymbol;
|
||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Field {
|
||||
@ -13,6 +14,7 @@ pub struct Field {
|
||||
is_mut: bool,
|
||||
declared_type: Option<Box<TypeUse>>,
|
||||
initializer: Option<Box<Expression>>,
|
||||
field_symbol: Option<Rc<RefCell<FieldSymbol>>>,
|
||||
}
|
||||
|
||||
impl Field {
|
||||
@ -31,18 +33,32 @@ impl Field {
|
||||
is_mut,
|
||||
declared_type: declared_type.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(
|
||||
&mut self,
|
||||
symbol_table: &mut SymbolTable,
|
||||
field_index: usize,
|
||||
) -> Result<(), Vec<Diagnostic>> {
|
||||
// 1. insert field symbol
|
||||
let to_insert =
|
||||
FieldSymbol::new(&self.declared_name, self.declared_name_source_range.clone());
|
||||
|
||||
symbol_table
|
||||
let field_symbol = symbol_table
|
||||
.insert_field_symbol(to_insert)
|
||||
.map_err(|e| match e {
|
||||
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 {
|
||||
initializer.gather_declared_names(symbol_table)?;
|
||||
}
|
||||
@ -80,4 +110,87 @@ impl Field {
|
||||
}
|
||||
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 {
|
||||
// we don't have a given return type, so it's void
|
||||
self.function_symbol.as_mut().unwrap().borrow_mut().set_return_type_info(
|
||||
TypeInfo::Void
|
||||
);
|
||||
self.function_symbol
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.set_return_type_info(TypeInfo::Void);
|
||||
}
|
||||
|
||||
// statements
|
||||
@ -206,6 +208,7 @@ impl Function {
|
||||
);
|
||||
|
||||
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();
|
||||
|
||||
// statements
|
||||
@ -216,7 +219,11 @@ impl Function {
|
||||
.enumerate()
|
||||
.map(|(i, statement)| {
|
||||
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)
|
||||
.flatten()
|
||||
@ -262,7 +269,11 @@ impl Function {
|
||||
|
||||
let entry_block = builder.get_block(entry_block_id).clone();
|
||||
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(),
|
||||
return_type_info,
|
||||
entry_block,
|
||||
|
||||
@ -2,6 +2,7 @@ pub mod add_expression;
|
||||
pub mod call;
|
||||
pub mod class;
|
||||
pub mod compilation_unit;
|
||||
pub mod constructor;
|
||||
pub mod double_literal;
|
||||
pub mod expression;
|
||||
pub mod expression_statement;
|
||||
|
||||
@ -74,6 +74,9 @@ impl NegativeExpression {
|
||||
.expect("Attempt to negate non-value expression");
|
||||
|
||||
match operand_as_ir {
|
||||
IrExpression::Field(self_variable, _) => {
|
||||
todo!()
|
||||
}
|
||||
IrExpression::Parameter(parameter) => {
|
||||
let destination = Rc::new(RefCell::new(IrVariable::new_vr(
|
||||
builder.new_t_var().into(),
|
||||
|
||||
@ -36,7 +36,7 @@ impl Parameter {
|
||||
let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new(
|
||||
&self.declared_name,
|
||||
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 {
|
||||
Ok(parameter_symbol) => {
|
||||
|
||||
@ -2,10 +2,8 @@ use crate::ast::expression_statement::ExpressionStatement;
|
||||
use crate::ast::ir_builder::IrBuilder;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::symbol::function_symbol::FunctionSymbol;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use crate::type_info::TypeInfo;
|
||||
|
||||
pub enum Statement {
|
||||
Let(LetStatement),
|
||||
@ -37,13 +35,12 @@ impl Statement {
|
||||
pub fn type_check(
|
||||
&mut self,
|
||||
symbol_table: &SymbolTable,
|
||||
is_last: bool,
|
||||
function_symbol: &Rc<RefCell<FunctionSymbol>>,
|
||||
must_return_type_info: Option<&TypeInfo>,
|
||||
) -> Result<(), Vec<Diagnostic>> {
|
||||
match self {
|
||||
Statement::Let(let_statement) => let_statement.type_check(symbol_table),
|
||||
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 {
|
||||
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 {
|
||||
PrimitiveTypeSymbol::Any => TypeInfo::Any,
|
||||
PrimitiveTypeSymbol::Int => TypeInfo::Integer,
|
||||
|
||||
@ -1,20 +1,26 @@
|
||||
#[derive(Debug)]
|
||||
pub struct Diagnostic {
|
||||
message: String,
|
||||
primary_label_message: Option<String>,
|
||||
error_code: Option<usize>,
|
||||
start: usize,
|
||||
end: usize,
|
||||
reporter_file: Option<&'static str>,
|
||||
reporter_line: Option<u32>,
|
||||
secondary_labels: Vec<SecondaryLabel>,
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
pub fn new(message: &str, start: usize, end: usize) -> Self {
|
||||
Self {
|
||||
message: message.into(),
|
||||
primary_label_message: None,
|
||||
error_code: None,
|
||||
start,
|
||||
end,
|
||||
reporter_line: None,
|
||||
reporter_file: None,
|
||||
secondary_labels: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +28,14 @@ impl Diagnostic {
|
||||
&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 {
|
||||
self.start
|
||||
}
|
||||
@ -30,13 +44,65 @@ impl Diagnostic {
|
||||
self.end
|
||||
}
|
||||
|
||||
pub fn with_reporter(&self, file: &'static str, line: u32) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
start: self.start,
|
||||
end: self.end,
|
||||
reporter_file: Some(file),
|
||||
reporter_line: Some(line),
|
||||
pub fn reporter(&self) -> Option<(&'static str, u32)> {
|
||||
if let Some(file) = self.reporter_file {
|
||||
Some((file, self.reporter_line.unwrap()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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::type_info::TypeInfo;
|
||||
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::collections::{HashMap, HashSet};
|
||||
@ -14,6 +15,7 @@ use std::fmt::{Display, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum IrExpression {
|
||||
Field(Rc<RefCell<IrVariable>>, usize),
|
||||
Parameter(Rc<IrParameter>),
|
||||
Variable(Rc<RefCell<IrVariable>>),
|
||||
Int(i32),
|
||||
@ -22,18 +24,11 @@ pub enum 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 {
|
||||
match self {
|
||||
IrExpression::Field(self_variable, _) => {
|
||||
todo!()
|
||||
}
|
||||
IrExpression::Parameter(ir_parameter) => {
|
||||
MoveOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||
}
|
||||
@ -56,6 +51,9 @@ impl IrExpression {
|
||||
|
||||
pub fn push_operand(&self, constants_table: &mut ConstantsTable) -> PushOperand {
|
||||
match self {
|
||||
IrExpression::Field(self_variable, _) => {
|
||||
todo!()
|
||||
}
|
||||
IrExpression::Parameter(ir_parameter) => {
|
||||
PushOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||
}
|
||||
@ -78,6 +76,9 @@ impl IrExpression {
|
||||
|
||||
pub fn add_operand(&self, constants_table: &mut ConstantsTable) -> AddOperand {
|
||||
match self {
|
||||
IrExpression::Field(self_variable, _) => {
|
||||
todo!()
|
||||
}
|
||||
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
|
||||
TypeInfo::Integer | TypeInfo::Double | TypeInfo::String => {
|
||||
AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||
@ -116,6 +117,9 @@ impl IrExpression {
|
||||
|
||||
pub fn subtract_operand(&self) -> SubtractOperand {
|
||||
match self {
|
||||
IrExpression::Field(self_variable, _) => {
|
||||
todo!()
|
||||
}
|
||||
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
|
||||
TypeInfo::Integer | TypeInfo::Double => SubtractOperand::Location(
|
||||
Location::StackFrameOffset(ir_parameter.stack_offset()),
|
||||
@ -151,6 +155,9 @@ impl IrExpression {
|
||||
|
||||
pub fn multiply_operand(&self) -> MultiplyOperand {
|
||||
match self {
|
||||
IrExpression::Field(self_variable, _) => {
|
||||
todo!()
|
||||
}
|
||||
IrExpression::Parameter(ir_parameter) => {
|
||||
MultiplyOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||
}
|
||||
@ -172,6 +179,9 @@ impl IrExpression {
|
||||
|
||||
pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand {
|
||||
match self {
|
||||
IrExpression::Field(self_variable, _) => {
|
||||
todo!()
|
||||
}
|
||||
IrExpression::Parameter(ir_parameter) => {
|
||||
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 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IrExpression::Field(self_variable, field_index) => {
|
||||
write!(f, "{}.{}", self_variable.borrow(), field_index)
|
||||
}
|
||||
IrExpression::Parameter(ir_parameter) => {
|
||||
write!(f, "{}", ir_parameter)
|
||||
}
|
||||
@ -219,11 +257,21 @@ impl Display for IrExpression {
|
||||
|
||||
impl VrUser for IrExpression {
|
||||
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||
// no defs for an expression
|
||||
HashSet::new()
|
||||
}
|
||||
|
||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||
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::Variable(ir_variable) => {
|
||||
if let IrVariableDescriptor::VirtualRegister(vr_variable) =
|
||||
@ -242,6 +290,9 @@ impl VrUser for IrExpression {
|
||||
|
||||
fn propagate_spills(&mut self, spills: &HashSet<IrVrVariableDescriptor>) {
|
||||
match self {
|
||||
IrExpression::Field(self_variable, field_index) => {
|
||||
// no-op, no defs
|
||||
}
|
||||
IrExpression::Parameter(_) => {
|
||||
// no-op
|
||||
}
|
||||
@ -278,6 +329,9 @@ impl VrUser for IrExpression {
|
||||
assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
||||
) {
|
||||
match self {
|
||||
IrExpression::Field(self_variable, field_index) => {
|
||||
// no-op
|
||||
}
|
||||
IrExpression::Parameter(_) => {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@ -4,8 +4,6 @@ use crate::ir::ir_block::IrBlock;
|
||||
use crate::ir::ir_parameter::IrParameter;
|
||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||
use crate::ir::register_allocation::HasVrUsers;
|
||||
use crate::symbol::Symbol;
|
||||
use crate::symbol::function_symbol::FunctionSymbol;
|
||||
use crate::type_info::TypeInfo;
|
||||
use dvm_lib::vm::function::Function;
|
||||
use std::cell::RefCell;
|
||||
@ -14,7 +12,7 @@ use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct IrFunction {
|
||||
function_symbol: Rc<RefCell<FunctionSymbol>>,
|
||||
fqn: Rc<str>,
|
||||
parameters: Vec<Rc<IrParameter>>,
|
||||
return_type_info: TypeInfo,
|
||||
entry: Rc<RefCell<IrBlock>>,
|
||||
@ -22,13 +20,13 @@ pub struct IrFunction {
|
||||
|
||||
impl IrFunction {
|
||||
pub fn new(
|
||||
function_symbol: Rc<RefCell<FunctionSymbol>>,
|
||||
fqn: Rc<str>,
|
||||
parameters: &[Rc<IrParameter>],
|
||||
return_type_info: &TypeInfo,
|
||||
entry: Rc<RefCell<IrBlock>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
function_symbol,
|
||||
fqn,
|
||||
parameters: parameters.to_vec(),
|
||||
return_type_info: return_type_info.clone(),
|
||||
entry,
|
||||
@ -47,7 +45,7 @@ impl IrFunction {
|
||||
self.entry.borrow().assemble(&mut builder, constants_table);
|
||||
let instructions = builder.take_instructions();
|
||||
Function::new(
|
||||
self.function_symbol.borrow().declared_name_owned(),
|
||||
self.fqn.clone(),
|
||||
self.parameters.len(),
|
||||
stack_size,
|
||||
instructions,
|
||||
@ -57,7 +55,7 @@ impl IrFunction {
|
||||
|
||||
impl Display for IrFunction {
|
||||
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() {
|
||||
write!(f, "{}: {}", parameter, parameter.type_info())?;
|
||||
if i < self.parameters.len() - 1 {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::ir::ir_add::IrAdd;
|
||||
use crate::ir::ir_allocate::IrAllocate;
|
||||
use crate::ir::ir_call::IrCall;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_multiply::IrMultiply;
|
||||
@ -14,6 +15,7 @@ pub enum IrOperation {
|
||||
Subtract(IrSubtract),
|
||||
Multiply(IrMultiply),
|
||||
Call(IrCall),
|
||||
Allocate(IrAllocate),
|
||||
}
|
||||
|
||||
impl Display for IrOperation {
|
||||
@ -34,6 +36,9 @@ impl Display for IrOperation {
|
||||
IrOperation::Call(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::Multiply(ir_multiply) => ir_multiply.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::Multiply(ir_multiply) => ir_multiply.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) => {
|
||||
ir_call.propagate_spills(spills);
|
||||
}
|
||||
IrOperation::Allocate(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,6 +107,7 @@ impl VrUser for IrOperation {
|
||||
IrOperation::Call(ir_call) => {
|
||||
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_call::IrCall;
|
||||
use crate::ir::ir_return::IrReturn;
|
||||
use crate::ir::ir_set_field::IrSetField;
|
||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@ -12,6 +13,7 @@ pub enum IrStatement {
|
||||
Assign(IrAssign),
|
||||
Call(IrCall),
|
||||
Return(IrReturn),
|
||||
SetField(IrSetField),
|
||||
}
|
||||
|
||||
impl VrUser for IrStatement {
|
||||
@ -20,6 +22,7 @@ impl VrUser for IrStatement {
|
||||
IrStatement::Assign(ir_assign) => ir_assign.vr_definitions(),
|
||||
IrStatement::Call(ir_call) => ir_call.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::Call(ir_call) => ir_call.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) => {
|
||||
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) => {
|
||||
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) => {
|
||||
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) => {
|
||||
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) => {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
@ -95,6 +102,10 @@ impl IrVrVariableDescriptor {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn name_owned(&self) -> Rc<str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn block_id(&self) -> usize {
|
||||
self.block_id
|
||||
}
|
||||
@ -139,6 +150,10 @@ impl IrStackVariableDescriptor {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn name_owned(&self) -> Rc<str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub fn set_offset(&mut self, offset: isize) {
|
||||
self.offset = Some(offset);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
mod assemble;
|
||||
pub mod ir_add;
|
||||
pub mod ir_allocate;
|
||||
pub mod ir_assign;
|
||||
pub mod ir_block;
|
||||
pub mod ir_call;
|
||||
@ -9,7 +10,9 @@ pub mod ir_multiply;
|
||||
pub mod ir_operation;
|
||||
pub mod ir_parameter;
|
||||
pub mod ir_return;
|
||||
pub mod ir_set_field;
|
||||
pub mod ir_statement;
|
||||
pub mod ir_subtract;
|
||||
pub mod ir_variable;
|
||||
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,
|
||||
"pub" => TokenKind::Public,
|
||||
"mut" => TokenKind::Mut,
|
||||
"ctor" => TokenKind::Ctor,
|
||||
_ => TokenKind::Identifier,
|
||||
};
|
||||
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::class::Class;
|
||||
use crate::ast::compilation_unit::CompilationUnit;
|
||||
use crate::ast::constructor::Constructor;
|
||||
use crate::ast::double_literal::DoubleLiteral;
|
||||
use crate::ast::expression::Expression;
|
||||
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> {
|
||||
input: &'a str,
|
||||
lexer: Lexer<'a>,
|
||||
@ -382,12 +389,17 @@ impl<'a> Parser<'a> {
|
||||
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
||||
let mut fields = vec![];
|
||||
let mut functions = vec![];
|
||||
let mut maybe_constructor: Option<Constructor> = None;
|
||||
|
||||
let mut diagnostics = vec![];
|
||||
|
||||
while self.current.is_some() && !self.peek_current(TokenKind::End) {
|
||||
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(_) => {}
|
||||
Err(mut member_diagnostics) => diagnostics.append(&mut member_diagnostics),
|
||||
},
|
||||
@ -399,6 +411,12 @@ impl<'a> Parser<'a> {
|
||||
Ok(function) => functions.push(function),
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
@ -409,6 +427,7 @@ impl<'a> Parser<'a> {
|
||||
Ok(Class::new(
|
||||
self.token_text(&identifier_token),
|
||||
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||
maybe_constructor,
|
||||
fields,
|
||||
functions,
|
||||
))
|
||||
@ -469,26 +488,29 @@ impl<'a> Parser<'a> {
|
||||
&mut self,
|
||||
fields: &mut Vec<Field>,
|
||||
functions: &mut Vec<Function>,
|
||||
maybe_ctor: &mut Option<Constructor>,
|
||||
) -> Result<(), Vec<Diagnostic>> {
|
||||
if self.lookahead.is_some() {
|
||||
if matches!(
|
||||
self.lookahead.as_ref().unwrap().kind(),
|
||||
TokenKind::Mut | TokenKind::Identifier
|
||||
) {
|
||||
fields.push(self.field()?);
|
||||
} else if matches!(self.lookahead.as_ref().unwrap().kind(), TokenKind::Fn) {
|
||||
functions.push(self.function()?);
|
||||
} else {
|
||||
let lookahead = self.lookahead.as_ref().unwrap();
|
||||
return Err(vec![Diagnostic::new(
|
||||
&format!(
|
||||
"Expected any of {:?}; found {:?}",
|
||||
[TokenKind::Mut, TokenKind::Identifier, TokenKind::Fn],
|
||||
lookahead.kind()
|
||||
),
|
||||
lookahead.start(),
|
||||
lookahead.end(),
|
||||
)]);
|
||||
match self.lookahead.as_ref().unwrap().kind() {
|
||||
TokenKind::Mut | TokenKind::Identifier => {
|
||||
fields.push(self.field()?);
|
||||
}
|
||||
TokenKind::Fn => functions.push(self.function()?),
|
||||
TokenKind::Ctor => {
|
||||
maybe_ctor.replace(self.constructor()?);
|
||||
}
|
||||
_ => {
|
||||
let lookahead = self.lookahead.as_ref().unwrap();
|
||||
return Err(vec![Diagnostic::new(
|
||||
&format!(
|
||||
"Expected any of {:?}; found {:?}",
|
||||
[TokenKind::Mut, TokenKind::Identifier, TokenKind::Fn],
|
||||
lookahead.kind()
|
||||
),
|
||||
lookahead.start(),
|
||||
lookahead.end(),
|
||||
)]);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} 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>> {
|
||||
let is_public = if self.current.is_some() && self.peek_current(TokenKind::Public) {
|
||||
self.advance();
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::symbol::class_symbol::ClassSymbol;
|
||||
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||
use crate::symbol::field_symbol::FieldSymbol;
|
||||
use crate::symbol::function_symbol::FunctionSymbol;
|
||||
use std::cell::RefCell;
|
||||
@ -11,6 +12,7 @@ pub struct ClassScope {
|
||||
class_symbols: HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>,
|
||||
field_symbols: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>,
|
||||
function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
|
||||
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
|
||||
}
|
||||
|
||||
impl ClassScope {
|
||||
@ -21,6 +23,7 @@ impl ClassScope {
|
||||
class_symbols: HashMap::new(),
|
||||
field_symbols: HashMap::new(),
|
||||
function_symbols: HashMap::new(),
|
||||
constructor_symbol: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +51,14 @@ impl ClassScope {
|
||||
&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 {
|
||||
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::symbol::Symbol;
|
||||
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||
use crate::symbol::field_symbol::FieldSymbol;
|
||||
use crate::symbol::function_symbol::FunctionSymbol;
|
||||
use std::cell::RefCell;
|
||||
@ -10,6 +11,7 @@ pub struct ClassSymbol {
|
||||
declared_name: Rc<str>,
|
||||
declared_name_source_range: SourceRange,
|
||||
is_extern: bool,
|
||||
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
|
||||
fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>,
|
||||
functions: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
|
||||
}
|
||||
@ -24,10 +26,27 @@ impl ClassSymbol {
|
||||
declared_name: declared_name.clone(),
|
||||
declared_name_source_range,
|
||||
is_extern,
|
||||
constructor_symbol: None,
|
||||
fields: 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 {
|
||||
|
||||
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 {
|
||||
pub fn type_info(&self) -> TypeInfo {
|
||||
match self {
|
||||
ExpressibleSymbol::Class(class_symbol) => {
|
||||
todo!()
|
||||
}
|
||||
ExpressibleSymbol::Field(field_symbol) => {
|
||||
todo!()
|
||||
}
|
||||
ExpressibleSymbol::Class(class_symbol) => TypeInfo::ClassInstance(class_symbol.clone()),
|
||||
ExpressibleSymbol::Field(field_symbol) => field_symbol.borrow().type_info().clone(),
|
||||
ExpressibleSymbol::Function(function_symbol) => {
|
||||
TypeInfo::Function(function_symbol.clone()) // n.b. not the return type!
|
||||
}
|
||||
@ -43,6 +39,8 @@ impl ExpressibleSymbol {
|
||||
todo!()
|
||||
}
|
||||
ExpressibleSymbol::Field(field_symbol) => {
|
||||
// this, unfortunately, is going to need more context, because fields live longer
|
||||
// than function calls
|
||||
todo!()
|
||||
}
|
||||
ExpressibleSymbol::Function(_) => {
|
||||
|
||||
@ -7,6 +7,7 @@ pub struct FieldSymbol {
|
||||
declared_name: Rc<str>,
|
||||
declared_name_source_range: SourceRange,
|
||||
type_info: Option<TypeInfo>,
|
||||
field_index: Option<usize>,
|
||||
}
|
||||
|
||||
impl FieldSymbol {
|
||||
@ -15,8 +16,25 @@ impl FieldSymbol {
|
||||
declared_name: declared_name.clone(),
|
||||
declared_name_source_range,
|
||||
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 {
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
use crate::source_range::SourceRange;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub mod callable_symbol;
|
||||
pub mod class_symbol;
|
||||
pub mod constructor_symbol;
|
||||
pub mod expressible_symbol;
|
||||
pub mod field_symbol;
|
||||
pub mod function_symbol;
|
||||
|
||||
@ -7,6 +7,7 @@ use crate::scope::function_scope::FunctionScope;
|
||||
use crate::scope::module_scope::ModuleScope;
|
||||
use crate::symbol::Symbol;
|
||||
use crate::symbol::class_symbol::ClassSymbol;
|
||||
use crate::symbol::constructor_symbol::ConstructorSymbol;
|
||||
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
||||
use crate::symbol::field_symbol::FieldSymbol;
|
||||
use crate::symbol::function_symbol::FunctionSymbol;
|
||||
@ -123,6 +124,32 @@ impl SymbolTable {
|
||||
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(
|
||||
&mut self,
|
||||
field_symbol: FieldSymbol,
|
||||
|
||||
@ -47,4 +47,5 @@ pub enum TokenKind {
|
||||
SelfKw,
|
||||
Public,
|
||||
Mut,
|
||||
Ctor,
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ pub enum TypeInfo {
|
||||
Double,
|
||||
String,
|
||||
Function(Rc<RefCell<FunctionSymbol>>),
|
||||
Class(Rc<RefCell<ClassSymbol>>),
|
||||
ClassInstance(Rc<RefCell<ClassSymbol>>),
|
||||
Void,
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ impl Display for TypeInfo {
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
TypeInfo::Class(class_symbol) => {
|
||||
TypeInfo::ClassInstance(class_symbol) => {
|
||||
write!(f, "{}", class_symbol.borrow().declared_name())
|
||||
}
|
||||
TypeInfo::Void => write!(f, "Void"),
|
||||
@ -67,9 +67,9 @@ impl TypeInfo {
|
||||
TypeInfo::Function(_) => {
|
||||
unimplemented!("Type matching on Functions not yet supported.")
|
||||
}
|
||||
TypeInfo::Class(class_symbol) => {
|
||||
TypeInfo::ClassInstance(class_symbol) => {
|
||||
match other {
|
||||
TypeInfo::Class(other_class_symbol) => {
|
||||
TypeInfo::ClassInstance(other_class_symbol) => {
|
||||
class_symbol.borrow().declared_name()
|
||||
== 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 ArgCount = usize;
|
||||
pub type CallerPopCount = usize;
|
||||
pub type ClassFqn = Rc<str>;
|
||||
pub type FieldIndex = usize;
|
||||
|
||||
pub enum Instruction {
|
||||
Move(MoveOperand, Location),
|
||||
@ -15,6 +17,10 @@ pub enum Instruction {
|
||||
InvokeStatic(FunctionName, ArgCount),
|
||||
InvokePlatformStatic(FunctionName, ArgCount),
|
||||
|
||||
Allocate(ClassFqn, Location),
|
||||
GetField(Location, FieldIndex, Location),
|
||||
SetField(Location, FieldIndex, SetFieldOperand),
|
||||
|
||||
Add(AddOperand, AddOperand, Location),
|
||||
Subtract(SubtractOperand, SubtractOperand, Location),
|
||||
Multiply(MultiplyOperand, MultiplyOperand, Location),
|
||||
@ -64,6 +70,15 @@ impl Display for Instruction {
|
||||
Instruction::Return => {
|
||||
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();
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
Instruction::Add(left_operand, right_operand, destination) => {
|
||||
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