diff --git a/src/Makefile.am b/src/Makefile.am index 0dfcd35..2af7b05 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/apfl.h b/src/apfl.h index 20de5f4..d1549c0 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -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; diff --git a/src/context.c b/src/context.c index 6b09325..f81f787 100644 --- a/src/context.c +++ b/src/context.c @@ -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) { diff --git a/src/context.h b/src/context.h index e85cce2..8fdbbfa 100644 --- a/src/context.h +++ b/src/context.h @@ -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); diff --git a/src/format.h b/src/format.h index c6dd257..9c93183 100644 --- a/src/format.h +++ b/src/format.h @@ -6,22 +6,11 @@ extern "C" { #endif #include -#include -#include #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 diff --git a/src/globals.c b/src/globals.c new file mode 100644 index 0000000..ef6362d --- /dev/null +++ b/src/globals.c @@ -0,0 +1,49 @@ +#include + +#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; +} diff --git a/src/globals.h b/src/globals.h new file mode 100644 index 0000000..4d8a242 --- /dev/null +++ b/src/globals.h @@ -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 diff --git a/src/messages.c b/src/messages.c index 6678aef..dc27a7e 100644 --- a/src/messages.c +++ b/src/messages.c @@ -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", }; diff --git a/src/value.c b/src/value.c index 0daba01..10c74ee 100644 --- a/src/value.c +++ b/src/value.c @@ -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) { diff --git a/src/value.h b/src/value.h index b09423c..364a0b5 100644 --- a/src/value.h +++ b/src/value.h @@ -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 *);