use lex_ast::canonicalize_program;
use lex_bytecode::vm::Vm;
use lex_bytecode::Value;
use lex_runtime::{check_program, DefaultHandler, Policy};
use lex_syntax::parse_source;
fn run_one(src: &str, entry: &str, args: Vec<Value>) -> Value {
let prog = parse_source(src).unwrap();
let stages = canonicalize_program(&prog);
let bc = lex_bytecode::compile_program(&stages);
let policy = Policy::pure();
check_program(&bc, &policy).expect("program type-checks under pure policy");
let handler = DefaultHandler::new(policy);
let mut vm = Vm::with_handler(&bc, Box::new(handler));
vm.call(entry, args).unwrap()
}
#[test]
fn str_plus_str_concatenates() {
let src = r#"
fn cat(a :: Str, b :: Str) -> Str { a + b }
"#;
let r = run_one(
src,
"cat",
vec![Value::Str("hello, ".into()), Value::Str("world".into())],
);
assert_eq!(r, Value::Str("hello, world".into()));
}
#[test]
fn str_plus_chains_left_to_right() {
let src = r#"
fn three(a :: Str, b :: Str, c :: Str) -> Str { a + b + c }
"#;
let r = run_one(
src,
"three",
vec![
Value::Str("foo".into()),
Value::Str("-".into()),
Value::Str("bar".into()),
],
);
assert_eq!(r, Value::Str("foo-bar".into()));
}
#[test]
fn int_plus_still_works() {
let src = r#"
fn add(a :: Int, b :: Int) -> Int { a + b }
"#;
let r = run_one(src, "add", vec![Value::Int(2), Value::Int(40)]);
assert_eq!(r, Value::Int(42));
}
#[test]
fn float_plus_still_works() {
let src = r#"
fn addf(a :: Float, b :: Float) -> Float { a + b }
"#;
let r = run_one(src, "addf", vec![Value::Float(1.5), Value::Float(2.25)]);
assert_eq!(r, Value::Float(3.75));
}
fn type_errors(src: &str) -> Vec<lex_types::TypeError> {
let prog = parse_source(src).unwrap();
let stages = canonicalize_program(&prog);
match lex_types::check_program(&stages) {
Err(errs) => errs,
Ok(_) => panic!("type check unexpectedly succeeded"),
}
}
#[test]
fn str_minus_str_is_a_type_error() {
let errs = type_errors(r#"
fn bad(a :: Str, b :: Str) -> Str { a - b }
"#);
let msg = format!("{errs:?}");
assert!(
msg.contains("Int or Float"),
"expected the numeric-only diagnostic, got: {msg}"
);
}
#[test]
fn str_plus_int_is_a_type_error() {
let errs = type_errors(r#"
fn bad(a :: Str, b :: Int) -> Str { a + b }
"#);
let msg = format!("{errs:?}");
assert!(
msg.contains("Str") || msg.contains("Int"),
"expected a useful diagnostic mentioning Str or Int, got: {msg}"
);
}