changeset 102:da80a755723f

built-ins: Implement `for`, add some more built-ins, use macros for errors
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 28 Aug 2019 10:29:52 +0200
parents 1e5a8a96753d
children 354afb93f898
files src/built-ins.c src/types.h
diffstat 2 files changed, 112 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/src/built-ins.c	Wed Aug 28 10:28:34 2019 +0200
+++ b/src/built-ins.c	Wed Aug 28 10:29:52 2019 +0200
@@ -19,20 +19,18 @@
 };
 
 static const struct ybuiltin_id_mapping YBUILTIN_ID_MAPPING[] = {
-    {"__invalid", YBUILTIN_UNDEF}, {"for", YBUILTIN_FOR}, {"let", YBUILTIN_LET},
-    {"defn", YBUILTIN_DEFN},       {"+", YBUILTIN_PLUS},  {"-", YBUILTIN_MINUS},
-    {"*", YBUILTIN_MULT},          {"/", YBUILTIN_DIV},   {"if", YBUILTIN_IF},
-    {"seq", YBUILTIN_SEQ},         {"==", YBUILTIN_EQ},   {"<", YBUILTIN_LT},
+    {"undef", YBUILTIN_UNDEF}, {"for", YBUILTIN_FOR}, {"let", YBUILTIN_LET},
+    {"defn", YBUILTIN_DEFN},   {"+", YBUILTIN_PLUS},  {"-", YBUILTIN_MINUS},
+    {"*", YBUILTIN_MULT},      {"/", YBUILTIN_DIV},   {"if", YBUILTIN_IF},
+    {"seq", YBUILTIN_SEQ},     {"==", YBUILTIN_EQ},   {"<", YBUILTIN_LT},
+    {"car", YBUILTIN_CAR},     {"cdr", YBUILTIN_CDR}, {"push", YBUILTIN_PUSH},
 };
 
 static const char* YBUILTIN_ENUM_STR[] = {
-    "BUILTIN:UNDEF", "BUILTIN:FOR", "BUILTIN:LET", "BUILTIN:DEFN",
-
-    "BUILTIN:+",     "BUILTIN:-",   "BUILTIN:*",   "BUILTIN:/",
-
-    "BUILTIN:IF",    "BUILTIN:SEQ",
-
-    "BUILTIN:EQ",    "BUILTIN:LT",
+    "BUILTIN:UNDEF", "BUILTIN:FOR", "BUILTIN:LET",  "BUILTIN:DEFN",
+    "BUILTIN:+",     "BUILTIN:-",   "BUILTIN:*",    "BUILTIN:/",
+    "BUILTIN:IF",    "BUILTIN:SEQ", "BUILTIN:EQ",   "BUILTIN:LT",
+    "BUILTIN:CAR",   "BUILTIN:CDR", "BUILTIN:PUSH",
 };
 
 /// Ownership of msg is transferred to this function, ownership of offending is
@@ -51,7 +49,7 @@
     return exception;
 }
 
-
+/// Return the ID corresponding to the builtin name in the string.
 static YBUILTIN_TYPE ybuiltin_id(ystr_t* sym) {
     const size_t builtins =
         sizeof(YBUILTIN_ID_MAPPING) / sizeof(struct ybuiltin_id_mapping);
@@ -63,6 +61,7 @@
     return YBUILTIN_UNDEF;
 }
 
+/// Make a symbolic reference into a built-in reference, if possible.
 bool ybuiltin_translate(yexpr_t* expr) {
     if (expr->typ != YEXPR_REF) return false;
     if (yref_type(&expr->value.ref) != YREF_SYM) return false;
@@ -75,50 +74,120 @@
 
 static void nothing(void){};
 
-/// Expects a list expression starting with built-in "let" on the stack. It is not modified.
+/// Return from a built-in function with a "bad expression" exception.
+#define BADEXPRFAIL(BUILTINTYPE, invalid_expr)                              \
+    return ybuiltin_type_error(                                             \
+        YBUILTIN_##BUILTINTYPE,                                             \
+        ystr_new("BUG: not a valid `" #BUILTINTYPE "`expression on stack"), \
+        &invalid_expr)
+
+/// Return from a built-in function with a "not enough arguments" exception.
+#define NOTENOUGHFAIL(BUILTINTYPE) \
+    return ybuiltin_type_error(    \
+        YBUILTIN_##BUILTINTYPE,    \
+        ystr_new("BUG: not enough arguments on call stack"), NULL);
+/// Return from a built-in function with a "type mismatch" exception.
+
+#define TYPEFAIL(BUILTINTYPE, invalid_expr, want)                     \
+    {                                                                 \
+        yexpr_t exc = ybuiltin_type_error(                            \
+            YBUILTIN_##BUILTINTYPE,                                   \
+            ystr_new("type mismatch for " #BUILTINTYPE "; want " want \
+                     "received something else"),                      \
+            &invalid_expr);                                           \
+        return exc;                                                   \
+    }
+/// Returns a YEXPR_UNDEF expression. TODO: Automatically add symbolic reference
+/// "undef".
+yexpr_t ybuiltin_fn_undef(yeval_state_t* state) { return yexpr_new(); }
+
+/// Expects a list expression starting with built-in "for" on the stack. It is
+/// not modified.
+///
+/// The list should follow this scheme: (BUILTIN:FOR ref iterated-items
+/// [expr...] expr)
+///
+/// Returns a list of each of the last expressions in the for loop. If the last
+/// expression is an undefined value (`(undef)`), the value is not added to the
+/// resulting list.
+yexpr_t ybuiltin_fn_for(yeval_state_t* state) {
+#define _pattern "(for ref (item [...]) [exprs...] expr)"
+    yexpr_t for_expr;
+    yvec_t* for_list = &for_expr.value.list;
+    size_t items = 0, counter = 0;
+    yvec_t result;
+
+    if (!yvec_pop(&state->call_stack, &for_expr)) NOTENOUGHFAIL(FOR);
+    if (for_expr.typ != YEXPR_LIST) BADEXPRFAIL(FOR, for_expr);
+    if (for_expr.value.list.len < 4) TYPEFAIL(FOR, for_expr, _pattern);
+
+    yexpr_t* for_id = YVEC_AT(for_list, counter++, yexpr_t);
+    if (for_id->typ != YEXPR_BUILTIN || for_id->value.builtin != YBUILTIN_FOR)
+        TYPEFAIL(FOR, for_expr, _pattern);
+    yexpr_t* for_ref = YVEC_AT(for_list, counter++, yexpr_t);
+    if (for_ref->typ != YEXPR_REF) TYPEFAIL(FOR, for_expr, _pattern);
+    yexpr_t* for_items = YVEC_AT(for_list, counter++, yexpr_t);
+    if (for_items->typ != YEXPR_LIST) TYPEFAIL(FOR, for_expr, _pattern);
+    items = for_items->value.list.len;
+    YVEC_INIT(&result, items, yexpr_t);
+
+    yvec_t* for_items_l = &for_items->value.list;
+    yeval_state_t emptystate = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)};
+    // counter now points to the first expression.
+    for (size_t i = 0; i < for_items_l->len; i++) {
+        yexpr_t* expr = YVEC_AT(for_items_l, i, yexpr_t);
+        yexpr_t evald = yeval(&emptystate, expr, false);
+        yvalue_t value = {.typ = YVALUE_EXPR, .value.expr = evald};
+
+        // For each item, bind reference and execute body.
+        yvalue_set(for_ref->value.ref, &value);
+        yexpr_t expr_result = yeval(&emptystate, expr, false);
+        // Make deep copy of result
+        yexpr_t expr_result_cp = yexpr_copy(&expr_result);
+
+        if (expr_result.typ != YEXPR_UNDEF) YVEC_PUSH(&result, &expr_result_cp);
+
+        yexpr_destroy(&expr_result);
+        yvalue_destroy(&value);
+    }
+
+    yexpr_t result_expr = yexpr_new();
+    yexpr_set_list(&result_expr, result);
+    return result_expr;
+
+#undef _pattern
+}
+
+/// Expects a list expression starting with built-in "let" on the stack. It is
+/// not modified.
 ///
 /// Returns an UNDEF expression by default, or an EXCEPTION if something went
 /// wrong.
 yexpr_t ybuiltin_fn_let(yeval_state_t* state) {
+#define _pattern "(let ref [expr ...] expr)"
     yexpr_t let, *ref;
     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;
+    if (!yvec_pop(&state->call_stack, &let)) NOTENOUGHFAIL(LET);
+    if (let.typ != YEXPR_LIST) BADEXPRFAIL(LET, let);
+    if (let.value.list.len < 3) BADEXPRFAIL(LET, let);
 
     yexpr_t let_id = *YVEC_AT(letlist, counter++, yexpr_t);
     if (let_id.typ != YEXPR_BUILTIN || let_id.value.builtin != YBUILTIN_LET)
-        goto badexprfail;
+        BADEXPRFAIL(LET, let);
 
     ref = YVEC_AT(letlist, counter++, yexpr_t);
-    if (ref->typ != YEXPR_REF) goto typefail;
+    if (ref->typ != YEXPR_REF) TYPEFAIL(LET, let, _pattern);
 
+    assert(counter == 2);
     yvalue_t newvalue = {
         .typ = YVALUE_EXPR,
-        .value.expr = yeval_list_return_last(state, letlist, 2)};
+        .value.expr = yeval_list_return_last(state, letlist, counter)};
     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);
-
-typefail:
-    nothing();
-    yexpr_t exc = ybuiltin_type_error(
-        YBUILTIN_LET,
-        ystr_new(
-            "type mismatch: want (let reference [exprs...] expr), received something else"),
-        &let);
-    return exc;
+#undef _pattern
 }
 
 /**
@@ -126,7 +195,8 @@
  *
  * TODO: write built-ins.
  */
-static const ybuiltin_fn YBUILTIN_FNS[] = {NULL, NULL, ybuiltin_fn_let};
+static const ybuiltin_fn YBUILTIN_FNS[] = {ybuiltin_fn_undef, ybuiltin_fn_for,
+                                           ybuiltin_fn_let};
 
 const char* ybuiltin_name(YBUILTIN_TYPE t) {
     assert(t * sizeof(const char*) < sizeof(YBUILTIN_ENUM_STR));
--- a/src/types.h	Wed Aug 28 10:28:34 2019 +0200
+++ b/src/types.h	Wed Aug 28 10:29:52 2019 +0200
@@ -65,6 +65,10 @@
 
     YBUILTIN_EQ = 10,
     YBUILTIN_LT = 11,
+
+    YBUILTIN_CAR = 12,
+    YBUILTIN_CDR = 13,
+    YBUILTIN_PUSH = 14,
 } YBUILTIN_TYPE;
 
 /**