#include "include/ink.h" #ifndef NOSTDLIB #include #include #include #include #ifdef INSTRUMENTATION #include #endif #endif #define INK_RESERVED (-1) #define INK_FUNCTION_KW (-2) #define INK_DO_KW (-3) #define INK_END_KW (-4) #define INK_LABEL (-5) #define INK_RETURN (-6) #define INK_MACRO_KW (-7) #define _KEYWORD_INK_FUNCTION "fn" #define _KEYWORD_INK_MACRO "macro" #define _KEYWORD_INK_DO "do" #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)) #ifdef __GNUC__ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else #define likely(x) (!!(x)) #define unlikely(x) (!!(x)) #endif struct label { int active; int dest; char* name; }; #ifdef NOSTDLIB static size_t strlen(const char* c) { size_t j; j = 0; while(*(c++)) { j++; } return j; } static void* memcpy(void* _dest, const void* _src, size_t sz) { char* dest; const char* src; dest = _dest; src = _src; while(sz--) { *(dest++) = *(src++); } return dest; } static int strcmp(const char* dest, const char* src) { while(*dest != 0 && *src != 0) { if(*(dest++) != *(src++)) { return 1; } } return 0; } static void* memmove(void* _dest, const void* _src, size_t sz) { char* dest; const char* src; dest = _dest; src = _src; if (src < dest) { src += sz; dest += sz; while (sz-- > 0) { *--dest = *--src; } } else { while (sz-- > 0) { *dest++ = *src++; } } return dest; } static void* memset(void* _dest, int src, size_t sz) { char* dest; dest = _dest; while(sz--) { *(dest++) = src++; } return dest; } static int isspace(int d) { return d == ' ' || d == '\t' || d == '\n'; } static int isdigit(int d) { return '0' <= d && d <= '9'; } static int atoi(const char* c) { int ret; ret = 0; while(*c) { ret *= 10; ret += *c - '0'; ++c; } return ret; } #endif int ink_add_native(struct context* ctx, const char* name, void(*value)(struct context*)) { int len; char* copy; if(ctx->native_words == NULL) { ctx->native_words = ctx->inner_malloc(ctx, sizeof(struct native_fn) * 8); ctx->native_words_top = 0; ctx->native_words_capacity = 8; } else if(ctx->native_words_top == ctx->native_words_capacity) { int new_count; void* renewed; new_count = (ctx->native_words_capacity + ctx->native_words_capacity/2); renewed = ctx->inner_realloc(ctx, ctx->native_words, sizeof(struct native_fn) * new_count); if(renewed == NULL) { return -3; } else { ctx->native_words = renewed; ctx->native_words_capacity = new_count; } } len = strlen(name); copy = ctx->inner_malloc(ctx, len+1); if(copy == NULL) { return -4; } memcpy(copy, name, len); copy[len] = 0; ctx->native_words[ctx->native_words_top].value = value; ctx->native_words[ctx->native_words_top].name = copy; ctx->native_words_top++; return 0; } static int ink_add_indigenous(struct context* ctx, const char* name, struct elem* m, size_t count) { int len, i; char* copy; if(ctx->words == NULL) { ctx->words = ctx->malloc(ctx, sizeof(struct fn) * 8); ctx->words_top = 0; ctx->words_capacity = 8; } else if(ctx->words_top == ctx->words_capacity) { int new_count; void* renewed; new_count = (ctx->words_capacity + ctx->words_capacity/2); renewed = ctx->realloc(ctx, ctx->words, sizeof(struct fn) * new_count); if(renewed == NULL) { return -1; } else { ctx->words = renewed; ctx->words_capacity = new_count; } } for(i = 0; i < ctx->words_top; ++i) { if(strcmp(name, ctx->words[i].name) == 0) { if(ctx->words[i].things != NULL) ctx->free(ctx, ctx->words[i].things); ctx->words[i].things = ctx->malloc(ctx, sizeof(struct elem) * count); memcpy(ctx->words[i].things, m, sizeof(struct elem) * count); ctx->words[i].size = count; return i; } } len = strlen(name); copy = ctx->malloc(ctx, len+1); if(copy == NULL) { return -2; } memcpy(copy, name, len); copy[len] = 0; ctx->words[ctx->words_top].things = ctx->malloc(ctx, sizeof(struct elem) * count); memcpy(ctx->words[ctx->words_top].things, m, sizeof(struct elem) * count); ctx->words[ctx->words_top].size = count; ctx->words[ctx->words_top].name = copy; return ctx->words_top++; } /** * * @param ctx The context * @param name The name to add * @internal add a lexed string to the parser * @return the id of the string in the list */ static int ink_add_lex_string(struct context* ctx, const char* name) { int i; int len; if(ctx->lex_reserved_words == NULL) { ctx->lex_reserved_words = ctx->inner_malloc(ctx, sizeof(char*) * 8); ctx->lex_reserved_words_top = 0; ctx->lex_reserved_words_capacity = 8; } else if(ctx->lex_reserved_words_top == ctx->lex_reserved_words_capacity) { int new_count; void* renewed; new_count = (ctx->lex_reserved_words_capacity + ctx->lex_reserved_words_capacity/2); renewed = ctx->inner_realloc(ctx, ctx->lex_reserved_words, sizeof(struct native_fn) * new_count); if(renewed == NULL) { return -5; } else { ctx->lex_reserved_words = renewed; ctx->lex_reserved_words_capacity = new_count; } } for(i = 0; i < ctx->lex_reserved_words_top; i++) { if(strcmp(ctx->lex_reserved_words[i], name) == 0) { return i; } } len = strlen(name); i = ctx->lex_reserved_words_top; ctx->lex_reserved_words[i] = ctx->malloc(ctx, len+1); memcpy(ctx->lex_reserved_words[i], name, len); ctx->lex_reserved_words[i][len] = 0; ctx->lex_reserved_words_top++; return i; } int ink_push(struct context* ctx, struct elem value) { struct ink_routine* current; if(ctx->routine_current >= ctx->routines_top) return -65; current = ctx->routines + ctx->routine_current; if(current->stack == NULL) { current->stack = ctx->malloc(ctx, sizeof(struct elem) * 8); current->top = 0; current->capacity = 8; } else if(current->top == current->capacity) { int new_count; void* renewed; new_count = (current->capacity + current->capacity/2); renewed = ctx->realloc(ctx, current->stack, sizeof(struct elem) * new_count); #ifndef NOEXTRACHECKS if(renewed == NULL) { return -18; } #endif current->stack = renewed; current->capacity = new_count; } current->stack[current->top] = value; current->top++; return 0; } int ink_push_fn(struct context* ctx, struct stack_frame value) { struct ink_routine* current; if(ctx->routine_current >= ctx->routines_top) return -55; current = ctx->routines + ctx->routine_current; if(current->panic) return -56; if(current->function_stack == NULL) { current->function_stack = ctx->malloc(ctx, sizeof(struct stack_frame) * 8); current->function_stack_top = 0; current->function_stack_capacity = 8; } else if(current->function_stack_top == current->function_stack_capacity) { int new_count; void* renewed; new_count = (current->function_stack_capacity + current->function_stack_capacity/2); renewed = ctx->realloc(ctx, current->function_stack, sizeof(struct stack_frame) * new_count); if(renewed == NULL) { return -9; } else { current->function_stack = renewed; current->function_stack_capacity = new_count; } } current->function_stack[current->function_stack_top] = value; current->function_stack_top++; return 0; } void ink_pop_fn(struct context* ctx) { if(ctx->routine_current >= ctx->routines_top) return; if(ctx->routines[ctx->routine_current].panic) return; if(ctx->routines[ctx->routine_current].function_stack == NULL) return; if(ctx->routines[ctx->routine_current].function_stack_top == 0) return; ctx->routines[ctx->routine_current].function_stack_top--; } void ink_pop(struct context* ctx) { if(ctx->routine_current >= ctx->routines_top) return; if(ctx->routines[ctx->routine_current].panic) return; if(ctx->routines[ctx->routine_current].stack == NULL) return; if(ctx->routines[ctx->routine_current].top == 0) return; ctx->routines[ctx->routine_current].top--; } struct context* ink_make_context(void*(*malloc)(struct context*, size_t), void*(*realloc)(struct context*, void*, size_t), void(*free)(struct context*, void*), int(*putchar)(struct context*, int)) { struct context* ctx; ctx = (struct context*)malloc(NULL, sizeof(struct context)); ctx->malloc = malloc; ctx->realloc = realloc; ctx->free = free; ctx->inner_malloc = malloc; ctx->inner_realloc = realloc; ctx->inner_free = free; ctx->putchar = putchar; ctx->panic = 0; ctx->routines = NULL; ctx->routines_capacity = 0; ctx->routines_top = 0; ctx->types = NULL; ctx->types_capacity = 0; ctx->types_top = 0; ctx->native_words = NULL; ctx->native_words_capacity = 0; ctx->native_words_top = 0; ctx->words = NULL; ctx->words_capacity = 0; ctx->words_top = 0; ctx->lex_reserved_words = NULL; ctx->lex_reserved_words_capacity = 0; ctx->lex_reserved_words_top = 0; ctx->collections = 0; ctx->steps = 0; return ctx; } void ink_make_context_inplace(struct context* location, void*(*malloc)(struct context*, size_t), void*(*realloc)(struct context*, void*, size_t), void(*free)(struct context*, void*), int(*putchar)(struct context*, int)) { struct context* ctx = location; ctx->malloc = malloc; ctx->realloc = realloc; ctx->free = free; ctx->inner_malloc = malloc; ctx->inner_realloc = realloc; ctx->inner_free = free; ctx->putchar = putchar; ctx->panic = 0; ctx->routines = NULL; ctx->routines_capacity = 0; ctx->routines_top = 0; ctx->types = NULL; ctx->types_capacity = 0; ctx->types_top = 0; ctx->native_words = NULL; ctx->native_words_capacity = 0; ctx->native_words_top = 0; ctx->words = NULL; ctx->words_capacity = 0; ctx->words_top = 0; ctx->lex_reserved_words = NULL; ctx->lex_reserved_words_capacity = 0; ctx->lex_reserved_words_top = 0; ctx->collections = 0; ctx->steps = 0; } /** * Allocates a string that contains the integer * @param _ context (used to allocate) * @param cpy the value * @return the allocated string, needs to be freed by ctx->free * @internal this function is slightly cursed */ static char* ink_itoa(struct context* _, int cpy) { char* n; char* it; n = _->malloc(_, 16); n[15] = 0; it = n+15; do { it--; *it = (cpy % 10) + '0'; cpy = cpy / 10; } while(cpy); memmove(n, it, 16 - (it-n)); return n; } #ifndef NOSTDLIB static void* ink_malloc(struct context* _, size_t sz) { _=_; return malloc(sz); } static void* ink_realloc(struct context* _, void* ptr, size_t sz) { _=_; return realloc(ptr, sz); } static void ink_free(struct context* _, void* ptr) { _=_; free(ptr); } static int ink_putchar(struct context* _, int c) { _=_; return putchar(c); } struct context* ink_make_default_context(void) { struct context* ctx; ctx = ink_make_context(ink_malloc, ink_realloc, ink_free, ink_putchar); ink_std_library(ctx); return ctx; } #endif #ifndef NOSTRINGLITERALS static void new_protected_array(struct context* ctx); #endif static int ink_consume_one(int* end, struct context* pContext, char* r, int is_str) { int i; int done; struct elem value; int err; #ifndef NOSTRINGLITERALS if(is_str) { struct ink_routine* routine = pContext->routines + pContext->routine_current; struct ink_array* ary; int it = 0; new_protected_array(pContext); if(routine->top < 1) { pContext->panic = -1; return -8746; } value = routine->stack[routine->top - 1]; ary = ink_get_value(pContext, value); #ifndef NOEXTRACHECKS if(ary == NULL) { pContext->panic = -1; return -8747; } #endif for(;it != *end;++it) { struct elem character; character.type = INK_INTEGER; /* TODO: codepoint conversion and coalescence is required here */ character.value = r[it]; array_push(pContext, routine, ary, character); } *end = 0; return 0; } #endif is_str = is_str; if(*end == 0) { return 0; } r[*end] = 0; done = 0; if (strcmp(r, _KEYWORD_INK_FUNCTION) == 0) { value.value = 0; value.type = INK_FUNCTION_KW; done = 1; } if (strcmp(r, _KEYWORD_INK_MACRO) == 0) { value.value = 0; value.type = INK_MACRO_KW; done = 1; } if (!done && strcmp(r, _KEYWORD_INK_DO) == 0) { value.value = 0; value.type = INK_DO_KW; done = 1; } if (!done && strcmp(r, _KEYWORD_INK_END) == 0) { value.value = 0; value.type = INK_END_KW; done = 1; } if (!done && strcmp(r, _KEYWORD_INK_RETURN) == 0) { value.value = 0; value.type = INK_RETURN; done = 1; } if(done) { err = ink_push(pContext, value); if(err < 0) { return -19; } } if (!done) { for (i = 0; i < pContext->words_top; ++i) { if (strcmp(r, pContext->words[i].name) == 0) { value.value = i; value.type = INK_FUNCTION; err = ink_push(pContext, value); if(err < 0) { return -20; } done = 1; break; } } } if (!done) { for (i = 0; i < pContext->native_words_top; ++i) { if (strcmp(r, pContext->native_words[i].name) == 0) { value.value = i; value.type = INK_NATIVE_FUNCTION; err = ink_push(pContext, value); if(err < 0) { return -21; } done = 1; break; } } } if (!done) { for(i = (r[0] == '-'); i < *end; i++) { if(!isdigit(r[i])){ goto not_an_int; } } value.value = atoi(r); value.type = INK_INTEGER; err = ink_push(pContext, value); if(err < 0) { return -22; } done = 1; } not_an_int: if (!done) { i = ink_add_lex_string(pContext, r); if(i < 0) { pContext->panic = 1; return -7; } value.value = i; if(r[strlen(r) - 1] == ':') { value.type = INK_LABEL; } else { value.type = INK_RESERVED; } err = ink_push(pContext, value); #ifndef NOEXTRACHECKS if(err < 0) { return -23; } #endif } *end = 0; return 0; } static int ink_lex(struct context *pContext, const char* buffer) { /* Limits the token size to 127 chars */ char r[128]; int end; int err; #ifndef NOSTRINGLITERALS int parses_string; #endif end = 0; restart_after_comment: #ifndef NOSTRINGLITERALS parses_string = 0; #endif while(*buffer != 0) { #ifndef NOSTRINGLITERALS if(parses_string) { switch(*buffer) { case '"': { if(*(buffer+1) == 0 || isspace(*(buffer+1))) { err = ink_consume_one(&end, pContext, r, 1); if(err < 0) { pContext->panic = 1; return -995; } parses_string = 0; } else if(*(buffer+1) == '"') { r[end] = *buffer; ++end; ++buffer; } else if(*(buffer+1) == '/' && *(buffer+2) == '"') { r[end] = '\n'; ++end; ++buffer; ++buffer; } else { pContext->panic = 1; return -994; } }break; default: r[end] = *buffer; ++end; } } else /* go on parsing something else if it is not a string, like this to be able to disable strings */ #endif if(isspace(*buffer)) { if(end == 1 && r[0] == '#') { while(*buffer != '\n' && *buffer != 0) { ++buffer; } end = 0; goto restart_after_comment; } err = ink_consume_one(&end, pContext, r, 0); /* Send the token off to the wizard (ink_parse) */ #ifndef NOEXTRACHECKS if(err < 0) { pContext->panic = 1; return -8; } #endif } else /* ... */ #ifndef NOSTRINGLITERALS if(end == 0 && *buffer == '"' && !parses_string) { parses_string = 1; } else /* ... */ #endif { r[end] = *buffer; ++end; } ++buffer; } err = ink_consume_one(&end, pContext, r, 0); #ifndef NOEXTRACHECKS if(err < 0) { pContext->panic = 1; return -9; } #endif return 0; } static int lblcmp(const char* label, const char* other, size_t label_sz) { while (label_sz != 1) { if(*other == 0) return 1; if(*label != *other) return 1; ++label; ++other; label_sz--; } return 0; } int ink_make_routine(struct context* ctx) { struct ink_routine* it; struct ink_routine* end; /* Allocate space if needed */ if(ctx->routines == NULL) { ctx->routines = ctx->inner_malloc(ctx, sizeof(struct ink_routine) * 8); ctx->routines_top = 0; ctx->routines_capacity = 8; it = ctx->routines; end = ctx->routines + 8; for(;it != end;++it) { it->stack = NULL; it->function_stack = NULL; it->top = it->capacity = 0; it->function_stack_top = it->function_stack_capacity = 0; 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; void* renewed; new_count = (ctx->routines_capacity + ctx->routines_capacity/2); renewed = ctx->inner_realloc(ctx, ctx->routines, sizeof(struct ink_routine) * new_count); #ifndef NOEXTRACHECKS if(renewed == NULL) { return -99; } #endif ctx->routines = renewed; it = ctx->routines + ctx->routines_capacity; end = ctx->routines + new_count; for(;it != end;++it) { it->stack = NULL; it->function_stack = NULL; it->top = it->capacity = 0; it->function_stack_top = it->function_stack_capacity = 0; it->panic = INK_ROUTINE_CAN_REUSE; it->parse_error.is_set = 0; it->runtime_error.is_set = 0; } ctx->routines_capacity = new_count; } it = ctx->routines; end = ctx->routines + ctx->routines_capacity; /* Looks for a reusable routine space then uses it */ for(;it != end;++it) { if(it->panic == INK_ROUTINE_CAN_REUSE) { int idx; it->panic = 0; it->top = 0; it->function_stack_top = 0; idx = it - ctx->routines; if(idx >= ctx->routines_top) { ctx->routines_top = idx + 1; } return idx; } } /* FIXME: Maybe we need to abort here, this seems like quite an unsteady state */ return -758; } int ink_kill_routine(struct context* ctx, int routine){ struct ink_routine* curr; if(routine < 0 || routine >= ctx->routines_top) { return 0; } curr = ctx->routines + routine; if(curr->panic == INK_ROUTINE_CAN_REUSE) { return 0; } if(curr->stack != NULL) { ctx->free(ctx, curr->stack); curr->stack = NULL; curr->top = curr->capacity = 0; } if(curr->function_stack != NULL) { ctx->free(ctx, curr->function_stack); curr->function_stack = NULL; curr->function_stack_top = curr->function_stack_capacity = 0; } curr->panic = INK_ROUTINE_CAN_REUSE; return 1; } /** * * @param pContext * @param executable_buffer * @param executable_buffer_top * @internal Loop from hell */ static int ink_parse(struct context* pContext, struct elem* executable_buffer, int* executable_buffer_top) { struct ink_routine* currentRoutine; int i, function_buffer_top, function_name, mode; int func_is_macro = 0; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" int err; #pragma GCC diagnostic pop #define LABEL_BUFFER 128 #define FUNCTION_BUFFER 256 struct label labels[LABEL_BUFFER]; struct elem function_buffer[FUNCTION_BUFFER]; /* TODO: add checks for overflows in these arrays */ currentRoutine = pContext->routines + pContext->routine_current; function_buffer_top = 0; function_name = -1; #define MODE_EXECUTABLE 0 #define MODE_FUNCTION 1 #define MODE_DO 2 mode = MODE_EXECUTABLE; memset(labels, 0, sizeof(struct label)*LABEL_BUFFER); /* Loop from hell, good luck, pro-tip: leave the parser alone */ for(i = 0; i < currentRoutine->top; ++i) { struct elem current; current = currentRoutine->stack[i]; switch (mode) { case MODE_EXECUTABLE: switch(current.type) { case INK_FUNCTION_KW: mode = MODE_FUNCTION; function_name = -1; func_is_macro = 0; goto next_token; case INK_MACRO_KW: mode = MODE_FUNCTION; func_is_macro = 1; function_name = -1; goto next_token; #ifndef NOEXTRACHECKS 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; #endif default: executable_buffer[*executable_buffer_top] = current; *executable_buffer_top += 1; } break; case MODE_FUNCTION: if(current.type == INK_DO_KW) { #ifndef NOEXTRACHECKS 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; } #endif mode = MODE_DO; memset(labels, 0, sizeof(struct label)*128); goto next_token; } #ifndef NOEXTRACHECKS 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; } #endif function_name = current.value; break; case MODE_DO: if(current.type == INK_END_KW) { int j; for(j = 0; j < function_buffer_top; j++) { struct elem pt; pt = function_buffer[j]; if(pt.type == INK_LABEL) { int k; for(k = 0; k < LABEL_BUFFER; k++) { if(labels[k].active) { #ifndef NOEXTRACHECKS 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; } #endif } else { labels[k].active = 1; labels[k].name = pContext->lex_reserved_words[pt.value]; labels[k].dest = j; memcpy(function_buffer+j, function_buffer+j+1, sizeof(struct elem)*(function_buffer_top-j-1)); function_buffer_top--; j--; break; } } } } for(j = 0; j < function_buffer_top; j++) { struct elem pt; pt = function_buffer[j]; if(pt.type == INK_RESERVED) { int k; for(k = 0; k < LABEL_BUFFER; k++) { if(labels[k].active) { int label_sz; const char* lbl; lbl = labels[k].name; label_sz = strlen(lbl); if(lblcmp(labels[k].name, pContext->lex_reserved_words[pt.value], label_sz) == 0) { function_buffer[j].type = INK_INTEGER; function_buffer[j].value = labels[k].dest - j; break; } } else break; } } } err = ink_add_indigenous(pContext, pContext->lex_reserved_words[function_name], function_buffer, function_buffer_top); #ifndef NOEXTRACHECKS if(err < 0) { pContext->panic = 1; return -33; } #endif /* Handle the case where what we just pushed should be transformed into a macro instead */ if(func_is_macro) { struct stack_frame macro_frame; struct elem macro_id; struct ink_routine* macro_cooking_routine; int step_value; int parser_routine_index = pContext->routine_current; int macro_cooking_routine_ref = ink_make_routine(pContext); #ifndef NOEXTRACHECKS if(macro_cooking_routine_ref < 0) { pContext->panic = 1; return macro_cooking_routine_ref; } #endif pContext->routine_current = macro_cooking_routine_ref; macro_cooking_routine = pContext->routines + macro_cooking_routine_ref; macro_id.type = INK_FUNCTION; macro_id.value = err; macro_frame.executing = macro_id; macro_frame.index = 0; ink_push_fn(pContext, macro_frame); step_value = 1; while(step_value > 0 && macro_cooking_routine->panic == 0 && pContext->panic == 0) { step_value = ink_step(pContext); } #ifndef NOEXTRACHECKS if(macro_cooking_routine->panic != 0 || pContext->panic != 0 || step_value < 0) { pContext->panic = 1; return -138445; } #endif pContext->routine_current = parser_routine_index; err = ink_add_indigenous(pContext, pContext->lex_reserved_words[function_name], macro_cooking_routine->stack, macro_cooking_routine->top); macro_cooking_routine->panic = INK_ROUTINE_SUCCESS; #ifndef NOEXTRACHECKS if(err < 0) { pContext->panic = 1; return -542151; } #endif } function_buffer_top = 0; mode = MODE_EXECUTABLE; goto next_token; } function_buffer[function_buffer_top] = current; function_buffer_top += 1; break; } next_token: i=i; } #ifndef NOEXTRACHECKS 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; } #endif return 0; #undef MODE_EXECUTABLE #undef MODE_FUNCTION #undef MODE_DO #undef LABEL_BUFFER #undef FUNCTION_BUFFER } int ink_step(struct context *pContext) { struct ink_routine* currentRoutine; struct stack_frame frame; struct stack_frame* top; struct elem next; int t; currentRoutine = pContext->routines + pContext->routine_current; pContext->steps++; if(currentRoutine->function_stack_top == 0) return 0; if(pContext->panic) { return -1; } top = ¤tRoutine->function_stack[currentRoutine->function_stack_top-1]; t = top->executing.type; switch(t) { case INK_NATIVE_FUNCTION: if(top->index != 0) { ink_pop_fn(pContext); } else { top->index++; #ifndef NOEXTRACHECKS 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; } #endif pContext->native_words[top->executing.value].value(pContext); } break; case INK_FUNCTION: #ifndef NOEXTRACHECKS if(pContext->words_top <= top->executing.value) { currentRoutine->runtime_error.is_set = 1; currentRoutine->runtime_error.error_message = "Bytecode contained out of bound artificial word"; pContext->panic = 1; return -1; } #endif if(top->index >= pContext->words[top->executing.value].size) { ink_pop_fn(pContext); } else { next = pContext->words[top->executing.value].things[top->index]; if(next.type == INK_RETURN) { ink_pop_fn(pContext); return 1; } frame.executing = next; frame.index = 0; t = ink_push_fn(pContext, frame); #ifndef NOEXTRACHECKS if(t < 0) { pContext->panic = 1; currentRoutine->runtime_error.is_set = 1; currentRoutine->runtime_error.error_message = "Instruction pointer underflow"; return -11; } #endif top->index++; } break; default: t = ink_push(pContext, top->executing); #ifndef NOEXTRACHECKS 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; } #endif ink_pop_fn(pContext); break; } return 1; } int ink_compile(struct context *pContext, const char* buffer) { int routine, saved, executable_buffer_top; /* Main function has a size limit of 256 (need to know that for REPL */ struct elem executable_buffer[256]; struct ink_routine* currentRoutine; int err; struct stack_frame frame; char* integer; size_t integer_size; char main_fn[32] = "__-MAIN-__"; routine = ink_make_routine(pContext); saved = pContext->routine_current; pContext->routine_current = routine; currentRoutine = pContext->routines + routine; currentRoutine->stack = NULL; currentRoutine->top = 0; currentRoutine->capacity = 0; err = ink_lex(pContext, buffer); if(err < 0) { #ifndef NOEXTRACHECKS 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; } #endif pContext->panic = 1; return -1; } executable_buffer_top = 0; err = ink_parse(pContext, executable_buffer, &executable_buffer_top); if(err < 0) { #ifndef NOEXTRACHECKS 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; } #endif pContext->panic = 1; return -1; } if(executable_buffer_top != 0) { integer = ink_itoa(pContext, routine); integer_size = strlen(integer); memcpy(main_fn + 10, integer, integer_size); pContext->free(pContext, integer); integer = NULL; 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) { #ifndef NOEXTRACHECKS 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; } #endif pContext->panic = 1; return -1; } frame.executing.type = INK_FUNCTION; frame.index = 0; err = ink_push_fn(pContext, frame); pContext->routines[pContext->routine_current].top = 0; #ifndef NOEXTRACHECKS 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; } #endif } else { pContext->routines[pContext->routine_current].panic = INK_ROUTINE_SUCCESS; } pContext->routine_current = saved; return routine; } int ink_can_run(struct context* pContext) { int it; 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 && !pContext->routines[it].parse_error.is_set && !pContext->routines[it].runtime_error.is_set ) { return 1; } } return 0; } int ink_step_everyone(struct context* pContext) { int idx; int out; pContext->routine_current = -1; for(;;) { top_label: /* Increment to next runnable routine */ do{ ++(pContext->routine_current); } 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; /* Kill? */ if(pContext->routines[pContext->routine_current].panic == INK_ROUTINE_SUCCESS) { goto top_label; /* We used to kill the routines here, idk if this cleanup is wise as it may happen in somewhat interstitial * moments and affect performance * ink_kill_routine(pContext, pContext->routine_current); */ } /* Step! */ for(idx = 0; idx < INK_STEP_BATCH_COUNT; ++idx) { out = ink_step(pContext); if (unlikely(out == 0)) { pContext->routines[pContext->routine_current].panic = INK_ROUTINE_SUCCESS; goto top_label; } else if (unlikely(out < 0)) { pContext->routines[pContext->routine_current].panic = out; goto top_label; } } } return 0; } int ink_new_type( struct context* ctx, const char* type_name, int size, void (*collect)(struct context*,void*), struct ink_collection_list (*gc)(struct context*,void*) ) { if(ctx->panic) return -128; /* Resize for push */ if(ctx->types == NULL) { ctx->types = ctx->inner_malloc(ctx, sizeof(struct ink_type) * 8); ctx->types_top = 0; ctx->types_capacity = 8; } else if(ctx->types_top == ctx->types_capacity) { int new_count; void* renewed; new_count = (ctx->types_capacity + ctx->types_capacity/2); renewed = ctx->inner_realloc(ctx, ctx->types, sizeof(struct ink_type) * new_count); if(renewed == NULL) { return -129; } else { ctx->types = renewed; ctx->types_capacity = new_count; } } /* Push */ ctx->types[ctx->types_top].name = type_name; ctx->types[ctx->types_top].element_size = size; ctx->types[ctx->types_top].elements = NULL; ctx->types[ctx->types_top].elements_top = 0; ctx->types[ctx->types_top].elements_capacity = 0; ctx->types[ctx->types_top].collect = collect; ctx->types[ctx->types_top].gc = gc; ctx->types_top++; /* Satisfying the minimal value requirement */ return ctx->types_top - 1 + 16; } static struct element_slab* ink_get_value_link(struct context* ctx, struct elem ref) { int type_id; if(ref.type < 16) return NULL; type_id = ref.type - 16; if(type_id >= ctx->types_top) return NULL; if(ctx->types[type_id].element_size == 0) return NULL; if(ref.value < 0) return NULL; if(ref.value >= ctx->types[type_id].elements_top) return NULL; if(! ctx->types[type_id].elements[ref.value].in_use) return NULL; return ctx->types[type_id].elements + ref.value; } void* ink_get_value(struct context* ctx, struct elem ref) { struct element_slab* s; s = ink_get_value_link(ctx, ref); if(s == NULL) return NULL; return s->data; } struct elem ink_make_native_unsafe(struct context* ctx, int type, void* ptr, int is_protected) { int type_id; struct elem ret; int g, i; if(type < 16) { ret.type = 0; ret.value = -130; return ret; } /* Apply invariant of the user defined types */ type_id = type - 16; if(type_id >= ctx->types_top) { ret.type = 0; ret.value = -129; return ret; } if(ctx->panic) { ret.type = 0; ret.value = -135; return ret; } /* Resize for push of value in store */ if(ctx->types[type_id].elements == NULL) { ctx->types[type_id].elements = ctx->inner_malloc(ctx, sizeof(struct element_slab) * 8); ctx->types[type_id].elements_top = 0; ctx->types[type_id].elements_capacity = 8; memset(ctx->types[type_id].elements + ctx->types[type_id].elements_top, 0, sizeof(struct element_slab)*(ctx->types[type_id].elements_capacity - ctx->types[type_id].elements_top)); } else if(ctx->types[type_id].elements_top == ctx->types[type_id].elements_capacity) { int new_count; void* renewed; new_count = (ctx->types[type_id].elements_capacity + ctx->types[type_id].elements_capacity/2); renewed = ctx->inner_realloc(ctx, ctx->types[type_id].elements, sizeof(struct element_slab) * new_count); if(renewed == NULL) { ret.type = 0; ret.value = -129; return ret; } else { ctx->types[type_id].elements = renewed; ctx->types[type_id].elements_capacity = new_count; memset(ctx->types[type_id].elements + ctx->types[type_id].elements_top, 0, sizeof(struct element_slab)*(ctx->types[type_id].elements_capacity - ctx->types[type_id].elements_top)); } } /* Push value in store */ g = ctx->types[type_id].elements_capacity; for(i = 0; i < g; ++i) { if(! ctx->types[type_id].elements[i].in_use) { ctx->types[type_id].elements[i].in_use = 1; ctx->types[type_id].elements[i].uses = 1; ctx->types[type_id].elements[i].is_protected = is_protected; if(ctx->types[type_id].element_size < 0) { ctx->types[type_id].elements[i].data = ptr; } else { void* new_ptr = ctx->malloc(ctx, ctx->types[type_id].element_size); if(new_ptr == NULL) { ret.type = 0; ret.value = -139; return ret; } memcpy(new_ptr, ptr, ctx->types[type_id].element_size); ctx->types[type_id].elements[i].data = new_ptr; } ctx->types[type_id].elements_top = max(ctx->types[type_id].elements_top, i+1); ret.type = type; ret.value = i; return ret; } } ret.type = 0; ret.value = -140; return ret; } struct elem ink_make_native(struct context* ctx, int type, void* ptr) { return ink_make_native_unsafe(ctx, type, ptr, 0); } void ink_clean_routines(struct context* ctx) { int i; #ifndef NOEXTRACHECKS int j; struct elem null; null.value = 0; null.type = INK_INTEGER; #endif for(i = 0; i < ctx->routines_top; ++i) { if(ctx->routines[i].panic == INK_ROUTINE_CAN_REUSE || ctx->routines[i].panic == INK_ROUTINE_SUCCESS) { #ifndef NOEXTRACHECKS if(ctx->routines[i].stack != NULL) { for (j = 0; j < ctx->routines[i].top; ++j) { ctx->routines[i].stack[j] = null; } } if(ctx->routines[i].function_stack != NULL) { for (j = 0; j < ctx->routines[i].function_stack_top; ++j) { ctx->routines[i].function_stack[j].executing = null; ctx->routines[i].function_stack[j].index = 0; } } #endif ctx->routines[i].top = 0; ctx->routines[i].function_stack_top = 0; ctx->routines[i].panic = INK_ROUTINE_CAN_REUSE; } } } void ink_gc(struct context* ctx) { int i, j, k; int marked; struct element_slab* v; struct elem* thing; for(i = 0; i < ctx->types_top; ++i) { for(j = 0; j < ctx->types[i].elements_top; ++j) { ctx->types[i].elements[j].uses = 0; } } /* Start by marking the roots of the routines, Clear the routines if possible */ for(i = 0; i < ctx->routines_top; ++i) { if(ctx->routines[i].panic == INK_ROUTINE_SUCCESS) { #ifndef NOEXTRACHECKS if(ctx->routines[i].stack != NULL) { ctx->free(ctx, ctx->routines[i].stack); ctx->routines[i].stack = NULL; } #endif ctx->routines[i].top = 0; #ifndef NOEXTRACHECKS if(ctx->routines[i].function_stack != NULL) { ctx->free(ctx, ctx->routines[i].function_stack); ctx->routines[i].function_stack = NULL; } #endif ctx->routines[i].function_stack_top = 0; ctx->routines[i].panic = INK_ROUTINE_CAN_REUSE; } else if(ctx->routines[i].panic == INK_ROUTINE_CAN_REUSE) { continue; } else for(j = 0; j < ctx->routines[i].top; ++j) { v = ink_get_value_link(ctx, ctx->routines[i].stack[j]); if(v != NULL && !v->uses) ++v->uses; } } for(i = 0; i < ctx->words_top; ++i) { struct fn* function = ctx->words + i; for(j = 0; j < function->size; ++j) { thing = function->things + j; v = ink_get_value_link(ctx, *thing); if (v != NULL && !v->uses) { ++v->uses; } } } /* Mark the rest of the data */ do { marked = 0; for (i = 0; i < ctx->types_top; ++i) { for (j = 0; j < ctx->types[i].elements_top; ++j) { /* Only mark from things that are active and detected as in use */ if (ctx->types[i].elements[j].in_use && ctx->types[i].elements[j].is_protected && ctx->types[i].elements[j].uses) { struct ink_collection_list c; c = ctx->types[i].gc(ctx, ctx->types[i].elements[j].data); for (k = 0; k < c.count; ++k) { v = ink_get_value_link(ctx, c.elements[k]); /* Never mark twice to avoid infinite loops with e.g. arrays that contain themselves */ if (v != NULL && !v->uses) { ++v->uses; marked = 1; } } if (c.elements != NULL) { ctx->inner_free(ctx, c.elements); c.elements = NULL; } } } } } while(marked); /* Sweep phase: explore any allocated data and sweep the unused away */ for(i = 0; i < ctx->types_top; ++i) { for(j = 0; j < ctx->types[i].elements_top; ++j) { if(ctx->types[i].elements[j].uses == 0 && ctx->types[i].elements[j].in_use && ctx->types[i].elements[j].is_protected == 0) { ctx->collections++; ctx->types[i].collect(ctx, ctx->types[i].elements[j].data); if(ctx->types[i].element_size > 0 && ctx->types[i].elements[j].data != NULL) { ctx->free(ctx, ctx->types[i].elements[j].data); } ctx->types[i].elements[j].data = NULL; ctx->types[i].elements[j].uses = 0; ctx->types[i].elements[j].in_use = 0; } } } } /**********************************************************************************************************************/ static void print_stacktrace(struct context* _) { int i; struct ink_routine* currentRoutine; currentRoutine = _->routines + _->routine_current; for(i = 0; i < currentRoutine->function_stack_top; ++i) { struct elem thing; char *n; thing = currentRoutine->function_stack[i].executing; switch(thing.type) { case INK_NATIVE_FUNCTION: { n = _->native_words[thing.value].name; while (*n) { _->putchar(_, *n); ++n; } _->putchar(_, 10); break; } case INK_FUNCTION:{ n = _->words[thing.value].name; while (*n) { _->putchar(_, *n); ++n; } _->putchar(_, ':'); n = ink_itoa(_, currentRoutine->function_stack[i].index); while (*n) { _->putchar(_, *n); ++n; } _->putchar(_, 10); break; } default: break; } } } static void add_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { ctx->panic = 1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = a.value + b.value; } static void sub_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = b.value - a.value; } static void mult_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = b.value * a.value; } static void div_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = b.value / a.value; } static void is_equal(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; struct elem ret; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; ink_pop(ctx); ink_pop(ctx); ret.type = INK_INTEGER; ret.value = a.value == b.value && a.type == b.type; ink_push(ctx, ret); } static void is_different(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; struct elem ret; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; ink_pop(ctx); ink_pop(ctx); ret.type = INK_INTEGER; ret.value = !(a.value == b.value && a.type == b.type); ink_push(ctx, ret); } #ifndef NOEXTRAARITHMETIC static void rem_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = b.value % a.value; } static void xor_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = b.value ^ a.value; } static void gt_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = b.value > a.value; } static void gte_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = b.value >= a.value; } static void lte_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = b.value <= a.value; } #endif /* NOEXTRAARITHMETIC */ static void lt_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); currentRoutine->stack[currentRoutine->top-1].value = b.value < a.value; } static void dupe_elem(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" int err; #pragma GCC diagnostic pop currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 1) { ctx->panic = 1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; err = ink_push(ctx, a); #ifndef NOEXTRACHECKS if(err < 0) ctx->panic = 1; #endif } static void drop_elem(struct context* ctx) { #ifndef NOEXTRACHECKS struct ink_routine* currentRoutine; currentRoutine = ctx->routines + ctx->routine_current; if(currentRoutine->top < 1) { ctx->panic = 1; return; } #endif ink_pop(ctx); } static void pluck_elem(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; int position; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" int err; #pragma GCC diagnostic pop currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 1) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; #ifndef NOEXTRACHECKS if(a.type != INK_INTEGER) { ctx->panic = 1; return; } #endif position = currentRoutine->top - (a.value + 1); #ifndef NOEXTRACHECKS if(position >= currentRoutine->top || position < 0) { ctx->panic = 1; return; } #endif ink_pop(ctx); err = ink_push(ctx, currentRoutine->stack[position]); #ifndef NOEXTRACHECKS if(err < 0) ctx->panic = 1; #endif } static void swap_elem(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; struct elem b; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; b = currentRoutine->stack[currentRoutine->top-2]; currentRoutine->stack[currentRoutine->top-2] = a; currentRoutine->stack[currentRoutine->top-1] = b; } static void return_if(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 1) { ctx->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; #ifndef NOEXTRACHECKS if(a.type != INK_INTEGER) { ctx->panic = 1; return; } #endif if(a.value) { ink_pop_fn(ctx); ink_pop_fn(ctx); } ink_pop(ctx); return; } static void jump_if(struct context* ctx) { struct ink_routine* currentRoutine; struct elem label; struct elem condition; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2) { ctx->panic = -1; return; } #endif label = currentRoutine->stack[currentRoutine->top-1]; condition = currentRoutine->stack[currentRoutine->top-2]; #ifndef NOEXTRACHECKS if(label.type != INK_INTEGER || condition.type != INK_INTEGER) { ctx->panic = -1; return; } #endif ink_pop(ctx); ink_pop(ctx); ink_pop_fn(ctx); if(condition.value) { currentRoutine->function_stack[currentRoutine->function_stack_top - 1].index += label.value - 2; } return; } static void print_int(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; char* n; char* str; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 1 || currentRoutine->stack[currentRoutine->top-1].type != INK_INTEGER) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; ink_pop(ctx); n = ink_itoa(ctx, a.value); str = n; while (*str) { ctx->putchar(ctx, *str); ++str; } ctx->free(ctx, n); n = NULL; } static void print_as_utf8(struct context* ctx) { struct ink_routine* currentRoutine; struct elem a; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 1 || currentRoutine->stack[currentRoutine->top-1].type != INK_INTEGER) { ctx->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; if(a.value <= 0x7F) { ctx->putchar(ctx, a.value); } else if(a.value <= 0x7FF) { ctx->putchar(ctx, ((a.value & 0xFC0) >> 6) | 192); ctx->putchar(ctx, (a.value & 0x3F) | 128); } else if(a.value <= 0xFFFF) { ctx->putchar(ctx, ((a.value & 0x3F000) >> 12) | 224); ctx->putchar(ctx, ((a.value & 0xFC0) >> 6) | 128); ctx->putchar(ctx, (a.value & 0x3F) | 128); } else if(a.value <= 0x10FFFF) { ctx->putchar(ctx, ((a.value & 0x3C0000) >> 18) | 240); ctx->putchar(ctx, ((a.value & 0x3F000) >> 12) | 128); ctx->putchar(ctx, ((a.value & 0xFC0) >> 6) | 128); ctx->putchar(ctx, (a.value & 0x3F) | 128); } else { ctx->panic = -1; return; } ink_pop(ctx); } int get_type_by_name(struct context* ctx, const char* name) { int i; for(i = 0; i < ctx->types_top; ++i) { if(strcmp(ctx->types[i].name, name) == 0) { return i + 16; } } return -1; } static void run_gc(struct context* ctx) { ink_gc(ctx); } static void clear_stack(struct context* ctx) { struct ink_routine* currentRoutine; currentRoutine = ctx->routines + ctx->routine_current; while (currentRoutine->top >= 1) { ink_pop(ctx); } return; } static void dump_stack(struct context* ctx) { struct ink_routine* currentRoutine; int index; char* idx; char* type; char* value; char* it; currentRoutine = ctx->routines + ctx->routine_current; index = currentRoutine->top; while (index) { --index; idx = ink_itoa(ctx,index); type = ink_itoa(ctx, currentRoutine->stack[index].type); value = ink_itoa(ctx,currentRoutine->stack[index].value); for(it = idx; *it; ++it) ctx->putchar(ctx, *it); ctx->putchar(ctx, ' ');ctx->putchar(ctx, '|');ctx->putchar(ctx, ' '); for(it = type; *it; ++it) ctx->putchar(ctx, *it); ctx->putchar(ctx, ' ');ctx->putchar(ctx, '|');ctx->putchar(ctx, ' '); for(it = value; *it; ++it) ctx->putchar(ctx, *it); ctx->putchar(ctx, '\n'); if(value != NULL) ctx->free(ctx, value); if(type != NULL) ctx->free(ctx, type); if(idx != NULL) ctx->free(ctx, idx); value = type = idx = NULL; } return; } static void collect_noop() {} static struct ink_collection_list gc_noop() { struct ink_collection_list c; c.elements = NULL; c.count = 0; return c; } #ifndef NOARRAYLIB static void collect_array(struct context* ctx, void* array) { struct ink_array* ary; ary = array; if(ary->elements != NULL) { ctx->free(ctx, ary->elements); ary->elements = NULL; } } static struct ink_collection_list gc_array(struct context* ctx, void* array) { struct ink_array* ary; struct ink_collection_list c; ary = array; c.elements = ctx->inner_malloc(ctx, sizeof(struct elem)*ary->top); c.count = ary->top; memcpy(c.elements, ary->elements, sizeof(struct elem)*ary->top); return c; } static void new_array(struct context* ctx) { int tid; struct elem e; struct ink_array ary; tid = get_type_by_name(ctx, "array"); ary.elements = NULL; ary.top = 0; ary.capacity = 0; ary.flags = 0; e = ink_make_native(ctx, tid, &ary); ink_push(ctx, e); } #ifndef NOSTRINGLITERALS static void new_protected_array(struct context* ctx) { int tid; struct elem e; struct ink_array ary; tid = get_type_by_name(ctx, "array"); ary.elements = NULL; ary.top = 0; ary.capacity = 0; e = ink_make_native_unsafe(ctx, tid, &ary, 1); ink_push(ctx, e); } #endif static void push_array_stack_delim(struct context* ctx) { int tid; struct elem e; tid = get_type_by_name(ctx, "array_marker"); e.type = tid; e.value = 0; 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(ctx, 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(ctx, 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(array_push_s(ctx, ary, value)) { currentRoutine->panic = -1; } } static void push_array(struct context* ctx) { struct elem a; struct ink_routine* currentRoutine; struct ink_array* ary; #ifndef NOEXTRACHECKS int tid; tid = get_type_by_name(ctx, "array"); #endif currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 2 || currentRoutine->stack[currentRoutine->top-1].type != tid) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top-1]; ary= ink_get_value(ctx, a); #ifndef NOEXTRACHECKS if(ary == NULL) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); array_push(ctx, currentRoutine, ary, currentRoutine->stack[currentRoutine->top-1]); ink_pop(ctx); } static void push_delimited_array(struct context* ctx) { int tid, idx, counter, i; struct elem a; struct ink_routine* currentRoutine; struct ink_array* ary; tid = get_type_by_name(ctx, "array_marker"); currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if(currentRoutine->top < 1) { currentRoutine->panic = -1; return; } #endif new_array(ctx); a = currentRoutine->stack[currentRoutine->top-1]; ink_pop(ctx); ary= ink_get_value(ctx, a); for(idx = 1; idx <= currentRoutine->top; ++idx) { if(currentRoutine->stack[currentRoutine->top-idx].type == tid) { break; } } /* Save for cleanup */ counter = idx; /* Don't copy the delimiter */ idx -= 1; ary->elements = malloc(sizeof(struct elem) * idx); #ifndef NOEXTRACHECKS if(ary->elements == NULL) { currentRoutine->panic = -541; return; } #endif ary->capacity = idx; ary->top = 0; /* Copy the data */ for(i = currentRoutine->top - idx; i < currentRoutine->top; ++i) { ary->elements[ary->top] = currentRoutine->stack[i]; ++(ary->top); } /* Cleanup */ while(counter--) { ink_pop(ctx); } /* Put value in place */ ink_push(ctx, a); } static void index_array(struct context* ctx) { struct ink_routine *currentRoutine; struct elem a; struct ink_array *ary; struct elem idx; #ifndef NOEXTRACHECKS int tid; tid = get_type_by_name(ctx, "array"); #endif currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if (currentRoutine->top < 2 || currentRoutine->stack[currentRoutine->top - 1].type != tid || currentRoutine->stack[currentRoutine->top - 2].type != INK_INTEGER) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top - 1]; ary = ink_get_value(ctx, a); #ifndef NOEXTRACHECKS if (ary == NULL) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); idx = currentRoutine->stack[currentRoutine->top - 1]; ink_pop(ctx); #ifndef NOEXTRACHECKS if(ary->top <= idx.value) { currentRoutine->panic = -1; return; } #endif ink_push(ctx, ary->elements[idx.value]); } static void set_array(struct context* ctx) { struct ink_routine *currentRoutine; struct elem a; struct ink_array *ary; struct elem idx; struct elem value; #ifndef NOEXTRACHECKS int tid; tid = get_type_by_name(ctx, "array"); #endif currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if (currentRoutine->top < 3 || currentRoutine->stack[currentRoutine->top - 1].type != tid || currentRoutine->stack[currentRoutine->top - 2].type != INK_INTEGER) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top - 1]; ary = ink_get_value(ctx, a); #ifndef NOEXTRACHECKS if (ary == NULL) { currentRoutine->panic = -1; return; } #endif idx = currentRoutine->stack[currentRoutine->top - 2]; value = currentRoutine->stack[currentRoutine->top - 3]; #ifndef NOEXTRACHECKS if(ary->top <= idx.value) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); ink_pop(ctx); ink_pop(ctx); ary->elements[idx.value] = value; } static void get_size_array(struct context* ctx) { struct ink_routine *currentRoutine; struct elem a; struct ink_array *ary; struct elem sz; #ifndef NOEXTRACHECKS int tid; tid = get_type_by_name(ctx, "array"); #endif currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if (currentRoutine->top < 1 || currentRoutine->stack[currentRoutine->top - 1].type != tid) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top - 1]; ary = ink_get_value(ctx, a); #ifndef NOEXTRACHECKS if (ary == NULL) { currentRoutine->panic = -1; return; } #endif ink_pop(ctx); sz.type = INK_INTEGER; sz.value = ary->top; ink_push(ctx, sz); } static void is_array(struct context* ctx) { int tid; struct ink_routine *currentRoutine; struct elem a; tid = get_type_by_name(ctx, "array"); currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if (currentRoutine->top < 1) { currentRoutine->panic = -1; return; } #endif a.type = INK_INTEGER; a.value = currentRoutine->stack[currentRoutine->top - 1].type == tid; ink_pop(ctx); ink_push(ctx, a); } static void is_int(struct context* ctx) { struct ink_routine *currentRoutine; struct elem a; currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if (currentRoutine->top < 1) { currentRoutine->panic = -1; return; } #endif a.type = INK_INTEGER; a.value = currentRoutine->stack[currentRoutine->top - 1].type == INK_INTEGER; ink_pop(ctx); ink_push(ctx, a); } static void print_array_of_codepoints(struct context* ctx) { int i; struct ink_routine *currentRoutine; struct elem a; struct ink_array *ary; #ifndef NOEXTRACHECKS int tid; tid = get_type_by_name(ctx, "array"); #endif currentRoutine = ctx->routines + ctx->routine_current; #ifndef NOEXTRACHECKS if (currentRoutine->top < 1 || currentRoutine->stack[currentRoutine->top - 1].type != tid) { currentRoutine->panic = -1; return; } #endif a = currentRoutine->stack[currentRoutine->top - 1]; ary = ink_get_value(ctx, a); #ifndef NOEXTRACHECKS for(i = 0; i < ary->top; ++i) { if(ary->elements[i].type != INK_INTEGER) { currentRoutine->panic = -1; return; } } #endif ink_pop(ctx); for(i = 0; i < ary->top; ++i) { ink_push(ctx, ary->elements[i]); print_as_utf8(ctx); } } static void arrayify_stack(struct context* ctx) { struct ink_routine* currentRoutine; struct elem array_ref; struct ink_array* ary; int idx; currentRoutine = ctx->routines + ctx->routine_current; new_array(ctx); if(currentRoutine->panic < 0) return; array_ref = currentRoutine->stack[currentRoutine->top - 1]; ary = ink_get_value(ctx, array_ref); #ifndef NOEXTRACHECKS if(ary == NULL) { currentRoutine->panic = -717; return; } #endif ink_pop(ctx); for(idx = 0; idx < currentRoutine->top; ++idx) { array_push(ctx, currentRoutine, ary, currentRoutine->stack[idx]); } while (currentRoutine->top > 0) { ink_pop(ctx); } ink_push(ctx, array_ref); return; } #endif /* NOARRAYLIB */ int ink_std_library(struct context* ctx) { int v; v = 0; v += ink_add_native(ctx, "sys.trace", print_stacktrace); v += ink_add_native(ctx, "sys.gc", run_gc); v += ink_add_native(ctx, "print_int", print_int); v += ink_add_native(ctx, "print_utf8", print_as_utf8); v += ink_add_native(ctx, "+", add_int); v += ink_add_native(ctx, "-", sub_int); v += ink_add_native(ctx, "*", mult_int); v += ink_add_native(ctx, "/", div_int); v += ink_add_native(ctx, "==", is_equal); v += ink_add_native(ctx, "!=", is_different); v += ink_add_native(ctx, "<", lt_int); v += ink_add_native(ctx, "swap", swap_elem); v += ink_add_native(ctx, "dup", dupe_elem); v += ink_add_native(ctx, "drop", drop_elem); v += ink_add_native(ctx, "stack.clear", clear_stack); v += ink_add_native(ctx, "stack.dump", dump_stack); v += ink_add_native(ctx, "pluck", pluck_elem); v += ink_add_native(ctx, "return_if", return_if); v += ink_add_native(ctx, "jump_if", jump_if); v += ink_add_native(ctx, "is.int", is_int); #ifndef NOEXTRAARITHMETIC v += ink_add_native(ctx, ">", gt_int); v += ink_add_native(ctx, ">=", gte_int); v += ink_add_native(ctx, "=<", lte_int); v += ink_add_native(ctx, "%", rem_int); v += ink_add_native(ctx, "int.xor", xor_int); #endif /* NOEXTRAARITHMETIC */ #ifndef NOARRAYLIB ink_new_type(ctx, "array", sizeof(struct ink_array), collect_array, gc_array); ink_new_type(ctx, "array_marker", 0, collect_noop, gc_noop); v += ink_add_native(ctx, "[", push_array_stack_delim); v += ink_add_native(ctx, "]", push_delimited_array); v += ink_add_native(ctx, "array.new", new_array); v += ink_add_native(ctx, "array.push", push_array); v += ink_add_native(ctx, "array.index", index_array); v += ink_add_native(ctx, "array.set", set_array); v += ink_add_native(ctx, "array.size", get_size_array); v += ink_add_native(ctx, "array.print_utf8", print_array_of_codepoints); v += ink_add_native(ctx, "is.array", is_array); v += ink_add_native(ctx, "stack.to_array", arrayify_stack); #endif /* NOARRAYLIB */ return v; }