changeset 83:b86bc111f4a4

built-ins: Refactor let and other built-in functionality
author Lewin Bormann <lbo@spheniscida.de>
date Mon, 26 Aug 2019 10:06:52 +0200
parents 7f331aa5abd1
children 6a42872a9f81
files doc/execution.md src/CMakeLists.txt src/built-ins.c src/built-ins.h src/eval.h src/types.h
diffstat 6 files changed, 50 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/doc/execution.md	Mon Aug 26 09:41:13 2019 +0200
+++ b/doc/execution.md	Mon Aug 26 10:06:52 2019 +0200
@@ -120,6 +120,11 @@
 All this logic happens within `yeval()`; it binds arguments when calling
 functions and handles the return value.
 
+Built-in functions are called with arguments on the call stack. Those arguments
+are shallow copies from the expressions, so built-ins should not modify them
+except where it's guaranteed not to have side effects (scalar values, for
+example).
+
 ## Evaluation
 
 -> see `yeval()` in `eval.h`.
--- a/src/CMakeLists.txt	Mon Aug 26 09:41:13 2019 +0200
+++ b/src/CMakeLists.txt	Mon Aug 26 10:06:52 2019 +0200
@@ -11,6 +11,7 @@
 ADD_LIBRARY(core STATIC
     atom.c
     built-ins.c
+    eval.c
     expr.c
     func.c
     value.c)
--- a/src/built-ins.c	Mon Aug 26 09:41:13 2019 +0200
+++ b/src/built-ins.c	Mon Aug 26 10:06:52 2019 +0200
@@ -6,6 +6,13 @@
 
 // TODO: Write standard library!
 
+/**
+ * @brief Type of a built-in function called during execution.
+ *
+ * The functions return a yexpr_t of which the caller assumes ownership.
+ */
+typedef yexpr_t (*ybuiltin_fn)(yeval_state_t* state);
+
 struct ybuiltin_id_mapping {
     const char* id;
     YBUILTIN_TYPE builtin;
@@ -57,30 +64,48 @@
 
 static void nothing(void){};
 
-/// Expects a reference and a value on the stack. They may not depend on other
-/// references except for functions, i.e. should have been copied using
-/// `yexpr_copy()` (which is the convention for calling functions anyway).
+/// Expects a list expression starting with built-in "let" on the stack.
 ///
 /// Returns an UNDEF expression by default, or an EXCEPTION if something went
 /// wrong.
-yexpr_t ybuiltin_fn_let(yvec_t* call_stack) {
-    yexpr_t ref, val;
-    if (!yvec_pop(call_stack, &val)) goto notenoughfail;
-    if (!yvec_pop(call_stack, &ref)) goto notenoughfail;
+yexpr_t ybuiltin_fn_let(yeval_state_t* state) {
+    yexpr_t let, *ref, *val;
+    size_t counter = 0;
+    yvec_t* letlist = &let.value.list;
+
+    if (!yvec_pop(&state->call_stack, &let)) goto notenoughfail;
+    if (let.typ != YEXPR_LIST) goto badexprfail;
+    if (let.value.list.len < 3) goto notenoughfail;
+
+    yexpr_t let_id = *YVEC_AT(letlist, counter++, yexpr_t);
+    if (let_id.typ != YEXPR_BUILTIN || let_id.value.builtin != YBUILTIN_LET)
+        goto badexprfail;
 
-    if (ref.typ != YEXPR_REF || val.typ == YEXPR_UNDEF) goto typefail;
-    yvalue_t newvalue = {.typ = YVALUE_EXPR, .value.expr = val};
-    yvalue_set(ref.value.ref, &newvalue);
+    ref = YVEC_AT(letlist, counter++, yexpr_t);
+    val = YVEC_AT(letlist, counter++, yexpr_t);
+    if (ref->typ != YEXPR_REF || val->typ == YEXPR_UNDEF) goto typefail;
+
+    yvalue_t newvalue = {
+        .typ = YVALUE_EXPR,
+        .value.expr = yeval(state, val, /* in_place= */ false)};
+    yvalue_set(ref->value.ref, &newvalue);
     return yexpr_new();
+
+badexprfail:
+    return ybuiltin_type_error(
+        YBUILTIN_LET, ystr_new("BUG: not a let expression on stack"), &let);
+
 notenoughfail:
     return ybuiltin_type_error(
-        YBUILTIN_LET, ystr_new("BUG: not enough arguments on call stack"),
-        NULL);
+        YBUILTIN_LET,
+        ystr_new("BUG: not enough arguments on call stack or to builtin"),
+        &let);
+
 typefail:
     nothing();
     yexpr_t args = yexpr_new();
-    yexpr_init_or_extend(&args, ref);
-    yexpr_init_or_extend(&args, val);
+    yexpr_init_or_extend(&args, *ref);
+    yexpr_init_or_extend(&args, *val);
     yexpr_t exc = ybuiltin_type_error(
         YBUILTIN_LET,
         ystr_new(
@@ -102,8 +127,8 @@
     return YBUILTIN_ENUM_STR[t];
 }
 
-yexpr_t ybuiltin_run(YBUILTIN_TYPE t, yvec_t* call_stack) {
+yexpr_t ybuiltin_run(YBUILTIN_TYPE t, yeval_state_t* state) {
     assert((t * sizeof(ybuiltin_fn)) < sizeof(YBUILTIN_FNS));
-    return YBUILTIN_FNS[t](call_stack);
+    return YBUILTIN_FNS[t](state);
 }
 
--- a/src/built-ins.h	Mon Aug 26 09:41:13 2019 +0200
+++ b/src/built-ins.h	Mon Aug 26 10:06:52 2019 +0200
@@ -1,6 +1,7 @@
 #ifndef src_built_ins_h
 #define src_built_ins_h
 
+#include <src/eval.h>
 #include <src/expr.h>
 #include <src/types.h>
 
@@ -20,7 +21,7 @@
 /**
  * @brief Run a built-in function. The caller assumes ownership of the returned value.
  */
-yexpr_t ybuiltin_run(YBUILTIN_TYPE t, yvec_t* call_stack);
+yexpr_t ybuiltin_run(YBUILTIN_TYPE t, yeval_state_t* state);
 
 /**
  * @}
--- a/src/eval.h	Mon Aug 26 09:41:13 2019 +0200
+++ b/src/eval.h	Mon Aug 26 10:06:52 2019 +0200
@@ -1,6 +1,7 @@
 #ifndef src_eval_h
 #define src_eval_h
 
+#include <src/types.h>
 #include <src/base/vec.h>
 
 /**
--- a/src/types.h	Mon Aug 26 09:41:13 2019 +0200
+++ b/src/types.h	Mon Aug 26 10:06:52 2019 +0200
@@ -117,13 +117,6 @@
 typedef struct yexpr_t yexpr_t;
 
 /**
- * @brief Type of a built-in function called during execution.
- *
- * The functions return a yexpr_t of which the caller assumes ownership.
- */
-typedef yexpr_t (*ybuiltin_fn)(yvec_t* call_stack);
-
-/**
  * @}
  */
 #endif