globals: Add if, ==, dump and basic math ops
This commit is contained in:
parent
4f9a33628c
commit
7c63a6b189
3 changed files with 193 additions and 0 deletions
|
|
@ -668,6 +668,12 @@ size_t apfl_len(apfl_ctx, apfl_stackidx);
|
|||
// Get a string view into a APFL_VALUE_STRING value. The value stays on the stack.
|
||||
// The returned string view is only guaranteed to be valid, as long as the value is on the stack.
|
||||
struct apfl_string_view apfl_get_string(apfl_ctx, apfl_stackidx);
|
||||
// Pops a value from the stack and returns whether is was truthy. All values except false and nil are truthy.
|
||||
bool apfl_is_truthy(apfl_ctx, apfl_stackidx);
|
||||
// Pops a number from the stack and returns it.
|
||||
apfl_number apfl_get_number(apfl_ctx, apfl_stackidx);
|
||||
// Pops two values from the stack and returns whether they are equal.
|
||||
bool apfl_eq(apfl_ctx, apfl_stackidx, apfl_stackidx);
|
||||
|
||||
// Push a C function onto the stack with nslots slots (initialized to nil)
|
||||
void apfl_push_cfunc(apfl_ctx, apfl_cfunc, size_t nslots);
|
||||
|
|
|
|||
|
|
@ -1029,6 +1029,48 @@ error:
|
|||
return (struct apfl_string_view) {.bytes = NULL, .len = 0};
|
||||
}
|
||||
|
||||
enum apfl_value_type
|
||||
apfl_get_type(apfl_ctx ctx, apfl_stackidx index)
|
||||
{
|
||||
struct apfl_value value = apfl_stack_must_get(ctx, index);
|
||||
return apfl_value_type_to_abstract_type(value.type);
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_is_truthy(apfl_ctx ctx, apfl_stackidx index)
|
||||
{
|
||||
struct apfl_value value = apfl_stack_must_get(ctx, index);
|
||||
switch (value.type) {
|
||||
case VALUE_NIL:
|
||||
return false;
|
||||
case VALUE_BOOLEAN:
|
||||
return value.boolean;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
apfl_number
|
||||
apfl_get_number(apfl_ctx ctx, apfl_stackidx index)
|
||||
{
|
||||
struct apfl_value value = apfl_stack_must_pop(ctx, index);
|
||||
if (value.type == VALUE_NUMBER) {
|
||||
return value.number;
|
||||
}
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type);
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_eq(apfl_ctx ctx, apfl_stackidx _a, apfl_stackidx _b)
|
||||
{
|
||||
struct apfl_value a = apfl_stack_must_get(ctx, _a);
|
||||
struct apfl_value b = apfl_stack_must_get(ctx, _b);
|
||||
|
||||
bool eq = apfl_value_eq(a, b);
|
||||
assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){_a, _b}));
|
||||
return eq;
|
||||
}
|
||||
|
||||
static struct scope *
|
||||
closure_scope_for_func_inner(apfl_ctx ctx)
|
||||
{
|
||||
|
|
|
|||
145
src/globals.c
145
src/globals.c
|
|
@ -1,3 +1,4 @@
|
|||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "apfl.h"
|
||||
|
|
@ -13,6 +14,134 @@
|
|||
} \
|
||||
} 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)
|
||||
{
|
||||
|
|
@ -37,8 +166,24 @@ print(apfl_ctx ctx)
|
|||
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},
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue