From 6cdddb8543a7fea0256b09f57f719860726977f5 Mon Sep 17 00:00:00 2001 From: Ludovic 'Archivist' Lagouardette Date: Tue, 21 May 2024 21:38:46 +0200 Subject: [PATCH] added more docs --- README.md | 12 ++++++++++++ lib.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc6c921..bcf101b 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,15 @@ To make the library not use the standard library, build it with `NOSTDLIB` defin All of these functions need to work for `ink` to work. It is easy to add new functions to the interpreter. In the future, I will add a garbage collector to handle cleaning dynamically allocated resources. + +It is possible to segregate unsafe allocations (allocations that should be hidden from the interpreter) by setting the +`inner_` versions of the library functions to different allocation functions. + +## Limits + +- Token size is limited to 127 bytes (see `ink_lex`) +- Values and indices are limited to the platform size of `int` +- Main function has a size limit of 256 tokens (see `ink_compile`) +- Functions have a size limit of 256 tokens (see `ink_parse`) +- Functions have a count limit 128 labels (see `ink_parse`) +- Only non-main functions can use labels \ No newline at end of file diff --git a/lib.c b/lib.c index 039b9ea..77c824a 100644 --- a/lib.c +++ b/lib.c @@ -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) {