From f5e6d3477db9a8abfd530a859f07a0910e4a15cc Mon Sep 17 00:00:00 2001 From: Ludovic 'Archivist' Lagouardette Date: Tue, 27 Aug 2024 18:02:24 +0200 Subject: [PATCH] added better error feedback and detection --- CMakeLists.txt | 2 +- include/ink.h | 18 ++++++- lib.c | 124 ++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 116 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 570311c..c92f0e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ add_library(ink lib.c include/ink.h) # Uncomment to disable array types # add_definitions(-DNOARRAYLIB) -# Uncomment to disable string literals +# Uncomment to disable string literal # add_definitions(-DNOSTRINGLITERALS) # Ensures the interpreter doesn't use the standard C library functions diff --git a/include/ink.h b/include/ink.h index 351aa16..665a28a 100644 --- a/include/ink.h +++ b/include/ink.h @@ -1,5 +1,6 @@ #pragma once #include "stddef.h" +#include "stdint.h" /** @@ -66,12 +67,26 @@ struct native_fn { void (*value)(struct context *); }; +struct parse_error { + int is_set; + const char* error_message; + size_t offset; +}; + +struct runtime_error { + int is_set; + const char* error_message; +}; + /** * Represents the narrow execution context of a single thread of execution within ink. */ struct ink_routine { int panic; - + + struct parse_error parse_error; + struct runtime_error runtime_error; + struct elem *stack; int capacity; int top; @@ -338,6 +353,7 @@ struct ink_array { int top; int capacity; struct elem* elements; + uint16_t flags; }; /** diff --git a/lib.c b/lib.c index 9c370bf..e433c2b 100644 --- a/lib.c +++ b/lib.c @@ -21,6 +21,8 @@ #define _KEYWORD_INK_END "end" #define _KEYWORD_INK_RETURN "return" +#define INK_ARRAY_FLAG_PROTECTED 1 + #define min(x, y) ((x) > (y) ? (y) : (x)) #define max(x, y) ((x) < (y) ? (y) : (x)) @@ -591,6 +593,8 @@ int ink_make_routine(struct context* ctx) { it->stack = NULL; it->function_stack = NULL; it->panic = INK_ROUTINE_CAN_REUSE; + it->parse_error.is_set = 0; + it->runtime_error.is_set = 0; } } else if(ctx->routines_top == ctx->routines_capacity) { int new_count; @@ -604,7 +608,11 @@ int ink_make_routine(struct context* ctx) { it = ctx->routines + ctx->routines_capacity; end = ctx->routines + new_count; for(;it != end;++it) { + it->stack = NULL; + it->function_stack = NULL; it->panic = INK_ROUTINE_CAN_REUSE; + it->parse_error.is_set = 0; + it->runtime_error.is_set = 0; } ctx->routines_capacity = new_count; } @@ -617,12 +625,9 @@ int ink_make_routine(struct context* ctx) { if(it->panic == INK_ROUTINE_CAN_REUSE) { int idx; it->panic = 0; - it->stack = NULL; it->top = 0; - it->capacity = 0; - it->function_stack = NULL; it->function_stack_top = 0; - it->function_stack_capacity = 0; + idx = it - ctx->routines; if(idx >= ctx->routines_top) { ctx->routines_top = idx + 1; @@ -630,6 +635,7 @@ int ink_make_routine(struct context* ctx) { return idx; } } + /* FIXME: Maybe we need to abort here, this seems like quite an unsteady state */ return -758; } @@ -691,7 +697,14 @@ static int ink_parse(struct context* pContext, struct elem* executable_buffer, i function_name = -1; goto next_token; case INK_DO_KW: + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Found start of function body unexpectedly"; + currentRoutine->parse_error.offset= i; + return -26; case INK_END_KW: + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Found end of function unexpectedly"; + currentRoutine->parse_error.offset= i; return -26; default: executable_buffer[*executable_buffer_top] = current; @@ -701,6 +714,9 @@ static int ink_parse(struct context* pContext, struct elem* executable_buffer, i case MODE_FUNCTION: if(current.type == INK_DO_KW) { if(function_name == -1) { + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Found start of function body before the name of the function was provided"; + currentRoutine->parse_error.offset= i; return -27; } else { mode = MODE_DO; @@ -709,9 +725,15 @@ static int ink_parse(struct context* pContext, struct elem* executable_buffer, i } } if(function_name != -1) { + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Function name was not found"; + currentRoutine->parse_error.offset= i; return -28; } if(current.type != INK_RESERVED) { + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Expected special token"; + currentRoutine->parse_error.offset= i; return -29; } function_name = current.value; @@ -728,6 +750,9 @@ static int ink_parse(struct context* pContext, struct elem* executable_buffer, i if(labels[k].active) { if(strcmp(labels[k].name, pContext->lex_reserved_words[pt.value]) == 0) { labels[k].dest = j; + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Label duplicate label in function"; + currentRoutine->parse_error.offset= i; return -30; break; } @@ -779,6 +804,9 @@ static int ink_parse(struct context* pContext, struct elem* executable_buffer, i next_token: i=i; } if(mode == MODE_FUNCTION || mode == MODE_DO) { + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Expected a function to be complete"; + currentRoutine->parse_error.offset= i; return -32; } return 0; @@ -812,6 +840,8 @@ int ink_step(struct context *pContext) { } else { top->index++; if(pContext->native_words_top <= top->executing.value) { + currentRoutine->runtime_error.is_set = 1; + currentRoutine->runtime_error.error_message = "Bytecode contained out of bound executable word"; pContext->panic = 1; return -1; } @@ -820,7 +850,9 @@ int ink_step(struct context *pContext) { break; case INK_FUNCTION: if(pContext->words_top <= top->executing.value) { - pContext->panic = 1; + currentRoutine->runtime_error.is_set = 1; + currentRoutine->runtime_error.error_message = "Bytecode contained out of bound artificial word"; + pContext->panic = 1; return -1; } if(top->index >= pContext->words[top->executing.value].size) { @@ -836,6 +868,8 @@ int ink_step(struct context *pContext) { t = ink_push_fn(pContext, frame); if(t < 0) { pContext->panic = 1; + currentRoutine->runtime_error.is_set = 1; + currentRoutine->runtime_error.error_message = "Instruction pointer underflow"; return -11; } top->index++; @@ -844,6 +878,8 @@ int ink_step(struct context *pContext) { default: t = ink_push(pContext, top->executing); if(t < 0) { + currentRoutine->runtime_error.is_set = 1; + currentRoutine->runtime_error.error_message = "Literal token could not be pushed"; pContext->panic = 1; return -25; } @@ -873,12 +909,22 @@ int ink_compile(struct context *pContext, const char* buffer) { currentRoutine->capacity = 0; err = ink_lex(pContext, buffer); if(err < 0) { + if(!currentRoutine->parse_error.is_set) { + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Unknown lexer error"; + currentRoutine->parse_error.offset = -1; + } pContext->panic = 1; return -1; } executable_buffer_top = 0; err = ink_parse(pContext, executable_buffer, &executable_buffer_top); if(err < 0) { + if(!currentRoutine->parse_error.is_set) { + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Unknown parser error"; + currentRoutine->parse_error.offset = -1; + } pContext->panic = 1; return -1; } @@ -890,6 +936,11 @@ int ink_compile(struct context *pContext, const char* buffer) { main_fn[10 + integer_size] = 0; frame.executing.value = ink_add_indigenous(pContext, main_fn, executable_buffer, executable_buffer_top); if (frame.executing.value < 0) { + if(!currentRoutine->parse_error.is_set) { + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Could not start execution: no valid way to create a frame"; + currentRoutine->parse_error.offset = -1; + } pContext->panic = 1; return -1; } @@ -898,6 +949,11 @@ int ink_compile(struct context *pContext, const char* buffer) { err = ink_push_fn(pContext, frame); pContext->routines[pContext->routine_current].top = 0; if (err < 0) { + if(!currentRoutine->parse_error.is_set) { + currentRoutine->parse_error.is_set = 1; + currentRoutine->parse_error.error_message = "Could not push any executable frame: push failed"; + currentRoutine->parse_error.offset = -1; + } pContext->panic = 1; return -1; } @@ -914,7 +970,11 @@ int ink_can_run(struct context* pContext) { if(pContext->panic) return 0; if(pContext->routines_top == 0) return 0; for(it = 0; it < pContext->routines_top; ++it) { - if(pContext->routines[it].panic == 0) { + if( + pContext->routines[it].panic == 0 + && !pContext->routines[it].parse_error.is_set + && !pContext->routines[it].runtime_error.is_set + ) { return 1; } } @@ -928,7 +988,12 @@ int ink_step_everyone(struct context* pContext) { /* Increment to next runnable routine */ do{ ++(pContext->routine_current); - } while(pContext->routine_current < pContext->routines_top && pContext->routines[pContext->routine_current].panic != 0); + } while( + pContext->routine_current < pContext->routines_top + && pContext->routines[pContext->routine_current].panic != 0 + && pContext->routines[pContext->routine_current].parse_error.is_set + && pContext->routines[pContext->routine_current].runtime_error.is_set + ); /* Exit condition */ if(pContext->routine_current >= pContext->routines_top) break; @@ -1676,6 +1741,7 @@ static void new_array(struct context* ctx) { ary.elements = NULL; ary.top = 0; ary.capacity = 0; + ary.flags = 0; e = ink_make_native(ctx, tid, &ary); ink_push(ctx, e); } @@ -1703,26 +1769,32 @@ static void push_array_stack_delim(struct context* ctx) { ink_push(ctx, e); } +int array_push_s(struct context* ctx, struct ink_array* ary, struct elem value) { + if(ary->elements == NULL) { + ary->elements = ctx->malloc(sizeof(struct elem) * 8); + ary->top = 0; + ary->capacity = 8; + } else if(ary->top == ary->capacity) { + int new_count; + void* renewed; + new_count = (ary->capacity + ary->capacity/2); + renewed = ctx->realloc(ary->elements, sizeof(struct elem) * new_count); + if(renewed == NULL) { + return 1; + } else { + ary->elements = renewed; + ary->capacity = new_count; + } + } + ary->elements[ary->top] = value; + ary->top++; + return 0; +} + void array_push(struct context* ctx, struct ink_routine* currentRoutine, struct ink_array* ary, struct elem value) { - if(ary->elements == NULL) { - ary->elements = ctx->malloc(sizeof(struct elem) * 8); - ary->top = 0; - ary->capacity = 8; - } else if(ary->top == ary->capacity) { - int new_count; - void* renewed; - new_count = (ary->capacity + ary->capacity/2); - renewed = ctx->realloc(ary->elements, sizeof(struct elem) * new_count); - if(renewed == NULL) { - currentRoutine->panic = -1; - return; - } else { - ary->elements = renewed; - ary->capacity = new_count; - } - } - ary->elements[ary->top] = value; - ary->top++; + if(array_push_s(ctx, ary, value)) { + currentRoutine->panic = -1; + } } static void push_array(struct context* ctx) {