mod common;
use std::sync::{Arc, Mutex};
use bcgen_lang::{BodyMode, Compiler, Intrinsics, Options};
use bvm_lang::{Program, Vm, VmError};
use common::hb::H;
use common::to_rv;
use hir_lang::{ExprId, Hir, ItemId, PathRoot, Vis};
use intern_lang::Interner;
use resolve_lang::{DefKind, Export, MapEnv, Namespace, Policy, Resolver};
fn resolve(hir: Hir, names: &Interner) -> Hir {
let mut env = MapEnv::new();
if let Some(s) = names.get("echo") {
env = env.with_prelude(
hir_lang::Name::new(s),
Namespace::Value,
Export::new(hir_lang::Res::Extern(s), DefKind::Extern),
);
}
let unit = hir.unit();
let res = Resolver::new(Policy::php())
.with_env(&env)
.resolve(hir, names)
.unwrap_or_else(|e| panic!("resolve: {e}"));
assert!(res.is_clean(), "resolution diagnostics");
res.hir(unit).cloned().unwrap_or_else(|| panic!("unit"))
}
fn run(h: H, items: &[ItemId], body: Option<ExprId>, mode: BodyMode) -> Vec<String> {
let (hir, names) = h.finish(items, body);
let hir = resolve(hir, &names);
let opts = Options::new()
.with_body(mode)
.with_intrinsics(Intrinsics::mox());
let out = Compiler::new(opts)
.compile(&hir, &names)
.unwrap_or_else(|e| panic!("compile error: {e}"));
let printed = Arc::new(Mutex::new(Vec::new()));
let host = common::host(printed.clone());
let program = Program::load(out.module().clone(), &host).unwrap_or_else(|e| {
panic!(
"load error: {e}\n{}",
bytecode_lang::disassemble(out.module())
)
});
let mut vm = Vm::new(&program);
match vm.run(out.entry(), &[]) {
Ok(v) => {
let _ = to_rv(&vm, v, 0);
}
Err(VmError::Raised { kind, .. }) => panic!("raised {}", kind.code()),
Err(e) => panic!("unexpected VM error: {e:?}"),
}
match printed.lock() {
Ok(v) => v.clone(),
Err(_) => Vec::new(),
}
}
fn echo(h: &mut H, args: &[ExprId]) -> ExprId {
let e = h.uname("echo");
h.call(e, args)
}
fn class_s(h: &mut H) -> ItemId {
let a = h.int(10);
let b = h.int(20);
let arr = h.array(&[a, b]);
let c = h.class_const("C", Vis::Public, Some(arr));
let a = h.int(30);
let b = h.int(40);
let arr = h.array(&[a, b]);
let p = h.static_prop("p", Vis::Public, Some(arr));
h.class("S", None, &[], &[c, p])
}
fn echo_s(h: &mut H) -> ExprId {
let c = h.spath(PathRoot::Relative, &["S", "C"]);
let one = h.int(1);
let c1 = h.index(c, one);
let sp = h.str(" ");
let p = h.spath(PathRoot::Relative, &["S", "p"]);
let zero = h.int(0);
let p0 = h.index(p, zero);
echo(h, &[c1, sp, p0])
}
const BOTH: [BodyMode; 2] = [BodyMode::Entry, BodyMode::Start];
#[test]
fn computed_constants_and_statics_work_at_the_root() {
for mode in BOTH {
let mut h = H::new();
let s = class_s(&mut h);
let e = echo_s(&mut h);
assert_eq!(run(h, &[s], Some(e), mode), ["20 30"], "{mode:?}");
}
}
#[test]
fn computed_constants_and_statics_work_in_a_namespace() {
for mode in BOTH {
let mut h = H::new();
let s = class_s(&mut h);
let e = echo_s(&mut h);
let a = h.module("A", &[s], Some(e));
assert_eq!(run(h, &[a], None, mode), ["20 30"], "{mode:?}");
}
}
#[test]
fn a_namespace_global_and_constant_are_set_before_the_namespace_body() {
for mode in BOTH {
let mut h = H::new();
let one = h.int(1);
let two = h.int(2);
let sum = h.op(hir_lang::OpKind::Add, &[one, two]);
let k = h.constant("K", sum);
let a = h.int(5);
let b = h.int(6);
let sum = h.op(hir_lang::OpKind::Add, &[a, b]);
let g = h.global("g", Some(sum));
let kr = h.item_ref("K", k);
let sp = h.str(" ");
let gr = h.item_ref("g", g);
let e = echo(&mut h, &[kr, sp, gr]);
let a = h.module("A", &[k, g], Some(e));
assert_eq!(run(h, &[a], None, mode), ["3 11"], "{mode:?}");
}
}
#[test]
fn a_doubly_nested_namespace_runs_after_all_the_initializers() {
for mode in BOTH {
let mut h = H::new();
let s = class_s(&mut h);
let inner = echo_s(&mut h);
let b = h.module("B", &[s], Some(inner));
let o = h.str("outer");
let outer = echo(&mut h, &[o]);
let a = h.module("A", &[b], Some(outer));
assert_eq!(run(h, &[a], None, mode), ["outer", "20 30"], "{mode:?}");
}
}
#[test]
fn a_namespace_body_sees_a_later_sibling_namespaces_initializers() {
for mode in BOTH {
let mut h = H::new();
let one = h.int(1);
let two = h.int(2);
let sum = h.op(hir_lang::OpKind::Add, &[one, two]);
let k = h.constant("K", sum);
let b = h.module("B", &[k], None);
let kr = h.item_ref("K", k);
let e = echo(&mut h, &[kr]);
let a = h.module("A", &[], Some(e));
assert_eq!(run(h, &[a, b], None, mode), ["3"], "{mode:?}");
}
}