deimos-lang/e2e-tests/src/lib.rs
2026-03-13 20:21:49 -05:00

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);
}
}