diff --git a/dm-std-lib/src/lib.rs b/dm-std-lib/src/lib.rs index 3126eaa..d53184e 100644 --- a/dm-std-lib/src/lib.rs +++ b/dm-std-lib/src/lib.rs @@ -41,6 +41,9 @@ pub fn std_core_println(args: &[Value]) -> Result> { None => Err(Box::new(StdCoreError::new("Missing to_print arg"))), Some(to_print) => { match to_print { + Value::Object(o) => { + println!("{}", o.borrow()); + } Value::Int(i) => { println!("{}", i); } diff --git a/dm/src/run.rs b/dm/src/run.rs index 50d6a5c..98d80e1 100644 --- a/dm/src/run.rs +++ b/dm/src/run.rs @@ -12,6 +12,7 @@ use dvm_lib::vm::function::Function; use dvm_lib::vm::operand::Operand; use dvm_lib::vm::{CallStack, DvmContext, call}; use std::path::PathBuf; +use std::rc::Rc; pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usize) { let input = std::fs::read_to_string(script).unwrap(); @@ -51,7 +52,7 @@ pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usiz } } - let mut ir_functions = compilation_unit.to_ir(&symbol_table); + let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table); if show_ir { for ir_function in &ir_functions { @@ -68,6 +69,11 @@ pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usiz functions.push(function); } + let classes = ir_classes + .iter() + .map(|ir_class| ir_class.to_vm_class()) + .collect::>(); + if show_asm { for function in &functions { println!("{}", function); @@ -85,6 +91,12 @@ pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usiz .insert(function.name_owned(), function); } + for class in classes { + dvm_context + .classes_mut() + .insert(class.fqn().into(), Rc::new(class)); + } + for (name, content) in &constants_table.string_constants() { dvm_context.constants_mut().insert( name.clone(), diff --git a/dmc-lib/src/ast/class.rs b/dmc-lib/src/ast/class.rs index b5927e4..8ff217e 100644 --- a/dmc-lib/src/ast/class.rs +++ b/dmc-lib/src/ast/class.rs @@ -1,10 +1,13 @@ use crate::ast::constructor::Constructor; use crate::ast::field::Field; use crate::ast::fqn_context::FqnContext; +use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::function::Function; use crate::diagnostic::{Diagnostic, SecondaryLabel}; +use crate::ir::ir_class::{IrClass, IrField}; use crate::ir::ir_function::IrFunction; use crate::source_range::SourceRange; +use crate::symbol::Symbol; use crate::symbol::class_symbol::ClassSymbol; use crate::symbol_table::{SymbolInsertError, SymbolTable}; use std::cell::RefCell; @@ -43,16 +46,17 @@ impl Class { symbol_table: &mut SymbolTable, fqn_context: &mut FqnContext, ) -> Result<(), Vec> { - // 0. Push class name on fqn - fqn_context.push(self.declared_name.clone()); - // 1. insert class symbol let to_insert = ClassSymbol::new( &self.declared_name, self.declared_name_source_range.clone(), + fqn_context.resolve(&self.declared_name), false, ); + // 1a. Push class name on fqn + fqn_context.push(self.declared_name.clone()); + let class_symbol = symbol_table .insert_class_symbol(to_insert) .map_err(|e| match e { @@ -260,7 +264,7 @@ impl Class { } } - pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec { + pub fn to_ir(&self, symbol_table: &SymbolTable) -> (IrClass, Vec) { let mut ir_functions: Vec = vec![]; if let Some(constructor) = &self.constructor { ir_functions.push(constructor.to_ir( @@ -277,77 +281,23 @@ impl Class { )); } - ir_functions - } -} + let class_symbol = self.class_symbol.as_ref().unwrap().borrow(); -#[cfg(test)] -mod tests { - use super::*; - use crate::constants_table::ConstantsTable; - use crate::parser::parse_compilation_unit; - - #[test] - fn complete_example_no_diagnostics() { - let parse_result = parse_compilation_unit( - " - 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 - ", + let ir_class = IrClass::new( + class_symbol.declared_name_owned(), + fqn_parts_to_string(class_symbol.fqn_parts()).into(), + self.fields + .iter() + .map(|field| { + IrField::new( + field.declared_name().into(), + field.field_symbol().borrow().field_index(), + field.field_symbol().borrow().type_info().clone(), + ) + }) + .collect(), ); - let mut compilation_unit = match parse_result { - Ok(compilation_unit) => compilation_unit, - Err(diagnostics) => { - panic!("{:?}", diagnostics); - } - }; - - let mut symbol_table = SymbolTable::new(); - match compilation_unit.gather_declared_names(&mut symbol_table) { - Ok(_) => {} - Err(diagnostics) => { - panic!("{:?}", diagnostics); - } - } - - match compilation_unit.check_name_usages(&symbol_table) { - Ok(_) => {} - Err(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); - for ir_function in &mut ir_functions { - println!("{}", ir_function); - } - 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) - } + (ir_class, ir_functions) } } diff --git a/dmc-lib/src/ast/compilation_unit.rs b/dmc-lib/src/ast/compilation_unit.rs index 0d73d6d..1bf30ea 100644 --- a/dmc-lib/src/ast/compilation_unit.rs +++ b/dmc-lib/src/ast/compilation_unit.rs @@ -3,6 +3,7 @@ use crate::ast::extern_function::ExternFunction; use crate::ast::fqn_context::FqnContext; use crate::ast::function::Function; use crate::diagnostic::Diagnostic; +use crate::ir::ir_class::IrClass; use crate::ir::ir_function::IrFunction; use crate::symbol_table::SymbolTable; @@ -138,16 +139,21 @@ impl CompilationUnit { } } - pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec { + pub fn to_ir(&self, symbol_table: &SymbolTable) -> (Vec, Vec) { let mut functions: Vec = vec![]; + let mut classes: Vec = vec![]; + self.functions .iter() .map(|f| f.to_ir(symbol_table, None)) .for_each(|f| functions.push(f)); - self.classes - .iter() - .flat_map(|c| c.to_ir(symbol_table)) - .for_each(|f| functions.push(f)); - functions + + for class in &self.classes { + let (class, mut class_functions) = class.to_ir(symbol_table); + functions.append(&mut class_functions); + classes.push(class); + } + + (classes, functions) } } diff --git a/dmc-lib/src/ast/mod.rs b/dmc-lib/src/ast/mod.rs index 8d52256..7b417e8 100644 --- a/dmc-lib/src/ast/mod.rs +++ b/dmc-lib/src/ast/mod.rs @@ -10,7 +10,7 @@ pub mod extern_function; pub mod field; pub mod fqn; pub mod fqn_context; -mod fqn_util; +pub mod fqn_util; pub mod function; pub mod identifier; pub mod integer_literal; diff --git a/dmc-lib/src/ir/ir_class.rs b/dmc-lib/src/ir/ir_class.rs new file mode 100644 index 0000000..3436f02 --- /dev/null +++ b/dmc-lib/src/ir/ir_class.rs @@ -0,0 +1,61 @@ +use crate::ast::fqn_util::fqn_parts_to_string; +use crate::type_info::TypeInfo; +use dvm_lib::vm::class::{Class, Field}; +use dvm_lib::vm::type_info::TypeInfo as VmTypeInfo; +use std::rc::Rc; + +pub struct IrClass { + declared_name: Rc, + fqn: Rc, + fields: Vec, +} + +impl IrClass { + pub fn new(declared_name: Rc, fqn: Rc, fields: Vec) -> Self { + Self { + declared_name, + fqn, + fields, + } + } + + pub fn to_vm_class(&self) -> Class { + Class::new( + self.declared_name.clone(), + self.fqn.clone(), + self.fields.iter().map(IrField::to_vm_field).collect(), + ) + } +} + +pub struct IrField { + debug_name: Rc, + field_index: usize, + type_info: TypeInfo, +} + +impl IrField { + pub fn new(debug_name: Rc, field_index: usize, type_info: TypeInfo) -> Self { + Self { + debug_name, + field_index, + type_info, + } + } + + pub fn to_vm_field(&self) -> Field { + Field::new( + self.debug_name.clone(), + self.field_index, + match &self.type_info { + TypeInfo::Integer => VmTypeInfo::Int, + TypeInfo::Double => VmTypeInfo::Double, + TypeInfo::String => VmTypeInfo::String, + TypeInfo::ClassInstance(class_symbol) => VmTypeInfo::ClassInstance( + fqn_parts_to_string(class_symbol.borrow().fqn_parts()).into(), + ), + _ => panic!(), + }, + ) + } +} diff --git a/dmc-lib/src/ir/mod.rs b/dmc-lib/src/ir/mod.rs index 726bdaf..1302c43 100644 --- a/dmc-lib/src/ir/mod.rs +++ b/dmc-lib/src/ir/mod.rs @@ -4,6 +4,7 @@ pub mod ir_allocate; pub mod ir_assign; pub mod ir_block; pub mod ir_call; +pub mod ir_class; pub mod ir_expression; pub mod ir_function; pub mod ir_get_field_ref; diff --git a/dmc-lib/src/symbol/class_symbol.rs b/dmc-lib/src/symbol/class_symbol.rs index 1f5a7f8..657e076 100644 --- a/dmc-lib/src/symbol/class_symbol.rs +++ b/dmc-lib/src/symbol/class_symbol.rs @@ -10,6 +10,7 @@ use std::rc::Rc; pub struct ClassSymbol { declared_name: Rc, declared_name_source_range: SourceRange, + fqn_parts: Vec>, is_extern: bool, constructor_symbol: Option>>, fields: HashMap, Rc>>, @@ -20,11 +21,13 @@ impl ClassSymbol { pub fn new( declared_name: &Rc, declared_name_source_range: SourceRange, + fqn_parts: Vec>, is_extern: bool, ) -> Self { Self { declared_name: declared_name.clone(), declared_name_source_range, + fqn_parts, is_extern, constructor_symbol: None, fields: HashMap::new(), @@ -32,6 +35,10 @@ impl ClassSymbol { } } + pub fn fqn_parts(&self) -> &[Rc] { + &self.fqn_parts + } + pub fn set_constructor_symbol( &mut self, constructor_symbol: Option>>, diff --git a/dvm-lib/src/vm/class.rs b/dvm-lib/src/vm/class.rs new file mode 100644 index 0000000..f88c7e2 --- /dev/null +++ b/dvm-lib/src/vm/class.rs @@ -0,0 +1,57 @@ +use crate::vm::type_info::TypeInfo; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; + +pub struct Class { + declared_name: Rc, + fqn: Rc, + fields: Vec, +} + +impl Class { + pub fn new(declared_name: Rc, fqn: Rc, fields: Vec) -> Self { + Self { + declared_name, + fqn, + fields, + } + } + + pub fn fqn(&self) -> &str { + &self.fqn + } + + pub fn fields(&self) -> &[Field] { + &self.fields + } +} + +impl Debug for Class { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.fqn) + } +} + +impl PartialEq for Class { + fn eq(&self, other: &Self) -> bool { + self.fqn == other.fqn + } +} + +impl Eq for Class {} + +pub struct Field { + debug_name: Rc, + field_index: usize, + type_info: TypeInfo, +} + +impl Field { + pub fn new(debug_name: Rc, field_index: usize, type_info: TypeInfo) -> Self { + Self { + debug_name, + field_index, + type_info, + } + } +} diff --git a/dvm-lib/src/vm/mod.rs b/dvm-lib/src/vm/mod.rs index 261db3f..0bff5ae 100644 --- a/dvm-lib/src/vm/mod.rs +++ b/dvm-lib/src/vm/mod.rs @@ -1,7 +1,9 @@ use crate::instruction::Instruction; use crate::platform_function::PlatformFunction; +use crate::vm::class::Class; use crate::vm::constant::Constant; use crate::vm::function::Function; +use crate::vm::object::get_object; use crate::vm::operand::Operand; use crate::vm::operand_helpers::{ add_operand_to_value, move_operand_to_value, multiply_operand_to_value, push_operand_to_value, @@ -9,19 +11,23 @@ use crate::vm::operand_helpers::{ }; use crate::vm::util::*; use crate::vm::value::Value; +use std::cell::RefCell; use std::collections::HashMap; use std::ptr; use std::rc::Rc; +pub mod class; pub mod constant; pub mod function; pub mod object; pub mod operand; mod operand_helpers; +pub mod type_info; mod util; pub mod value; pub struct DvmContext { + classes: HashMap, Rc>, functions: HashMap, Function>, platform_functions: HashMap, PlatformFunction>, constants: HashMap, Constant>, @@ -30,12 +36,21 @@ pub struct DvmContext { impl DvmContext { pub fn new() -> Self { Self { + classes: HashMap::new(), functions: HashMap::new(), platform_functions: HashMap::new(), constants: HashMap::new(), } } + pub fn classes(&self) -> &HashMap, Rc> { + &self.classes + } + + pub fn classes_mut(&mut self) -> &mut HashMap, Rc> { + &mut self.classes + } + pub fn functions(&self) -> &HashMap, Function> { &self.functions } @@ -320,11 +335,15 @@ pub fn call<'a>( /* Object instructions */ Instruction::Allocate(class_fqn, destination) => { - // let class = context.classes().get(class_fqn).expect("No such class loaded: {}") - // let object: Gc> = Object::new(class, heap) - // let value = Value::Object(object) - // put_value(registers, call_stack.top_mut(), destination, result) - todo!() + let class = context + .classes() + .get(class_fqn) + .expect(&format!("No such class loaded: {}", class_fqn)); + let object = get_object(class); + let as_rc = Rc::new(RefCell::new(object)); + let value = Value::Object(as_rc); + put_value(registers, call_stack.top_mut(), destination, value); + call_stack.top_mut().increment_ip(); } Instruction::GetFieldPointer(self_location, field_index, destination) => { diff --git a/dvm-lib/src/vm/object.rs b/dvm-lib/src/vm/object.rs index 482bbfe..b3faa24 100644 --- a/dvm-lib/src/vm/object.rs +++ b/dvm-lib/src/vm/object.rs @@ -1,6 +1,10 @@ +use crate::vm::class::Class; use crate::vm::value::Value; use std::alloc::{Layout, alloc}; +use std::fmt::{Display, Formatter}; +use std::rc::Rc; +#[derive(Debug)] #[repr(C)] pub struct Object { header: ObjectHeader, @@ -8,6 +12,10 @@ pub struct Object { } impl Object { + pub fn header(&self) -> &ObjectHeader { + &self.header + } + pub fn fields(&self) -> &[Value] { &self.fields } @@ -17,22 +25,45 @@ impl Object { } } -pub struct ObjectHeader { - field_count: usize, +impl PartialEq for Object { + fn eq(&self, other: &Self) -> bool { + todo!("eq on Object not yet supported") + } } -fn get_object(field_count: usize) -> Box { +impl Display for Object { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let self_class = &self.header.self_class; + write!(f, "{}@{:p}", self_class.fqn(), &self) + } +} + +#[derive(Debug)] +pub struct ObjectHeader { + self_class: Rc, +} + +impl ObjectHeader { + pub fn self_class(&self) -> &Rc { + &self.self_class + } +} + +// this will be more tied to the gc later +pub fn get_object(class: &Rc) -> Box { + let field_count = class.fields().len(); + let (layout, fields_base) = Layout::array::(field_count) .and_then(|fields_array_layout| Layout::new::().extend(fields_array_layout)) .unwrap(); - println!("{:?}", layout); let ptr = unsafe { alloc(layout) }; if ptr.is_null() { panic!("failed to allocate memory"); } unsafe { - ptr.cast::() - .write(ObjectHeader { field_count }); + ptr.cast::().write(ObjectHeader { + self_class: class.clone(), + }); let fields_ptr = ptr.add(fields_base).cast::(); // initialize each field to Value::Null for i in 0..field_count { @@ -44,17 +75,3 @@ fn get_object(field_count: usize) -> Box { Box::from_raw(std::ptr::slice_from_raw_parts(ptr as *mut Value, field_count) as *mut Object) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn melt_the_processor() { - let mut o = get_object(3); - assert_eq!(o.header.field_count, 3); - assert_eq!(o.fields.len(), 3); - o.fields.fill(Value::Int(42)); - assert!(o.fields.iter().all(|v| *v == Value::Int(42))); - } -} diff --git a/dvm-lib/src/vm/type_info.rs b/dvm-lib/src/vm/type_info.rs new file mode 100644 index 0000000..41de60c --- /dev/null +++ b/dvm-lib/src/vm/type_info.rs @@ -0,0 +1,41 @@ +use crate::vm::class::Class; +use crate::vm::value::Value; +use std::collections::HashMap; +use std::rc::Rc; + +pub enum TypeInfo { + ClassInstance(Rc), + Int, + Double, + String, +} + +impl TypeInfo { + pub fn is_instance_of(&self, value: &Value, classes: &HashMap, Rc>) -> bool { + match self { + TypeInfo::ClassInstance(class_fqn) => match value { + Value::Object(o) => { + let o_ref = o.borrow(); + let o_self_class = o_ref.header().self_class(); + + let check_class = classes.get(class_fqn); + if check_class.is_none() { + panic!("No such class {}", class_fqn); + } + + *check_class.unwrap() == *o_self_class + } + _ => false, + }, + TypeInfo::Int => { + matches!(value, Value::Int(_)) + } + TypeInfo::Double => { + matches!(value, Value::Double(_)) + } + TypeInfo::String => { + matches!(value, Value::String(_)) + } + } + } +} diff --git a/dvm-lib/src/vm/value.rs b/dvm-lib/src/vm/value.rs index ecb68fe..c4c16cd 100644 --- a/dvm-lib/src/vm/value.rs +++ b/dvm-lib/src/vm/value.rs @@ -1,10 +1,11 @@ use crate::vm::object::Object; use std::cell::RefCell; -use std::fmt::{Display, Formatter}; +use std::fmt::{Debug, Display, Formatter}; use std::rc::Rc; #[derive(Clone, Debug, PartialEq)] pub enum Value { + Object(Rc>>), Int(i32), Double(f64), String(Rc), @@ -39,18 +40,27 @@ impl Value { } } - pub fn unwrap_object(&self) -> &Rc> { - todo!() + pub fn unwrap_object(&self) -> &Rc>> { + match self { + Value::Object(o) => o, + _ => panic!("Attempt to unwrap Object; found {:?}", self), + } } - pub fn unwrap_object_mut(&mut self) -> &mut Rc> { - todo!() + pub fn unwrap_object_mut(&mut self) -> &mut Rc>> { + match self { + Value::Object(o) => o, + _ => panic!("Attempt to unwrap Object; found {:?}", self), + } } } impl Display for Value { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { + Value::Object(o) => { + write!(f, "{}", o.borrow()) + } Value::Int(i) => { write!(f, "{}", i) } diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index 3e2a765..7c0c060 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -4,6 +4,7 @@ mod e2e_tests { use dmc_lib::diagnostic::Diagnostic; use dmc_lib::parser::parse_compilation_unit; use dmc_lib::symbol_table::SymbolTable; + use dvm_lib::vm::class::Class; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; use dvm_lib::vm::operand::Operand; @@ -57,7 +58,7 @@ mod e2e_tests { } } - let mut ir_functions = compilation_unit.to_ir(&symbol_table); + let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table); let mut functions: Vec = vec![]; let mut constants_table = ConstantsTable::new(); @@ -68,8 +69,19 @@ mod e2e_tests { functions.push(function); } + let mut classes: Vec = vec![]; + for ir_class in &ir_classes { + classes.push(ir_class.to_vm_class()); + } + let mut dvm_context = DvmContext::new(); + for class in classes { + dvm_context + .classes_mut() + .insert(class.fqn().into(), Rc::new(class)); + } + for function in functions { dvm_context .functions_mut() @@ -201,6 +213,41 @@ mod e2e_tests { Value::Double(1.0), ) } + + #[test] + fn two_classes() { + let context = prepare_context( + " + 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 foo(n: Int) -> Foo + Foo(n) + end + ", + ); + let result = get_result(&context, "foo", &[Value::Int(42)]); + assert!(result.is_some()); + let value = result.unwrap(); + assert!(matches!(value, Value::Object(_))); + let o = value.unwrap_object().borrow(); + assert_eq!(o.fields().len(), 1); + assert_eq!(o.fields()[0].unwrap_int(), 42); + } } #[cfg(test)]