|
|
@ -170,6 +170,13 @@ static int ink_add_indigenous(struct context* ctx, const char* name, struct elem |
|
|
|
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; |
|
|
|
if(ctx->lex_reserved_words == NULL) { |
|
|
@ -424,6 +431,7 @@ static int ink_consume_one(int* end, struct context* pContext, char** buffer, ch |
|
|
|
|
|
|
|
static int ink_lex(struct context *pContext, char* buffer) { |
|
|
|
int i; |
|
|
|
// Limits the token size to 127 chars |
|
|
|
char r[128]; |
|
|
|
int end = 0; |
|
|
|
int err; |
|
|
@ -461,6 +469,7 @@ static int lblcmp(const char* label, const char* other, size_t label_sz) { |
|
|
|
} |
|
|
|
|
|
|
|
int ink_make_routine(struct context* ctx) { |
|
|
|
// Allocate space if needed |
|
|
|
if(ctx->routines == NULL) { |
|
|
|
ctx->routines = ctx->inner_malloc(sizeof(struct ink_routine) * 8); |
|
|
|
ctx->routines_top = 0; |
|
|
@ -490,7 +499,7 @@ int ink_make_routine(struct context* ctx) { |
|
|
|
|
|
|
|
struct ink_routine* it = ctx->routines; |
|
|
|
struct ink_routine* 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) { |
|
|
|
it->panic = 0; |
|
|
@ -550,6 +559,8 @@ static int ink_parse(struct context* pContext, struct elem* executable_buffer, i |
|
|
|
#define MODE_DO 2 |
|
|
|
int mode = 0; |
|
|
|
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]; |
|
|
@ -736,6 +747,7 @@ void ink_compile(struct context *pContext, char* buffer) { |
|
|
|
return; |
|
|
|
} |
|
|
|
int i = 0; |
|
|
|
// Main function has a size limit of 256 (need to know that for REPL |
|
|
|
struct elem executable_buffer[256]; |
|
|
|
int executable_buffer_top = 0; |
|
|
|
err = ink_parse(pContext, executable_buffer, &executable_buffer_top); |
|
|
@ -781,13 +793,19 @@ int ink_step_everyone(struct context* pContext) { |
|
|
|
int out; |
|
|
|
pContext->routine_current = -1; |
|
|
|
for(;;) { |
|
|
|
// Increment to next runnable routine |
|
|
|
do{ |
|
|
|
++(pContext->routine_current); |
|
|
|
} while(pContext->routine_current < pContext->routines_top && pContext->routines[pContext->routine_current].panic != 0); |
|
|
|
// Exit condition |
|
|
|
if(pContext->routine_current >= pContext->routines_top) break; |
|
|
|
|
|
|
|
// Kill? |
|
|
|
if(pContext->routines[pContext->routine_current].panic == INK_ROUTINE_SUCCESS) { |
|
|
|
ink_kill_routine(pContext, pContext->routine_current); |
|
|
|
} |
|
|
|
|
|
|
|
//Step! |
|
|
|
out = ink_step(pContext); |
|
|
|
if(out == 0) { |
|
|
|
pContext->routines[pContext->routine_current].panic = INK_ROUTINE_SUCCESS; |
|
|
@ -806,6 +824,7 @@ int ink_new_type( |
|
|
|
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(sizeof(struct ink_type) * 8); |
|
|
|
ctx->types_top = 0; |
|
|
@ -820,6 +839,8 @@ int ink_new_type( |
|
|
|
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; |
|
|
@ -858,6 +879,8 @@ struct elem ink_make_native(struct context* ctx, int type, void* ptr) { |
|
|
|
ret.value = -130; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
// Apply invariant of the user defined types |
|
|
|
int type_id = type - 16; |
|
|
|
if(type_id >= ctx->types_top) { |
|
|
|
struct elem ret; |
|
|
@ -872,6 +895,8 @@ struct elem ink_make_native(struct context* ctx, int type, void* ptr) { |
|
|
|
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(sizeof(struct element_slab) * 8); |
|
|
|
ctx->types[type_id].elements_top = 0; |
|
|
@ -891,6 +916,8 @@ struct elem ink_make_native(struct context* ctx, int type, void* ptr) { |
|
|
|
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 |
|
|
|
int g = ctx->types[type_id].elements_capacity; |
|
|
|
int i; |
|
|
|
for(i = 0; i < g; ++i) { |
|
|
@ -931,6 +958,7 @@ void ink_gc(struct context* ctx) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Start by marking the roots of the routines |
|
|
|
for(i = 0; i < ctx->routines_top; ++i) { |
|
|
|
for(j = 0; j < ctx->routines[i].top; ++j) { |
|
|
|
struct element_slab* v = ink_get_value_link(ctx, ctx->routines[i].stack[j]); |
|
|
@ -938,15 +966,18 @@ void ink_gc(struct context* ctx) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Mark the rest of the data |
|
|
|
int marked; |
|
|
|
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].uses) { |
|
|
|
struct ink_collection_list c = ctx->types[i].gc(ctx, ctx->types[i].elements[j].data); |
|
|
|
for (k = 0; k < c.count; ++k) { |
|
|
|
struct element_slab *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; |
|
|
@ -958,6 +989,7 @@ void ink_gc(struct context* ctx) { |
|
|
|
} |
|
|
|
} 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) { |
|
|
|