#![expect(clippy::expect_used, reason = "test harness")]
use brink_compiler::{AnalysisOptions, Dialect};
use brink_format::StoryData;
use brink_format::Value;
use brink_runtime::{DotNetRng, FallbackHandler, FlowInstance, FunctionEval, link};
const SRC: &str = "-> END\n\
=== function make_doubler() ===\n\
~ return #fn(double)\n\
=== function make_adder() ===\n\
~ return bind(#fn(add), 10)\n\
=== function double(x: int): int ===\n\
~ return x + x\n\
=== function add(a: int, b: int): int ===\n\
~ return a + b\n";
fn compiled() -> StoryData {
let options = AnalysisOptions {
dialect: Dialect::Brink,
..AnalysisOptions::default()
};
let out = brink_compiler::compile_with_options("story.ink", |_p| Ok(SRC.to_owned()), options);
assert!(out.is_ok(), "compile failed: {out:?}");
out.expect("just asserted above").data
}
fn reinvoke(name: &str, args: &[Value]) -> Value {
let data = compiled();
let linked = link(&data);
assert!(linked.is_ok(), "link failed: {:?}", linked.as_ref().err());
let (program, line_tables) = linked.expect("just asserted above");
let (mut flow, mut world) = FlowInstance::new_at_root(&program);
let found = program.find_address(name);
assert!(found.is_some(), "no address for {name:?}");
let (idx, _) = found.expect("just asserted above");
let made = flow.begin_function_eval::<DotNetRng>(
&program,
&line_tables,
&mut world,
&FallbackHandler,
idx,
&[],
None,
);
assert!(made.is_ok(), "{name} eval failed: {made:?}");
let made = made.expect("just asserted above");
assert!(
matches!(made, FunctionEval::Returned(_)),
"{name} must return a value, not await an external: {made:?}"
);
let FunctionEval::Returned(callee) = made else {
unreachable!("just asserted above")
};
let invocation = flow.begin_function_value_eval::<DotNetRng>(
&program,
&line_tables,
&mut world,
&FallbackHandler,
&callee,
args,
None,
);
assert!(invocation.is_ok(), "re-invoke failed: {invocation:?}");
let outcome = invocation.expect("just asserted above");
assert!(
matches!(outcome, FunctionEval::Returned(_)),
"the callee must return a value, not await an external: {outcome:?}"
);
let FunctionEval::Returned(v) = outcome else {
unreachable!("just asserted above")
};
v
}
#[test]
fn a_bare_function_value_binds_the_arguments_the_host_supplies() {
assert_eq!(
reinvoke("make_doubler", &[Value::Int(21)]).as_int(),
Some(42)
);
}
#[test]
fn a_curried_closure_binds_its_captured_prefix_and_the_supplied_rest() {
assert_eq!(reinvoke("make_adder", &[Value::Int(5)]).as_int(), Some(15));
}