Mercurial > lbo > hg > ylisp
view src/eval.c @ 177:e55251d97380
eval: destroy unused element result
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Thu, 12 Sep 2019 08:24:29 +0200 |
parents | 800a02381024 |
children | 3ec2b2edb977 |
line wrap: on
line source
#include "eval.h" #include <assert.h> #include "built-ins.h" #include "expr.h" #include "value.h" static yexpr_t yeval_undef_ref(yref_t* ref) { yexpr_t exception = yexpr_new(); ystr_t msg = ystr_new(NULL); ystr_t dbg = yref_debug(ref); ystr_build(&msg, "undefined reference: %s", ystr_str(&dbg)); ystr_destroy(&dbg); yexpr_set_exception(&exception, msg); return exception; } yexpr_t yeval_call_func(yeval_state_t* state, yfunc_t* ref, yexpr_t* call) { assert(call->typ == YEXPR_LIST); if (ref->args.len != call->value.list.len - 1) { yexpr_t exc = yexpr_new(); ystr_t msg = ystr_new(NULL); ystr_build(&msg, "Unexpected number of arguments in call to function %s: " "Want %u, got %u", ystr_str(&ref->name), ref->args.len, call->value.list.len - 1); yexpr_set_exception(&exc, msg); return exc; } yvec_t* args = &ref->args; yexpr_t evald_args[32]; assert(args->len <= 32); for (size_t i = 0; i < args->len; i++) { evald_args[i] = yeval(state, YVEC_AT(&call->value.list, i + 1, yexpr_t), false); } for (size_t i = 0; i < args->len; i++) { YVEC_PUSH(&state->call_stack, &evald_args[i]); } yexpr_t result = yeval_list_return_last(state, &ref->body, 0); for (size_t i = 0; i < args->len; i++) { yexpr_t old; yvec_pop(&state->call_stack, &old); yexpr_destroy(&old); } return result; } yexpr_t yeval_list_return_last(yeval_state_t* state, yvec_t* list, size_t off) { if (list->len - off == 0) return yexpr_new(); assert(list->size == sizeof(yexpr_t)); for (size_t i = off; i < (list->len - (unsigned int)1); i++) { yeval(state, YVEC_AT(list, i, yexpr_t), true); } return yeval(state, YVEC_AT(list, list->len - 1, yexpr_t), false); } yexpr_t yeval(yeval_state_t* state, yexpr_t* expr, bool in_place) { yexpr_t result; if (in_place) result = yexpr_new(); switch (expr->typ) { case YEXPR_UNDEF: // UNDEFs are often the result of removing defns from the parsed // tree. case YEXPR_ATOM: case YEXPR_NUMBER: case YEXPR_EXCEPTION: case YEXPR_BUILTIN: if (in_place) break; // values can't have side effects // Flat copy is enough. return *expr; case YEXPR_STRING: if (in_place) break; result = *expr; result.value.str = ystr_clone(&expr->value.str); return result; case YEXPR_REF: // Reference is resolved if expr-ref. func-refs are not // resolved. NOTE: Can this result in infinite recursion? if (in_place) break; // values can't have side effects // YREF_STACK references a value pushed on the stack. if (yref_type(&expr->value.ref) == YREF_STACK) { result = yexpr_copy(YVEC_AT( &state->call_stack, state->call_stack.len - 1 - yref_cix(&expr->value.ref), yexpr_t)); return result; } yvalue_t* val = yvalue_get(expr->value.ref); if (val == NULL) return yeval_undef_ref(&expr->value.ref); if (val->typ == YVALUE_EXPR) { result = yexpr_copy(&val->value.expr); return result; } else if (val->typ == YVALUE_FUNC) { return *expr; } // Undefined reference! return yeval_undef_ref(&expr->value.ref); case YEXPR_LIST: // Is empty list? if (expr->value.list.len == 0) { if (in_place) break; result = *expr; result.value.list = yvec_clone(&expr->value.list); return result; } // Is builtin call? yexpr_t* first = YVEC_AT(&expr->value.list, 0, yexpr_t); if (first->typ == YEXPR_BUILTIN) { // both must be yexpr_t vecs. assert(expr->value.list.size == state->call_stack.size); // Builtins expect their call list on the stack. YVEC_PUSH(&state->call_stack, expr); return ybuiltin_run(first->value.builtin, state); } // Is function call? if (first->typ == YEXPR_REF) { yexpr_t ref = yeval(state, first, false); // If resolved reference is still possibly a function if (ref.typ == YEXPR_REF) { yvalue_t* val = yvalue_get(ref.value.ref); if (val == NULL) { return yeval_undef_ref(&expr->value.ref); } // Call function. if (val->typ == YVALUE_FUNC) { result = yeval_call_func(state, &val->value.func, expr); if (in_place) { yexpr_destroy(&result); return result; } return result; } else if (val->typ == YVALUE_EXPR) { assert(val->typ == YVALUE_FUNC /* yeval() should have returned a copy of the expression if first element was a reference to an expression */); } assert(false /* yeval() on a reference should have prevented any other case */); } yexpr_destroy(&ref); } result = *expr; // Don't store results if (in_place) { for (size_t i = 0; i < result.value.list.len; i++) { yexpr_t* elem = YVEC_AT(&result.value.list, i, yexpr_t); yexpr_t elem_result = yeval(state, elem, /* in_place= */ true); yexpr_destroy(&elem_result); } return result; } // Store results; input list is copied shallowly, and assigned // with copied values from yeval. result.value.list = yvec_clone(&expr->value.list); for (size_t i = 0; i < result.value.list.len; i++) { yexpr_t* elem = YVEC_AT(&result.value.list, i, yexpr_t); yexpr_t elem_result = yeval(state, elem, /* in_place= */ false); *elem = elem_result; } return result; default: assert(false /* yeval is not yet fully implemented */); } return result; }