apfl/src/globals.c

409 lines
13 KiB
C
Raw Normal View History

#include <assert.h>
#include <stdio.h>
#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);
}
2022-11-20 12:47:38 +00:00
#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);
2022-10-30 21:44:34 +00:00
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));
}
}
2022-10-30 21:44:34 +00:00
static void
require_exactly_one_arg(apfl_ctx ctx, const char *error)
2022-10-30 21:44:34 +00:00
{
size_t len = apfl_len(ctx, 0);
if (len != 1) {
apfl_raise_const_error(ctx, APFL_RESULT_ERR, error);
2022-10-30 21:44:34 +00:00
}
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");
2022-10-30 21:44:34 +00:00
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)));
}
2022-10-31 14:51:56 +00:00
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},
2022-11-20 12:47:38 +00:00
{">", 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},
2022-10-30 21:44:34 +00:00
{"tostring", tostring},
{"not", not},
{"len", len},
{"type", type},
2022-10-31 14:51:56 +00:00
{"while", impl_while},
{"gc", impl_gc},
{NULL, NULL},
};
const struct global_def *
apfl_globals(void)
{
return globals;
}