mod common;
use std::sync::{Arc, Mutex};
use bcgen_lang::{Compiler, LineIndex, Options};
use bvm_lang::{Program, Value, Vm};
use common::hb::H;
use hir_lang::{
Arm, Binder, BinderKind, Capture, CaptureMode, Closure, DefaultEval, Effects, Expr, ExprId,
Lit, Name, Ns, OpKind, ParamKind, Pat, Res, Span, UnitId,
};
use resolve_lang::{DefKind, Export, MapEnv, Namespace, Policy, Resolver};
fn name(h: &mut H, s: &str) -> ExprId {
h.tick();
let n = h.name(s);
h.b.name_expr(n)
}
fn let_named(h: &mut H, s: &str, init: ExprId) -> hir_lang::StmtId {
let b = h.local(s);
h.let_(b, Some(init))
}
fn concat(h: &mut H, parts: &[ExprId]) -> ExprId {
let mut acc = parts[0];
for &p in &parts[1..] {
acc = h.intrinsic("concat", &[acc, p]);
}
acc
}
fn echo(h: &mut H, args: &[ExprId]) -> hir_lang::StmtId {
let e = name(h, "echo");
let c = h.call(e, args);
h.stmt(c)
}
fn foreach(
h: &mut H,
coll: ExprId,
k: &str,
v: &str,
body: Vec<hir_lang::StmtId>,
) -> hir_lang::StmtId {
let it = h.local("it");
let iter = h.intrinsic("iter", &[coll]);
let lit = h.let_(it, Some(iter));
let vit = h.var(it);
let next = h.intrinsic("iter_next", &[vit]);
let null = h.pat(Pat::Lit(Lit::Null));
let brk = h.brk(None, None);
let a1 = h.arm(null, None, brk);
let kb = h.local(k);
let vb = h.local(v);
let pk = h.pbind(kb);
let pv = h.pbind(vb);
let elems = h.b.list(&[pk, pv]);
let tup = h.pat(Pat::Tuple { elems, rest: None });
let blk = h.block(&body, None);
let a2 = h.arm(tup, None, blk);
let m = h.match_(next, &[a1, a2]);
let lp = h.loop_(None, m, None);
let outer = h.block(&[lit], Some(lp));
h.stmt(outer)
}
fn program() -> (hir_lang::Hir, intern_lang::Interner) {
let mut h = H::new();
let (p1, _) = h.param("name", ParamKind::Normal, None, false);
let hello = h.str("Hello");
let (p2, _) = h.param("greeting", ParamKind::Normal, Some(hello), false);
let g = name(&mut h, "greeting");
let sep = h.str(", ");
let n = name(&mut h, "name");
let bang = h.str("!");
let msg = concat(&mut h, &[g, sep, n, bang]);
let ret = h.ret(Some(msg));
let body = h.block(&[], Some(ret));
let greet = h.func("greet", &[p1, p2], body, DefaultEval::PerCall);
let (pn, _) = h.param("n", ParamKind::Normal, None, false);
let n1 = name(&mut h, "n");
let two = h.int(2);
let lt = h.op(OpKind::Lt, &[n1, two]);
let n2 = name(&mut h, "n");
let mut rec = Vec::new();
for k in [1, 2] {
let f = name(&mut h, "fib");
let nn = name(&mut h, "n");
let kk = h.int(k);
let sub = h.op(OpKind::Sub, &[nn, kk]);
rec.push(h.call(f, &[sub]));
}
let sum = h.op(OpKind::Add, &[rec[0], rec[1]]);
let ife = h.if_(lt, n2, Some(sum));
let ret = h.ret(Some(ife));
let fbody = h.block(&[], Some(ret));
let fib = h.func("fib", &[pn], fbody, DefaultEval::PerCall);
let mut stmts = Vec::new();
let names: Vec<ExprId> = ["Ada", "Linus", "Grace"].iter().map(|s| h.str(s)).collect();
let arr = h.array(&names);
stmts.push(let_named(&mut h, "people", arr));
let people = name(&mut h, "people");
let i = name(&mut h, "i");
let colon = h.str(": ");
let gr = name(&mut h, "greet");
let p = name(&mut h, "p");
let call = h.call(gr, &[p]);
let e1 = echo(&mut h, &[i, colon, call]);
stmts.push(foreach(&mut h, people, "i", "p", vec![e1]));
let gr = name(&mut h, "greet");
let mox = h.str("Mox");
let welcome = h.str("Welcome");
let named = h.named("greeting", welcome);
let c = h.call_args(gr, &[hir_lang::Arg::positional(mox), named]);
stmts.push(echo(&mut h, &[c]));
let zero = h.int(0);
stmts.push(let_named(&mut h, "total", zero));
let one = h.int(1);
let ib = h.local("i");
let li = h.let_(ib, Some(one));
let vi = name(&mut h, "i");
let ten = h.int(10);
let le = h.op(OpKind::Le, &[vi, ten]);
let empty = h.block(&[], None);
let brk = h.brk(None, None);
let brkb = h.block(&[], Some(brk));
let guard = h.if_(le, empty, Some(brkb));
let gs = h.stmt(guard);
let vi2 = name(&mut h, "i");
let two = h.int(2);
let rem = h.op(OpKind::Rem, &[vi2, two]);
let z = h.int(0);
let even = h.op(OpKind::Eq, &[rem, z]);
let cont = h.cont(None);
let cb = h.block(&[], Some(cont));
let ifc = h.if_(even, cb, None);
let cs = h.stmt(ifc);
let t = name(&mut h, "total");
let vi3 = name(&mut h, "i");
let add = h.compound(t, hir_lang::Op::new(OpKind::Add), vi3);
let adds = h.stmt(add);
let body = h.block(&[gs, cs, adds], None);
let ti = name(&mut h, "i");
let vi4 = name(&mut h, "i");
let one2 = h.int(1);
let inc = h.op(OpKind::Add, &[vi4, one2]);
let step = h.assign(ti, inc);
let lp = h.loop_(None, body, Some(step));
let forb = h.block(&[li], Some(lp));
stmts.push(h.stmt(forb));
let odd = h.str("odd sum ");
let tt = name(&mut h, "total");
stmts.push(echo(&mut h, &[odd, tt]));
h.tick();
let total_outer = {
let n = h.name("total");
h.b.name_path(n, Ns::Value)
};
let tn = h.name("total");
let cap_b =
h.b.binder(Binder::new(tn, BinderKind::Capture).with_mutable(true));
let cap = Capture {
outer: total_outer,
binder: cap_b,
mode: CaptureMode::ByRef,
};
let (px, _) = h.param("x", ParamKind::Normal, None, false);
let t2 = name(&mut h, "total");
let x = name(&mut h, "x");
let addx = h.compound(t2, hir_lang::Op::new(OpKind::Add), x);
let ab = h.block(&[], Some(addx));
let params = h.b.list(&[px]);
let caps = h.b.list(&[cap]);
let clo = h.expr(Expr::Closure(Closure {
params,
ret: None,
body: ab,
effects: Effects::THROWS,
implicit: None,
captures: caps,
self_binder: None,
defaults: DefaultEval::PerCall,
}));
stmts.push(let_named(&mut h, "add", clo));
let a = name(&mut h, "add");
let hundred = h.int(100);
let call = h.call(a, &[hundred]);
stmts.push(h.stmt(call));
let tl = h.str("total ");
let tt = name(&mut h, "total");
stmts.push(echo(&mut h, &[tl, tt]));
let one = h.int(1);
let zero = h.int(0);
let div = h.op(OpKind::Div, &[one, zero]);
let e = name(&mut h, "echo");
let tb = h.call(e, &[div]);
let eb = h.local("e");
let pe = h.pbind(eb);
let caught = h.str("caught");
let ec = name(&mut h, "echo");
let cbody = h.call(ec, &[caught]);
let arm = Arm {
pat: pe,
guard: None,
body: cbody,
};
let fin = h.str("finally");
let ef = name(&mut h, "echo");
let fbody = h.call(ef, &[fin]);
let tr = h.try_(tb, &[arm], Some(fbody));
stmts.push(h.stmt(tr));
let empty = h.array(&[]);
stmts.push(let_named(&mut h, "counts", empty));
let items: Vec<ExprId> = [3, 1, 3, 2, 3].iter().map(|&v| h.int(v)).collect();
let coll = h.array(&items);
let c1 = name(&mut h, "counts");
let v1 = name(&mut h, "v");
let target = h.index(c1, v1);
let c2 = name(&mut h, "counts");
let v2 = name(&mut h, "v");
let read = h.index(c2, v2);
let w = h.pat(Pat::Wild);
let z = h.int(0);
let dflt = Arm {
pat: w,
guard: None,
body: z,
};
let coalesce = h.try_(read, &[dflt], None);
let one = h.int(1);
let plus = h.op(OpKind::Add, &[coalesce, one]);
let asg = h.assign(target, plus);
let s = h.stmt(asg);
stmts.push(foreach(&mut h, coll, "k", "v", vec![s]));
let th = h.str("threes ");
let c3 = name(&mut h, "counts");
let three = h.int(3);
let get = h.index(c3, three);
stmts.push(echo(&mut h, &[th, get]));
let fl = h.str("fib ");
let f = name(&mut h, "fib");
let twenty = h.int(20);
let fc = h.call(f, &[twenty]);
stmts.push(echo(&mut h, &[fl, fc]));
let tot = name(&mut h, "total");
let body = h.block(&stmts, Some(tot));
let _ = Span::empty(0);
h.finish(&[greet, fib], Some(body))
}
#[test]
fn a_mox_program_runs_end_to_end() {
let (hir, mut names) = program();
let echo = Name::new(names.intern("echo"));
let env = MapEnv::new().with_prelude(
echo,
Namespace::Value,
Export::new(Res::Extern(echo.sym), DefKind::Extern),
);
let res = Resolver::new(Policy::new())
.with_env(&env)
.resolve(hir, &names)
.expect("resolves");
assert!(res.is_clean(), "{:?}", res.diagnostics());
let hir = res.hir(UnitId::new(0)).expect("unit").clone();
let lines = LineIndex::new("main.mox", 0, "");
let out = Compiler::new(Options::new())
.with_locator(&lines)
.compile(&hir, &names)
.expect("compiles");
let printed = Arc::new(Mutex::new(Vec::new()));
let program =
Program::load(out.module().clone(), &common::host(printed.clone())).expect("loads");
let mut vm = Vm::new(&program);
let result = vm.run(out.entry(), &[]).expect("runs");
assert_eq!(result, Value::Int(125));
let printed = printed.lock().expect("lock").clone();
assert_eq!(
printed,
vec![
"0: Hello, Ada!",
"1: Hello, Linus!",
"2: Hello, Grace!",
"Welcome, Mox!",
"odd sum 25",
"total 125",
"caught",
"finally",
"threes 3",
"fib 6765",
]
);
let (r, rp) = common::run_reference(&hir, &names, common::Profile::PHP, 10_000_000);
assert_eq!(r, common::Outcome::Value(common::RV::Int(125)));
assert_eq!(rp, printed);
}