smpli 0.4.0

Interpreter for the SMPL language
Documentation
use failure::Error;

use smpl::*;
use smpl::prelude::parse_module;

use crate::*;

macro_rules! include_test {
    ($file_name: expr) => {{
        include_str!(concat!("../../eval-tests/", $file_name))
    }}
}

macro_rules! expect_value {
    ($name: ident, module :: $mod_name: expr, eval :: $fn_name: expr, args :: $args: expr, finalizer :: $finalizer: expr) => {
        expect_value!($name,
            module :: $mod_name,
            eval :: $fn_name,
            args :: $args,
            finalizer :: $finalizer,
            builtins :: |vm| { vm }
        );
    };

    ($name: ident, module :: $mod_name: expr, eval :: $fn_name: expr, args :: $args: expr, finalizer :: $finalizer: expr, builtins :: $builtins: expr) => {
        #[test]
        fn $name() {
            let code = include_test!(concat!(stringify!($name), ".smpl"));
            let result = setup_and_run!(code, $mod_name, $fn_name, $args, $builtins);

            $finalizer(result);
        }
    };

    ($name: ident, module :: $mod_name: expr, eval :: $fn_name: expr, args :: $args: expr, expect :: $expect: expr, builtins :: $builtins: expr) => {
        expect_value!($name,
            module :: $mod_name,
            eval :: $fn_name,
            args :: $args,
            finalizer :: |result| {
                assert_eq!($expect, result);
            },
            builtins :: $builtins
        );
    };

    ($name: ident, module :: $mod_name: expr, eval :: $fn_name: expr, args :: $args: expr, expect :: $expect: expr) => {
        expect_value!($name,
            module :: $mod_name,
            eval :: $fn_name,
            args :: $args,
            finalizer :: |result| {
                assert_eq!($expect, result);
            },
            builtins :: |vm| { vm }
        );
    };
}

macro_rules! setup_and_run {
    ($mod1: expr, $mod_name: expr, $fn_name: expr, $args: expr) => {{
        setup_and_run!(
            $mod1,
            $mod_name,
            $fn_name,
            $args,
            |vm: VmModule | { vm }
        )
    }};

    ($mod1: expr, $mod_name: expr, $fn_name: expr, $args: expr, $builtins: expr) => {{

        let module = UnparsedModule::anonymous($mod1);
        let parsed = parse_module(module).expect("Failed to parse module");
        let module = $builtins(VmModule::new(parsed));

        let modules = vec![module];
        let avm = AVM::new(Std::std(), modules).unwrap();

        let a_fn_handle = avm.query_module($mod_name, $fn_name)
            .expect("Query error")
            .expect("Unknown query");

        let a_result = avm.spawn_executor(a_fn_handle, $args, SpawnOptions {
            type_check: false
        })
            .expect("Executor spawn error error")
            .execute_sync()
            .expect("Executor run error");

        a_result
    }};
}

macro_rules! wrap_input {
    ($input: expr) => {{
        UnparsedModule::anonymous($input)
    }}
}

async fn add(mut args: Vec<Value>) -> Result<Value, Error> {
    let lhs = args.get(0).unwrap();
    let rhs = args.get(1).unwrap();

    let lhs = irmatch!(lhs; Value::Int(i) => i);
    let rhs = irmatch!(rhs; Value::Int(i) => i);

    return Ok(Value::Int(lhs + rhs));
}

async fn var_arg_sum(args: Vec<Value>) -> Result<Value, Error> {

    let mut sum = 0;

    for arg in args.iter() {
        let value = irmatch!(arg; Value::Int(i) => i);
        sum += value;
    }

    return Ok(Value::Int(sum));
}

expect_value!(interpreter_basic,
    module :: "mod1",
    eval :: "test",
    args :: vec![Value::Int(5), Value::Int(7)],
    expect :: Value::Int(12)
);

expect_value!(interpreter_struct,
    module :: "mod1",
    eval :: "test",
    args :: vec![Value::Int(5), Value::Int(7)],
    finalizer :: |result|  {
        let result = irmatch!(result; Value::Struct(s) => s.get_field("f").unwrap());
        let result = irmatch!(result; Value::Int(i) => i);

        assert_eq!(12, result);
    }
);

expect_value!(interpreter_builtin,
    module :: "mod1",
    eval :: "test",
    args :: vec![Value::Int(5), Value::Int(7)],
    expect :: Value::Int(12),
    builtins :: |vm: VmModule| {
        vm.add_builtin("add", erase(add))
    }
);

expect_value!(interpreter_builtin_unchecked_params,
    module :: "mod1",
    eval :: "test",
    args :: vec![Value::Int(5), Value::Int(7)],
    expect :: Value::Int(114),
    builtins :: |vm: VmModule| {
        vm.add_builtin("sum", erase(var_arg_sum))
    }
);

#[test]
fn interpreter_intermod_builtin() {
    let mod1 =
"mod mod1;

builtin fn add(a: int, b: int) -> int;

fn test(a: int, b: int) -> int {
return add(a, b);
}";

    let mod2 =
"mod mod2;

use mod1;

fn test2() -> int {
return mod1::add(1, 2);
}
";

    let m1 = parse_module(wrap_input!(mod1)).unwrap();
    let m2 = parse_module(wrap_input!(mod2)).unwrap();

    let m1 = VmModule::new(m1)
        .add_builtin("add", erase(add));
    let m2 = VmModule::new(m2);

    let modules = vec![m1, m2];

    let mut avm = AVM::new(Std::no_std(), modules).unwrap();

    let a_fn_handle = avm.query_module("mod2", "test2").unwrap().unwrap();

    let a_result = avm.spawn_executor(a_fn_handle, vec![], SpawnOptions {
        type_check: false,
    })
        .unwrap()
        .execute_sync()
        .unwrap();


    assert_eq!(Value::Int(3), a_result);
}

expect_value!(interpreter_field_access,
    module :: "mod1",
    eval :: "test",
    args :: vec![],
    expect :: Value::Int(1337)
);

expect_value!(interpreter_array,
    module :: "mod1",
    eval :: "test",
    args :: vec![],
    expect :: Value::Int(1 + 2 + 3 + 4 + 5)
);

expect_value!(interpreter_fn_value,
    module :: "mod1",
    eval :: "test",
    args :: vec![],
    expect :: Value::Int(420)
);

expect_value!(interpreter_optional_local_type_annotation,
    module :: "mod1",
    eval :: "test",
    args :: vec![],
    expect :: Value::Int(420)
);

expect_value!(interpreter_recursive_fn_call,
    module :: "mod1",
    eval :: "recurse",
    args :: vec![Value::Int(2)],
    expect :: Value::Int(3)
);

expect_value!(interpreter_mutually_recursive_fn_call,
    module :: "mod1",
    eval :: "recurse_a",
    args :: vec![Value::Int(1)],
    expect :: Value::Int(-5)
);

expect_value!(interpreter_loaded_builtin,
    module :: "mod1",
    eval :: "test_floor",
    args :: vec![],
    expect :: Value::Float(1.0)
);

expect_value!(interpreter_anonymous_fn_call,
    module :: "mod1",
    eval :: "test",
    args :: vec![],
    expect :: Value::Int(15)
);

expect_value!(interpreter_anonymous_fn_arg,
    module :: "mod1",
    eval :: "test",
    args :: vec![],
    expect :: Value::Int(15)
);

expect_value!(interpreter_anonymous_fn_nested,
    module :: "mod1",
    eval :: "test",
    args :: vec![Value::Int(21)],
    expect :: Value::Int(43)
);

expect_value!(interpreter_fn_piping,
    module :: "mod1",
    eval :: "test",
    args :: vec![],
    expect :: Value::Int(5)
);

expect_value!(interpreter_builtin_bind,
    module :: "mod1",
    eval :: "bar",
    args :: vec![],
    expect :: Value::Int(8),
    builtins :: |vm: VmModule| {
        vm.add_builtin("add", erase(add))
    }
);

expect_value!(interpreter_complex_if,
    module :: "mod1",
    eval :: "foo",
    args :: vec![],
    expect :: Value::Int(1000)
);

expect_value!(interpreter_uni_expr,
    module :: "mod1",
    eval :: "foo",
    args :: vec![],
    expect :: Value::Bool(true)
);

expect_value!(interpreter_2d_array,
    module :: "mod1",
    eval :: "foo",
    args :: vec![],
    expect :: Value::Int(0)
);

expect_value!(interpreter_structs_complex,
    module :: "mod1",
    eval :: "foo",
    args :: vec![],
    expect :: Value::Int(0)
);

expect_value!(interpreter_while_loop,
    module :: "mod1",
    eval :: "foo",
    args :: vec![],
    expect :: Value::Int(137)
);

#[test]
fn interpreter_bind_fn_type_app_mod_access() {
    let mod1 =
"mod mod1;

fn ident(type T)(t: T) -> T {
    return t;
}";

    let mod2 =
"mod mod2;

use mod1;

fn test() -> int {
    let my_ident: fn(int) -> int = mod1::ident(type int);
    let result: int = my_ident(1337);

    return result;
}";


    let mod1 = UnparsedModule::anonymous(mod1);
    let mod1 = parse_module(mod1).expect("Failed to parse module");
    let mod1 = VmModule::new(mod1);

    let mod2 = UnparsedModule::anonymous(mod2);
    let mod2 = parse_module(mod2).expect("Failed to parse module");
    let mod2 = VmModule::new(mod2);

    let modules = vec![mod1, mod2];
    let avm = AVM::new(Std::std(), modules).unwrap();

    let fn_handle = avm.query_module("mod2", "test").unwrap().unwrap();

    let result = avm.spawn_executor(fn_handle, vec![], SpawnOptions {
        type_check: false
    })
        .expect("Executor spawn error error")
        .execute_sync()
        .expect("Executor run error");

    assert_eq!(result, Value::Int(1337));
}

expect_value!(interpreter_array_path_assignment,
    module :: "mod1",
    eval :: "test",
    args :: vec![],
    expect :: {
        let array = Array::new_init(vec![
            Value::Int(1),
            Value::Int(1),
            Value::Int(2),
            Value::Int(3),
        ]);

        Value::Array(array)
    }
);