badvpn/ncd/NCDEvaluator.c
Ambroz Bizjak 347fa1a7a5 ncd: Store the NCDStingIndex pointer in NCDValMem.
This cuts down on the boilerplate especially with ncd_make_boolean().
2015-05-09 00:01:49 +02:00

446 lines
14 KiB
C

/**
* @file NCDEvaluator.c
* @author Ambroz Bizjak <ambrop7@gmail.com>
*
* @section LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stddef.h>
#include <limits.h>
#include <misc/debug.h>
#include <misc/balloc.h>
#include <base/BLog.h>
#include <ncd/make_name_indices.h>
#include "NCDEvaluator.h"
#include <generated/blog_channel_ncd.h>
#define NCDEVALUATOR_DEFAULT_VARARRAY_CAPACITY 64
#define NCDEVALUATOR_DEFAULT_CALLARRAY_CAPACITY 16
#define MAX_LOCAL_IDS (NCDVAL_TOPPLID / 2)
#include "NCDEvaluator_var_vec.h"
#include <structure/Vector_impl.h>
#include "NCDEvaluator_call_vec.h"
#include <structure/Vector_impl.h>
struct NCDEvaluator__eval_context {
NCDEvaluator *eval;
NCDEvaluator_EvalFuncs const *funcs;
};
static int expr_init (struct NCDEvaluator__Expr *o, NCDEvaluator *eval, NCDValue *value);
static void expr_free (struct NCDEvaluator__Expr *o);
static int expr_eval (struct NCDEvaluator__Expr *o, struct NCDEvaluator__eval_context const *context, NCDValMem *out_newmem, NCDValRef *out_val);
static int add_expr_recurser (NCDEvaluator *o, NCDValue *value, NCDValMem *mem, NCDValRef *out);
static int replace_placeholders_callback (void *arg, int plid, NCDValMem *mem, NCDValRef *out);
static int expr_init (struct NCDEvaluator__Expr *o, NCDEvaluator *eval, NCDValue *value)
{
ASSERT((NCDValue_Type(value), 1))
NCDValMem_Init(&o->mem, eval->string_index);
NCDValRef ref;
if (!add_expr_recurser(eval, value, &o->mem, &ref)) {
goto fail1;
}
o->ref = NCDVal_ToSafe(ref);
if (!NCDVal_IsSafeRefPlaceholder(o->ref)) {
if (!NCDValReplaceProg_Init(&o->prog, ref)) {
BLog(BLOG_ERROR, "NCDValReplaceProg_Init failed");
goto fail1;
}
}
return 1;
fail1:
NCDValMem_Free(&o->mem);
return 0;
}
static void expr_free (struct NCDEvaluator__Expr *o)
{
if (!NCDVal_IsSafeRefPlaceholder(o->ref)) {
NCDValReplaceProg_Free(&o->prog);
}
NCDValMem_Free(&o->mem);
}
static int expr_eval (struct NCDEvaluator__Expr *o, struct NCDEvaluator__eval_context const *context, NCDValMem *out_newmem, NCDValRef *out_val)
{
if (!NCDVal_IsSafeRefPlaceholder(o->ref)) {
if (!NCDValMem_InitCopy(out_newmem, &o->mem)) {
BLog(BLOG_ERROR, "NCDValMem_InitCopy failed");
goto fail0;
}
if (!NCDValReplaceProg_Execute(o->prog, out_newmem, replace_placeholders_callback, (void *)context)) {
goto fail_free;
}
*out_val = NCDVal_FromSafe(out_newmem, o->ref);
} else {
NCDValMem_Init(out_newmem, context->eval->string_index);
NCDValRef ref;
if (!replace_placeholders_callback((void *)context, NCDVal_GetSafeRefPlaceholderId(o->ref), out_newmem, &ref) || NCDVal_IsInvalid(ref)) {
goto fail_free;
}
*out_val = ref;
}
return 1;
fail_free:
NCDValMem_Free(out_newmem);
fail0:
return 0;
}
static int add_expr_recurser (NCDEvaluator *o, NCDValue *value, NCDValMem *mem, NCDValRef *out)
{
switch (NCDValue_Type(value)) {
case NCDVALUE_STRING: {
const char *str = NCDValue_StringValue(value);
size_t len = NCDValue_StringLength(value);
NCD_string_id_t string_id = NCDStringIndex_GetBin(o->string_index, str, len);
if (string_id < 0) {
BLog(BLOG_ERROR, "NCDStringIndex_GetBin failed");
goto fail;
}
*out = NCDVal_NewIdString(mem, string_id);
if (NCDVal_IsInvalid(*out)) {
goto fail;
}
} break;
case NCDVALUE_LIST: {
*out = NCDVal_NewList(mem, NCDValue_ListCount(value));
if (NCDVal_IsInvalid(*out)) {
goto fail;
}
for (NCDValue *e = NCDValue_ListFirst(value); e; e = NCDValue_ListNext(value, e)) {
NCDValRef vval;
if (!add_expr_recurser(o, e, mem, &vval)) {
goto fail;
}
if (!NCDVal_ListAppend(*out, vval)) {
BLog(BLOG_ERROR, "depth limit exceeded");
goto fail;
}
}
} break;
case NCDVALUE_MAP: {
*out = NCDVal_NewMap(mem, NCDValue_MapCount(value));
if (NCDVal_IsInvalid(*out)) {
goto fail;
}
for (NCDValue *ekey = NCDValue_MapFirstKey(value); ekey; ekey = NCDValue_MapNextKey(value, ekey)) {
NCDValue *eval = NCDValue_MapKeyValue(value, ekey);
NCDValRef vkey;
NCDValRef vval;
if (!add_expr_recurser(o, ekey, mem, &vkey) ||
!add_expr_recurser(o, eval, mem, &vval)
) {
goto fail;
}
int inserted;
if (!NCDVal_MapInsert(*out, vkey, vval, &inserted)) {
BLog(BLOG_ERROR, "depth limit exceeded");
goto fail;
}
if (!inserted) {
BLog(BLOG_ERROR, "duplicate key in map");
goto fail;
}
}
} break;
case NCDVALUE_VAR: {
struct NCDEvaluator__Var var;
if (!ncd_make_name_indices(o->string_index, NCDValue_VarName(value), &var.varnames, &var.num_names)) {
BLog(BLOG_ERROR, "ncd_make_name_indices failed");
goto fail_var0;
}
size_t index;
struct NCDEvaluator__Var *varptr = NCDEvaluator__VarVec_Push(&o->vars, &index);
if (!varptr) {
BLog(BLOG_ERROR, "NCDEvaluator__VarVec_Push failed");
goto fail_var1;
}
if (index >= MAX_LOCAL_IDS) {
BLog(BLOG_ERROR, "too many variables");
goto fail_var2;
}
*varptr = var;
*out = NCDVal_NewPlaceholder(mem, ((int)index << 1) | 0);
break;
fail_var2:
NCDEvaluator__VarVec_Pop(&o->vars, NULL);
fail_var1:
BFree(var.varnames);
fail_var0:
goto fail;
} break;
case NCDVALUE_INVOC: {
struct NCDEvaluator__Call call;
NCDValue *func = NCDValue_InvocFunc(value);
if (NCDValue_Type(func) != NCDVALUE_STRING) {
BLog(BLOG_ERROR, "call function is not a string");
goto fail_invoc0;
}
call.func_name_id = NCDStringIndex_GetBin(o->string_index, NCDValue_StringValue(func), NCDValue_StringLength(func));
if (call.func_name_id < 0) {
BLog(BLOG_ERROR, "NCDStringIndex_GetBin failed");
goto fail_invoc0;
}
NCDValue *arg = NCDValue_InvocArg(value);
if (NCDValue_Type(arg) != NCDVALUE_LIST) {
BLog(BLOG_ERROR, "call argument is not a list literal!?");
goto fail_invoc0;
}
if (!(call.args = BAllocArray(NCDValue_ListCount(arg), sizeof(call.args[0])))) {
BLog(BLOG_ERROR, "BAllocArray failed");
goto fail_invoc0;
}
call.num_args = 0;
for (NCDValue *e = NCDValue_ListFirst(arg); e; e = NCDValue_ListNext(arg, e)) {
if (!expr_init(&call.args[call.num_args], o, e)) {
goto fail_invoc1;
}
call.num_args++;
}
size_t index;
struct NCDEvaluator__Call *callptr = NCDEvaluator__CallVec_Push(&o->calls, &index);
if (!callptr) {
BLog(BLOG_ERROR, "NCDEvaluator__CallVec_Push failed");
goto fail_invoc1;
}
if (index >= MAX_LOCAL_IDS) {
BLog(BLOG_ERROR, "too many variables");
goto fail_invoc2;
}
*callptr = call;
*out = NCDVal_NewPlaceholder(mem, ((int)index << 1) | 1);
break;
fail_invoc2:
NCDEvaluator__CallVec_Pop(&o->calls, NULL);
fail_invoc1:
while (call.num_args-- > 0) {
expr_free(&call.args[call.num_args]);
}
BFree(call.args);
fail_invoc0:
goto fail;
} break;
default: {
BLog(BLOG_ERROR, "expression type not supported");
goto fail;
} break;
}
return 1;
fail:
return 0;
}
static int replace_placeholders_callback (void *arg, int plid, NCDValMem *mem, NCDValRef *out)
{
struct NCDEvaluator__eval_context const *context = arg;
NCDEvaluator *o = context->eval;
int type = plid & 1;
int index = plid >> 1;
ASSERT(index >= 0)
ASSERT(index < MAX_LOCAL_IDS)
int res;
switch (type) {
case 0: {
struct NCDEvaluator__Var *var = NCDEvaluator__VarVec_Get(&o->vars, index);
res = context->funcs->func_eval_var(context->funcs->user, var->varnames, var->num_names, mem, out);
} break;
case 1: {
struct NCDEvaluator__Call *call = NCDEvaluator__CallVec_Get(&o->calls, index);
NCDEvaluatorArgs args;
args.context = context;
args.call_index = index;
res = context->funcs->func_eval_call(context->funcs->user, call->func_name_id, args, mem, out);
} break;
default: {
ASSERT(0)
res = 0;
} break;
}
ASSERT(res == 0 || res == 1)
return res;
}
int NCDEvaluator_Init (NCDEvaluator *o, NCDStringIndex *string_index)
{
o->string_index = string_index;
if (!NCDEvaluator__VarVec_Init(&o->vars, NCDEVALUATOR_DEFAULT_VARARRAY_CAPACITY)) {
BLog(BLOG_ERROR, "NCDEvaluator__VarVec_Init failed");
goto fail0;
}
if (!NCDEvaluator__CallVec_Init(&o->calls, NCDEVALUATOR_DEFAULT_CALLARRAY_CAPACITY)) {
BLog(BLOG_ERROR, "NCDEvaluator__CallVec_Init failed");
goto fail1;
}
return 1;
fail1:
NCDEvaluator__VarVec_Free(&o->vars);
fail0:
return 0;
}
void NCDEvaluator_Free (NCDEvaluator *o)
{
for (size_t i = 0; i < o->vars.count; i++) {
BFree(o->vars.elems[i].varnames);
}
for (size_t i = 0; i < o->calls.count; i++) {
struct NCDEvaluator__Call *call = NCDEvaluator__CallVec_Get(&o->calls, i);
while (call->num_args-- > 0) {
expr_free(&call->args[call->num_args]);
}
BFree(call->args);
}
NCDEvaluator__CallVec_Free(&o->calls);
NCDEvaluator__VarVec_Free(&o->vars);
}
int NCDEvaluatorExpr_Init (NCDEvaluatorExpr *o, NCDEvaluator *eval, NCDValue *value)
{
return expr_init(&o->expr, eval, value);
}
void NCDEvaluatorExpr_Free (NCDEvaluatorExpr *o)
{
expr_free(&o->expr);
}
int NCDEvaluatorExpr_Eval (NCDEvaluatorExpr *o, NCDEvaluator *eval, NCDEvaluator_EvalFuncs const *funcs, NCDValMem *out_newmem, NCDValRef *out_val)
{
ASSERT(funcs)
ASSERT(out_newmem)
ASSERT(out_val)
struct NCDEvaluator__eval_context context;
context.eval = eval;
context.funcs = funcs;
return expr_eval(&o->expr, &context, out_newmem, out_val);
}
size_t NCDEvaluatorArgs_Count (NCDEvaluatorArgs *o)
{
struct NCDEvaluator__Call *call = NCDEvaluator__CallVec_Get(&o->context->eval->calls, o->call_index);
return call->num_args;
}
int NCDEvaluatorArgs_EvalArgNewMem (NCDEvaluatorArgs *o, size_t index, NCDValMem *out_newmem, NCDValRef *out_ref)
{
struct NCDEvaluator__Call *call = NCDEvaluator__CallVec_Get(&o->context->eval->calls, o->call_index);
ASSERT(index < call->num_args)
return expr_eval(&call->args[index], o->context, out_newmem, out_ref);
}
int NCDEvaluatorArgs_EvalArg (NCDEvaluatorArgs *o, size_t index, NCDValMem *mem, NCDValRef *out_ref)
{
int res = 0;
NCDValMem temp_mem;
NCDValRef temp_ref;
if (!NCDEvaluatorArgs_EvalArgNewMem(o, index, &temp_mem, &temp_ref)) {
goto fail0;
}
NCDValRef ref = NCDVal_NewCopy(mem, temp_ref);
if (NCDVal_IsInvalid(ref)) {
goto fail1;
}
*out_ref = ref;
res = 1;
fail1:
NCDValMem_Free(&temp_mem);
fail0:
return res;
}