From 7c63a6b18994aaec770f65c7bc62b734a0859501 Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Tue, 12 Jul 2022 23:24:19 +0200 Subject: [PATCH] globals: Add if, ==, dump and basic math ops --- src/apfl.h | 6 +++ src/context.c | 42 +++++++++++++++ src/globals.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) diff --git a/src/apfl.h b/src/apfl.h index d1549c0..48de64e 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -668,6 +668,12 @@ size_t apfl_len(apfl_ctx, apfl_stackidx); // Get a string view into a APFL_VALUE_STRING value. The value stays on the stack. // The returned string view is only guaranteed to be valid, as long as the value is on the stack. struct apfl_string_view apfl_get_string(apfl_ctx, apfl_stackidx); +// Pops a value from the stack and returns whether is was truthy. All values except false and nil are truthy. +bool apfl_is_truthy(apfl_ctx, apfl_stackidx); +// Pops a number from the stack and returns it. +apfl_number apfl_get_number(apfl_ctx, apfl_stackidx); +// Pops two values from the stack and returns whether they are equal. +bool apfl_eq(apfl_ctx, apfl_stackidx, apfl_stackidx); // Push a C function onto the stack with nslots slots (initialized to nil) void apfl_push_cfunc(apfl_ctx, apfl_cfunc, size_t nslots); diff --git a/src/context.c b/src/context.c index f81f787..2312156 100644 --- a/src/context.c +++ b/src/context.c @@ -1029,6 +1029,48 @@ error: return (struct apfl_string_view) {.bytes = NULL, .len = 0}; } +enum apfl_value_type +apfl_get_type(apfl_ctx ctx, apfl_stackidx index) +{ + struct apfl_value value = apfl_stack_must_get(ctx, index); + return apfl_value_type_to_abstract_type(value.type); +} + +bool +apfl_is_truthy(apfl_ctx ctx, apfl_stackidx index) +{ + struct apfl_value value = apfl_stack_must_get(ctx, index); + switch (value.type) { + case VALUE_NIL: + return false; + case VALUE_BOOLEAN: + return value.boolean; + default: + return true; + } +} + +apfl_number +apfl_get_number(apfl_ctx ctx, apfl_stackidx index) +{ + struct apfl_value value = apfl_stack_must_pop(ctx, index); + if (value.type == VALUE_NUMBER) { + return value.number; + } + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type); +} + +bool +apfl_eq(apfl_ctx ctx, apfl_stackidx _a, apfl_stackidx _b) +{ + struct apfl_value a = apfl_stack_must_get(ctx, _a); + struct apfl_value b = apfl_stack_must_get(ctx, _b); + + bool eq = apfl_value_eq(a, b); + assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){_a, _b})); + return eq; +} + static struct scope * closure_scope_for_func_inner(apfl_ctx ctx) { diff --git a/src/globals.c b/src/globals.c index ef6362d..312e583 100644 --- a/src/globals.c +++ b/src/globals.c @@ -1,3 +1,4 @@ +#include #include #include "apfl.h" @@ -13,6 +14,134 @@ } \ } while (0) +static void +call_then_or_else(apfl_ctx ctx, size_t arg) +{ + apfl_get_list_member_by_index(ctx, 0, arg); + apfl_list_create(ctx, 0); + apfl_call(ctx, -2, -1); +} + +static void +impl_if(apfl_ctx ctx) +{ + size_t len = apfl_len(ctx, 0); + + if (len < 2) { + apfl_raise_const_error(ctx, APFL_RESULT_ERR, "if needs at least 2 arguments"); + } + + size_t i = 0; + while (i+1 < len) { + size_t cond = i++; + size_t then = i++; + + apfl_get_list_member_by_index(ctx, 0, cond); + if (apfl_get_type(ctx, -1) == APFL_VALUE_FUNC) { + apfl_list_create(ctx, 0); + apfl_call(ctx, -2, -1); + } + + if (apfl_is_truthy(ctx, -1)) { + apfl_get_list_member_by_index(ctx, 0, then); + apfl_list_create(ctx, 0); + apfl_call(ctx, -2, -1); + return; + } + } + + if (i == len) { + // Empty else. Return nil + apfl_push_nil(ctx); + } else { + assert(i+1 == len /*exactly one argument remains as final else*/); + + call_then_or_else(ctx, i); + } +} + +static void +impl_eq(apfl_ctx ctx) +{ + size_t len = apfl_len(ctx, 0); + if (len < 2) { + apfl_raise_const_error(ctx, APFL_RESULT_ERR, "== needs at least 2 arguments"); + } + + for (size_t i = 0; i < len-1; i++) { + apfl_get_list_member_by_index(ctx, 0, i); + apfl_get_list_member_by_index(ctx, 0, i+1); + if (!apfl_eq(ctx, -2, -1)) { + apfl_push_bool(ctx, false); + return; + } + } + + apfl_push_bool(ctx, true); +} + +#define IMPL_MATH_OP(name, op) \ + static apfl_number \ + name(apfl_ctx ctx, apfl_number a, apfl_number b) \ + { \ + (void)ctx; \ + return a op b; \ + } + +#define IMPL_MATH_OP_FUNC(impl, name, single, op) \ + static void \ + impl(apfl_ctx ctx) \ + { \ + size_t len = apfl_len(ctx, 0); \ + if (len < 1) { \ + apfl_raise_const_error(ctx, APFL_RESULT_ERR, name " needs at least 1 argument"); \ + } \ + \ + apfl_get_list_member_by_index(ctx, 0, 0); \ + apfl_number num = apfl_get_number(ctx, -1); \ + if (len == 1) { \ + single(num); \ + return; \ + } \ + \ + for (size_t i = 1; i < len; i++) { \ + apfl_get_list_member_by_index(ctx, 0, i); \ + num = op(ctx, num, apfl_get_number(ctx, -1)); \ + } \ + \ + apfl_push_number(ctx, num); \ + } + +static apfl_number +single_identity(apfl_number number) +{ + return number; +} + +static apfl_number +single_negate(apfl_number number) +{ + return -number; +} + +IMPL_MATH_OP(op_plus, +) +IMPL_MATH_OP(op_minus, -) +IMPL_MATH_OP(op_mult, *) +IMPL_MATH_OP_FUNC(impl_plus, "+", single_identity, op_plus) +IMPL_MATH_OP_FUNC(impl_minus, "-", single_negate, op_minus) +IMPL_MATH_OP_FUNC(impl_mult, "*", single_identity, op_mult) + +static apfl_number +op_div(apfl_ctx ctx, apfl_number a, apfl_number b) +{ + if (b == 0.0 || b == -0.0) { // apfl_number is a double, so there are technically two zeroes :/ + apfl_raise_const_error(ctx, APFL_RESULT_ERR, "Division by zero"); + } + return a / b; +} + +IMPL_MATH_OP_FUNC(impl_div, "/", single_identity, op_div) + static void print(apfl_ctx ctx) { @@ -37,8 +166,24 @@ print(apfl_ctx ctx) apfl_push_nil(ctx); } +static void +dump(apfl_ctx ctx) +{ + struct apfl_format_writer w = apfl_format_file_writer(stdout); + apfl_get_list_member_by_index(ctx, 0, 0); + apfl_drop(ctx, 0); + TRY_FORMAT(ctx, apfl_debug_print_val(ctx, -1, w)); +} + static const struct global_def globals[] = { + {"if", impl_if}, + {"==", impl_eq}, + {"+", impl_plus}, + {"-", impl_minus}, + {"*", impl_mult}, + {"/", impl_div}, {"print", print}, + {"dump", dump}, {NULL, NULL}, };