deimos-lang/e2e-tests/src/lib.rs
2026-08-27 05:45:44 -05:00

260 lines
6.9 KiB
Rust

#[cfg(test)]
mod e2e_tests {
use dmc_lib::compile_compilation_unit;
use dmc_lib::constants_table::ConstantsTable;
use dmc_lib::diagnostic::{Diagnostic, Diagnostics};
use dmc_lib::intrinsics::add_primitive_symbols_and_type_infos;
use dmc_lib::semantic_analysis::analysis_context::AnalysisContext;
use dvm_lib::vm::constant::{Constant, StringConstant};
use dvm_lib::vm::value::Value;
use dvm_lib::vm::{DvmContext, call};
use std::error::Error;
use std::rc::Rc;
const REGISTER_COUNT: usize = 16;
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) -> Result<DvmContext, Diagnostics> {
let mut ctx = AnalysisContext::new();
let global_scope_id = ctx.push_scope("global");
add_primitive_symbols_and_type_infos(&mut ctx, global_scope_id);
let mut constants_table = ConstantsTable::new();
let compile_compilation_unit_result =
compile_compilation_unit(input, &mut ctx, REGISTER_COUNT, &mut constants_table);
let mut dvm_context = DvmContext::new();
for (name, function) in compile_compilation_unit_result?.functions {
dvm_context.functions_mut().insert(name, 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<Value> {
call(&dvm_context, function_name, &arguments, REGISTER_COUNT)
}
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<Diagnostic>> {
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<Diagnostic>> {
let context = prepare_context(
"
class Foo<T>
mut t: T
ctor(_t: T)
t = _t
end
end
fn main() -> Foo<Int>
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(())
}
#[test]
fn string_len() -> Result<(), Diagnostics> {
fn core_string_len(args: &[Value]) -> Result<Value, Box<dyn Error>> {
let this = args[0].unwrap_string();
let len = this.len();
Ok(Value::Int(len.try_into()?))
}
let mut context = prepare_context(
"
fn main() -> Int
\"Hello, World!\".len()
end
",
)?;
context
.platform_functions_mut()
.insert("core::String::len".into(), core_string_len);
let result = get_result(&context, "main", &vec![]);
assert!(result.is_some());
let value = result.unwrap();
assert!(matches!(value, Value::Int(13)));
Ok(())
}
}