From 8d947244c9afc2f323a16fb6d00eec76733f6be6 Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Fri, 24 Jun 2022 23:13:44 +0200 Subject: [PATCH] Implement exceptions-like error handling Most functzions will no longer return an enum apfl_result, but will raise an error that bubbles up. --- src/apfl.h | 41 ++-- src/context.c | 328 +++++++++++++++++++++++++------- src/context.h | 21 ++ src/eval.c | 219 ++++++++++----------- src/main.c | 11 +- src/messages.c | 8 + webpage/playground/playground.c | 11 +- 7 files changed, 450 insertions(+), 189 deletions(-) diff --git a/src/apfl.h b/src/apfl.h index 395a0e9..e6e88d8 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -9,6 +9,12 @@ extern "C" { #include #include +#ifdef __GNUC__ +# define APFL_NORETURN __attribute__((noreturn)) +#else +# define APFL_NORETURN +#endif + // Allocator // apfl_allocator_cb is called to (re)allocate and free memory. @@ -603,46 +609,59 @@ apfl_ctx apfl_ctx_new(struct apfl_allocator); void apfl_ctx_destroy(apfl_ctx); +typedef void (*apfl_panic_callback)(apfl_ctx, bool with_error_on_stack, void *); + +void apfl_ctx_set_panic_callback(apfl_ctx, apfl_panic_callback, void *); + typedef struct apfl_iterative_runner_data *apfl_iterative_runner; apfl_iterative_runner apfl_iterative_runner_new(apfl_ctx, struct apfl_source_reader); bool apfl_iterative_runner_next(apfl_iterative_runner); enum apfl_result apfl_iterative_runner_get_result(apfl_iterative_runner); +bool apfl_iterative_runner_has_error_on_stack(apfl_iterative_runner); void apfl_iterative_runner_destroy(apfl_iterative_runner); // Get the type of a value on the stack enum apfl_value_type apfl_get_type(apfl_ctx, apfl_stackidx); // Push a nil onto the stack -enum apfl_result apfl_push_nil(apfl_ctx); +void apfl_push_nil(apfl_ctx); // Push a boolean onto the stack -enum apfl_result apfl_push_bool(apfl_ctx, bool); +void apfl_push_bool(apfl_ctx, bool); // Push a number onto the stack -enum apfl_result apfl_push_number(apfl_ctx, apfl_number); +void apfl_push_number(apfl_ctx, apfl_number); // Push a string onto the stack -enum apfl_result apfl_push_string_view_copy(apfl_ctx, struct apfl_string_view); +void apfl_push_string_view_copy(apfl_ctx, struct apfl_string_view); // Push a constant string. -enum apfl_result apfl_push_const_string(apfl_ctx, const char *); +void apfl_push_const_string(apfl_ctx, const char *); // Create a new empty list on the stack -enum apfl_result apfl_list_create(apfl_ctx, size_t initial_capacity); +void apfl_list_create(apfl_ctx, size_t initial_capacity); // Append a value to a list. The value will be dropped. -enum apfl_result apfl_list_append(apfl_ctx, apfl_stackidx list, apfl_stackidx value); +void apfl_list_append(apfl_ctx, apfl_stackidx list, apfl_stackidx value); // Append a list to another list. The second list will be dropped. -enum apfl_result apfl_list_append_list(apfl_ctx, apfl_stackidx a, apfl_stackidx b); +void apfl_list_append_list(apfl_ctx, apfl_stackidx a, apfl_stackidx b); // Create a new empty dict on the stack -enum apfl_result apfl_dict_create(apfl_ctx); +void apfl_dict_create(apfl_ctx); // Set a value in a dictionary. k and v will be dropped. -enum apfl_result apfl_dict_set(apfl_ctx, apfl_stackidx dict, apfl_stackidx k, apfl_stackidx v); +void apfl_dict_set(apfl_ctx, apfl_stackidx dict, apfl_stackidx k, apfl_stackidx v); // Get a value from a container (list or dict) and push it on the stack. container and k will be dropped. -enum apfl_result apfl_get_member(apfl_ctx, apfl_stackidx container, apfl_stackidx k); +void apfl_get_member(apfl_ctx, apfl_stackidx container, apfl_stackidx k); bool apfl_debug_print_val(apfl_ctx, apfl_stackidx, struct apfl_format_writer); struct apfl_messages { + const char *invalid_stack_index; const char *could_not_alloc_mem; const char *input_error_while_parsing; const char *unexpected_end_of_file; const char *feature_not_implemented; + const char *corrupted_bytecode; + const char *not_a_list; + const char *not_a_dict; + const char *key_doesnt_exist; + const char *value_is_not_a_container; + const char *wrong_key_type; + const char *variable_doesnt_exist; }; extern const struct apfl_messages apfl_messages; diff --git a/src/context.c b/src/context.c index 3c84143..483f045 100644 --- a/src/context.c +++ b/src/context.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "apfl.h" #include "alloc.h" @@ -11,6 +12,139 @@ #include "strings.h" #include "value.h" +static bool try_push_const_string(apfl_ctx ctx, const char *string); + +APFL_NORETURN static void +panic(apfl_ctx ctx, bool with_error_on_stack, enum apfl_result result) +{ + (void)ctx; + (void)result; + (void)with_error_on_stack; + fprintf(stderr, "panic!\n"); + // TODO: more details + abort(); +} + +#define RESULT_OFF_FOR_LONGJMP 1 + +enum apfl_result +apfl_protected(apfl_ctx ctx, apfl_protected_callback cb, void *opaque, bool *with_error_on_stack) +{ + struct error_handler *prev_handler = ctx->error_handler; + + size_t stack_len = ctx->stack->len; + size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc); + + struct error_handler handler; + ctx->error_handler = &handler; + + enum apfl_result result = APFL_RESULT_OK; + int rv = setjmp(handler.jump); + if (rv == 0) { + cb(ctx, opaque); + } else { + result = (enum apfl_result)(rv - RESULT_OFF_FOR_LONGJMP); + assert(result != APFL_RESULT_OK); + + struct apfl_value err; + if ((*with_error_on_stack = handler.with_error_on_stack)) { + if (!apfl_stack_pop(ctx, &err, -1)) { + *with_error_on_stack = false; + // TODO: Indicate error during error handling + } + } + + apfl_gc_tmproots_restore(&ctx->gc, tmproots); + assert(stack_len <= ctx->stack->cap); + ctx->stack->len = stack_len; + + if (*with_error_on_stack) { + if (!apfl_stack_push(ctx, err)) { + *with_error_on_stack = false; + // TODO: Indicate error during error handling + } + } + } + + ctx->error_handler = prev_handler; + + return result; +} + +APFL_NORETURN static void +raise_error(apfl_ctx ctx, bool with_error_on_stack, enum apfl_result type) +{ + assert(type != APFL_RESULT_OK); + + if (ctx->error_handler != NULL) { + ctx->error_handler->with_error_on_stack = with_error_on_stack; + longjmp(ctx->error_handler->jump, (int)type + RESULT_OFF_FOR_LONGJMP); + } + + if (ctx->panic_callback != NULL) { + ctx->panic_callback(ctx, with_error_on_stack, ctx->panic_callback_data); + } + + panic(ctx, with_error_on_stack, type); +} + +APFL_NORETURN void +apfl_raise_error_with_type(apfl_ctx ctx, apfl_stackidx idx, enum apfl_result type) +{ + bool ok = apfl_stack_move_to_top(ctx, idx); + raise_error(ctx, ok, type); +} + +APFL_NORETURN void +apfl_raise_const_error(apfl_ctx ctx, enum apfl_result type, const char *message) +{ + raise_error(ctx, try_push_const_string(ctx, message), type); +} + +APFL_NORETURN void +apfl_raise_alloc_error(apfl_ctx ctx) +{ + apfl_raise_const_error(ctx, APFL_RESULT_ERR_FATAL, apfl_messages.could_not_alloc_mem); +} + +APFL_NORETURN void +apfl_raise_invalid_stackidx(apfl_ctx ctx) +{ + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.invalid_stack_index); +} + +APFL_NORETURN void +apfl_raise_error_object(apfl_ctx ctx, struct apfl_error error) +{ + enum apfl_result errtype = APFL_RESULT_ERR; + if (apfl_error_is_fatal_type(error.type)) { + errtype = APFL_RESULT_ERR_FATAL; + } + + const char *const_str = apfl_error_as_const_string(error); + if (const_str != NULL) { + apfl_raise_const_error(ctx, errtype, const_str); + } + + struct apfl_string string; + if (!apfl_error_as_string(error, ctx->gc.allocator, &string)) { + apfl_raise_alloc_error(ctx); + } + + if (!apfl_move_string_onto_stack(ctx, string)) { + apfl_raise_alloc_error(ctx); + } + + apfl_raise_error_with_type(ctx, -1, errtype); +} + +void +apfl_ctx_set_panic_callback(apfl_ctx ctx, apfl_panic_callback cb, void *opaque) +{ + ctx->panic_callback = cb; + ctx->panic_callback_data = opaque; +} + static struct stack * stack_new(struct gc *gc) { @@ -34,6 +168,14 @@ apfl_stack_deinit(struct apfl_allocator allocator, struct stack *stack) FREE_LIST(allocator, stack->items, stack->cap); } +void +apfl_stack_must_push(apfl_ctx ctx, struct apfl_value value) +{ + if (!apfl_stack_push(ctx, value)) { + apfl_raise_alloc_error(ctx); + } +} + bool apfl_stack_push(apfl_ctx ctx, struct apfl_value value) { @@ -161,6 +303,23 @@ apfl_stack_push_placeholder(apfl_ctx ctx) return stack_get_pointer(ctx, -1); } +bool +apfl_move_string_onto_stack(apfl_ctx ctx, struct apfl_string string) +{ + struct apfl_value *value = apfl_stack_push_placeholder(ctx); + if (value == NULL) { + return false; + } + + if ((value->string = apfl_string_move_into_new_gc_string(&ctx->gc, &string)) == NULL) { + return false; + } + + value->type = VALUE_STRING; + + return true; +} + bool apfl_stack_drop(apfl_ctx ctx, apfl_stackidx index) { @@ -168,6 +327,29 @@ apfl_stack_drop(apfl_ctx ctx, apfl_stackidx index) return apfl_stack_pop(ctx, &value, index); } +bool +apfl_stack_move_to_top(apfl_ctx ctx, apfl_stackidx index) +{ + struct apfl_value val; + if (!stack_get_and_adjust_index(ctx, &val, &index)) { + return false; + } + + size_t absindex = (size_t)index; + + // If we're here, index is an absolute address and is guaranteed to be < len + assert(ctx->stack->len >= absindex+1); + + memmove( + &ctx->stack->items[absindex + 1], + &ctx->stack->items[absindex], + ctx->stack->len - absindex - 1 + ); + ctx->stack->items[ctx->stack->len-1] = val; + + return true; +} + void apfl_stack_clear(apfl_ctx ctx) { @@ -203,6 +385,10 @@ apfl_ctx_new(struct apfl_allocator base_allocator) goto error; } + ctx->error_handler = NULL; + ctx->panic_callback = NULL; + ctx->panic_callback_data = NULL; + return ctx; error: @@ -228,43 +414,41 @@ apfl_ctx_destroy(apfl_ctx ctx) #define CREATE_GC_OBJECT_VALUE_ON_STACK(ctx, TYPE, MEMB, NEW) \ struct apfl_value *value = apfl_stack_push_placeholder(ctx); \ if (value == NULL) { \ - return APFL_RESULT_ERR_FATAL; \ + apfl_raise_alloc_error(ctx); \ } \ \ struct apfl_value new_value = {.type = TYPE}; \ if ((new_value.MEMB = NEW) == NULL) { \ assert(apfl_stack_drop(ctx, -1)); \ - return APFL_RESULT_ERR_FATAL; \ + apfl_raise_alloc_error(ctx); \ } \ \ - *value = new_value; \ - \ - return APFL_RESULT_OK; + *value = new_value; -enum apfl_result +void apfl_push_nil(apfl_ctx ctx) { - return apfl_stack_push(ctx, (struct apfl_value) { + apfl_stack_must_push(ctx, (struct apfl_value) { .type = VALUE_NIL, - }) ? APFL_RESULT_OK : APFL_RESULT_ERR; + }); } -enum apfl_result +void apfl_push_bool(apfl_ctx ctx, bool b) { - return apfl_stack_push(ctx, (struct apfl_value) { + apfl_stack_must_push(ctx, (struct apfl_value) { .type = VALUE_BOOLEAN, .boolean = b, - }) ? APFL_RESULT_OK : APFL_RESULT_ERR; + }); } -enum apfl_result +void apfl_push_number(apfl_ctx ctx, apfl_number num) { - return apfl_stack_push(ctx, (struct apfl_value) { + apfl_stack_must_push(ctx, (struct apfl_value) { .type = VALUE_NUMBER, .number = num, - }) ? APFL_RESULT_OK : APFL_RESULT_ERR; + }); } static struct apfl_string * @@ -284,7 +468,7 @@ new_copied_string(struct gc *gc, struct apfl_string_view sv) return out; } -enum apfl_result +void apfl_push_string_view_copy(apfl_ctx ctx, struct apfl_string_view sv) { CREATE_GC_OBJECT_VALUE_ON_STACK( @@ -295,16 +479,24 @@ apfl_push_string_view_copy(apfl_ctx ctx, struct apfl_string_view sv) ) } -enum apfl_result -apfl_push_const_string(apfl_ctx ctx, const char *string) +static bool +try_push_const_string(apfl_ctx ctx, const char *string) { return apfl_stack_push(ctx, (struct apfl_value) { .type = VALUE_CONST_STRING, .const_string = apfl_string_view_from(string), - }) ? APFL_RESULT_OK : APFL_RESULT_ERR_FATAL; + }); } -enum apfl_result +void +apfl_push_const_string(apfl_ctx ctx, const char *string) +{ + if (!try_push_const_string(ctx, string)) { + apfl_raise_alloc_error(ctx); + } +} + +void apfl_list_create(apfl_ctx ctx, size_t initial_cap) { CREATE_GC_OBJECT_VALUE_ON_STACK( @@ -315,77 +507,74 @@ apfl_list_create(apfl_ctx ctx, size_t initial_cap) ) } -enum apfl_result +void apfl_list_append(apfl_ctx ctx, apfl_stackidx list_index, apfl_stackidx value_index) { struct apfl_value *list_val = stack_get_pointer(ctx, list_index); if (list_val == NULL) { - return APFL_RESULT_ERR; + apfl_raise_invalid_stackidx(ctx); } if (list_val->type != VALUE_LIST) { - return APFL_RESULT_ERR; + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.not_a_list); } struct apfl_value value; if (!apfl_stack_get(ctx, &value, value_index)) { - return APFL_RESULT_ERR; + apfl_raise_invalid_stackidx(ctx); } - enum apfl_result result = apfl_list_splice( + if (!apfl_list_splice( &ctx->gc, &list_val->list, list_val->list->len, 0, &value, 1 - ) - ? APFL_RESULT_OK - : APFL_RESULT_ERR; + )) { + apfl_raise_alloc_error(ctx); + } assert(apfl_stack_drop(ctx, value_index)); - - return result; } -enum apfl_result +void apfl_list_append_list(apfl_ctx ctx, apfl_stackidx dst_index, apfl_stackidx src_index) { struct apfl_value *dst_val = stack_get_pointer(ctx, dst_index); if (dst_val == NULL) { - return APFL_RESULT_ERR; + apfl_raise_invalid_stackidx(ctx); } if (dst_val->type != VALUE_LIST) { - return APFL_RESULT_ERR; + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.not_a_list); } struct apfl_value src_val; if (!apfl_stack_get(ctx, &src_val, src_index)) { - return APFL_RESULT_ERR; + apfl_raise_invalid_stackidx(ctx); } if (src_val.type != VALUE_LIST) { assert(apfl_stack_drop(ctx, src_index)); - return APFL_RESULT_ERR; + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.not_a_list); } - enum apfl_result result = apfl_list_splice( + if (!apfl_list_splice( &ctx->gc, &dst_val->list, dst_val->list->len, 0, src_val.list->items, src_val.list->len - ) - ? APFL_RESULT_OK - : APFL_RESULT_ERR; + )) { + apfl_raise_alloc_error(ctx); + } assert(apfl_stack_drop(ctx, src_index)); - return result; } -enum apfl_result +void apfl_dict_create(apfl_ctx ctx) { CREATE_GC_OBJECT_VALUE_ON_STACK( @@ -396,7 +585,7 @@ apfl_dict_create(apfl_ctx ctx) ) } -enum apfl_result +void apfl_dict_set( apfl_ctx ctx, apfl_stackidx dict_index, @@ -412,43 +601,23 @@ apfl_dict_set( || !apfl_stack_get(ctx, &v, v_index) || (dict_value = stack_get_pointer(ctx, dict_index)) == NULL ) { - return APFL_RESULT_ERR; + apfl_raise_invalid_stackidx(ctx); } if (dict_value->type != VALUE_DICT) { assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, v_index})); - return APFL_RESULT_ERR; + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.not_a_dict); } if (!apfl_dict_set_raw(&ctx->gc, &dict_value->dict, k, v)) { assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, v_index})); - return APFL_RESULT_ERR; + apfl_raise_alloc_error(ctx); } assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, v_index})); - return APFL_RESULT_OK; } -static enum apfl_result -apfl_get_member_inner( - apfl_ctx ctx, - struct apfl_value container, - struct apfl_value k -) { - struct apfl_value *value = apfl_stack_push_placeholder(ctx); - if (value == NULL) { - return APFL_RESULT_ERR; - } - - if (apfl_value_get_item(container, k, value) != GET_ITEM_OK) { - assert(apfl_stack_drop(ctx, -1)); - return APFL_RESULT_ERR; - } - - return APFL_RESULT_OK; -} - -enum apfl_result +void apfl_get_member( apfl_ctx ctx, apfl_stackidx container_index, @@ -460,12 +629,35 @@ apfl_get_member( !stack_get_and_adjust_index(ctx, &container, &container_index) || !stack_get_and_adjust_index(ctx, &k, &k_index) ) { - return APFL_RESULT_ERR; + apfl_raise_invalid_stackidx(ctx); + } + + struct apfl_value *value = apfl_stack_push_placeholder(ctx); + if (value == NULL) { + assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, container_index})); + apfl_raise_alloc_error(ctx); + } + + enum get_item_result result = apfl_value_get_item(container, k, value); + if (result != GET_ITEM_OK) { + assert(apfl_stack_drop(ctx, -1)); } - enum apfl_result result = apfl_get_member_inner(ctx, container, k); assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, container_index})); - return result; + + switch (result) { + case GET_ITEM_OK: + break; + case GET_ITEM_KEY_DOESNT_EXIST: + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.key_doesnt_exist); + break; + case GET_ITEM_NOT_A_CONTAINER: + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.value_is_not_a_container); + break; + case GET_ITEM_WRONG_KEY_TYPE: + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_key_type); + break; + } } void diff --git a/src/context.h b/src/context.h index d783751..e99a61f 100644 --- a/src/context.h +++ b/src/context.h @@ -6,6 +6,7 @@ extern "C" { #endif #include +#include #include "bytecode.h" #include "hashmap.h" @@ -19,25 +20,45 @@ struct stack { size_t cap; }; +struct error_handler { + jmp_buf jump; + bool with_error_on_stack; +}; + struct apfl_ctx_data { struct gc gc; + apfl_panic_callback panic_callback; + void *panic_callback_data; struct scope *scope; struct stack *stack; + struct error_handler *error_handler; int execution_line; }; +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_stack_deinit(struct apfl_allocator, struct stack *); void apfl_gc_stack_traverse(struct stack *, gc_visitor, void *); bool apfl_stack_push(apfl_ctx, struct apfl_value); +void apfl_stack_must_push(apfl_ctx ctx, struct apfl_value value); bool apfl_stack_check_index(apfl_ctx, apfl_stackidx *); bool apfl_stack_pop(apfl_ctx, struct apfl_value *value, apfl_stackidx); bool apfl_stack_get(apfl_ctx, struct apfl_value *value, apfl_stackidx); bool apfl_stack_drop(apfl_ctx, apfl_stackidx); +bool apfl_stack_move_to_top(apfl_ctx, apfl_stackidx); void apfl_stack_clear(apfl_ctx); struct apfl_value *apfl_stack_push_placeholder(apfl_ctx); +bool apfl_move_string_onto_stack(apfl_ctx, struct apfl_string); + +typedef void (*apfl_protected_callback)(apfl_ctx, void *); +enum apfl_result apfl_protected(apfl_ctx, apfl_protected_callback, void *, bool *with_error_on_stack); #ifdef __cplusplus } diff --git a/src/eval.c b/src/eval.c index ee353dd..6b0ae91 100644 --- a/src/eval.c +++ b/src/eval.c @@ -11,57 +11,52 @@ #include "strings.h" #include "value.h" -#define TRY(ex) \ - do { \ - enum apfl_result result = (ex); \ - if (result != APFL_RESULT_OK) { \ - return result; \ - } \ - } while (0) - -static enum apfl_result -stack_push_or_fatal(apfl_ctx ctx, struct apfl_value value) -{ - return apfl_stack_push(ctx, value) ? APFL_RESULT_OK : APFL_RESULT_ERR_FATAL; -} - static void stack_must_drop(apfl_ctx ctx, apfl_stackidx index) { assert(apfl_stack_drop(ctx, index)); } -static enum apfl_result +static bool get_argument(size_t *i, struct instruction_list *ilist, union instruction_or_arg *arg) { if (*i >= ilist->len) { - return APFL_RESULT_ERR; + return false; } *arg = ilist->instructions[(*i)++]; - return APFL_RESULT_OK; + return true; } -static enum apfl_result +static void +must_get_argument(apfl_ctx ctx, size_t *i, struct instruction_list *ilist, union instruction_or_arg *arg) +{ + if (!get_argument(i, ilist, arg)) { + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode); + } +} + +static void variable_get(apfl_ctx ctx, struct apfl_string *name) { struct apfl_value value; if (!apfl_scope_get(ctx->scope, name, &value)) { - return APFL_RESULT_ERR; + // TODO: Include variable name in error + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.variable_doesnt_exist); } - return stack_push_or_fatal(ctx, value); + apfl_stack_must_push(ctx, value); } -static enum apfl_result +static void variable_set(apfl_ctx ctx, struct apfl_string *name, bool keep_on_stack) { struct apfl_value value; if (!apfl_stack_get(ctx, &value, -1)) { - return APFL_RESULT_ERR; + apfl_raise_invalid_stackidx(ctx); } if (!apfl_scope_set(&ctx->gc, ctx->scope, name, value)) { - return APFL_RESULT_ERR_FATAL; + apfl_raise_alloc_error(ctx); } if (keep_on_stack) { // If the value should be kept on the stack, the value is now in two @@ -71,19 +66,17 @@ variable_set(apfl_ctx ctx, struct apfl_string *name, bool keep_on_stack) } else { stack_must_drop(ctx, -1); } - return APFL_RESULT_OK; } -static enum apfl_result +static void variable_new(apfl_ctx ctx, struct apfl_string *name) { if (!apfl_scope_create_var(&ctx->gc, ctx->scope, name)) { - return APFL_RESULT_ERR_FATAL; + apfl_raise_alloc_error(ctx); } - return APFL_RESULT_OK; } -static enum apfl_result +static void evaluate(apfl_ctx ctx, size_t *i, struct instruction_list *ilist) { union instruction_or_arg arg; @@ -91,94 +84,104 @@ evaluate(apfl_ctx ctx, size_t *i, struct instruction_list *ilist) assert(*i < ilist->len); switch (ilist->instructions[(*i)++].instruction) { case INSN_NIL: - return apfl_push_nil(ctx); + apfl_push_nil(ctx); + return; case INSN_TRUE: - return apfl_push_bool(ctx, true); + apfl_push_bool(ctx, true); + return; case INSN_FALSE: - return apfl_push_bool(ctx, false); + apfl_push_bool(ctx, false); + return; case INSN_NUMBER: - TRY(get_argument(i, ilist, &arg)); - return apfl_push_number(ctx, arg.number); + must_get_argument(ctx, i, ilist, &arg); + apfl_push_number(ctx, arg.number); + return; case INSN_STRING: - TRY(get_argument(i, ilist, &arg)); - return stack_push_or_fatal(ctx, (struct apfl_value) { + must_get_argument(ctx, i, ilist, &arg); + apfl_stack_must_push(ctx, (struct apfl_value) { .type = VALUE_STRING, .string = arg.string, }); + return; case INSN_LIST: - TRY(get_argument(i, ilist, &arg)); - return apfl_list_create(ctx, arg.count); + must_get_argument(ctx, i, ilist, &arg); + apfl_list_create(ctx, arg.count); + return; case INSN_LIST_APPEND: - return apfl_list_append(ctx, -2, -1); + apfl_list_append(ctx, -2, -1); + return; case INSN_LIST_EXPAND_INTO: - return apfl_list_append_list(ctx, -2, -1); + apfl_list_append_list(ctx, -2, -1); + return; case INSN_DICT: - return apfl_dict_create(ctx); + apfl_dict_create(ctx); + return; case INSN_DICT_APPEND_KVPAIR: - return apfl_dict_set(ctx, -3, -2, -1); + apfl_dict_set(ctx, -3, -2, -1); + return; case INSN_GET_MEMBER: - return apfl_get_member(ctx, -2, -1); + apfl_get_member(ctx, -2, -1); + return; case INSN_VAR_NEW: - TRY(get_argument(i, ilist, &arg)); - return variable_new(ctx, arg.string); + must_get_argument(ctx, i, ilist, &arg); + variable_new(ctx, arg.string); + return; case INSN_VAR_GET: - TRY(get_argument(i, ilist, &arg)); - return variable_get(ctx, arg.string); + must_get_argument(ctx, i, ilist, &arg); + variable_get(ctx, arg.string); + return; case INSN_VAR_SET: - TRY(get_argument(i, ilist, &arg)); - return variable_set(ctx, arg.string, true); + must_get_argument(ctx, i, ilist, &arg); + variable_set(ctx, arg.string, true); + return; case INSN_NEXT_LINE: ctx->execution_line++; - return APFL_RESULT_OK; + return; case INSN_SET_LINE: - TRY(get_argument(i, ilist, &arg)); + must_get_argument(ctx, i, ilist, &arg); ctx->execution_line = arg.count; - return APFL_RESULT_OK; + return; } assert(false); - return APFL_RESULT_ERR; } -static enum apfl_result +static void evaluate_list(apfl_ctx ctx, struct instruction_list *ilist) { ctx->execution_line = ilist->line; size_t i = 0; while (i < ilist->len) { - TRY(evaluate(ctx, &i, ilist)); + evaluate(ctx, &i, ilist); } - return APFL_RESULT_OK; } -static enum apfl_result -eval_inner(apfl_ctx ctx, struct apfl_expr expr) +static void +eval_expr_inner(apfl_ctx ctx, struct apfl_expr expr) { struct instruction_list *ilist = apfl_instructions_new(&ctx->gc, expr.position.line); if (ilist == NULL) { - return APFL_RESULT_ERR_FATAL; + apfl_raise_alloc_error(ctx); } if (!apfl_gc_tmproot_add(&ctx->gc, GC_OBJECT_FROM(ilist, GC_TYPE_INSTRUCTIONS))) { - return APFL_RESULT_ERR_FATAL; + apfl_raise_alloc_error(ctx); } struct apfl_error error; if (!apfl_compile(&ctx->gc, expr, &error, ilist)) { - return APFL_RESULT_ERR; + apfl_raise_error_object(ctx, error); } - return evaluate_list(ctx, ilist); + evaluate_list(ctx, ilist); } -static enum apfl_result +static void eval_expr(apfl_ctx ctx, struct apfl_expr expr) { size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc); - enum apfl_result result = eval_inner(ctx, expr); + eval_expr_inner(ctx, expr); apfl_gc_tmproots_restore(&ctx->gc, tmproots); - - return result; } bool @@ -200,6 +203,7 @@ struct apfl_iterative_runner_data { apfl_tokenizer_ptr tokenizer; apfl_parser_ptr parser; enum apfl_result result; + bool with_error_on_stack; bool end; }; @@ -235,49 +239,49 @@ apfl_iterative_runner_new(apfl_ctx ctx, struct apfl_source_reader reader) return runner; } -static bool -move_string_onto_stack(apfl_ctx ctx, struct apfl_string string) -{ - struct apfl_value *value = apfl_stack_push_placeholder(ctx); - if (value == NULL) { - return false; - } - - if ((value->string = apfl_string_move_into_new_gc_string(&ctx->gc, &string)) == NULL) { - return false; - } - - value->type = VALUE_STRING; - - return true; -} - -static enum apfl_result +static void handle_parse_error(apfl_ctx ctx, struct apfl_error error) { - enum apfl_result result = APFL_RESULT_ERR; + enum apfl_result errtype = APFL_RESULT_ERR; if (apfl_error_is_fatal_type(error.type)) { - result = APFL_RESULT_ERR_FATAL; + errtype = APFL_RESULT_ERR_FATAL; } const char *const_str = apfl_error_as_const_string(error); if (const_str != NULL) { - return apfl_push_const_string(ctx, const_str) - ? result - : APFL_RESULT_ERR_FATAL; + apfl_raise_const_error(ctx, errtype, const_str); } struct apfl_string string; if (!apfl_error_as_string(error, ctx->gc.allocator, &string)) { - // TODO: Maybe try to push something on the stack in case of error (also for other error cases below)? - return APFL_RESULT_ERR_FATAL; + apfl_raise_alloc_error(ctx); } - if (!move_string_onto_stack(ctx, string)) { - return APFL_RESULT_ERR_FATAL; + if (!apfl_move_string_onto_stack(ctx, string)) { + apfl_raise_alloc_error(ctx); } - return result; + apfl_raise_error_with_type(ctx, -1, errtype); +} + +static void +iterative_runner_next_protected(apfl_ctx ctx, void *opaque) +{ + apfl_iterative_runner runner = opaque; + + switch (apfl_parser_next(runner->parser)) { + case APFL_PARSE_OK: + eval_expr(ctx, apfl_parser_get_expr(runner->parser)); + return; + case APFL_PARSE_ERROR: + handle_parse_error(runner->ctx, apfl_parser_get_error(runner->parser)); + return; + case APFL_PARSE_EOF: + runner->end = true; + return; + } + + assert(false); } bool @@ -289,22 +293,15 @@ apfl_iterative_runner_next(apfl_iterative_runner runner) apfl_stack_clear(runner->ctx); - switch (apfl_parser_next(runner->parser)) { - case APFL_PARSE_OK: - goto ok; - case APFL_PARSE_ERROR: - runner->result = handle_parse_error(runner->ctx, apfl_parser_get_error(runner->parser)); - return true; - case APFL_PARSE_EOF: - runner->end = true; - return false; - } + runner->with_error_on_stack = false; + runner->result = apfl_protected( + runner->ctx, + iterative_runner_next_protected, + runner, + &runner->with_error_on_stack + ); - assert(false); - -ok: - runner->result = eval_expr(runner->ctx, apfl_parser_get_expr(runner->parser)); - return true; + return !runner->end; } enum apfl_result @@ -313,6 +310,12 @@ apfl_iterative_runner_get_result(apfl_iterative_runner runner) return runner->result; } +bool +apfl_iterative_runner_has_error_on_stack(apfl_iterative_runner runner) +{ + return runner->with_error_on_stack; +} + void apfl_iterative_runner_destroy(apfl_iterative_runner runner) { diff --git a/src/main.c b/src/main.c index 9a4a75f..d3ec402 100644 --- a/src/main.c +++ b/src/main.c @@ -138,6 +138,14 @@ exit: return rv; } +static void +dump_stack_error(apfl_ctx ctx, apfl_iterative_runner runner) +{ + if (apfl_iterative_runner_has_error_on_stack(runner)) { + assert(apfl_debug_print_val(ctx, -1, apfl_format_file_writer(stderr))); + } +} + static int repl_eval(void) { @@ -162,10 +170,11 @@ repl_eval(void) assert(apfl_debug_print_val(ctx, -1, apfl_format_file_writer(stdout))); break; case APFL_RESULT_ERR: - assert(apfl_debug_print_val(ctx, -1, apfl_format_file_writer(stderr))); + dump_stack_error(ctx, runner); fprintf(stderr, "Error occurred during evaluation.\n"); break; case APFL_RESULT_ERR_FATAL: + dump_stack_error(ctx, runner); fprintf(stderr, "Fatal error occurred during evaluation.\n"); rv = 1; goto exit; diff --git a/src/messages.c b/src/messages.c index 6a4e366..b49acaa 100644 --- a/src/messages.c +++ b/src/messages.c @@ -1,8 +1,16 @@ #include "apfl.h" const struct apfl_messages apfl_messages = { + .invalid_stack_index = "Invalid stack index", .could_not_alloc_mem = "Could not allocate memory", .input_error_while_parsing = "Input error while parsing", .unexpected_end_of_file = "Unexpected end of file", .feature_not_implemented = "Feature not implemented", + .corrupted_bytecode = "Bytecode is corrupted", + .not_a_list = "Value is not a list", + .not_a_dict = "Value is not a dictionary", + .key_doesnt_exist = "Key does not exist", + .value_is_not_a_container = "Value is not a container (list or dictionary)", + .wrong_key_type = "Wrong key type", + .variable_doesnt_exist = "Variable does not exist", }; diff --git a/webpage/playground/playground.c b/webpage/playground/playground.c index 049bb1f..f4b86d5 100644 --- a/webpage/playground/playground.c +++ b/webpage/playground/playground.c @@ -62,6 +62,14 @@ playground_source_reader_cb(void *context, char *buf, size_t *len, bool need) return true; } +static void +dump_stack_error(apfl_ctx ctx, apfl_iterative_runner runner, struct apfl_format_writer w_err) +{ + if (apfl_iterative_runner_has_error_on_stack(runner)) { + assert(apfl_debug_print_val(ctx, -1, w_err)); + } +} + int main(void) { @@ -99,10 +107,11 @@ main(void) assert(apfl_debug_print_val(ctx, -1, w_ok)); break; case APFL_RESULT_ERR: - assert(apfl_debug_print_val(ctx, -1, w_err)); + dump_stack_error(ctx, runner, w_err); assert(apfl_format_put_string(w_err, "Error occurred during evaluation.\n")); break; case APFL_RESULT_ERR_FATAL: + dump_stack_error(ctx, runner, w_err); assert(apfl_format_put_string(w_err, "Fatal error occurred during evaluation.\n")); rv = 1; goto exit;