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"
|
2022-07-28 18:49:29 +00:00
|
|
|
#include "bytecode.h"
|
2022-07-12 20:13:07 +00:00
|
|
|
#include "context.h"
|
|
|
|
|
#include "globals.h"
|
|
|
|
|
#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"));
|
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, "Matcher\n"));
|
2022-08-12 22:50:26 +00:00
|
|
|
TRY_FORMAT(ctx, apfl_bytecode_dump_matcher(2, w, subfunction->matcher->instructions));
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
impl_while(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
size_t argc = apfl_len(ctx, 0);
|
|
|
|
|
if (argc != 2) {
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, "while needs 2 functions as arguments");
|
2022-10-31 14:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 0);
|
|
|
|
|
if (apfl_get_type(ctx, -1) != APFL_VALUE_FUNC) {
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, "while needs 2 functions as arguments");
|
2022-10-31 14:51:56 +00:00
|
|
|
}
|
|
|
|
|
apfl_get_list_member_by_index(ctx, 0, 1);
|
|
|
|
|
if (apfl_get_type(ctx, -1) != APFL_VALUE_FUNC) {
|
2022-12-09 20:22:50 +00:00
|
|
|
apfl_raise_const_error(ctx, "while needs 2 functions as arguments");
|
2022-10-31 14:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
} 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 {
|
|
|
|
|
apfl_push_const_string(ctx, "rb");
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
ONE_ARG(ctx, "loadfile");
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
apfl_load(ctx, apfl_stdio_source_reader(*fh), -2);
|
|
|
|
|
closefile(fh);
|
|
|
|
|
apfl_drop(ctx, -2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
loadstring(apfl_ctx ctx)
|
|
|
|
|
{
|
|
|
|
|
ONE_ARG(ctx, "loadstring");
|
|
|
|
|
apfl_tostring(ctx, -1);
|
|
|
|
|
apfl_push_const_string(ctx, "(loadstring)");
|
|
|
|
|
|
|
|
|
|
struct apfl_string_source_reader_data reader_data = apfl_string_source_reader_create(apfl_get_string(ctx, -2));
|
|
|
|
|
apfl_load(ctx, apfl_string_source_reader(&reader_data), -1);
|
|
|
|
|
apfl_drop(ctx, -2);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 20:13:07 +00:00
|
|
|
static const struct global_def globals[] = {
|
2022-07-12 21:24:19 +00:00
|
|
|
{"if", impl_if},
|
|
|
|
|
{"==", impl_eq},
|
2022-11-20 12:47:38 +00:00
|
|
|
{">", impl_gt},
|
|
|
|
|
{"<", impl_lt},
|
|
|
|
|
{">=", impl_ge},
|
|
|
|
|
{"<=", impl_le},
|
2022-07-12 21:24:19 +00:00
|
|
|
{"+", impl_plus},
|
|
|
|
|
{"-", impl_minus},
|
|
|
|
|
{"*", impl_mult},
|
|
|
|
|
{"/", impl_div},
|
2022-11-20 15:26:38 +00:00
|
|
|
{"&", impl_concat},
|
|
|
|
|
{"join", impl_join},
|
2022-07-12 20:13:07 +00:00
|
|
|
{"print", print},
|
2022-07-12 21:24:19 +00:00
|
|
|
{"dump", dump},
|
2022-07-28 18:49:29 +00:00
|
|
|
{"disasm", disasm},
|
2022-10-30 21:44:34 +00:00
|
|
|
{"tostring", tostring},
|
2022-10-31 14:28:49 +00:00
|
|
|
{"not", not},
|
|
|
|
|
{"len", len},
|
|
|
|
|
{"type", type},
|
2022-10-31 14:51:56 +00:00
|
|
|
{"while", impl_while},
|
2022-11-19 21:07:26 +00:00
|
|
|
{"gc", impl_gc},
|
2023-01-26 20:22:34 +00:00
|
|
|
{"backtrace", impl_backtrace},
|
2023-01-29 15:46:00 +00:00
|
|
|
{"fopen", impl_fopen},
|
|
|
|
|
{"fread", impl_fread},
|
|
|
|
|
{"fwrite", impl_fwrite},
|
|
|
|
|
{"fclose", impl_fclose},
|
2023-01-30 21:50:01 +00:00
|
|
|
{"loadfile", loadfile},
|
|
|
|
|
{"loadstring", loadstring},
|
2022-07-12 20:13:07 +00:00
|
|
|
{NULL, NULL},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const struct global_def *
|
|
|
|
|
apfl_globals(void)
|
|
|
|
|
{
|
|
|
|
|
return globals;
|
|
|
|
|
}
|