feat: EvalState::new_value_apply

(cherry picked from commit f8143ae4ed7b62b86cb232f8d73e53bae30d632c)
This commit is contained in:
Robert Hensing 2024-05-24 09:34:20 +02:00
parent 8fcc645c5d
commit 78435d4ed7

View file

@ -293,6 +293,8 @@ impl EvalState {
}
/// Eagerly apply a function to an argument.
///
/// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`].
pub fn call(&self, f: Value, a: Value) -> Result<Value> {
let v = unsafe {
let value = self.new_value_uninitialized();
@ -309,6 +311,23 @@ impl EvalState {
Ok(v)
}
/// Apply a function to an argument, but don't evaluate the result just yet.
///
/// For an eager version, see [`call`][`EvalState::call`].
pub fn new_value_apply(&self, f: &Value, a: &Value) -> Result<Value> {
let value = self.new_value_uninitialized();
unsafe {
raw::init_apply(
self.context.ptr(),
value.raw_ptr(),
f.raw_ptr(),
a.raw_ptr(),
);
};
self.context.check_err()?;
Ok(value)
}
fn new_value_uninitialized(&self) -> Value {
let value = unsafe { raw::alloc_value(self.context.ptr(), self.raw_ptr()) };
Value::new(value)
@ -847,6 +866,23 @@ mod tests {
.unwrap();
}
#[test]
fn eval_state_apply() {
gc_registering_current_thread(|| {
let store = Store::open("auto").unwrap();
let es = EvalState::new(store, []).unwrap();
// This is a function that takes two arguments.
let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
let a = es.eval_from_string("2", "<test>").unwrap();
let v = es.new_value_apply(&f, &a).unwrap();
assert!(es.value_is_thunk(&v));
es.force(&v).unwrap();
let i = es.require_int(&v).unwrap();
assert!(i == 3);
})
.unwrap();
}
#[test]
fn eval_state_call_fail_body() {
gc_registering_current_thread(|| {
@ -868,6 +904,30 @@ mod tests {
.unwrap();
}
#[test]
fn eval_state_apply_fail_body() {
gc_registering_current_thread(|| {
let store = Store::open("auto").unwrap();
let es = EvalState::new(store, []).unwrap();
let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
let a = es.eval_from_string("true", "<test>").unwrap();
// Lazy => no error
let r = es.new_value_apply(&f, &a).unwrap();
// Force it => error
let res = es.force(&r);
match res {
Ok(_) => panic!("expected an error"),
Err(e) => {
if !e.to_string().contains("cannot coerce") {
eprintln!("{}", e);
assert!(false);
}
}
}
})
.unwrap();
}
#[test]
fn eval_state_call_fail_args() {
gc_registering_current_thread(|| {
@ -888,4 +948,28 @@ mod tests {
})
.unwrap();
}
#[test]
fn eval_state_apply_fail_args() {
gc_registering_current_thread(|| {
let store = Store::open("auto").unwrap();
let es = EvalState::new(store, []).unwrap();
let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap();
let a = es.eval_from_string("{}", "<test>").unwrap();
// Lazy => no error
let r = es.new_value_apply(&f, &a).unwrap();
// Force it => error
let res = es.force(&r);
match res {
Ok(_) => panic!("expected an error"),
Err(e) => {
if !e.to_string().contains("called without required argument") {
eprintln!("{}", e);
assert!(false);
}
}
}
})
.unwrap();
}
}