#[cfg(test)] mod e2e_tests { use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::Diagnostic; use dmc_lib::intrinsics::{insert_intrinsic_symbols, insert_intrinsic_types}; use dmc_lib::parser::parse_compilation_unit; use dmc_lib::symbol_table::SymbolTable; use dmc_lib::types_table::TypesTable; 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; use dvm_lib::vm::value::Value; use dvm_lib::vm::{CallStack, DvmContext, call}; use std::rc::Rc; const REGISTER_COUNT: usize = 8; fn report_diagnostics(diagnostics: &[Diagnostic]) -> ! { eprintln!( "{}", diagnostics .iter() .map(|d| format!("{:?}", d)) .collect::>() .join("\n") ); panic!("There were diagnostics."); } fn prepare_context(input: &str) -> Result> { let parse_result = parse_compilation_unit(input); let mut compilation_unit = match parse_result { Ok(compilation_unit) => compilation_unit, Err(diagnostics) => { report_diagnostics(&diagnostics); } }; let mut symbol_table = SymbolTable::new(); symbol_table.push_module_scope("global_scope"); insert_intrinsic_symbols(&mut symbol_table); let mut types_table = TypesTable::new(); insert_intrinsic_types(&symbol_table, &mut types_table); compilation_unit.init_scopes(&mut symbol_table); compilation_unit.gather_symbols_into(&mut symbol_table)?; compilation_unit.check_names(&mut symbol_table)?; compilation_unit.gather_types_into(&symbol_table, &mut types_table)?; compilation_unit.type_check(&symbol_table, &mut types_table)?; let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table, &types_table); let mut functions: Vec = vec![]; let mut constants_table = ConstantsTable::new(); for ir_function in &mut ir_functions { let (_, stack_size) = ir_function.assign_registers(REGISTER_COUNT); let function = ir_function.assemble(stack_size, &mut constants_table); 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() .insert(function.name_owned(), function); } for (name, content) in &constants_table.string_constants() { dvm_context.constants_mut().insert( name.clone(), Constant::String(StringConstant::new(name.clone(), content.clone())), ); } Ok(dvm_context) } fn get_result( dvm_context: &DvmContext, function_name: &str, arguments: &[Value], ) -> Option { let mut registers: Vec = vec![Operand::Null; REGISTER_COUNT]; let mut call_stack = CallStack::new(); call( &dvm_context, &mut registers, &mut call_stack, function_name, &arguments, ) } fn assert_result(input: &str, function_name: &str, arguments: &[Value], expected_value: Value) { let context = match prepare_context(input) { Ok(context) => context, Err(diagnostics) => report_diagnostics(&diagnostics), }; match get_result(&context, function_name, arguments) { None => panic!("Call returned no value"), Some(result_value) => { assert_eq!(result_value, expected_value); } } } #[test] fn add_1_2() { assert_result( " fn add(a: Int, b: Int) -> Int a + b end ", "add", &vec![Value::Int(1), Value::Int(2)], Value::Int(3), ); } #[test] fn bunch_of_adding() { assert_result( " fn add(a: Int, b: Int) -> Int a + b end fn greetAndAdd(a: Int, b: Int) -> String \"Hello. \" + a + \" plus \" + b + \" is \" + add(a, b) end ", "greetAndAdd", &vec![Value::Int(1), Value::Int(2)], Value::String(Rc::from("Hello. 1 plus 2 is 3")), ); } #[test] fn simple_subtract() { assert_result("fn sub() -> Int 3 - 2 end", "sub", &vec![], Value::Int(1)) } #[test] fn add_two_doubles() { assert_result( "fn add(a: Double, b: Double) -> Double a + b end", "add", &vec![Value::Double(1.23), Value::Double(1.23)], Value::Double(1.23 + 1.23), ); } #[test] fn add_two_double_variables() { assert_result( " fn add() -> Double let a = 1.0 let b = 2.0 a + b end ", "add", &vec![Value::Double(1.0), Value::Double(2.0)], Value::Double(3.0), ); } #[test] fn subtract_two_doubles() { assert_result( "fn subtract(a: Double, b: Double) -> Double a - b end", "subtract", &vec![Value::Double(3.0), Value::Double(2.0)], Value::Double(1.0), ); } #[test] fn subtract_two_double_variables() { assert_result( " fn subtract() -> Double let a = 3.0 let b = 2.0 a - b end ", "subtract", &vec![], Value::Double(1.0), ) } #[test] fn simple_assign() { assert_result( " fn assign() -> Int let mut x = 21 x = x + x x end ", "assign", &vec![], Value::Int(42), ); } #[test] fn assign_field() -> Result<(), Vec> { let context = prepare_context( " class Foo mut bar = 21 ctor(_bar: Int) bar = _bar end end fn foo() -> Foo Foo(42) end ", )?; let result = get_result(&context, "foo", &vec![]); 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); Ok(()) } #[test] fn generic_field_and_ctor_param() -> Result<(), Vec> { let context = prepare_context( " class Foo mut t: T ctor(_t: T) t = _t end end fn main() -> Foo Foo(42) end ", )?; let result = get_result(&context, "main", &vec![]); 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); Ok(()) } }