#[cfg(test)] mod e2e_tests { use dmc_lib::constants_table::ConstantsTable; 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; 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) -> DvmContext { 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(); match compilation_unit.gather_declared_names(&mut symbol_table) { Ok(_) => {} Err(diagnostics) => { report_diagnostics(&diagnostics); } } match compilation_unit.check_name_usages(&symbol_table) { Ok(_) => {} Err(diagnostics) => { report_diagnostics(&diagnostics); } } match compilation_unit.type_check(&symbol_table) { Ok(_) => {} Err(diagnostics) => { report_diagnostics(&diagnostics); } } let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_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())), ); } 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 = prepare_context(input); 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 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)] mod diagnostic_tests { use dmc_lib::diagnostic::Diagnostic; use dmc_lib::parser::parse_compilation_unit; use dmc_lib::symbol_table::SymbolTable; fn get_diagnostics(input: &str) -> Vec { let parse_result = parse_compilation_unit(input); let mut compilation_unit = match parse_result { Ok(compilation_unit) => compilation_unit, Err(diagnostics) => { return diagnostics; } }; let mut symbol_table = SymbolTable::new(); match compilation_unit.gather_declared_names(&mut symbol_table) { Ok(_) => {} Err(diagnostics) => { return diagnostics; } } match compilation_unit.check_name_usages(&symbol_table) { Ok(_) => {} Err(diagnostics) => { return diagnostics; } } match compilation_unit.type_check(&symbol_table) { Ok(_) => vec![], Err(diagnostics) => diagnostics, } } #[test] fn wrong_return_type() { let diagnostics = get_diagnostics("fn main() -> String 42 end"); assert_eq!(diagnostics.len(), 1); } }