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 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!("expected type error but check passed"),
}
}
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("type-checks");
let handler = DefaultHandler::new(policy);
let mut vm = Vm::with_handler(&bc, Box::new(handler));
vm.call(entry, args).unwrap()
}
#[test]
fn fold_closure_with_alias_param() {
let src = r#"
import "std.list" as list
type Error = { code :: Str }
type Errors = List[Error]
fn flatten(parts :: List[Errors]) -> Errors {
list.fold(parts, [], fn (acc :: Errors, e :: Errors) -> Errors {
list.concat(acc, e)
})
}
"#;
let e1 = Value::Record(indexmap::indexmap! {
"code".into() => Value::Str("e1".into())
});
let e2 = Value::Record(indexmap::indexmap! {
"code".into() => Value::Str("e2".into())
});
let parts = Value::List(vec![
Value::List(vec![e1.clone()].into()),
Value::List(vec![e2.clone()].into()),
].into());
let result = run_one(src, "flatten", vec![parts]);
assert_eq!(result, Value::List(vec![e1, e2].into()));
}
#[test]
fn map_closure_with_alias_return() {
let src = r#"
import "std.list" as list
type Name = Str
fn names(xs :: List[Str]) -> List[Name] {
list.map(xs, fn (s :: Str) -> Name { s })
}
"#;
let result = run_one(src, "names", vec![
Value::List(vec![Value::Str("alice".into()), Value::Str("bob".into())].into())
]);
assert_eq!(result, Value::List(vec![
Value::Str("alice".into()), Value::Str("bob".into())
].into()));
}
#[test]
fn filter_closure_with_alias_param() {
let src = r#"
import "std.list" as list
type Score = Int
fn high(scores :: List[Score]) -> List[Score] {
list.filter(scores, fn (s :: Score) -> Bool { s > 50 })
}
"#;
let result = run_one(src, "high", vec![
Value::List(vec![Value::Int(30), Value::Int(70), Value::Int(90)].into())
]);
assert_eq!(result, Value::List(vec![Value::Int(70), Value::Int(90)].into()));
}
#[test]
fn nominal_alias_distinction_preserved() {
let errs = type_errors(r#"
type Metres = Int
type Seconds = Int
fn bad(m :: Metres, s :: Seconds) -> Metres { m + s }
"#);
let _ = errs; }