mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-22 14:06:16 +02:00
C API: Add nix_init_apply
Thunks are relevant when initializing attrsets and lists, passing arguments. This is an important way to produce them.
This commit is contained in:
parent
05b9dac754
commit
ad643cde58
4 changed files with 157 additions and 2 deletions
|
@ -93,6 +93,8 @@ nix_err nix_expr_eval_from_string(
|
||||||
* @param[in] arg The argument to pass to the function.
|
* @param[in] arg The argument to pass to the function.
|
||||||
* @param[out] value The result of the function call.
|
* @param[out] value The result of the function call.
|
||||||
* @return NIX_OK if the function call was successful, an error code otherwise.
|
* @return NIX_OK if the function call was successful, an error code otherwise.
|
||||||
|
* @see nix_init_apply() for a similar function that does not performs the call immediately, but stores it as a thunk.
|
||||||
|
* Note the different argument order.
|
||||||
*/
|
*/
|
||||||
nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value);
|
nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value);
|
||||||
|
|
||||||
|
|
|
@ -404,6 +404,19 @@ nix_err nix_init_null(nix_c_context * context, Value * value)
|
||||||
NIXC_CATCH_ERRS
|
NIXC_CATCH_ERRS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg)
|
||||||
|
{
|
||||||
|
if (context)
|
||||||
|
context->last_err_code = NIX_OK;
|
||||||
|
try {
|
||||||
|
auto & v = check_value_not_null(value);
|
||||||
|
auto & f = check_value_not_null(fn);
|
||||||
|
auto & a = check_value_not_null(arg);
|
||||||
|
v.mkApp(&f, &a);
|
||||||
|
}
|
||||||
|
NIXC_CATCH_ERRS
|
||||||
|
}
|
||||||
|
|
||||||
nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val)
|
nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val)
|
||||||
{
|
{
|
||||||
if (context)
|
if (context)
|
||||||
|
|
|
@ -342,8 +342,24 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i);
|
||||||
* @param[out] value Nix value to modify
|
* @param[out] value Nix value to modify
|
||||||
* @return error code, NIX_OK on success.
|
* @return error code, NIX_OK on success.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
nix_err nix_init_null(nix_c_context * context, Value * value);
|
nix_err nix_init_null(nix_c_context * context, Value * value);
|
||||||
|
|
||||||
|
/** @brief Set the value to a thunk that will perform a function application when needed.
|
||||||
|
*
|
||||||
|
* Thunks may be put into attribute sets and lists to perform some computation lazily; on demand.
|
||||||
|
* However, note that in some places, a thunk must not be returned, such as in the return value of a PrimOp.
|
||||||
|
* In such cases, you may use nix_value_call() instead (but note the different argument order).
|
||||||
|
*
|
||||||
|
* @param[out] context Optional, stores error information
|
||||||
|
* @param[out] value Nix value to modify
|
||||||
|
* @param[in] fn function to call
|
||||||
|
* @param[in] arg argument to pass
|
||||||
|
* @return error code, NIX_OK on successful initialization.
|
||||||
|
* @see nix_value_call() for a similar function that performs the call immediately and only stores the return value.
|
||||||
|
* Note the different argument order.
|
||||||
|
*/
|
||||||
|
nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg);
|
||||||
|
|
||||||
/** @brief Set an external value
|
/** @brief Set an external value
|
||||||
* @param[out] context Optional, stores error information
|
* @param[out] context Optional, stores error information
|
||||||
* @param[out] value Nix value to modify
|
* @param[out] value Nix value to modify
|
||||||
|
@ -421,7 +437,7 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState *
|
||||||
/** @brief Insert bindings into a builder
|
/** @brief Insert bindings into a builder
|
||||||
* @param[out] context Optional, stores error information
|
* @param[out] context Optional, stores error information
|
||||||
* @param[in] builder BindingsBuilder to insert into
|
* @param[in] builder BindingsBuilder to insert into
|
||||||
* @param[in] name attribute name, copied into the symbol store
|
* @param[in] name attribute name, only used for the duration of the call.
|
||||||
* @param[in] value value to give the binding
|
* @param[in] value value to give the binding
|
||||||
* @return error code, NIX_OK on success.
|
* @return error code, NIX_OK on success.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "tests/nix_api_expr.hh"
|
#include "tests/nix_api_expr.hh"
|
||||||
#include "tests/string_callback.hh"
|
#include "tests/string_callback.hh"
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
@ -187,4 +188,127 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr)
|
||||||
free(out_name);
|
free(out_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_value_init)
|
||||||
|
{
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
// two = 2;
|
||||||
|
// f = a: a * a;
|
||||||
|
|
||||||
|
Value * two = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_int(ctx, two, 2);
|
||||||
|
|
||||||
|
Value * f = nix_alloc_value(ctx, state);
|
||||||
|
nix_expr_eval_from_string(
|
||||||
|
ctx, state, R"(
|
||||||
|
a: a * a
|
||||||
|
)",
|
||||||
|
"<test>", f);
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
// r = f two;
|
||||||
|
|
||||||
|
Value * r = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_apply(ctx, r, f, two);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
ValueType t = nix_get_type(ctx, r);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
ASSERT_EQ(t, NIX_TYPE_THUNK);
|
||||||
|
|
||||||
|
nix_value_force(ctx, state, r);
|
||||||
|
|
||||||
|
t = nix_get_type(ctx, r);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
ASSERT_EQ(t, NIX_TYPE_INT);
|
||||||
|
|
||||||
|
int n = nix_get_int(ctx, r);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
ASSERT_EQ(n, 4);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
nix_gc_decref(ctx, two);
|
||||||
|
nix_gc_decref(ctx, f);
|
||||||
|
nix_gc_decref(ctx, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_value_init_apply_error)
|
||||||
|
{
|
||||||
|
Value * some_string = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_string(ctx, some_string, "some string");
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
Value * v = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_apply(ctx, v, some_string, some_string);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
// All ok. Call has not been evaluated yet.
|
||||||
|
|
||||||
|
// Evaluate it
|
||||||
|
nix_value_force(ctx, state, v);
|
||||||
|
ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR);
|
||||||
|
ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("attempt to call something which is not a function but"));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
nix_gc_decref(ctx, some_string);
|
||||||
|
nix_gc_decref(ctx, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg)
|
||||||
|
{
|
||||||
|
// f is a lazy function: it does not evaluate its argument before returning its return value
|
||||||
|
// g is a helper to produce e
|
||||||
|
// e is a thunk that throws an exception
|
||||||
|
//
|
||||||
|
// r = f e
|
||||||
|
// r should not throw an exception, because e is not evaluated
|
||||||
|
|
||||||
|
Value * f = nix_alloc_value(ctx, state);
|
||||||
|
nix_expr_eval_from_string(
|
||||||
|
ctx, state, R"(
|
||||||
|
a: { foo = a; }
|
||||||
|
)",
|
||||||
|
"<test>", f);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
Value * e = nix_alloc_value(ctx, state);
|
||||||
|
{
|
||||||
|
Value * g = nix_alloc_value(ctx, state);
|
||||||
|
nix_expr_eval_from_string(
|
||||||
|
ctx, state, R"(
|
||||||
|
_ignore: throw "error message for test case nix_value_init_apply_lazy_arg"
|
||||||
|
)",
|
||||||
|
"<test>", g);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
nix_init_apply(ctx, e, g, g);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_gc_decref(ctx, g);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value * r = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_apply(ctx, r, f, e);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
nix_value_force(ctx, state, r);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
auto n = nix_get_attrs_size(ctx, r);
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_EQ(1, n);
|
||||||
|
|
||||||
|
// nix_get_attr_byname isn't lazy (it could have been) so it will throw the exception
|
||||||
|
Value * foo = nix_get_attr_byname(ctx, r, state, "foo");
|
||||||
|
ASSERT_EQ(nullptr, foo);
|
||||||
|
ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("error message for test case nix_value_init_apply_lazy_arg"));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
nix_gc_decref(ctx, f);
|
||||||
|
nix_gc_decref(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue