Implement first global function "print"

This introduces the globals and moves some previously non-public functions
into apfl.h.
This commit is contained in:
Laria 2022-07-12 22:13:07 +02:00
parent a4f7f0f2ff
commit 4f9a33628c
10 changed files with 225 additions and 17 deletions

View file

@ -13,6 +13,7 @@ libapfl_a_SOURCES += expr.c
libapfl_a_SOURCES += format.c
libapfl_a_SOURCES += gc.c
libapfl_a_SOURCES += hashmap.c
libapfl_a_SOURCES += globals.c
libapfl_a_SOURCES += messages.c
libapfl_a_SOURCES += parser.c
libapfl_a_SOURCES += position.c
@ -31,6 +32,7 @@ apfl_internal_headers += compile.h
apfl_internal_headers += context.h
apfl_internal_headers += format.h
apfl_internal_headers += gc.h
apfl_internal_headers += globals.h
apfl_internal_headers += hashmap.h
apfl_internal_headers += resizable.h
apfl_internal_headers += scope.h

View file

@ -123,6 +123,15 @@ struct apfl_format_writer apfl_format_file_writer(FILE *f);
struct apfl_format_writer apfl_format_string_writer(struct apfl_string_builder *sb);
bool apfl_format_put_string_view(struct apfl_format_writer, struct apfl_string_view);
#define apfl_format_put_string(w, s) apfl_format_put_string_view((w), apfl_string_view_from(s))
bool apfl_format_put_int(struct apfl_format_writer, int);
bool apfl_format_put_char(struct apfl_format_writer, char);
bool apfl_format_put_hexbyte(struct apfl_format_writer, unsigned char);
bool apfl_format_put_pos(struct apfl_format_writer, struct apfl_position);
bool apfl_format_put_indent(struct apfl_format_writer, unsigned);
bool apfl_format_put_number(struct apfl_format_writer, apfl_number);
// Tokens
enum apfl_token_type {
@ -628,6 +637,8 @@ typedef void (*apfl_cfunc)(apfl_ctx);
// Get the type of a value on the stack
enum apfl_value_type apfl_get_type(apfl_ctx, apfl_stackidx);
// Drop a value from the stack
void apfl_drop(apfl_ctx, apfl_stackidx);
// Push a nil onto the stack
void apfl_push_nil(apfl_ctx);
// Push a boolean onto the stack
@ -652,6 +663,11 @@ void apfl_dict_set(apfl_ctx, apfl_stackidx dict, apfl_stackidx k, apfl_stackidx
void apfl_get_member(apfl_ctx, apfl_stackidx container, apfl_stackidx k);
// Get a value from a list and push it on the stack. The list will stay on the stack.
void apfl_get_list_member_by_index(apfl_ctx, apfl_stackidx list, size_t index_in_list);
// Get the length of a string/list/dict. The value stays on the stack,
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);
// Push a C function onto the stack with nslots slots (initialized to nil)
void apfl_push_cfunc(apfl_ctx, apfl_cfunc, size_t nslots);
@ -669,6 +685,17 @@ enum apfl_result apfl_call_protected(apfl_ctx, apfl_stackidx func, apfl_stackidx
bool apfl_debug_print_val(apfl_ctx, apfl_stackidx, struct apfl_format_writer);
// Raise an error with a value from the stack as the message.
APFL_NORETURN void apfl_raise_error_with_type(apfl_ctx, apfl_stackidx, enum apfl_result type);
// Raise an error with a constant string as the message.
APFL_NORETURN void apfl_raise_const_error(apfl_ctx, enum apfl_result type, const char *message);
// Raise a memory allocation error.
APFL_NORETURN void apfl_raise_alloc_error(apfl_ctx);
// Raise an error for invalid stack indices.
APFL_NORETURN void apfl_raise_invalid_stackidx(apfl_ctx);
// Raise an error with a message derived from an struct apfl_error.
APFL_NORETURN void apfl_raise_error_object(apfl_ctx, struct apfl_error);
struct apfl_messages {
const char *invalid_stack_index;
const char *could_not_alloc_mem;
@ -686,6 +713,7 @@ struct apfl_messages {
const char *invalid_slotidx;
const char *not_a_function;
const char *wrong_type;
const char *io_error;
};
extern const struct apfl_messages apfl_messages;

View file

@ -7,6 +7,7 @@
#include "alloc.h"
#include "context.h"
#include "gc.h"
#include "globals.h"
#include "hashmap.h"
#include "resizable.h"
#include "strings.h"
@ -14,6 +15,7 @@
static bool try_push_const_string(apfl_ctx ctx, const char *string);
static bool current_stack_move_to_top(apfl_ctx, apfl_stackidx);
static struct apfl_string *new_copied_string(struct gc *, struct apfl_string_view);
APFL_NORETURN static void
panic(apfl_ctx ctx, bool with_error_on_stack, enum apfl_result result)
@ -384,6 +386,14 @@ apfl_stack_drop(apfl_ctx ctx, apfl_stackidx index)
return apfl_stack_pop(ctx, &value, index);
}
void
apfl_drop(apfl_ctx ctx, apfl_stackidx index)
{
if (!apfl_stack_drop(ctx, index)) {
apfl_raise_invalid_stackidx(ctx);
}
}
static bool
current_stack_move_to_top(apfl_ctx ctx, apfl_stackidx index)
{
@ -521,6 +531,56 @@ iterative_runners_list_new(void)
};
}
static bool
init_globals(apfl_ctx ctx)
{
struct gc *gc = &ctx->gc;
size_t tmproots = apfl_gc_tmproots_begin(gc);
for (
const struct global_def *global = apfl_globals();
global->name != NULL && global->func != NULL;
global++
) {
struct cfunction *cfunc = apfl_cfunc_new(gc, global->func, 0);
if (cfunc == NULL) {
goto error;
}
if (!apfl_gc_tmproot_add(gc, GC_OBJECT_FROM(cfunc, GC_TYPE_CFUNC))) {
goto error;
}
// TODO: It's silly that we need to copy the name into a new gc string.
// It should be possible for the scope to also have a const string
// as a name.
struct apfl_string *name = new_copied_string(gc, apfl_string_view_from(global->name));
if (name == NULL) {
goto error;
}
if (!apfl_gc_tmproot_add(gc, GC_OBJECT_FROM(name, GC_TYPE_STRING))) {
goto error;
}
if (!apfl_scope_set(gc, ctx->globals, name, (struct apfl_value) {
.type = VALUE_CFUNC,
.cfunc = cfunc,
})) {
goto error;
}
apfl_gc_tmproots_restore(gc, tmproots);
}
return true;
error:
apfl_gc_tmproots_restore(gc, tmproots);
return false;
}
apfl_ctx
apfl_ctx_new(struct apfl_allocator base_allocator)
{
@ -544,6 +604,10 @@ apfl_ctx_new(struct apfl_allocator base_allocator)
ctx->iterative_runners = iterative_runners_list_new();
if (!init_globals(ctx)) {
goto error;
}
return ctx;
error:
@ -912,6 +976,59 @@ apfl_get_list_member_by_index(
return;
}
size_t
apfl_len(apfl_ctx ctx, apfl_stackidx index)
{
struct apfl_value value = apfl_stack_must_get(ctx, index);
switch (value.type) {
case VALUE_NIL:
case VALUE_BOOLEAN:
case VALUE_NUMBER:
case VALUE_FUNC:
case VALUE_CFUNC:
case VALUE_USERDATA:
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type);
return 0;
case VALUE_STRING:
return value.string->len;
case VALUE_CONST_STRING:
return value.const_string.len;
case VALUE_LIST:
return apfl_list_len(value.list);
case VALUE_DICT:
return apfl_dict_len(value.dict);
}
assert(false);
return 0;
}
struct apfl_string_view
apfl_get_string(apfl_ctx ctx, apfl_stackidx index)
{
struct apfl_value value = apfl_stack_must_get(ctx, index);
switch (value.type) {
case VALUE_NIL:
case VALUE_BOOLEAN:
case VALUE_NUMBER:
case VALUE_FUNC:
case VALUE_CFUNC:
case VALUE_USERDATA:
case VALUE_LIST:
case VALUE_DICT:
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type);
goto error;
case VALUE_STRING:
return apfl_string_view_from(*value.string);
case VALUE_CONST_STRING:
return value.const_string;
}
error:
assert(false);
return (struct apfl_string_view) {.bytes = NULL, .len = 0};
}
static struct scope *
closure_scope_for_func_inner(apfl_ctx ctx)
{

View file

@ -86,12 +86,6 @@ struct apfl_ctx_data {
struct iterative_runners_list iterative_runners;
};
APFL_NORETURN void apfl_raise_error_with_type(apfl_ctx, apfl_stackidx, enum apfl_result type);
APFL_NORETURN void apfl_raise_const_error(apfl_ctx, enum apfl_result type, const char *message);
APFL_NORETURN void apfl_raise_alloc_error(apfl_ctx);
APFL_NORETURN void apfl_raise_invalid_stackidx(apfl_ctx);
APFL_NORETURN void apfl_raise_error_object(apfl_ctx, struct apfl_error);
void apfl_call_stack_entry_deinit(struct apfl_allocator, struct call_stack_entry *);
struct stack apfl_stack_new(void);

View file

@ -6,22 +6,11 @@ extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include "apfl.h"
#define FMT_TRY(x) do { if (!(x)) return false; } while (0)
bool apfl_format_put_string_view(struct apfl_format_writer, struct apfl_string_view);
#define apfl_format_put_string(w, s) apfl_format_put_string_view((w), apfl_string_view_from(s))
bool apfl_format_put_int(struct apfl_format_writer, int);
bool apfl_format_put_char(struct apfl_format_writer, char);
bool apfl_format_put_hexbyte(struct apfl_format_writer, unsigned char);
bool apfl_format_put_pos(struct apfl_format_writer, struct apfl_position);
bool apfl_format_put_indent(struct apfl_format_writer, unsigned);
bool apfl_format_put_number(struct apfl_format_writer, apfl_number);
#ifdef __cplusplus
}
#endif

49
src/globals.c Normal file
View file

@ -0,0 +1,49 @@
#include <stdio.h>
#include "apfl.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
print(apfl_ctx ctx)
{
struct apfl_format_writer w = apfl_format_file_writer(stdout);
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);
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 const struct global_def globals[] = {
{"print", print},
{NULL, NULL},
};
const struct global_def *
apfl_globals(void)
{
return globals;
}

21
src/globals.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef APFL_GLOBALS_H
#define APFL_GLOBALS_H
#ifdef __cplusplus
extern "C" {
#endif
#include "apfl.h"
struct global_def {
const char *name;
apfl_cfunc func;
};
const struct global_def *apfl_globals(void);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -17,4 +17,5 @@ const struct apfl_messages apfl_messages = {
.invalid_slotidx = "Invalid slot index",
.not_a_function = "Not a function",
.wrong_type = "Wrong type",
.io_error = "I/O error",
};

View file

@ -492,6 +492,12 @@ apfl_dict_set_raw(
return apfl_hashmap_set(&dict->map, &k, &v);
}
size_t
apfl_dict_len(struct dict_header *dict)
{
return apfl_hashmap_count(dict->map);
}
static bool
list_get_item(struct list_header *list, size_t index, struct apfl_value *out)
{

View file

@ -113,6 +113,7 @@ bool apfl_dict_set_raw(
struct apfl_value k,
struct apfl_value v
);
size_t apfl_dict_len(struct dict_header *);
void apfl_dict_deinit(struct dict_header *);
struct function *apfl_func_new(struct gc *, struct instruction_list *, struct scope *);