changeset 130:75ed83d94261

preprocess: Implement preprocessing of functions (defn)
author Lewin Bormann <lbo@spheniscida.de>
date Sun, 01 Sep 2019 19:23:45 +0200
parents e4c55f83eef0
children 0af2b62f49d2
files src/preprocess.c src/preprocess.h
diffstat 2 files changed, 117 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/preprocess.c	Sun Sep 01 19:22:31 2019 +0200
+++ b/src/preprocess.c	Sun Sep 01 19:23:45 2019 +0200
@@ -1,10 +1,13 @@
 #include "preprocess.h"
 
 #include "built-ins.h"
+#include "func.h"
 #include "value.h"
 
 void ypreprocess(yexpr_t* expr) {
-    assert(false /* not yet implemented */);
+    ypreprocess_resolve_builtins(expr);
+    ypreprocess_defn(expr);
+    // ypreprocess_refs(expr);
 }
 
 void ypreprocess_resolve_builtins(yexpr_t* expr) {
@@ -22,8 +25,7 @@
                 ybuiltin_translate(expr);
             break;
         case YEXPR_LIST:
-            if (expr->value.list.len == 0)
-                break;
+            if (expr->value.list.len == 0) break;
             yvec_t* list = &expr->value.list;
             for (size_t i = 0; i < list->len; i++) {
                 ypreprocess_resolve_builtins(YVEC_AT(list, i, yexpr_t));
@@ -34,6 +36,108 @@
     }
 }
 
+/**
+ * @brief Resolve references to arguments in the function body.
+ */
+static void ypreprocess_resolve_inner_refs(yexpr_t* body, yvec_t* arg_descs) {
+    switch (body->typ) {
+        case YEXPR_REF:
+            if (yref_type(&body->value.ref) != YREF_SYM) break;
+            // Check if reference is to known argument.
+            for (size_t i = 0; i < arg_descs->len; i++) {
+                yarg_desc_t* arg = YVEC_AT(arg_descs, i, yarg_desc_t);
+                if (0 == ystr_cmp(&body->value.ref.ref.sym, &arg->argname)) {
+                    yexpr_set_ref(body, yref_clone(&arg->argref));
+                    break;
+                }
+            }
+            break;
+        case YEXPR_LIST:
+            for (size_t i = 0; i < body->value.list.len; i++) {
+                yexpr_t* expr = YVEC_AT(&body->value.list, i, yexpr_t);
+                ypreprocess_resolve_inner_refs(expr, arg_descs);
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+/**
+ * @brief Store a `(defn ...)` as function and replace it with a YEXPR_UNDEF
+ * definition.
+ *
+ * `expr` must be a list starting with the `defn` built-in.
+ *
+ * Returns false if syntax is incorrect.
+ */
+static bool ypreprocess_compile_defn(yexpr_t* expr) {
+    yvec_t* list = &expr->value.list;
+    assert(4 <= list->len);
+    yexpr_t* defn = YVEC_AT(list, 0, yexpr_t);
+    yexpr_t* name = YVEC_AT(list, 1, yexpr_t);
+    yexpr_t* args = YVEC_AT(list, 2, yexpr_t);
+    if (defn->typ != YEXPR_BUILTIN || defn->value.builtin != YBUILTIN_DEFN)
+        return false;
+    if (name->typ != YEXPR_REF) return false;
+    assert(yref_type(&name->value.ref) == YREF_SYM);
+    if (args->typ != YEXPR_LIST) return false;
+    size_t n_args = args->value.list.len;
+
+    yvec_t body_exprs = YVEC_NEW(yvec_at(list, 3), list->len - 3, yexpr_t);
+    yfunc_t func;
+    yvec_t arg_descs = YVEC_NEW(NULL, args->value.list.len, yarg_desc_t);
+    for (size_t i = 0; i < args->value.list.len; i++) {
+        yarg_desc_t desc;
+        yexpr_t* arg = YVEC_AT(&args->value.list, i, yexpr_t);
+        assert(arg->typ == YEXPR_REF && yref_type(&arg->value.ref) == YREF_SYM);
+        desc.argname = ystr_clone(&arg->value.ref.ref.sym);
+        desc.argref = yref_new_cix(n_args - 1 - i);
+        YVEC_PUSH(&arg_descs, &desc);
+    }
+
+    func.name = name->value.ref.ref.sym;
+    func.args = arg_descs;
+    yexpr_t body = yexpr_new();
+    yexpr_set_list(&body, body_exprs);  // no destroy!
+    // Resolve references to arguments in body.
+    ypreprocess_resolve_inner_refs(&body, &arg_descs);
+    func.body = body_exprs;
+
+    // Store function as value at the named reference.
+    yvalue_t func_value;
+    func_value.typ = YVALUE_FUNC;
+    func_value.value.func = func;
+
+    yvalue_set(name->value.ref, &func_value);
+
+    // Clean up and return.
+    yexpr_destroy(defn);
+    yexpr_destroy(args);
+    yvec_destroy(list);
+    *expr = yexpr_new();  // no destroy!
+    return true;
+}
+
 void ypreprocess_defn(yexpr_t* expr) {
-    // `defn`s are already translated to built-in exprs.
+    // `defn`s are already translated to built-in exprs by the resolve_builtins
+    // stage. Now we extract them, store them as func values into the value
+    // table, and replace them with `undef` expressions that will not be
+    // executed.
+    switch (expr->typ) {
+        case YEXPR_LIST:
+            if (expr->value.list.len >= 4) {
+                yexpr_t* first = YVEC_AT(&expr->value.list, 0, yexpr_t);
+                if (first->typ == YEXPR_BUILTIN &&
+                    first->value.builtin == YBUILTIN_DEFN) {
+                    ypreprocess_compile_defn(expr);
+                    return;
+                }
+            }
+            for (size_t i = 0; i < expr->value.list.len; i++)
+                ypreprocess_defn(YVEC_AT(&expr->value.list, i, yexpr_t));
+            break;
+        default:
+            return;
+    }
 }
--- a/src/preprocess.h	Sun Sep 01 19:22:31 2019 +0200
+++ b/src/preprocess.h	Sun Sep 01 19:23:45 2019 +0200
@@ -30,6 +30,15 @@
 void ypreprocess_defn(yexpr_t* expr);
 
 /**
+ * @brief Replace symbolic IDs with per-scope-unique numeric IDs.
+ *
+ * Run order: 3
+ *
+ * NOTE: should not translate refs in e.g. defn (function name)
+ */
+void ypreprocess_refs(yexpr_t* expr);
+
+/**
  * @}
  */