#include #include #include "apfl.h" #include "context.h" #include "globals.h" #include "scope.h" #define TRY_FORMAT(ctx, x) \ do { \ if (!(x)) { \ apfl_raise_const_error((ctx), APFL_RESULT_ERR, apfl_messages.io_error); \ } \ } 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) { struct apfl_format_writer w = apfl_format_file_writer(stdout); size_t len = apfl_len(ctx, 0); for (size_t i = 0; i < len; i++) { if (i > 0) { TRY_FORMAT(ctx, apfl_format_put_string(w, " ")); } apfl_get_list_member_by_index(ctx, 0, i); struct apfl_string_view sv = apfl_get_string(ctx, -1); TRY_FORMAT(ctx, apfl_format_put_string(w, sv)); apfl_drop(ctx, -1); } if (len > 0) { TRY_FORMAT(ctx, apfl_format_put_string(w, "\n")); } 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}, }; const struct global_def * apfl_globals(void) { return globals; }