2022-07-12 21:24:19 +00:00
|
|
|
#include <assert.h>
|
2023-01-29 15:46:00 +00:00
|
|
|
#include <errno.h>
|
2022-07-12 20:13:07 +00:00
|
|
|
#include <stdio.h>
|
2023-01-29 15:46:00 +00:00
|
|
|
#include <string.h>
|
2022-07-12 20:13:07 +00:00
|
|
|
|
|
|
|
|
#include "apfl.h"
|
|
|
|
|
|
2023-01-29 15:46:00 +00:00
|
|
|
#include "alloc.h"
|
2023-03-05 21:55:20 +00:00
|
|
|
#include "builtins.h"
|
2022-07-28 18:49:29 +00:00
|
|
|
#include "bytecode.h"
|
2022-07-12 20:13:07 +00:00
|
|
|
#include "context.h"
|
2023-03-05 21:55:20 +00:00
|
|
|
#include "modules.h"
|
2023-07-03 21:33:19 +00:00
|
|
|
#include "parsing.h"
|
2022-07-12 20:13:07 +00:00
|
|
|
#include "scope.h"
|
|
|
|
|
|
2022-12-09 20:22:50 +00:00
|
|
|
#define TRY_FORMAT(ctx, x) \
|
|
|
|
|
do { \
|
|
|
|
|
if (!(x)) { \
|
|
|
|
|
apfl_raise_const_error((ctx), apfl_messages.io_error); \
|
|
|
|
|
} \
|
2022-07-12 20:13:07 +00:00
|
|
|
} while (0)
|
|
|
|
|
|
2022-07-12 21:24:19 +00:00
|
|
|
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) {
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, "if needs at least 2 arguments");
|
2022-07-12 21:24:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, "== needs at least 2 arguments");
|
2022-07-12 21:24:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) { \
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, name " needs at least 2 arguments"); \
|
2022-11-20 12:47:38 +00:00
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
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, "<=", <=)
|
|
|
|
|
|
2022-07-12 21:24:19 +00:00
|
|
|
#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) { \
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, name " needs at least 1 argument"); \
|
2022-07-12 21:24:19 +00:00
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0); \
|
|
|
|
|
apfl_number num = apfl_get_number(ctx, -1); \
|
|
|
|
|
if (len == 1) { \
|
2022-10-30 22:08:41 +00:00
|
|
|
apfl_push_number(ctx, single(num)); \
|
2022-07-12 21:24:19 +00:00
|
|
|
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 :/
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, "Division by zero");
|
2022-07-12 21:24:19 +00:00
|
|
|
}
|
|
|
|
|
return a / b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IMPL_MATH_OP_FUNC(impl_div, "/", single_identity, op_div)
|
|
|
|
|
|
2022-11-20 15:26:38 +00:00
|
|
|
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) {
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, "join expects exactly 2 arguments");
|
2022-11-20 15:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 20:13:07 +00:00
|
|
|
static void
|
|
|
|
|
print(apfl_ctx ctx)
|
|
|
|
|
{
|
2023-02-10 20:38:54 +00:00
|
|
|
struct apfl_io_writer w = apfl_get_output_writer(ctx);
|
2022-07-12 20:13:07 +00:00
|
|
|
|
|
|
|
|
size_t len = apfl_len(ctx, 0);
|
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
|
if (i > 0) {
|
2023-02-10 20:38:54 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, " "));
|
2022-07-12 20:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, i);
|
2022-10-30 21:44:34 +00:00
|
|
|
apfl_tostring(ctx, -1);
|
2022-07-12 20:13:07 +00:00
|
|
|
struct apfl_string_view sv = apfl_get_string(ctx, -1);
|
2023-02-10 20:38:54 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, sv));
|
2022-07-12 20:13:07 +00:00
|
|
|
apfl_drop(ctx, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (len > 0) {
|
2023-02-10 20:38:54 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, "\n"));
|
2022-07-12 20:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 21:24:19 +00:00
|
|
|
static void
|
|
|
|
|
dump(apfl_ctx ctx)
|
|
|
|
|
{
|
2023-02-10 20:38:54 +00:00
|
|
|
struct apfl_io_writer w = apfl_get_output_writer(ctx);
|
2022-07-12 21:24:19 +00:00
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
apfl_drop(ctx, 0);
|
|
|
|
|
TRY_FORMAT(ctx, apfl_debug_print_val(ctx, -1, w));
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-28 18:49:29 +00:00
|
|
|
static void
|
|
|
|
|
disasm(apfl_ctx ctx)
|
|
|
|
|
{
|
2023-02-10 20:38:54 +00:00
|
|
|
struct apfl_io_writer w = apfl_get_output_writer(ctx);
|
2022-07-28 18:49:29 +00:00
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
apfl_drop(ctx, 0);
|
|
|
|
|
struct apfl_value value = apfl_stack_must_get(ctx, -1);
|
2023-01-24 20:22:22 +00:00
|
|
|
if (value.type == VALUE_CFUNC) {
|
|
|
|
|
apfl_raise_const_error(ctx, "disasm needs a apfl function, got a native function instead");
|
|
|
|
|
} else if (value.type != VALUE_FUNC) {
|
|
|
|
|
apfl_raise_errorfmt(ctx, "disasm needs a apfl function, got value of type {value:type} instead", value);
|
2022-07-28 18:49:29 +00:00
|
|
|
}
|
|
|
|
|
|
2022-08-12 22:50:26 +00:00
|
|
|
struct function *func = value.func;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < func->subfunctions_len; i++) {
|
|
|
|
|
struct subfunction *subfunction = &func->subfunctions[i];
|
|
|
|
|
|
2023-02-10 20:38:54 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, "Subfunction #"));
|
2022-08-12 22:50:26 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_format_put_number(w, (int)i));
|
2023-02-10 20:38:54 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, "\n"));
|
2023-02-25 22:19:45 +00:00
|
|
|
if (subfunction->matcher == NULL) {
|
|
|
|
|
TRY_FORMAT(ctx, apfl_format_put_indent(w, 1));
|
|
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, "No matcher (matches everything)\n"));
|
|
|
|
|
} else {
|
|
|
|
|
TRY_FORMAT(ctx, apfl_format_put_indent(w, 1));
|
|
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, "Matcher\n"));
|
|
|
|
|
TRY_FORMAT(ctx, apfl_bytecode_dump_matcher(2, w, subfunction->matcher->instructions));
|
|
|
|
|
}
|
2022-08-12 22:50:26 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_format_put_indent(w, 1));
|
2023-02-10 20:38:54 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, "Instructions\n"));
|
2022-08-12 22:50:26 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_bytecode_dump(2, w, subfunction->body));
|
|
|
|
|
}
|
2022-07-28 18:49:29 +00:00
|
|
|
}
|
|
|
|
|
|
2022-10-30 21:44:34 +00:00
|
|
|
static void
|
2022-10-31 14:28:49 +00:00
|
|
|
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) {
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, error);
|
2022-10-30 21:44:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
apfl_drop(ctx, 0);
|
2022-10-31 14:28:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#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);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 21:33:19 +00:00
|
|
|
struct numparse_data {
|
|
|
|
|
struct apfl_string_view sv;
|
|
|
|
|
size_t off;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static enum read_result
|
|
|
|
|
numparse_read(void *opaque, unsigned char *b)
|
|
|
|
|
{
|
|
|
|
|
struct numparse_data *data = opaque;
|
|
|
|
|
if (data->off >= data->sv.len) {
|
|
|
|
|
return RR_EOF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*b = data->sv.bytes[data->off];
|
|
|
|
|
data->off++;
|
|
|
|
|
return RR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
numparse_unread(void *opaque)
|
|
|
|
|
{
|
|
|
|
|
struct numparse_data *data = opaque;
|
|
|
|
|
assert(data->off > 0);
|
|
|
|
|
data->off--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tonumber(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
(void)ctx;
|
|
|
|
|
if (apfl_len(ctx, 0) != 2) {
|
|
|
|
|
apfl_raise_const_error(ctx, "tonumber needs exactly 2 arguments");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
unsigned base = (unsigned)apfl_get_number(ctx, -1);
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
struct apfl_string_view sv = apfl_get_string(ctx, -1);
|
|
|
|
|
apfl_drop(ctx, 0);
|
|
|
|
|
|
|
|
|
|
if (base < 2 || base > 36) {
|
|
|
|
|
apfl_raise_const_error(ctx, "base must be between 2 and 36");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool negative = false;
|
|
|
|
|
if (sv.len > 0 && sv.bytes[0] == '-') {
|
|
|
|
|
negative = true;
|
|
|
|
|
sv = apfl_string_view_offset(sv, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_number number = 0;
|
|
|
|
|
struct numparse_data data = {
|
|
|
|
|
.sv = sv,
|
|
|
|
|
.off = 0,
|
|
|
|
|
};
|
|
|
|
|
bool ok = apfl_parse_number(base, numparse_read, numparse_unread, &data, &number);
|
|
|
|
|
assert(ok);
|
|
|
|
|
|
|
|
|
|
if (data.off != sv.len) {
|
|
|
|
|
apfl_raise_const_error(ctx, "Can not parse as number");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (negative) {
|
|
|
|
|
number *= -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_push_number(ctx, number);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 14:28:49 +00:00
|
|
|
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 void
|
|
|
|
|
type(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
ONE_ARG(ctx, "type");
|
2023-01-24 20:22:22 +00:00
|
|
|
apfl_push_const_string(ctx, apfl_type_name(apfl_get_type(ctx, -1)));
|
2022-10-31 14:28:49 +00:00
|
|
|
}
|
|
|
|
|
|
2022-10-31 14:51:56 +00:00
|
|
|
static void
|
2023-03-07 20:37:37 +00:00
|
|
|
impl_loop(apfl_ctx ctx)
|
2022-10-31 14:51:56 +00:00
|
|
|
{
|
2023-03-07 20:37:37 +00:00
|
|
|
ONE_ARG(ctx, "loop");
|
|
|
|
|
apfl_list_create(ctx, 0);
|
2022-10-31 14:51:56 +00:00
|
|
|
for (;;) {
|
2023-03-07 20:37:37 +00:00
|
|
|
apfl_copy(ctx, -2);
|
|
|
|
|
apfl_copy(ctx, -2);
|
2022-10-31 14:51:56 +00:00
|
|
|
apfl_call(ctx, -2, -1);
|
|
|
|
|
if (!apfl_is_truthy(ctx, -1)) {
|
2023-03-07 20:37:37 +00:00
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
return;
|
2022-10-31 14:51:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-19 21:07:26 +00:00
|
|
|
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")) {
|
2023-02-10 20:38:54 +00:00
|
|
|
struct apfl_io_writer w = apfl_get_output_writer(ctx);
|
2022-11-19 21:07:26 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_gc_debug_dump_graph(&ctx->gc, w));
|
|
|
|
|
} else if (apfl_string_eq(sv, "collect")) {
|
|
|
|
|
apfl_gc_full(&ctx->gc);
|
2023-07-22 20:30:07 +00:00
|
|
|
} else if (apfl_string_eq(sv, "blockstats")) {
|
|
|
|
|
struct apfl_io_writer w = apfl_get_output_writer(ctx);
|
|
|
|
|
TRY_FORMAT(ctx, apfl_gc_debug_blockstats(&ctx->gc, w));
|
2022-11-19 21:07:26 +00:00
|
|
|
} else {
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, "Unknown gc command");
|
2022-11-19 21:07:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_drop(ctx, -1);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-26 20:22:34 +00:00
|
|
|
static void
|
|
|
|
|
impl_backtrace(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
apfl_drop(ctx, -1);
|
|
|
|
|
|
2023-02-10 20:38:54 +00:00
|
|
|
struct apfl_io_writer w = apfl_get_output_writer(ctx);
|
2023-01-26 20:22:34 +00:00
|
|
|
|
|
|
|
|
size_t depth = apfl_call_stack_depth(ctx);
|
|
|
|
|
for (size_t i = 1; i < depth; i++) {
|
2023-02-10 20:38:54 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, "#"));
|
2023-01-26 20:22:34 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_format_put_int(w, (int)i));
|
2023-02-10 20:38:54 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, ": "));
|
2023-01-26 20:22:34 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_call_stack_entry_info_format(
|
|
|
|
|
w,
|
|
|
|
|
apfl_call_stack_inspect(ctx, i)
|
|
|
|
|
));
|
2023-02-10 20:38:54 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_io_write_string(w, "\n"));
|
2023-01-26 20:22:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-29 15:46:00 +00:00
|
|
|
static void
|
2023-01-30 21:50:01 +00:00
|
|
|
closefile(FILE **f)
|
2023-01-29 15:46:00 +00:00
|
|
|
{
|
2023-01-30 21:50:01 +00:00
|
|
|
if (*f != NULL) {
|
|
|
|
|
fclose(*f);
|
|
|
|
|
*f = NULL;
|
2023-01-29 15:46:00 +00:00
|
|
|
}
|
2023-01-30 21:50:01 +00:00
|
|
|
}
|
2023-01-29 15:46:00 +00:00
|
|
|
|
2023-01-30 21:50:01 +00:00
|
|
|
static void
|
|
|
|
|
file_onbeforecollect(void *opaque)
|
|
|
|
|
{
|
|
|
|
|
FILE **f = opaque;
|
|
|
|
|
closefile(f);
|
2023-01-29 15:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_native_object_type file_object = {
|
|
|
|
|
.size = sizeof(FILE *),
|
|
|
|
|
.onbeforecollect = file_onbeforecollect,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
getcstring(apfl_ctx ctx, apfl_stackidx index)
|
|
|
|
|
{
|
|
|
|
|
apfl_move_to_top_of_stack(ctx, index);
|
2023-02-13 21:31:18 +00:00
|
|
|
apfl_push_string_view_copy(ctx, (struct apfl_string_view) { .bytes = (unsigned char [1]) {'\0'}, .len = 1, });
|
2023-01-29 15:46:00 +00:00
|
|
|
apfl_concat_strings(ctx, -2, -1);
|
2023-02-13 21:31:18 +00:00
|
|
|
return (const char *)apfl_get_string(ctx, -1).bytes;
|
2023-01-29 15:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
raise_errno(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
char *err = strerror(errno);
|
|
|
|
|
apfl_push_string_view_copy(ctx, apfl_string_view_from(err));
|
|
|
|
|
apfl_raise_error(ctx, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
impl_fopen(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
size_t argc = apfl_len(ctx, 0);
|
|
|
|
|
if (argc == 0) {
|
|
|
|
|
apfl_raise_const_error(ctx, "fopen needs at least one argument");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
const char *filename = getcstring(ctx, -1);
|
|
|
|
|
|
|
|
|
|
if (argc > 1) {
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
} else {
|
2023-07-22 20:30:07 +00:00
|
|
|
apfl_push_const_string(ctx," rb");
|
2023-01-29 15:46:00 +00:00
|
|
|
}
|
|
|
|
|
const char *mode = getcstring(ctx, -1);
|
|
|
|
|
|
|
|
|
|
FILE **fh = apfl_push_native_object(ctx, &file_object);
|
|
|
|
|
|
|
|
|
|
*fh = fopen(filename, mode);
|
|
|
|
|
if (*fh == NULL) {
|
|
|
|
|
raise_errno(ctx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
impl_fread(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
size_t argc = apfl_len(ctx, 0);
|
|
|
|
|
if (argc != 2) {
|
|
|
|
|
apfl_raise_const_error(ctx, "fread needs exactly 2 arguments");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
FILE **fh = apfl_get_native_object(ctx, &file_object, -1);
|
|
|
|
|
if (*fh == NULL) {
|
|
|
|
|
apfl_raise_const_error(ctx, "File already closed");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
apfl_number presize = apfl_get_number(ctx, -1);
|
|
|
|
|
if (presize < 0) {
|
|
|
|
|
apfl_raise_const_error(ctx, "Can not read a negative amount of bytes from file");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t size = (size_t)presize;
|
|
|
|
|
|
2023-02-13 21:31:18 +00:00
|
|
|
unsigned char *buf = ALLOC_BYTES(ctx->gc.allocator, size);
|
2023-01-29 15:46:00 +00:00
|
|
|
if (buf == NULL) {
|
|
|
|
|
apfl_raise_alloc_error(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t actual = fread(buf, 1, size, *fh);
|
|
|
|
|
|
|
|
|
|
if (!apfl_move_string_onto_stack(ctx, (struct apfl_string) {
|
|
|
|
|
.bytes = buf,
|
|
|
|
|
.len = actual,
|
|
|
|
|
.cap = size,
|
|
|
|
|
})) {
|
|
|
|
|
FREE_BYTES(ctx->gc.allocator, buf, size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
impl_fwrite(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
size_t argc = apfl_len(ctx, 0);
|
|
|
|
|
if (argc != 2) {
|
|
|
|
|
apfl_raise_const_error(ctx, "fread needs exactly 2 arguments");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
FILE **fh = apfl_get_native_object(ctx, &file_object, -1);
|
|
|
|
|
if (*fh == NULL) {
|
|
|
|
|
apfl_raise_const_error(ctx, "File already closed");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
struct apfl_string_view sv = apfl_get_string(ctx, -1);
|
|
|
|
|
if (fwrite(sv.bytes, 1, sv.len, *fh) != sv.len) {
|
|
|
|
|
raise_errno(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
impl_fclose(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
ONE_ARG(ctx, "fclose");
|
|
|
|
|
FILE **fh = apfl_get_native_object(ctx, &file_object, -1);
|
2023-01-30 21:50:01 +00:00
|
|
|
closefile(fh);
|
2023-01-29 15:46:00 +00:00
|
|
|
apfl_drop(ctx, -1);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 21:50:01 +00:00
|
|
|
static void
|
|
|
|
|
loadfile(apfl_ctx ctx)
|
|
|
|
|
{
|
2023-03-30 18:33:53 +00:00
|
|
|
ONE_ARG(ctx, "load-file");
|
2023-01-30 21:50:01 +00:00
|
|
|
apfl_copy(ctx, -1);
|
|
|
|
|
const char *filename = getcstring(ctx, -1);
|
|
|
|
|
|
|
|
|
|
FILE **fh = apfl_push_native_object(ctx, &file_object);
|
|
|
|
|
*fh = fopen(filename, "rb");
|
|
|
|
|
if (*fh == NULL) {
|
|
|
|
|
raise_errno(ctx);
|
|
|
|
|
}
|
|
|
|
|
apfl_drop(ctx, -2); // drop cstring
|
|
|
|
|
|
2023-03-05 16:02:42 +00:00
|
|
|
struct apfl_io_reader r = apfl_io_file_reader(*fh);
|
|
|
|
|
apfl_load(ctx, apfl_io_reader_as_source_reader(&r), -2);
|
2023-01-30 21:50:01 +00:00
|
|
|
closefile(fh);
|
|
|
|
|
apfl_drop(ctx, -2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
loadstring(apfl_ctx ctx)
|
|
|
|
|
{
|
2023-03-30 18:33:53 +00:00
|
|
|
ONE_ARG(ctx, "load-string");
|
2023-01-30 21:50:01 +00:00
|
|
|
apfl_tostring(ctx, -1);
|
2023-03-30 18:33:53 +00:00
|
|
|
apfl_push_const_string(ctx, "(load-string)");
|
2023-01-30 21:50:01 +00:00
|
|
|
|
2023-03-05 16:02:42 +00:00
|
|
|
struct apfl_io_string_reader_data reader_data = apfl_io_string_reader_create(apfl_get_string(ctx, -2));
|
|
|
|
|
struct apfl_io_reader r = apfl_io_string_reader(&reader_data);
|
|
|
|
|
apfl_load(ctx, apfl_io_reader_as_source_reader(&r), -1);
|
2023-01-30 21:50:01 +00:00
|
|
|
apfl_drop(ctx, -2);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 16:02:42 +00:00
|
|
|
static void
|
|
|
|
|
serialize_bytecode(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
struct apfl_value value = apfl_stack_must_get(ctx, -1);
|
|
|
|
|
if (value.type == VALUE_CFUNC) {
|
|
|
|
|
apfl_raise_const_error(ctx, "-serialize-bytecode needs a apfl function, got a native function instead");
|
|
|
|
|
} else if (value.type != VALUE_FUNC) {
|
|
|
|
|
apfl_raise_errorfmt(ctx, "-serialize-bytecode needs a apfl function, got value of type {value:type} instead", value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
FILE **fh = apfl_get_native_object(ctx, &file_object, -1);
|
|
|
|
|
|
|
|
|
|
struct apfl_io_writer w = apfl_io_file_writer(*fh);
|
|
|
|
|
|
|
|
|
|
if (!apfl_bytecode_serialize(ctx->gc.allocator, w, value.func->subfunctions[0].body)) {
|
|
|
|
|
apfl_raise_const_error(ctx, "Could not serialize function");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
unserialize_bytecode(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
apfl_drop(ctx, -2);
|
|
|
|
|
FILE **fh = apfl_get_native_object(ctx, &file_object, -1);
|
|
|
|
|
|
|
|
|
|
struct apfl_io_reader r = apfl_io_file_reader(*fh);
|
|
|
|
|
|
|
|
|
|
apfl_load_bytecode(ctx, r);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 21:55:20 +00:00
|
|
|
static void
|
|
|
|
|
set_func_name(apfl_ctx ctx)
|
2022-07-12 20:13:07 +00:00
|
|
|
{
|
2023-03-05 21:55:20 +00:00
|
|
|
size_t argc = apfl_len(ctx, 0);
|
|
|
|
|
if (argc != 2) {
|
|
|
|
|
apfl_raise_const_error(ctx, "set-func-name needs exactly 2 arguments");
|
|
|
|
|
}
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
apfl_set_func_name(ctx, -2, -1);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 20:51:40 +00:00
|
|
|
static void
|
|
|
|
|
substring(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
struct apfl_string_view sv = apfl_get_string(ctx, -1);
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
apfl_number start = apfl_get_number(ctx, -1);
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 2);
|
|
|
|
|
apfl_number len = apfl_get_number(ctx, -1);
|
|
|
|
|
|
|
|
|
|
apfl_push_string_view_copy(ctx, apfl_string_view_substr(sv, (size_t)start, (size_t)len));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stringsearch(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
struct apfl_string_view haystack = apfl_get_string(ctx, -1);
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
struct apfl_string_view needle = apfl_get_string(ctx, -1);
|
|
|
|
|
|
|
|
|
|
ptrdiff_t off = apfl_string_view_search(haystack, needle);
|
|
|
|
|
if (off < 0) {
|
|
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
} else {
|
|
|
|
|
apfl_push_number(ctx, (apfl_number)off);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 20:37:37 +00:00
|
|
|
static bool
|
|
|
|
|
iterate_dict_callback(apfl_ctx ctx, void *opaque)
|
|
|
|
|
{
|
|
|
|
|
(void)opaque;
|
|
|
|
|
|
|
|
|
|
apfl_list_create(ctx, 2);
|
|
|
|
|
apfl_list_append(ctx, -1, -3);
|
|
|
|
|
apfl_list_append(ctx, -1, -2);
|
|
|
|
|
apfl_copy(ctx, -2);
|
|
|
|
|
apfl_call(ctx, -1, -2);
|
|
|
|
|
|
|
|
|
|
return apfl_is_truthy(ctx, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
iterate_dict(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
apfl_drop(ctx, 0);
|
|
|
|
|
apfl_iterate_dict(ctx, -2, NULL, iterate_dict_callback);
|
|
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-30 20:11:44 +00:00
|
|
|
static int argv_registry_key;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
apfl_argv_set(apfl_ctx ctx, apfl_stackidx args)
|
|
|
|
|
{
|
|
|
|
|
apfl_registry_set(ctx, &argv_registry_key, 0, args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
get_argv(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
if (!apfl_registry_try_get(ctx, &argv_registry_key, 0)) {
|
|
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-23 22:38:35 +00:00
|
|
|
static void
|
|
|
|
|
get_optional(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
size_t args = apfl_len(ctx, 0);
|
|
|
|
|
if (args != 2) {
|
|
|
|
|
apfl_raise_const_error(ctx, "get-optional needs exactly 2 arguments");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
apfl_stack_drop(ctx, 0);
|
|
|
|
|
|
|
|
|
|
if (apfl_get_member_if_exists(ctx, -1, -2)) {
|
|
|
|
|
apfl_sym_some(ctx);
|
|
|
|
|
apfl_push_pair(ctx, -1, -2);
|
|
|
|
|
} else {
|
|
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-29 20:33:40 +00:00
|
|
|
static void
|
|
|
|
|
impl_raise(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
ONE_ARG(ctx, "raise");
|
|
|
|
|
apfl_raise_error(ctx, -1);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-23 22:38:35 +00:00
|
|
|
static void
|
|
|
|
|
symbol(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
size_t args = apfl_len(ctx, 0);
|
|
|
|
|
switch (args) {
|
|
|
|
|
case 0:
|
|
|
|
|
apfl_push_anon_symbol(ctx);
|
|
|
|
|
return;
|
|
|
|
|
case 1:
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
apfl_drop(ctx, 0);
|
|
|
|
|
apfl_push_symbol(ctx, -1);
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
apfl_raise_const_error(ctx, "symbol need 0 or 1 argument");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 20:41:26 +00:00
|
|
|
struct splice_info {
|
|
|
|
|
size_t off;
|
|
|
|
|
size_t len;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static apfl_number
|
|
|
|
|
getnumarg(apfl_ctx ctx, size_t arg)
|
|
|
|
|
{
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, arg);
|
|
|
|
|
return apfl_get_number(ctx, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
splice_off(apfl_ctx ctx, size_t list_len, size_t arg)
|
|
|
|
|
{
|
|
|
|
|
size_t off;
|
|
|
|
|
apfl_number off_num = getnumarg(ctx, arg);
|
|
|
|
|
if (off_num < 0) {
|
|
|
|
|
off = (size_t)(-off_num);
|
|
|
|
|
off = off > list_len ? 0 : list_len - off;
|
|
|
|
|
} else {
|
|
|
|
|
off = (size_t)off_num;
|
|
|
|
|
if (off >= list_len) {
|
|
|
|
|
off = list_len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return off;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
splice_len(apfl_ctx ctx, size_t list_len, size_t off, size_t arg)
|
|
|
|
|
{
|
|
|
|
|
assert(off <= list_len);
|
|
|
|
|
list_len -= off;
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, arg);
|
|
|
|
|
if (apfl_get_type(ctx, -1) == APFL_VALUE_NIL) {
|
|
|
|
|
apfl_drop(ctx, -1);
|
|
|
|
|
return list_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_number num = apfl_get_number(ctx, -1);
|
|
|
|
|
if (num < 0) {
|
|
|
|
|
size_t len = (size_t)(-num);
|
|
|
|
|
return len > list_len ? 0 : list_len - len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t len = (size_t)num;
|
|
|
|
|
return len > list_len ? list_len : len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct splice_info
|
|
|
|
|
splice_get_list(apfl_ctx ctx, size_t args_base)
|
|
|
|
|
{
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, args_base);
|
|
|
|
|
if (apfl_get_type(ctx, -1) != APFL_VALUE_LIST) {
|
|
|
|
|
apfl_raise_errorfmt(
|
|
|
|
|
ctx,
|
|
|
|
|
"Expected a list argument to splice, got {stack:type} instead",
|
|
|
|
|
-1
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t list_len = apfl_len(ctx, -1);
|
|
|
|
|
|
|
|
|
|
size_t off = splice_off(ctx, list_len, args_base + 1);
|
|
|
|
|
size_t len = splice_len(ctx, list_len, off, args_base + 2);
|
|
|
|
|
|
|
|
|
|
return (struct splice_info) {
|
|
|
|
|
.off = off,
|
|
|
|
|
.len = len,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
splice(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
if (apfl_len(ctx, 0) != 6) {
|
|
|
|
|
apfl_raise_const_error(ctx, "splice needs exactly 6 arguments");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct splice_info a = splice_get_list(ctx, 0);
|
|
|
|
|
struct splice_info b = splice_get_list(ctx, 3);
|
|
|
|
|
|
|
|
|
|
apfl_list_splice(ctx, -2, a.off, a.len, -1, b.off, b.len);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 21:55:20 +00:00
|
|
|
static void
|
|
|
|
|
add_builtin(apfl_ctx ctx, const char *name, apfl_cfunc func)
|
|
|
|
|
{
|
|
|
|
|
apfl_push_const_string(ctx, name);
|
|
|
|
|
apfl_push_cfunc(ctx, func, 0);
|
|
|
|
|
apfl_push_const_string(ctx, name);
|
|
|
|
|
apfl_set_func_name(ctx, -2, -1);
|
|
|
|
|
apfl_dict_set(ctx, -3, -2, -1);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-04 20:46:22 +00:00
|
|
|
static int cmod_searcher_registry_key;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
cmod_searcher(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
ONE_ARG(ctx, "cmod-searcher");
|
|
|
|
|
if (!apfl_registry_try_get(ctx, &cmod_searcher_registry_key, 0)) {
|
|
|
|
|
apfl_drop(ctx, -1);
|
|
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (apfl_get_member_if_exists(ctx, -1, -2)) {
|
|
|
|
|
apfl_sym_some(ctx);
|
|
|
|
|
apfl_push_pair(ctx, -1, -2);
|
|
|
|
|
} else {
|
|
|
|
|
apfl_push_nil(ctx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
apfl_modules_register(apfl_ctx ctx, const char *name, apfl_stackidx modloader)
|
|
|
|
|
{
|
|
|
|
|
apfl_move_to_top_of_stack(ctx, modloader);
|
|
|
|
|
|
|
|
|
|
if (!apfl_registry_try_get(ctx, &cmod_searcher_registry_key, 0)) {
|
|
|
|
|
apfl_dict_create(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_push_const_string(ctx, name);
|
|
|
|
|
apfl_dict_set(ctx, -2, -1, -3);
|
|
|
|
|
|
|
|
|
|
apfl_registry_set(ctx, &cmod_searcher_registry_key, 0, -1);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 21:55:20 +00:00
|
|
|
void
|
|
|
|
|
apfl_builtins(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
apfl_dict_create(ctx);
|
|
|
|
|
|
|
|
|
|
add_builtin(ctx, "if", impl_if);
|
|
|
|
|
add_builtin(ctx, "==", impl_eq);
|
|
|
|
|
add_builtin(ctx, ">", impl_gt);
|
|
|
|
|
add_builtin(ctx, "<", impl_lt);
|
|
|
|
|
add_builtin(ctx, ">=", impl_ge);
|
|
|
|
|
add_builtin(ctx, "<=", impl_le);
|
|
|
|
|
add_builtin(ctx, "+", impl_plus);
|
|
|
|
|
add_builtin(ctx, "-", impl_minus);
|
|
|
|
|
add_builtin(ctx, "*", impl_mult);
|
|
|
|
|
add_builtin(ctx, "/", impl_div);
|
|
|
|
|
add_builtin(ctx, "&", impl_concat);
|
|
|
|
|
add_builtin(ctx, "join", impl_join);
|
|
|
|
|
add_builtin(ctx, "print", print);
|
|
|
|
|
add_builtin(ctx, "dump", dump);
|
|
|
|
|
add_builtin(ctx, "disasm", disasm);
|
|
|
|
|
add_builtin(ctx, "tostring", tostring);
|
|
|
|
|
add_builtin(ctx, "not", not);
|
|
|
|
|
add_builtin(ctx, "len", len);
|
|
|
|
|
add_builtin(ctx, "type", type);
|
2023-03-07 20:37:37 +00:00
|
|
|
add_builtin(ctx, "loop", impl_loop);
|
2023-03-05 21:55:20 +00:00
|
|
|
add_builtin(ctx, "gc", impl_gc);
|
|
|
|
|
add_builtin(ctx, "backtrace", impl_backtrace);
|
|
|
|
|
add_builtin(ctx, "fopen", impl_fopen);
|
|
|
|
|
add_builtin(ctx, "fread", impl_fread);
|
|
|
|
|
add_builtin(ctx, "fwrite", impl_fwrite);
|
|
|
|
|
add_builtin(ctx, "fclose", impl_fclose);
|
2023-03-30 18:33:53 +00:00
|
|
|
add_builtin(ctx, "load-file", loadfile);
|
|
|
|
|
add_builtin(ctx, "load-string", loadstring);
|
2023-03-05 21:55:20 +00:00
|
|
|
add_builtin(ctx, "-serialize-bytecode", serialize_bytecode);
|
|
|
|
|
add_builtin(ctx, "-unserialize-bytecode", unserialize_bytecode);
|
|
|
|
|
add_builtin(ctx, "set-func-name", set_func_name);
|
2023-03-07 20:51:40 +00:00
|
|
|
add_builtin(ctx, "substring", substring);
|
|
|
|
|
add_builtin(ctx, "stringsearch", stringsearch);
|
2023-03-07 20:37:37 +00:00
|
|
|
add_builtin(ctx, "iterate-dict", iterate_dict);
|
2023-03-23 22:38:35 +00:00
|
|
|
add_builtin(ctx, "symbol", symbol);
|
|
|
|
|
add_builtin(ctx, "get-optional", get_optional);
|
2023-03-29 20:33:40 +00:00
|
|
|
add_builtin(ctx, "raise", impl_raise);
|
2023-03-23 22:38:35 +00:00
|
|
|
add_builtin(ctx, "getsym-Some", apfl_sym_some);
|
2023-03-30 20:11:44 +00:00
|
|
|
add_builtin(ctx, "get-argv", get_argv);
|
2023-04-04 20:46:22 +00:00
|
|
|
add_builtin(ctx, "cmod-searcher", cmod_searcher);
|
2023-07-03 21:33:19 +00:00
|
|
|
add_builtin(ctx, "tonumber", tonumber);
|
2023-10-23 20:41:26 +00:00
|
|
|
add_builtin(ctx, "splice", splice);
|
2022-07-12 20:13:07 +00:00
|
|
|
}
|