Browse Source

added more docs

main
Ludovic 'Archivist' Lagouardette 4 months ago
parent
commit
6cdddb8543
2 changed files with 45 additions and 1 deletions
  1. +12
    -0
      README.md
  2. +33
    -1
      lib.c

+ 12
- 0
README.md View File

@ -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 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. 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

+ 33
- 1
lib.c View File

@ -170,6 +170,13 @@ static int ink_add_indigenous(struct context* ctx, const char* name, struct elem
return ctx->words_top++; 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) { static int ink_add_lex_string(struct context* ctx, const char* name) {
int i; int i;
if(ctx->lex_reserved_words == NULL) { 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) { static int ink_lex(struct context *pContext, char* buffer) {
int i; int i;
// Limits the token size to 127 chars
char r[128]; char r[128];
int end = 0; int end = 0;
int err; 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) { int ink_make_routine(struct context* ctx) {
// Allocate space if needed
if(ctx->routines == NULL) { if(ctx->routines == NULL) {
ctx->routines = ctx->inner_malloc(sizeof(struct ink_routine) * 8); ctx->routines = ctx->inner_malloc(sizeof(struct ink_routine) * 8);
ctx->routines_top = 0; ctx->routines_top = 0;
@ -490,7 +499,7 @@ int ink_make_routine(struct context* ctx) {
struct ink_routine* it = ctx->routines; struct ink_routine* it = ctx->routines;
struct ink_routine* end = ctx->routines + ctx->routines_capacity; struct ink_routine* end = ctx->routines + ctx->routines_capacity;
// Looks for a reusable routine space then uses it
for(;it != end;++it) { for(;it != end;++it) {
if(it->panic == INK_ROUTINE_CAN_REUSE) { if(it->panic == INK_ROUTINE_CAN_REUSE) {
it->panic = 0; it->panic = 0;
@ -550,6 +559,8 @@ static int ink_parse(struct context* pContext, struct elem* executable_buffer, i
#define MODE_DO 2 #define MODE_DO 2
int mode = 0; int mode = 0;
memset(labels, 0, sizeof(struct label)*LABEL_BUFFER); 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) { for(i = 0; i < currentRoutine->top; ++i) {
struct elem current; struct elem current;
current = currentRoutine->stack[i]; current = currentRoutine->stack[i];
@ -736,6 +747,7 @@ void ink_compile(struct context *pContext, char* buffer) {
return; return;
} }
int i = 0; int i = 0;
// Main function has a size limit of 256 (need to know that for REPL
struct elem executable_buffer[256]; struct elem executable_buffer[256];
int executable_buffer_top = 0; int executable_buffer_top = 0;
err = ink_parse(pContext, executable_buffer, &executable_buffer_top); err = ink_parse(pContext, executable_buffer, &executable_buffer_top);
@ -781,13 +793,19 @@ int ink_step_everyone(struct context* pContext) {
int out; int out;
pContext->routine_current = -1; pContext->routine_current = -1;
for(;;) { for(;;) {
// Increment to next runnable routine
do{ do{
++(pContext->routine_current); ++(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);
// Exit condition
if(pContext->routine_current >= pContext->routines_top) break; if(pContext->routine_current >= pContext->routines_top) break;
// Kill?
if(pContext->routines[pContext->routine_current].panic == INK_ROUTINE_SUCCESS) { if(pContext->routines[pContext->routine_current].panic == INK_ROUTINE_SUCCESS) {
ink_kill_routine(pContext, pContext->routine_current); ink_kill_routine(pContext, pContext->routine_current);
} }
//Step!
out = ink_step(pContext); out = ink_step(pContext);
if(out == 0) { if(out == 0) {
pContext->routines[pContext->routine_current].panic = INK_ROUTINE_SUCCESS; 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*) struct ink_collection_list (*gc)(struct context*,void*)
) { ) {
if(ctx->panic) return -128; if(ctx->panic) return -128;
// Resize for push
if(ctx->types == NULL) { if(ctx->types == NULL) {
ctx->types = ctx->inner_malloc(sizeof(struct ink_type) * 8); ctx->types = ctx->inner_malloc(sizeof(struct ink_type) * 8);
ctx->types_top = 0; ctx->types_top = 0;
@ -820,6 +839,8 @@ int ink_new_type(
ctx->types_capacity = new_count; ctx->types_capacity = new_count;
} }
} }
// Push
ctx->types[ctx->types_top].name = type_name; ctx->types[ctx->types_top].name = type_name;
ctx->types[ctx->types_top].element_size = size; ctx->types[ctx->types_top].element_size = size;
ctx->types[ctx->types_top].elements = NULL; 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; ret.value = -130;
return ret; return ret;
} }
// Apply invariant of the user defined types
int type_id = type - 16; int type_id = type - 16;
if(type_id >= ctx->types_top) { if(type_id >= ctx->types_top) {
struct elem ret; struct elem ret;
@ -872,6 +895,8 @@ struct elem ink_make_native(struct context* ctx, int type, void* ptr) {
ret.value = -135; ret.value = -135;
return ret; return ret;
} }
// Resize for push of value in store
if(ctx->types[type_id].elements == NULL) { 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 = ctx->inner_malloc(sizeof(struct element_slab) * 8);
ctx->types[type_id].elements_top = 0; 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)); 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 g = ctx->types[type_id].elements_capacity;
int i; int i;
for(i = 0; i < g; ++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(i = 0; i < ctx->routines_top; ++i) {
for(j = 0; j < ctx->routines[i].top; ++j) { for(j = 0; j < ctx->routines[i].top; ++j) {
struct element_slab* v = ink_get_value_link(ctx, ctx->routines[i].stack[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; int marked;
do { do {
marked = 0; marked = 0;
for (i = 0; i < ctx->types_top; ++i) { for (i = 0; i < ctx->types_top; ++i) {
for (j = 0; j < ctx->types[i].elements_top; ++j) { 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) { 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); struct ink_collection_list c = ctx->types[i].gc(ctx, ctx->types[i].elements[j].data);
for (k = 0; k < c.count; ++k) { for (k = 0; k < c.count; ++k) {
struct element_slab *v = ink_get_value_link(ctx, c.elements[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) { if (v != NULL && !v->uses) {
++v->uses; ++v->uses;
marked = 1; marked = 1;
@ -958,6 +989,7 @@ void ink_gc(struct context* ctx) {
} }
} while(marked); } while(marked);
// Sweep phase: explore any allocated data and sweep the unused away
for(i = 0; i < ctx->types_top; ++i) { for(i = 0; i < ctx->types_top; ++i) {
for(j = 0; j < ctx->types[i].elements_top; ++j) { 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) { if(ctx->types[i].elements[j].uses == 0 && ctx->types[i].elements[j].in_use) {

Loading…
Cancel
Save