Mercurial > lbo > hg > ylisp
changeset 132:bad34d9a4443
eval: Implement function calls (with stack-refs)
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 01 Sep 2019 19:24:24 +0200 |
parents | 0af2b62f49d2 |
children | 2af75be12687 |
files | src/eval.c src/eval.h |
diffstat | 2 files changed, 50 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/src/eval.c Sun Sep 01 19:24:03 2019 +0200 +++ b/src/eval.c Sun Sep 01 19:24:24 2019 +0200 @@ -4,12 +4,39 @@ #include "expr.h" #include "value.h" -yexpr_t yeval_call_func(yvalue_t* ref, yexpr_t* call) { return yexpr_new(); } +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; + size_t stack_start = state->call_stack.len - 1; + for (size_t i = 0; i < args->len; i++) { + yexpr_t evald = + yeval(state, YVEC_AT(&call->value.list, i + 1, yexpr_t), false); + YVEC_PUSH(&state->call_stack, &evald); + } + 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 - 1; i++) { + 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); @@ -19,6 +46,9 @@ 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: @@ -29,12 +59,20 @@ case YEXPR_STRING: if (in_place) break; result = *expr; - result.value.str = ystr_new(ystr_str(&expr->value.str)); + 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? + // 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->typ == YVALUE_EXPR) { result = yexpr_copy(&val->value.expr); @@ -66,14 +104,15 @@ yvalue_t* val = yvalue_get(first->value.ref); // Call function. if (val->typ == YVALUE_FUNC) { - result = yeval_call_func(val, expr); + 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) { - // resolve as normal below; this seems to be a "data list". + // resolve as normal below; this seems to be a "data + // list". } else { assert(val->typ == YVALUE_FUNC || val->typ == YVALUE_EXPR); } @@ -92,8 +131,8 @@ return result; } - // Store results; input list is copied shallowly, and assigned with - // copied values from yeval. + // 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);
--- a/src/eval.h Sun Sep 01 19:24:03 2019 +0200 +++ b/src/eval.h Sun Sep 01 19:24:24 2019 +0200 @@ -15,8 +15,8 @@ /// instance of this value per program, so per-eval-call information should be /// passed directly to yeval. typedef struct { - /// A vector of yexpr_t that is used by called built-in functions to take - /// arguments from. Normal functions use pre-bound references. + /// A vec of expressions that can be used for storing intermediate values in + /// a quick way (push/pop is fast on pre-allocated memory). yvec_t call_stack; // TODO: Maybe move value and atom tables in here? For now, it doesn't make