changeset 146:aa551e42de49

built-ins: Implement minus, multiplication, if, == built-ins
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 03 Sep 2019 12:15:51 +0200
parents 7887550c61c6
children 1e9d534b903d
files src/built-ins.c src/built-ins_test.c
diffstat 2 files changed, 136 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/built-ins.c	Tue Sep 03 12:15:21 2019 +0200
+++ b/src/built-ins.c	Tue Sep 03 12:15:51 2019 +0200
@@ -232,14 +232,142 @@
 #undef _pattern
 }
 
+/// Expects a list expression starting with built-in "-" on the stack. It is not
+/// modified. Returns a number expression or an exception.
+yexpr_t ybuiltin_fn_minus(yeval_state_t* state) {
+#define _pattern "(- numeric-expr numeric-expr [numeric-expr ...])"
+    yexpr_t minus;
+
+    if (!yvec_pop(&state->call_stack, &minus)) NOTENOUGHFAIL(MINUS);
+    if (minus.typ != YEXPR_LIST) BADEXPRFAIL(MINUS, minus);
+    if (minus.value.list.len < 3) BADEXPRFAIL(MINUS, minus);
+
+    yvec_t* minus_list = &minus.value.list;
+
+    long long result = 0;
+    bool first = true;
+    for (size_t i = 1; i < minus_list->len; i++) {
+        yexpr_t* expr = YVEC_AT(minus_list, i, yexpr_t);
+        yexpr_t evald = yeval(state, expr, false);
+        if (evald.typ != YEXPR_NUMBER) TYPEFAIL(MINUS, *expr, _pattern);
+        if (first) {
+            result += evald.value.n;
+            first = false;
+        } else {
+            result -= evald.value.n;
+        }
+        yexpr_destroy(&evald);
+    }
+
+    yexpr_t result_expr = yexpr_new();
+    yexpr_set_number(&result_expr, result);
+    return result_expr;
+
+#undef _pattern
+}
+
+/// Expects a list expression starting with built-in "*" on the stack. It is not
+/// modified. Returns a number expression or an exception.
+yexpr_t ybuiltin_fn_times(yeval_state_t* state) {
+#define _pattern "(* numeric-expr numeric-expr [numeric-expr ...])"
+    yexpr_t times;
+
+    if (!yvec_pop(&state->call_stack, &times)) NOTENOUGHFAIL(MULT);
+    if (times.typ != YEXPR_LIST) BADEXPRFAIL(MULT, times);
+    if (times.value.list.len < 3) BADEXPRFAIL(MULT, times);
+
+    yvec_t* times_list = &times.value.list;
+
+    long long result = 1;
+    for (size_t i = 1; i < times_list->len; i++) {
+        yexpr_t* expr = YVEC_AT(times_list, i, yexpr_t);
+        yexpr_t evald = yeval(state, expr, false);
+        if (evald.typ != YEXPR_NUMBER) TYPEFAIL(MULT, *expr, _pattern);
+        result *= evald.value.n;
+        yexpr_destroy(&evald);
+    }
+
+    yexpr_t result_expr = yexpr_new();
+    yexpr_set_number(&result_expr, result);
+    return result_expr;
+
+#undef _pattern
+}
+
+yexpr_t ybuiltin_fn_if(yeval_state_t* state) {
+#define _pattern \
+    "(if condition then-expr/(then-exprs) [else-expr/(else-exprs)])"
+    yexpr_t if_expr = yexpr_new();
+
+    if (!yvec_pop(&state->call_stack, &if_expr)) NOTENOUGHFAIL(IF);
+    if (if_expr.typ != YEXPR_LIST) BADEXPRFAIL(IF, if_expr);
+    if (if_expr.value.list.len < 3) BADEXPRFAIL(IF, if_expr);
+
+    yexpr_t* condition = YVEC_AT(&if_expr.value.list, 1, yexpr_t);
+    yexpr_t* then_expr = YVEC_AT(&if_expr.value.list, 2, yexpr_t);
+    yexpr_t* else_expr = NULL;
+    if (if_expr.value.list.len > 3)
+        else_expr = YVEC_AT(&if_expr.value.list, 3, yexpr_t);
+
+    yexpr_t cond_result = yeval(state, condition, false);
+    bool result = false;
+    if (cond_result.typ == YEXPR_ATOM &&
+        yatom_get_or_add("true") == cond_result.value.atom)
+        result = true;
+    if (cond_result.typ == YEXPR_NUMBER && cond_result.value.n != 0)
+        result = true;
+
+    if (result) {
+        if (then_expr->typ == YEXPR_LIST)
+            return yeval_list_return_last(state, &then_expr->value.list, 0);
+        return yeval(state, then_expr, false);
+    } else if (else_expr != NULL) {
+        if (else_expr->typ == YEXPR_LIST)
+            return yeval_list_return_last(state, &else_expr->value.list, 0);
+        return yeval(state, else_expr, false);
+    } else {
+        return yexpr_new();
+    }
+
+#undef _pattern
+}
+
+yexpr_t ybuiltin_fn_eq(yeval_state_t* state) {
+#define _pattern "(== expr expr [expr...])"
+    yexpr_t eq = yexpr_new();
+
+    if (!yvec_pop(&state->call_stack, &eq)) NOTENOUGHFAIL(EQ);
+    if (eq.typ != YEXPR_LIST) BADEXPRFAIL(EQ, eq);
+    if (eq.value.list.len < 3) BADEXPRFAIL(EQ, eq);
+
+    yexpr_t* first = YVEC_AT(&eq.value.list, 1, yexpr_t);
+    yexpr_t* second = YVEC_AT(&eq.value.list, 2, yexpr_t);
+    yexpr_t firstevald = yeval(state, first, false);
+    yexpr_t secondevald = yeval(state, second, false);
+    bool equal = yexpr_equal(&firstevald, &secondevald);
+    yexpr_destroy(&firstevald);
+    yexpr_destroy(&secondevald);
+
+    yexpr_t result = yexpr_new();
+    if (equal)
+        yexpr_set_atom(&result, yatom_get_or_add("true"));
+    else
+        yexpr_set_atom(&result, yatom_get_or_add("false"));
+    return result;
+#undef _pattern
+}
+
 /**
  * @brief Table of built-in functions, indexed by YBUILTIN_TYPE.
  *
  * TODO: write built-ins.
  */
 static const ybuiltin_fn YBUILTIN_FNS[] = {ybuiltin_fn_undef, ybuiltin_fn_for,
-                                           ybuiltin_fn_let, NULL,
-                                           ybuiltin_fn_plus};
+                                           ybuiltin_fn_let,   NULL,
+                                           ybuiltin_fn_plus,  ybuiltin_fn_minus,
+                                           ybuiltin_fn_times, NULL,
+                                           ybuiltin_fn_if,    NULL,
+                                           ybuiltin_fn_eq};
 
 const char* ybuiltin_name(YBUILTIN_TYPE t) {
     assert(t * sizeof(const char*) < sizeof(YBUILTIN_ENUM_STR));
--- a/src/built-ins_test.c	Tue Sep 03 12:15:21 2019 +0200
+++ b/src/built-ins_test.c	Tue Sep 03 12:15:51 2019 +0200
@@ -39,7 +39,10 @@
     yexpr_t* for_expr = YVEC_AT(&program.value.list, 0, yexpr_t);
     assert(for_expr->typ == YEXPR_LIST);
 
+    yexpr_debug(&program);
     ypreprocess_resolve_builtins(&program);
+    ypreprocess_refs(&program);
+    yexpr_debug(&program);
 
     yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)};
     YVEC_PUSH(&state.call_stack, for_expr);
@@ -65,9 +68,7 @@
     yexpr_t* for_expr = YVEC_AT(&program.value.list, 0, yexpr_t);
     assert(for_expr->typ == YEXPR_LIST);
 
-    assert(ybuiltin_translate(YVEC_AT(&for_expr->value.list, 0, yexpr_t)));
-    assert(ybuiltin_translate(YVEC_AT(
-        &YVEC_AT(&for_expr->value.list, 3, yexpr_t)->value.list, 0, yexpr_t)));
+    ypreprocess(&program);
     yexpr_debug(for_expr);
 
     yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)};
@@ -97,6 +98,7 @@
     assert(for_expr->typ == YEXPR_LIST);
 
     ypreprocess_resolve_builtins(&program);
+    ypreprocess_refs(&program);
     yexpr_debug(for_expr);
 
     yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)};
@@ -250,5 +252,6 @@
     test_builtin_let_parsed();
     test_builtin_let_recall();
     yvalue_free_all();
+    yatom_free_all();
     return 0;
 }