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