#include #include #include "apfl.h" #include "bytecode.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 IMPLEMENT_COMPARISON(impl_name, name, cmp) \ static void \ impl_name(apfl_ctx ctx) \ { \ size_t len = apfl_len(ctx, 0); \ if (len < 2) { \ apfl_raise_const_error(ctx, APFL_RESULT_ERR, name " 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_cmp(ctx, -2, -1) cmp 0)) { \ apfl_push_bool(ctx, false); \ return; \ } \ } \ \ apfl_push_bool(ctx, true); \ } \ IMPLEMENT_COMPARISON(impl_gt, ">", >) IMPLEMENT_COMPARISON(impl_lt, "<", <) IMPLEMENT_COMPARISON(impl_ge, ">=", >=) IMPLEMENT_COMPARISON(impl_le, "<=", <=) #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) { \ apfl_push_number(ctx, 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 impl_concat(apfl_ctx ctx) { apfl_push_const_string(ctx, ""); apfl_join_strings(ctx, -1, -2); } static void impl_join(apfl_ctx ctx) { size_t len = apfl_len(ctx, 0); if (len != 2) { apfl_raise_const_error(ctx, APFL_RESULT_ERR, "join expects exactly 2 arguments"); } apfl_get_list_member_by_index(ctx, 0, 0); apfl_get_list_member_by_index(ctx, 0, 1); apfl_drop(ctx, 0); apfl_join_strings(ctx, -2, -1); } static void print(apfl_ctx ctx) { struct apfl_format_writer w = apfl_get_output_writer(ctx); 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); apfl_tostring(ctx, -1); 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_get_output_writer(ctx); apfl_get_list_member_by_index(ctx, 0, 0); apfl_drop(ctx, 0); TRY_FORMAT(ctx, apfl_debug_print_val(ctx, -1, w)); } static void disasm(apfl_ctx ctx) { struct apfl_format_writer w = apfl_get_output_writer(ctx); apfl_get_list_member_by_index(ctx, 0, 0); apfl_drop(ctx, 0); struct apfl_value value = apfl_stack_must_get(ctx, -1); if (value.type != VALUE_FUNC) { apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type); } struct function *func = value.func; for (size_t i = 0; i < func->subfunctions_len; i++) { struct subfunction *subfunction = &func->subfunctions[i]; TRY_FORMAT(ctx, apfl_format_put_string(w, "Subfunction #")); TRY_FORMAT(ctx, apfl_format_put_number(w, (int)i)); TRY_FORMAT(ctx, apfl_format_put_string(w, "\n")); TRY_FORMAT(ctx, apfl_format_put_indent(w, 1)); TRY_FORMAT(ctx, apfl_format_put_string(w, "Matcher\n")); TRY_FORMAT(ctx, apfl_bytecode_dump_matcher(2, w, subfunction->matcher->instructions)); TRY_FORMAT(ctx, apfl_format_put_indent(w, 1)); TRY_FORMAT(ctx, apfl_format_put_string(w, "Instructions\n")); TRY_FORMAT(ctx, apfl_bytecode_dump(2, w, subfunction->body)); } } static void require_exactly_one_arg(apfl_ctx ctx, const char *error) { size_t len = apfl_len(ctx, 0); if (len != 1) { apfl_raise_const_error(ctx, APFL_RESULT_ERR, error); } apfl_get_list_member_by_index(ctx, 0, 0); apfl_drop(ctx, 0); } #define ONE_ARG(ctx, name) require_exactly_one_arg((ctx), name " needs exactly 1 argument") static void tostring(apfl_ctx ctx) { ONE_ARG(ctx, "tostring"); apfl_tostring(ctx, -1); } static void not(apfl_ctx ctx) { ONE_ARG(ctx, "not"); bool b = apfl_is_truthy(ctx, -1); apfl_push_bool(ctx, !b); } static void len(apfl_ctx ctx) { ONE_ARG(ctx, "len"); size_t l = apfl_len(ctx, -1); apfl_push_number(ctx, (apfl_number)l); } static const char * typestr(enum apfl_value_type type) { switch (type) { case APFL_VALUE_NIL: return "nil"; case APFL_VALUE_BOOLEAN: return "bool"; case APFL_VALUE_NUMBER: return "number"; case APFL_VALUE_STRING: return "string"; case APFL_VALUE_LIST: return "list"; case APFL_VALUE_DICT: return "dict"; case APFL_VALUE_FUNC: return "function"; case APFL_VALUE_USERDATA: return "userdata"; } return "?"; } static void type(apfl_ctx ctx) { ONE_ARG(ctx, "type"); apfl_push_const_string(ctx, typestr(apfl_get_type(ctx, -1))); } static void impl_while(apfl_ctx ctx) { size_t argc = apfl_len(ctx, 0); if (argc != 2) { apfl_raise_const_error(ctx, APFL_RESULT_ERR, "while needs 2 functions as arguments"); } apfl_get_list_member_by_index(ctx, 0, 0); if (apfl_get_type(ctx, -1) != APFL_VALUE_FUNC) { apfl_raise_const_error(ctx, APFL_RESULT_ERR, "while needs 2 functions as arguments"); } apfl_get_list_member_by_index(ctx, 0, 1); if (apfl_get_type(ctx, -1) != APFL_VALUE_FUNC) { apfl_raise_const_error(ctx, APFL_RESULT_ERR, "while needs 2 functions as arguments"); } apfl_drop(ctx, 0); apfl_push_nil(ctx); // Return value in case of no iteration for (;;) { apfl_copy(ctx, 0); apfl_list_create(ctx, 0); apfl_call(ctx, -2, -1); if (!apfl_is_truthy(ctx, -1)) { break; } apfl_drop(ctx, -1); apfl_copy(ctx, 1); apfl_list_create(ctx, 0); apfl_call(ctx, -2, -1); } } static void impl_gc(apfl_ctx ctx) { ONE_ARG(ctx, "gc"); apfl_tostring(ctx, -1); struct apfl_string_view sv = apfl_get_string(ctx, -1); if (apfl_string_eq(sv, "dump")) { struct apfl_format_writer w = apfl_get_output_writer(ctx); TRY_FORMAT(ctx, apfl_gc_debug_dump_graph(&ctx->gc, w)); } else if (apfl_string_eq(sv, "collect")) { apfl_gc_full(&ctx->gc); } else { apfl_raise_const_error(ctx, APFL_RESULT_ERR, "Unknown gc command"); } apfl_drop(ctx, -1); } static const struct global_def globals[] = { {"if", impl_if}, {"==", impl_eq}, {">", impl_gt}, {"<", impl_lt}, {">=", impl_ge}, {"<=", impl_le}, {"+", impl_plus}, {"-", impl_minus}, {"*", impl_mult}, {"/", impl_div}, {"&", impl_concat}, {"join", impl_join}, {"print", print}, {"dump", dump}, {"disasm", disasm}, {"tostring", tostring}, {"not", not}, {"len", len}, {"type", type}, {"while", impl_while}, {"gc", impl_gc}, {NULL, NULL}, }; const struct global_def * apfl_globals(void) { return globals; }