Преглед на файлове

ncd: Refactor the function call infrastructure.

Ambroz Bizjak преди 11 години
родител
ревизия
e2979251df
променени са 8 файла, в които са добавени 383 реда и са изтрити 222 реда
  1. 26 12
      ncd/NCDInterpreter.c
  2. 1 1
      ncd/NCDInterpreter.h
  3. 72 2
      ncd/NCDModule.c
  4. 153 60
      ncd/NCDModule.h
  5. 8 8
      ncd/extra/value_utils.c
  6. 3 3
      ncd/extra/value_utils.h
  7. 2 2
      ncd/module_common.h
  8. 118 134
      ncd/modules/basic_functions.c

+ 26 - 12
ncd/NCDInterpreter.c

@@ -80,6 +80,11 @@ struct process {
     struct statement statements[];
 };
 
+struct func_call_context {
+    struct statement *ps;
+    NCDEvaluatorArgs *args;
+};
+
 static void start_terminate (NCDInterpreter *interp, int exit_code);
 static char * implode_id_strings (NCDInterpreter *interp, const NCD_string_id_t *names, size_t num_names, char del);
 static void clear_process_cache (NCDInterpreter *interp);
@@ -121,7 +126,8 @@ static btime_t statement_instance_func_interp_getretrytime (void *vinterp);
 static int statement_instance_func_interp_loadgroup (void *vinterp, const struct NCDModuleGroup *group);
 static void process_moduleprocess_func_event (struct process *p, int event);
 static int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object);
-static void function_logfunc (void *vo);
+static void function_logfunc (void *user);
+static int function_eval_arg (void *user, size_t index, NCDValMem *mem, NCDValRef *out);
 
 #define STATEMENT_LOG(ps, channel, ...) if (BLog_WouldLog(BLOG_CURRENT_CHANNEL, channel)) statement_log(ps, channel, __VA_ARGS__)
 
@@ -215,8 +221,9 @@ int NCDInterpreter_Init (NCDInterpreter *o, NCDProgram program, struct NCDInterp
     o->module_iparams.func_interp_getargs = statement_instance_func_interp_getargs;
     o->module_iparams.func_interp_getretrytime = statement_instance_func_interp_getretrytime;
     o->module_iparams.func_loadgroup = statement_instance_func_interp_loadgroup;
-    o->module_func_params.logfunc = function_logfunc;
-    o->module_func_params.iparams = &o->module_iparams;
+    o->module_call_shared.logfunc = function_logfunc;
+    o->module_call_shared.func_eval_arg = function_eval_arg;
+    o->module_call_shared.iparams = &o->module_iparams;
     
     // init processes list
     LinkedList1_Init(&o->processes);
@@ -838,12 +845,11 @@ static int eval_func_eval_call (void *user, NCD_string_id_t func_name_id, NCDEva
         return 0;
     }
     
-    struct NCDModuleFunction_eval_params params;
-    params.ifunc = ifunc;
-    params.params = &p->interp->module_func_params;
-    params.interp_user = ps;
+    struct func_call_context context;
+    context.ps = ps;
+    context.args = &args;
     
-    return ifunc->function.func_eval(args, mem, out, &params);
+    return NCDCall_DoIt(&p->interp->module_call_shared, &context, ifunc, NCDEvaluatorArgs_Count(&args), mem, out);
 }
 
 void process_advance (struct process *p)
@@ -1372,11 +1378,19 @@ int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name,
     return process_find_object(p, p->num_statements, name, out_object);
 }
 
-void function_logfunc (void *vo)
+void function_logfunc (void *user)
 {
-    struct statement *ps = vo;
-    ASSERT(ps->inst.istate == SSTATE_FORGOTTEN)
+    struct func_call_context const *context = user;
+    ASSERT(context->ps->inst.istate == SSTATE_FORGOTTEN)
     
-    statement_logfunc(ps);
+    statement_logfunc(context->ps);
     BLog_Append("func_eval: ");
 }
+
+int function_eval_arg (void *user, size_t index, NCDValMem *mem, NCDValRef *out)
+{
+    struct func_call_context const *context = user;
+    ASSERT(context->ps->inst.istate == SSTATE_FORGOTTEN)
+    
+    return NCDEvaluatorArgs_EvalArg(context->args, index, mem, out);
+}

+ 1 - 1
ncd/NCDInterpreter.h

@@ -112,7 +112,7 @@ typedef struct {
     // common module parameters
     struct NCDModuleInst_params module_params;
     struct NCDModuleInst_iparams module_iparams;
-    struct NCDModuleFunction_params module_func_params;
+    struct NCDCall_interp_shared module_call_shared;
     
     // processes
     LinkedList1 processes;

+ 72 - 2
ncd/NCDModule.c

@@ -588,9 +588,79 @@ int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, NCD_string_id_t
     return res;
 }
 
-BLogContext NCDModuleFunction_LogContext (struct NCDModuleFunction_eval_params const *params)
+int NCDCall_DoIt (
+    struct NCDCall_interp_shared const *interp_shared, void *interp_user,
+    struct NCDInterpFunction const *interp_function,
+    size_t arg_count, NCDValMem *res_mem, NCDValRef *res_out
+)
 {
-    return BLog_MakeContext(params->params->logfunc, params->interp_user);
+    NCDValRef res = NCDVal_NewInvalid();
+    
+    NCDCall call;
+    call.interp_shared = interp_shared;
+    call.interp_user = interp_user;
+    call.interp_function = interp_function;
+    call.arg_count = arg_count;
+    call.res_mem = res_mem;
+    call.out_ref = &res;
+    
+    interp_function->function.func_eval(call);
+    
+    if (NCDVal_IsInvalid(res)) {
+        return 0;
+    }
+    
+    *res_out = res;
+    return 1;
+}
+
+struct NCDInterpFunction const * NCDCall_InterpFunction (NCDCall const *o)
+{
+    return o->interp_function;
+}
+
+struct NCDModuleInst_iparams const * NCDCall_Iparams (NCDCall const *o)
+{
+    return o->interp_shared->iparams;
+}
+
+size_t NCDCall_ArgCount (NCDCall const *o)
+{
+    return o->arg_count;
+}
+
+NCDValRef NCDCall_EvalArg (NCDCall const *o, size_t index, NCDValMem *mem)
+{
+    ASSERT(index < o->arg_count)
+    ASSERT(mem)
+    
+    NCDValRef res;
+    
+    int eval_res = o->interp_shared->func_eval_arg(o->interp_user, index, mem, &res);
+    
+    ASSERT(eval_res == 0 || eval_res == 1)
+    ASSERT(eval_res == 0 || (NCDVal_Assert(res), 1))
+    
+    if (!eval_res) {
+        res = NCDVal_NewInvalid();
+    }
+    
+    return res;
+}
+
+NCDValMem * NCDCall_ResMem (NCDCall const *o)
+{
+    return o->res_mem;
+}
+
+void NCDCall_SetResult (NCDCall const *o, NCDValRef ref)
+{
+    *o->out_ref = ref;
+}
+
+BLogContext NCDCall_LogContext (NCDCall const *o)
+{
+    return BLog_MakeContext(o->interp_shared->logfunc, o->interp_user);
 }
 
 static int process_args_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value)

+ 153 - 60
ncd/NCDModule.h

@@ -37,7 +37,6 @@
 #include <base/BLog.h>
 #include <ncd/NCDObject.h>
 #include <ncd/NCDStringIndex.h>
-#include <ncd/NCDEvaluator.h>
 
 #ifndef BADVPN_NO_PROCESS
 #include <system/BProcess.h>
@@ -60,6 +59,7 @@ struct NCDModuleProcess_s;
 struct NCDModuleGroup;
 struct NCDInterpModule;
 struct NCDInterpModuleGroup;
+struct NCDCall_interp_shared;
 struct NCDInterpFunction;
 
 /**
@@ -1018,67 +1018,172 @@ struct NCDInterpModuleGroup {
 };
 
 /**
- * Holds some things passed to function implementations which are presumed
- * to stay the same within an interpreter (for optimization purposes).
+ * An abstraction of function call evaluations, mutually decoupling the
+ * interpreter and the function implementations.
+ * 
+ * The function implementation is given an instance of this structure
+ * in the evaluation callback (func_eval), and uses it to request
+ * information and submit results. The function implementation does these
+ * things by calling the various NCDCall functions with the NCDCall
+ * instance. Note that function arguments are evaluated on demand from
+ * the function implementation. This enables behavior such as
+ * short-circuit evaluation of logical operators.
+ * 
+ * The NCDCall struct has a value semantic - it can be copied
+ * around freely by the function implementation during the
+ * lifetime of the evaluation call.
  */
-struct NCDModuleFunction_params {
-    /**
-     * Log function which appends a log prefix with {@link BLog_Append}.
-     * Its user argument will be the user member of {@link NCDModuleFunction_eval_params}.
-     */
-    BLog_logfunc logfunc;
-    
-    /**
-     * Pointer to an {@link NCDModuleInst_iparams} structure, which exposes
-     * services provided by the interpreter.
-     */
-    const struct NCDModuleInst_iparams *iparams;
-};
+typedef struct {
+    struct NCDCall_interp_shared const *interp_shared;
+    void *interp_user;
+    struct NCDInterpFunction const *interp_function;
+    size_t arg_count;
+    NCDValMem *res_mem;
+    NCDValRef *out_ref;
+} NCDCall;
 
 /**
- * Contains parameters to {@link NCDModuleFunction_func_eval} that are passed indirectly.
+ * Used by the intepreter to call a function.
+ * 
+ * It calls the func_eval callback of the function implementation
+ * with an appropriately initialized NCDCall value. This is the only
+ * NCDCall related function used by the interpreter.
+ * 
+ * As part of the call, callbacks to the interpreter (given in interp_shared)
+ * may occur. All of these callbacks are passed interp_user as the first
+ * argument. The callbacks are:
+ * - logfunc (to log a message),
+ * - func_eval_arg (to evaluate a particular function argument).
+ * 
+ * @param interp_shared pointer to things expected to be the same for all
+ *   function calls by the interpreter. This includes interpreter callbacks.
+ * @param interp_user pointer to be passed through to interpreter callbacks
+ * @param interp_function the function to call. The evaluation function of
+ *   the function implementation that will be called is taken from here
+ *   (interp_function->function.func_eval), but this is also exposed to the
+ *   function implementation, so it should be initialized appropriately.
+ * @param arg_count number of arguments passed to the function.
+ *   The function implementation is only permitted to attempt evaluation
+ *   of arguments with indices lesser than arg_count.
+ * @param res_mem the (initialized) value memory object for the result to
+ *   be stored into. However this may also be used as storage for temporary
+ *   values computed as part of the call.
+ * @param res_out on success, *res_out will be set to the result of the call
+ * @return 1 on success, 0 on error
  */
-struct NCDModuleFunction_eval_params {
+int NCDCall_DoIt (
+    struct NCDCall_interp_shared const *interp_shared, void *interp_user,
+    struct NCDInterpFunction const *interp_function,
+    size_t arg_count, NCDValMem *res_mem, NCDValRef *res_out
+) WARN_UNUSED;
+
+/**
+ * Returns a pointer to the NCDInterpFunction object for the function,
+ * initialized by the interpreter. This alows, among other things,
+ * determining which function is being called, and getting the module
+ * group's private data pointer.
+ */
+struct NCDInterpFunction const * NCDCall_InterpFunction (NCDCall const *o);
+
+/**
+ * Returns a pointer to an NCDModuleInst_iparams structure, exposing
+ * services provided by the interpreter.
+ */
+struct NCDModuleInst_iparams const * NCDCall_Iparams (NCDCall const *o);
+
+/**
+ * Returns the number of arguments for the function call.
+ */
+size_t NCDCall_ArgCount (NCDCall const *o);
+
+/**
+ * Attempts to evaluate a function argument.
+ * 
+ * @param index the index of the argument to be evaluated. Must be
+ *   in the range [0, ArgCount).
+ * @param mem the value memory object for the value to be stored into.
+ *   However temporary value data may also be stored here.
+ * @return on success, the value reference to the argument value;
+ *   on failure, an invalid value reference
+ */
+NCDValRef NCDCall_EvalArg (NCDCall const *o, size_t index, NCDValMem *mem);
+
+/**
+ * Returns a pointer to the value memory object that the result
+ * of the call should be stored into. The memory object may also
+ * be used to store temporary value data.
+ */
+NCDValMem * NCDCall_ResMem (NCDCall const *o);
+
+/**
+ * Provides result value of the evaluation to the interpreter.
+ * Note that this only stores the result without any immediate
+ * action.
+ * 
+ * Passing an invalid value reference indicates failure of the
+ * call, though failure is also assumed if this function is
+ * not called at all during the call. When a non-invalid
+ * value reference is passed (indicating success), it must point
+ * to a value stored within the memory object returned by
+ * NCDCall_ResMem.
+ * 
+ * @param ref the result value for the call, or an invalid
+ *   value reference to indicate failure of the call
+ */
+void NCDCall_SetResult (NCDCall const *o, NCDValRef ref);
+
+/**
+ * Returns a log context that can be used to log messages
+ * associated with the call.
+ */
+BLogContext NCDCall_LogContext (NCDCall const *o);
+
+/**
+ * A structure initialized by the interpreter and passed
+ * to NCDCall_DoIt for function evaluations.
+ * It contains a pointer to things expected to be the same for all
+ * function calls by the interpreter, so it can be initialized once
+ * and not for every call.
+ */
+struct NCDCall_interp_shared {
     /**
-     * A pointer to the {@link NCDInterpFunction} structure for the function being called.
+     * A callack for log messages originating from the function call.
+     * The first argument is the interp_user argument to NCDCall_DoIt.
      */
-    struct NCDInterpFunction const *ifunc;
+    BLog_logfunc logfunc;
     
     /**
-     * Pointer to an additional structure needed for the call, containing callbacks to
-     * the interpreter.
+     * A callback for evaluating function arguments.
+     * 
+     * @param user interpreter private data (the interp_user argument
+     *   to NCDCall_DoIt)
+     * @param index the index of the argument to be evaluated.
+     *   This will be in the range [0, arg_count).
+     * @param mem the value memory object where the result of the
+     *   evaluation should be stored. Temporary value data may also
+     *   be stored here.
+     * @param out on success, *out should be set to the value reference
+     *   to the result of the evaluation. An invalid value reference is
+     *   permitted, in which case failure is assumed.
+     * @return 1 on success (but see above), 0 on failure
      */
-    struct NCDModuleFunction_params const *params;
+    int (*func_eval_arg) (void *user, size_t index, NCDValMem *mem, NCDValRef *out);
     
     /**
-     * This is passed back to interpeter callbacks done as part of function evaluation.
+     * A pointer to an NCDModuleInst_iparams structure, exposing
+     * services provided by the interpreter.
      */
-    void *interp_user;
+    struct NCDModuleInst_iparams const *iparams;
 };
 
 /**
- * Callback for evaluating a function (defined by an {@link NCDModuleFunction} structure).
+ * This structure is initialized statically by a function
+ * implementation to describe the function and provide
+ * the resources required for its evaluation by the interpreter.
  * 
- * @param args arguments to evaluate the function with.
- *             These arguments are provided in a lazy manner - an argument
- *             must be evaluated to obtain its value.
- * @param mem an existing value memory object to store the value into
- * @param out on success, *out should be set to the value reference to
- *            the evaluated value, residing in the specified memory
- *            object. It is permitted to return success but store an
- *            invalid value reference here, which is understood as an
- *            error.
- * @param params indirectly passed arguments
- * @return 1 on success, 0 on error (but see the description of the out parameter).
- */
-typedef int (*NCDModuleFunction_func_eval) (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params);
-
-/**
- * Returns a logging context for use by the function implementation during
- * function evaluation.
+ * These structures always appear in arrays, pointed to by
+ * the functions pointer in the NCDModuleGroup structure.
  */
-BLogContext NCDModuleFunction_LogContext (struct NCDModuleFunction_eval_params const *params);
-
 struct NCDModuleFunction {
     /**
      * The name of the function.
@@ -1088,22 +1193,8 @@ struct NCDModuleFunction {
     
     /**
      * Callback for evaluating the function.
-     * 
-     * @param group the group the function belongs to
-     * @param func pointer to this structure. It can be used to implement
-     *             different functions in the same callback, by reading the
-     *             func_name member.
-     * @param args used to access the arguments to the function, in a lazily
-     *             evaluated manner
-     * @param mem an existing value memory object to store the result to
-     * @param out on success, *out should be set to the value reference to
-     *            the evaluated value, residing in the specified memory
-     *            object. It is permitted to return success but store an
-     *            invalid value reference here, which is understood as an
-     *            error.
-     * @return 1 on success, 0 on error (but see above).
      */
-    NCDModuleFunction_func_eval func_eval;
+    void (*func_eval) (NCDCall call);
 };
 
 /**
@@ -1113,6 +1204,8 @@ struct NCDModuleFunction {
 struct NCDInterpFunction {
     /**
      * A copy of the original NCDModuleFunction structure.
+     * We could just use a pointer to the original statically defined structure,
+     * but then we wouldn't be annoying the premature-optimization hipsters.
      */
     struct NCDModuleFunction function;
     

+ 8 - 8
ncd/extra/value_utils.c

@@ -38,7 +38,7 @@
 #include <system/BTime.h>
 #include <ncd/NCDVal.h>
 #include <ncd/NCDStringIndex.h>
-#include <ncd/NCDEvaluator.h>
+#include <ncd/NCDModule.h>
 #include <ncd/static_strings.h>
 
 #include "value_utils.h"
@@ -169,10 +169,10 @@ char * ncd_strdup (NCDValRef stringnonulls)
     return b_cstring_strdup(cstr, 0, cstr.length);
 }
 
-int ncd_eval_func_args_ext (NCDEvaluatorArgs args, size_t start, size_t count, NCDValMem *mem, NCDValRef *out)
+int ncd_eval_func_args_ext (NCDCall const *call, size_t start, size_t count, NCDValMem *mem, NCDValRef *out)
 {
-    ASSERT(start <= NCDEvaluatorArgs_Count(&args))
-    ASSERT(count <= NCDEvaluatorArgs_Count(&args) - start)
+    ASSERT(start <= NCDCall_ArgCount(call))
+    ASSERT(count <= NCDCall_ArgCount(call) - start)
     
     *out = NCDVal_NewList(mem, count);
     if (NCDVal_IsInvalid(*out)) {
@@ -180,8 +180,8 @@ int ncd_eval_func_args_ext (NCDEvaluatorArgs args, size_t start, size_t count, N
     }
     
     for (size_t i = 0; i < count; i++) {
-        NCDValRef elem;
-        if (!NCDEvaluatorArgs_EvalArg(&args, start + i, mem, &elem)) {
+        NCDValRef elem = NCDCall_EvalArg(call, start + i, mem);
+        if (NCDVal_IsInvalid(elem)) {
             goto fail;
         }
         if (!NCDVal_ListAppend(*out, elem)) {
@@ -195,7 +195,7 @@ fail:
     return 0;
 }
 
-int ncd_eval_func_args (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out)
+int ncd_eval_func_args (NCDCall const *call, NCDValMem *mem, NCDValRef *out)
 {
-    return ncd_eval_func_args_ext(args, 0, NCDEvaluatorArgs_Count(&args), mem, out);
+    return ncd_eval_func_args_ext(call, 0, NCDCall_ArgCount(call), mem, out);
 }

+ 3 - 3
ncd/extra/value_utils.h

@@ -36,7 +36,7 @@
 #include <system/BTime.h>
 #include <ncd/NCDVal.h>
 #include <ncd/NCDStringIndex.h>
-#include <ncd/NCDEvaluator.h>
+#include <ncd/NCDModule.h>
 
 int ncd_is_none (NCDValRef val);
 NCDValRef ncd_make_boolean (NCDValMem *mem, int value, NCDStringIndex *string_index);
@@ -46,7 +46,7 @@ int ncd_read_time (NCDValRef string, btime_t *out) WARN_UNUSED;
 NCD_string_id_t ncd_get_string_id (NCDValRef string, NCDStringIndex *string_index);
 NCDValRef ncd_make_uintmax (NCDValMem *mem, uintmax_t value);
 char * ncd_strdup (NCDValRef stringnonulls);
-int ncd_eval_func_args_ext (NCDEvaluatorArgs args, size_t start, size_t count, NCDValMem *mem, NCDValRef *out) WARN_UNUSED;
-int ncd_eval_func_args (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out) WARN_UNUSED;
+int ncd_eval_func_args_ext (NCDCall const *call, size_t start, size_t count, NCDValMem *mem, NCDValRef *out) WARN_UNUSED;
+int ncd_eval_func_args (NCDCall const *call, NCDValMem *mem, NCDValRef *out) WARN_UNUSED;
 
 #endif

+ 2 - 2
ncd/module_common.h

@@ -40,5 +40,5 @@
 #define ModuleLc(i) NCDModuleInst_Backend_LogContext((i))
 #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
 
-#define FunctionLc(params) NCDModuleFunction_LogContext((params))
-#define FunctionLog(params, ...) BContextLog(FunctionLc(params), __VA_ARGS__)
+#define FunctionLc(call) NCDCall_LogContext((call))
+#define FunctionLog(call, ...) BContextLog(FunctionLc(call), __VA_ARGS__)

+ 118 - 134
ncd/modules/basic_functions.c

@@ -36,151 +36,144 @@
 
 // Trivial functions.
 
-static int error_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void error_eval (NCDCall call)
 {
-    FunctionLog(params, BLOG_ERROR, "error: failing");
-    return 0;
+    FunctionLog(&call, BLOG_ERROR, "error: failing");
 }
 
-static int identity_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void identity_eval (NCDCall call)
 {
-    if (NCDEvaluatorArgs_Count(&args) != 1) {
-        FunctionLog(params, BLOG_ERROR, "identity: need one argument");
-        return 0;
+    if (NCDCall_ArgCount(&call) != 1) {
+        FunctionLog(&call, BLOG_ERROR, "identity: need one argument");
     }
-    return NCDEvaluatorArgs_EvalArg(&args, 0, mem, out);
+    NCDCall_SetResult(&call, NCDCall_EvalArg(&call, 0, NCDCall_ResMem(&call)));
 }
 
 
 // Logical functions.
 
-static int if_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void if_eval (NCDCall call)
 {
-    if (NCDEvaluatorArgs_Count(&args) != 3) {
-        FunctionLog(params, BLOG_ERROR, "if: need three arguments");
-        return 0;
+    if (NCDCall_ArgCount(&call) != 3) {
+        FunctionLog(&call, BLOG_ERROR, "if: need three arguments");
+        return;
     }
-    NCDValRef cond;
-    if (!NCDEvaluatorArgs_EvalArg(&args, 0, mem, &cond)) {
-        return 0;
+    NCDValRef cond = NCDCall_EvalArg(&call, 0, NCDCall_ResMem(&call));
+    if (NCDVal_IsInvalid(cond)) {
+        return;
     }
     int cond_val;
     if (!ncd_read_boolean(cond, &cond_val)) {
-        FunctionLog(params, BLOG_ERROR, "if: bad condition");
-        return 0;
+        FunctionLog(&call, BLOG_ERROR, "if: bad condition");
+        return;
     }
     int eval_arg = 2 - cond_val;
-    return NCDEvaluatorArgs_EvalArg(&args, eval_arg, mem, out);
+    NCDCall_SetResult(&call, NCDCall_EvalArg(&call, eval_arg, NCDCall_ResMem(&call)));
 }
 
-static int bool_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void bool_eval (NCDCall call)
 {
-    if (NCDEvaluatorArgs_Count(&args) != 1) {
-        FunctionLog(params, BLOG_ERROR, "bool: need one argument");
-        return 0;
+    if (NCDCall_ArgCount(&call) != 1) {
+        FunctionLog(&call, BLOG_ERROR, "bool: need one argument");
+        return;
     }
-    NCDValRef arg;
-    if (!NCDEvaluatorArgs_EvalArg(&args, 0, mem, &arg)) {
-        return 0;
+    NCDValRef arg = NCDCall_EvalArg(&call, 0, NCDCall_ResMem(&call));
+    if (NCDVal_IsInvalid(arg)) {
+        return;
     }
     int arg_val;
     if (!ncd_read_boolean(arg, &arg_val)) {
-        FunctionLog(params, BLOG_ERROR, "bool: bad argument");
-        return 0;
+        FunctionLog(&call, BLOG_ERROR, "bool: bad argument");
+        return;
     }
-    *out = ncd_make_boolean(mem, arg_val, params->params->iparams->string_index);
-    return 1;
+    NCDCall_SetResult(&call, ncd_make_boolean(NCDCall_ResMem(&call), arg_val, NCDCall_Iparams(&call)->string_index));
 }
 
-static int not_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void not_eval (NCDCall call)
 {
-    if (NCDEvaluatorArgs_Count(&args) != 1) {
-        FunctionLog(params, BLOG_ERROR, "not: need one argument");
-        return 0;
+    if (NCDCall_ArgCount(&call) != 1) {
+        FunctionLog(&call, BLOG_ERROR, "not: need one argument");
+        return;
     }
-    NCDValRef arg;
-    if (!NCDEvaluatorArgs_EvalArg(&args, 0, mem, &arg)) {
-        return 0;
+    NCDValRef arg = NCDCall_EvalArg(&call, 0, NCDCall_ResMem(&call));
+    if (NCDVal_IsInvalid(arg)) {
+        return;
     }
     int arg_val;
     if (!ncd_read_boolean(arg, &arg_val)) {
-        FunctionLog(params, BLOG_ERROR, "not: bad argument");
-        return 0;
+        FunctionLog(&call, BLOG_ERROR, "not: bad argument");
+        return;
     }
-    *out = ncd_make_boolean(mem, !arg_val, params->params->iparams->string_index);
-    return 1;
+    NCDCall_SetResult(&call, ncd_make_boolean(NCDCall_ResMem(&call), !arg_val, NCDCall_Iparams(&call)->string_index));
 }
 
-static int and_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void and_eval (NCDCall call)
 {
-    size_t count = NCDEvaluatorArgs_Count(&args);
+    size_t count = NCDCall_ArgCount(&call);
     int res = 1;
     for (size_t i = 0; i < count; i++) {
-        NCDValRef arg;
-        if (!NCDEvaluatorArgs_EvalArg(&args, i, mem, &arg)) {
-            return 0;
+        NCDValRef arg = NCDCall_EvalArg(&call, i, NCDCall_ResMem(&call));
+        if (NCDVal_IsInvalid(arg)) {
+            return;
         }
         int arg_val;
         if (!ncd_read_boolean(arg, &arg_val)) {
-            FunctionLog(params, BLOG_ERROR, "and: bad argument");
-            return 0;
+            FunctionLog(&call, BLOG_ERROR, "and: bad argument");
+            return;
         }
         if (!arg_val) {
             res = 0;
             break;
         }
     }
-    *out = ncd_make_boolean(mem, res, params->params->iparams->string_index);
-    return 1;
+    NCDCall_SetResult(&call, ncd_make_boolean(NCDCall_ResMem(&call), res, NCDCall_Iparams(&call)->string_index));
 }
 
-static int or_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void or_eval (NCDCall call)
 {
-    size_t count = NCDEvaluatorArgs_Count(&args);
+    size_t count = NCDCall_ArgCount(&call);
     int res = 0;
     for (size_t i = 0; i < count; i++) {
-        NCDValRef arg;
-        if (!NCDEvaluatorArgs_EvalArg(&args, i, mem, &arg)) {
-            return 0;
+        NCDValRef arg = NCDCall_EvalArg(&call, i, NCDCall_ResMem(&call));
+        if (NCDVal_IsInvalid(arg)) {
+            return;
         }
         int arg_val;
         if (!ncd_read_boolean(arg, &arg_val)) {
-            FunctionLog(params, BLOG_ERROR, "or: bad argument");
-            return 0;
+            FunctionLog(&call, BLOG_ERROR, "or: bad argument");
+            return;
         }
         if (arg_val) {
             res = 1;
             break;
         }
     }
-    *out = ncd_make_boolean(mem, res, params->params->iparams->string_index);
-    return 1;
+    NCDCall_SetResult(&call, ncd_make_boolean(NCDCall_ResMem(&call), res, NCDCall_Iparams(&call)->string_index));
 }
 
-static int imp_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void imp_eval (NCDCall call)
 {
-    if (NCDEvaluatorArgs_Count(&args) != 2) {
-        FunctionLog(params, BLOG_ERROR, "imp: need two arguments");
-        return 0;
+    if (NCDCall_ArgCount(&call) != 2) {
+        FunctionLog(&call, BLOG_ERROR, "imp: need two arguments");
+        return;
     }
     int res = 0;
     for (size_t i = 0; i < 2; i++) {
-        NCDValRef arg;
-        if (!NCDEvaluatorArgs_EvalArg(&args, i, mem, &arg)) {
-            return 0;
+        NCDValRef arg = NCDCall_EvalArg(&call, i, NCDCall_ResMem(&call));
+        if (NCDVal_IsInvalid(arg)) {
+            return;
         }
         int arg_val;
         if (!ncd_read_boolean(arg, &arg_val)) {
-            FunctionLog(params, BLOG_ERROR, "imp: bad argument");
-            return 0;
+            FunctionLog(&call, BLOG_ERROR, "imp: bad argument");
+            return;
         }
         if (arg_val == i) {
             res = 1;
             break;
         }
     }
-    *out = ncd_make_boolean(mem, res, params->params->iparams->string_index);
-    return 1;
+    NCDCall_SetResult(&call, ncd_make_boolean(NCDCall_ResMem(&call), res, NCDCall_Iparams(&call)->string_index));
 }
 
 
@@ -188,24 +181,21 @@ static int imp_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, stru
 
 typedef int (*value_compare_func) (int cmp);
 
-static int value_compare_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params, value_compare_func func)
+static void value_compare_eval (NCDCall call, value_compare_func func)
 {
-    int res = 0;
-    if (NCDEvaluatorArgs_Count(&args) != 2) {
-        FunctionLog(params, BLOG_ERROR, "value_compare: need two arguments");
-        goto fail0;
+    if (NCDCall_ArgCount(&call) != 2) {
+        FunctionLog(&call, BLOG_ERROR, "value_compare: need two arguments");
+        return;
     }
     NCDValRef vals[2];
     for (int i = 0; i < 2; i++) {
-        if (!NCDEvaluatorArgs_EvalArg(&args, i, mem, &vals[i])) {
-            goto fail0;
+        vals[i] = NCDCall_EvalArg(&call, i, NCDCall_ResMem(&call));
+        if (NCDVal_IsInvalid(vals[i])) {
+            return;
         }
     }
     int value = func(NCDVal_Compare(vals[0], vals[1]));
-    *out = ncd_make_boolean(mem, value, params->params->iparams->string_index);
-    res = 1;
-fail0:
-    return res;
+    NCDCall_SetResult(&call, ncd_make_boolean(NCDCall_ResMem(&call), value, NCDCall_Iparams(&call)->string_index));
 }
 
 #define DEFINE_VALUE_COMPARE(name, expr) \
@@ -213,9 +203,9 @@ static int value_compare_##name##_func (int cmp) \
 { \
     return expr; \
 } \
-static int value_compare_##name##_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params) \
+static void value_compare_##name##_eval (NCDCall call) \
 { \
-    return value_compare_eval(args, mem, out, params, value_compare_##name##_func); \
+    return value_compare_eval(call, value_compare_##name##_func); \
 }
 
 DEFINE_VALUE_COMPARE(lesser, (cmp < 0))
@@ -228,58 +218,55 @@ DEFINE_VALUE_COMPARE(different, (cmp != 0))
 
 // Concatenation functions.
 
-static int concat_recurser (ExpString *estr, NCDValRef arg, struct NCDModuleFunction_eval_params const *params)
+static int concat_recurser (ExpString *estr, NCDValRef arg, NCDCall const *call)
 {
     if (NCDVal_IsString(arg)) {
         if (!ExpString_AppendBinary(estr, (uint8_t const *)NCDVal_StringData(arg), NCDVal_StringLength(arg))) {
-            FunctionLog(params, BLOG_ERROR, "ExpString_AppendBinary failed");
+            FunctionLog(call, BLOG_ERROR, "ExpString_AppendBinary failed");
             return 0;
         }
     } else if (NCDVal_IsList(arg)) {
         size_t count = NCDVal_ListCount(arg);
         for (size_t i = 0; i < count; i++) {
-            if (!concat_recurser(estr, NCDVal_ListGet(arg, i), params)) {
+            if (!concat_recurser(estr, NCDVal_ListGet(arg, i), call)) {
                 return 0;
             }
         }
     } else {
-        FunctionLog(params, BLOG_ERROR, "concat: value is not a string or list");
+        FunctionLog(call, BLOG_ERROR, "concat: value is not a string or list");
         return 0;
     }
     return 1;
 }
 
-static int concat_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void concat_eval (NCDCall call)
 {
-    int res = 0;
     ExpString estr;
     if (!ExpString_Init(&estr)) {
-        FunctionLog(params, BLOG_ERROR, "ExpString_Init failed");
+        FunctionLog(&call, BLOG_ERROR, "ExpString_Init failed");
         goto fail0;
     }
-    size_t count = NCDEvaluatorArgs_Count(&args);
+    size_t count = NCDCall_ArgCount(&call);
     for (size_t i = 0; i < count; i++) {
-        NCDValRef arg;
-        if (!NCDEvaluatorArgs_EvalArg(&args, i, mem, &arg)) {
+        NCDValRef arg = NCDCall_EvalArg(&call, i, NCDCall_ResMem(&call));
+        if (NCDVal_IsInvalid(arg)) {
             goto fail1;
         }
-        if (!concat_recurser(&estr, arg, params)) {
+        if (!concat_recurser(&estr, arg, &call)) {
             goto fail1;
         }
     }
-    *out = NCDVal_NewStringBin(mem, (uint8_t const *)ExpString_Get(&estr), ExpString_Length(&estr));
-    res = 1;
+    NCDCall_SetResult(&call, NCDVal_NewStringBin(NCDCall_ResMem(&call), (uint8_t const *)ExpString_Get(&estr), ExpString_Length(&estr)));
 fail1:
     ExpString_Free(&estr);
 fail0:
-    return res;
+    return;
 }
 
-static int concatlist_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params)
+static void concatlist_eval (NCDCall call)
 {
-    int res = 0;
     NCDValRef args_list;
-    if (!ncd_eval_func_args(args, mem, &args_list)) {
+    if (!ncd_eval_func_args(&call, NCDCall_ResMem(&call), &args_list)) {
         goto fail0;
     }
     size_t arg_count = NCDVal_ListCount(args_list);
@@ -287,31 +274,31 @@ static int concatlist_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *ou
     for (size_t i = 0; i < arg_count; i++) {
         NCDValRef arg = NCDVal_ListGet(args_list, i);
         if (!NCDVal_IsList(arg)) {
-            FunctionLog(params, BLOG_ERROR, "concatlist: argument is not a list");
+            FunctionLog(&call, BLOG_ERROR, "concatlist: argument is not a list");
             goto fail0;
         }
         elem_count += NCDVal_ListCount(arg);
     }
-    *out = NCDVal_NewList(mem, elem_count);
-    if (NCDVal_IsInvalid(*out)) {
+    NCDValRef res = NCDVal_NewList(NCDCall_ResMem(&call), elem_count);
+    if (NCDVal_IsInvalid(res)) {
         goto fail0;
     }
     for (size_t i = 0; i < arg_count; i++) {
         NCDValRef arg = NCDVal_ListGet(args_list, i);
         size_t arg_list_count = NCDVal_ListCount(arg);
         for (size_t j = 0; j < arg_list_count; j++) {
-            NCDValRef copy = NCDVal_NewCopy(mem, NCDVal_ListGet(arg, j));
+            NCDValRef copy = NCDVal_NewCopy(NCDCall_ResMem(&call), NCDVal_ListGet(arg, j));
             if (NCDVal_IsInvalid(copy)) {
                 goto fail0;
             }
-            if (!NCDVal_ListAppend(*out, copy)) {
+            if (!NCDVal_ListAppend(res, copy)) {
                 goto fail0;
             }
         }
     }
-    res = 1;
+    NCDCall_SetResult(&call, res);
 fail0:
-    return res;
+    return;
 }
 
 
@@ -319,29 +306,27 @@ fail0:
 
 typedef int (*integer_compare_func) (uintmax_t n1, uintmax_t n2);
 
-static int integer_compare_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params, integer_compare_func func)
+static void integer_compare_eval (NCDCall call, integer_compare_func func)
 {
-    int res = 0;
-    if (NCDEvaluatorArgs_Count(&args) != 2) {
-        FunctionLog(params, BLOG_ERROR, "integer_compare: need two arguments");
+    if (NCDCall_ArgCount(&call) != 2) {
+        FunctionLog(&call, BLOG_ERROR, "integer_compare: need two arguments");
         goto fail0;
     }
     uintmax_t ints[2];
     for (int i = 0; i < 2; i++) {
-        NCDValRef arg;
-        if (!NCDEvaluatorArgs_EvalArg(&args, i, mem, &arg)) {
+        NCDValRef arg = NCDCall_EvalArg(&call, i, NCDCall_ResMem(&call));
+        if (NCDVal_IsInvalid(arg)) {
             goto fail0;
         }
         if (!ncd_read_uintmax(arg, &ints[i])) {
-            FunctionLog(params, BLOG_ERROR, "integer_compare: wrong value");
+            FunctionLog(&call, BLOG_ERROR, "integer_compare: wrong value");
             goto fail0;
         }
     }
     int value = func(ints[0], ints[1]);
-    *out = ncd_make_boolean(mem, value, params->params->iparams->string_index);
-    res = 1;
+    NCDCall_SetResult(&call, ncd_make_boolean(NCDCall_ResMem(&call), value, NCDCall_Iparams(&call)->string_index));
 fail0:
-    return res;
+    return;
 }
 
 #define DEFINE_INT_COMPARE(name, expr) \
@@ -349,9 +334,9 @@ static int integer_compare_##name##_func (uintmax_t n1, uintmax_t n2) \
 { \
     return expr; \
 } \
-static int integer_compare_##name##_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params) \
+static void integer_compare_##name##_eval (NCDCall call) \
 { \
-    return integer_compare_eval(args, mem, out, params, integer_compare_##name##_func); \
+    return integer_compare_eval(call, integer_compare_##name##_func); \
 }
 
 DEFINE_INT_COMPARE(lesser, (n1 < n2))
@@ -364,49 +349,47 @@ DEFINE_INT_COMPARE(different, (n1 != n2))
 
 // Integer operators.
 
-typedef int (*integer_operator_func) (uintmax_t n1, uintmax_t n2, uintmax_t *out, struct NCDModuleFunction_eval_params const *params);
+typedef int (*integer_operator_func) (uintmax_t n1, uintmax_t n2, uintmax_t *out, NCDCall const *call);
 
-static int integer_operator_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params, integer_operator_func func)
+static void integer_operator_eval (NCDCall call, integer_operator_func func)
 {
-    int res = 0;
-    if (NCDEvaluatorArgs_Count(&args) != 2) {
-        FunctionLog(params, BLOG_ERROR, "integer_operator: need two arguments");
+    if (NCDCall_ArgCount(&call) != 2) {
+        FunctionLog(&call, BLOG_ERROR, "integer_operator: need two arguments");
         goto fail0;
     }
     uintmax_t ints[2];
     for (int i = 0; i < 2; i++) {
-        NCDValRef arg;
-        if (!NCDEvaluatorArgs_EvalArg(&args, i, mem, &arg)) {
+        NCDValRef arg = NCDCall_EvalArg(&call, i, NCDCall_ResMem(&call));
+        if (NCDVal_IsInvalid(arg)) {
             goto fail0;
         }
         if (!ncd_read_uintmax(arg, &ints[i])) {
-            FunctionLog(params, BLOG_ERROR, "integer_operator: wrong value");
+            FunctionLog(&call, BLOG_ERROR, "integer_operator: wrong value");
             goto fail0;
         }
     }
     uintmax_t value;
-    if (!func(ints[0], ints[1], &value, params)) {
+    if (!func(ints[0], ints[1], &value, &call)) {
         goto fail0;
     }
-    *out = ncd_make_uintmax(mem, value);
-    res = 1;
+    NCDCall_SetResult(&call, ncd_make_uintmax(NCDCall_ResMem(&call), value));
 fail0:
-    return res;
+    return;
 }
 
 #define DEFINE_INT_OPERATOR(name, expr, check_expr, check_err_str) \
-static int integer_operator_##name##_func (uintmax_t n1, uintmax_t n2, uintmax_t *out, struct NCDModuleFunction_eval_params const *params) \
+static int integer_operator_##name##_func (uintmax_t n1, uintmax_t n2, uintmax_t *out, NCDCall const *call) \
 { \
     if (check_expr) { \
-        FunctionLog(params, BLOG_ERROR, check_err_str); \
+        FunctionLog(call, BLOG_ERROR, check_err_str); \
         return 0; \
     } \
     *out = expr; \
     return 1; \
 } \
-static int integer_operator_##name##_eval (NCDEvaluatorArgs args, NCDValMem *mem, NCDValRef *out, struct NCDModuleFunction_eval_params const *params) \
+static void integer_operator_##name##_eval (NCDCall call) \
 { \
-    return integer_operator_eval(args, mem, out, params, integer_operator_##name##_func); \
+    return integer_operator_eval(call, integer_operator_##name##_func); \
 }
 
 DEFINE_INT_OPERATOR(add, (n1 + n2), (n1 > UINTMAX_MAX - n2), "addition overflow")
@@ -415,6 +398,7 @@ DEFINE_INT_OPERATOR(multiply, (n1 * n2), (n2 != 0 && n1 > UINTMAX_MAX / n2), "mu
 DEFINE_INT_OPERATOR(divide, (n1 / n2), (n2 == 0), "division quotient is zero")
 DEFINE_INT_OPERATOR(modulo, (n1 % n2), (n2 == 0), "modulo modulus is zero")
 
+
 static struct NCDModuleFunction const functions[] = {
     {
         .func_name = "__error__",