296 lines
7.9 KiB
Rust
296 lines
7.9 KiB
Rust
#[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::<Vec<_>>()
|
|
.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<Function> = 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<Class> = 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<Value> {
|
|
let mut registers: Vec<Operand> = 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<Diagnostic> {
|
|
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);
|
|
}
|
|
}
|