changeset 156:3cd189bd595c

eval: Handle function-value calls correctly
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 03 Sep 2019 15:35:33 +0200
parents cfe8ddaa770c
children 15825a4a3580
files src/eval.c src/eval_test.c
diffstat 2 files changed, 69 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c	Tue Sep 03 15:35:04 2019 +0200
+++ b/src/eval.c	Tue Sep 03 15:35:33 2019 +0200
@@ -4,6 +4,16 @@
 #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) {
@@ -73,18 +83,15 @@
                 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!
-            yexpr_t exception = yexpr_new();
-            ystr_t msg = ystr_new("Undefined reference!");
-            yexpr_set_exception(&exception, msg);
-            return exception;
+            return yeval_undef_ref(&expr->value.ref);
         case YEXPR_LIST:
             // Is empty list?
             if (expr->value.list.len == 0) {
@@ -102,30 +109,39 @@
                 YVEC_PUSH(&state->call_stack, expr);
                 return ybuiltin_run(first->value.builtin, state);
             }
+
             // Is function call?
             if (first->typ == YEXPR_REF) {
-                yvalue_t* val = yvalue_get(first->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;
+                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);
                     }
-                    return result;
-                } else if (val->typ == YVALUE_EXPR) {
-                    // resolve as normal below; this seems to be a "data
-                    // list".
-                } else {
-                    yexpr_t exc = yexpr_new();
-                    ystr_t msg = ystr_new(NULL);
-                    ystr_build(&msg, "possibly undefined function: ");
-                    ystr_t dbg = yref_debug(&first->value.ref);
-                    ystr_append(&msg, &dbg);
-                    ystr_destroy(&dbg);
-                    yexpr_set_exception(&exc, msg);
-                    return exc;
-                    assert(val->typ == YVALUE_FUNC || val->typ == YVALUE_EXPR);
+                    // 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) {
+                        // resolve as normal below; this seems to be a "data
+                        // list".
+                    } else {
+                        yexpr_t exc = yexpr_new();
+                        ystr_t msg = ystr_new(NULL);
+                        ystr_build(&msg, "possibly undefined function: ");
+                        ystr_t dbg = yref_debug(&first->value.ref);
+                        ystr_append(&msg, &dbg);
+                        ystr_destroy(&dbg);
+                        yexpr_set_exception(&exc, msg);
+                        return exc;
+                        assert(val->typ == YVALUE_FUNC ||
+                               val->typ == YVALUE_EXPR);
+                    }
                 }
             }
 
--- a/src/eval_test.c	Tue Sep 03 15:35:04 2019 +0200
+++ b/src/eval_test.c	Tue Sep 03 15:35:33 2019 +0200
@@ -83,10 +83,37 @@
     ystr_destroy(&input);
 }
 
+void test_eval_higher_order(void) {
+    fprintf(stderr, "test_eval_higher_order ===========\n");
+
+    // Calculate faculty values from 1 to 10.
+    ystr_t input = ystr_new(
+            "(defn f (a) (+ a 1))"
+            "(defn g (fn) (fn 3))"
+            "(g f)");
+
+    yexpr_t program = yexpr_new();
+    ystr_t error = ystr_new(NULL);
+    assert(yparse_str(&input, &program, &error));
+    ypreprocess(&program);
+
+    yeval_state_t state;
+    state.call_stack = YVEC_NEW(NULL, 4, yexpr_t);
+    yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0);
+    assert(YEXPR_NUMBER == result.typ && 4 == result.value.n);
+
+    yexpr_destroy(&program);
+    yvec_destroy(&state.call_stack);
+    ystr_destroy(&error);
+    yexpr_destroy(&result);
+    ystr_destroy(&input);
+}
+
 int main(void) {
     test_eval_edge_cases();
     test_eval_func_wrong_call();
     test_eval_recursion();
+    test_eval_higher_order();
     yvalue_free_all();
     yatom_free_all();
     return 0;