#include "ink.h" #ifndef NOSTDLIB #include #include #include #include #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 _KEYWORD_INK_FUNCTION "fn" #define _KEYWORD_INK_DO "do" #define _KEYWORD_INK_END "end" #define _KEYWORD_INK_RETURN "return" struct label { int active; int dest; char* name; }; #ifdef NOSTDLIB static size_t strlen(const char* c) { size_t j = 0; while(*(c++)) { j++; } return j; } static void* memcpy(void* _dest, const void* _src, size_t sz) { char* dest = _dest; const char* 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 = _dest; const char* 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; 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 = 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*)) { if(ctx->native_words == NULL) { ctx->native_words = ctx->malloc(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 = (ctx->native_words_capacity + ctx->native_words_capacity/2); void* renewed = ctx->realloc(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; } } int len = strlen(name); char* copy = ctx->malloc(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) { if(ctx->words == NULL) { ctx->words = ctx->malloc(sizeof(struct fn) * 8); ctx->words_top = 0; ctx->words_capacity = 8; } else if(ctx->words_top == ctx->words_capacity) { int new_count = (ctx->words_capacity + ctx->words_capacity/2); void* renewed = ctx->realloc(ctx->words, sizeof(struct native_fn) * new_count); if(renewed == NULL) { return -1; } else { ctx->words = renewed; ctx->words_capacity = new_count; } } int i; for(i = 0; i < ctx->words_top; ++i) { if(strcmp(name, ctx->words[i].name) == 0) { ctx->free(ctx->words[i].things); ctx->words[i].things = ctx->malloc(sizeof(struct elem) * count); memcpy(ctx->words[i].things, m, sizeof(struct elem) * count); ctx->words[i].size = count; return i; } } int len = strlen(name); char* copy = ctx->malloc(len+1); if(copy == NULL) { return -2; } memcpy(copy, name, len); copy[len] = 0; ctx->words[ctx->words_top].things = ctx->malloc(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++; } static int ink_add_lex_string(struct context* ctx, const char* name) { int i; if(ctx->lex_reserved_words == NULL) { ctx->lex_reserved_words = ctx->malloc(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 = (ctx->lex_reserved_words_capacity + ctx->lex_reserved_words_capacity/2); void* renewed = ctx->realloc(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; } } int len = strlen(name); i = ctx->lex_reserved_words_top; ctx->lex_reserved_words[i] = ctx->malloc(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) { if(ctx->stack == NULL) { ctx->stack = ctx->malloc(sizeof(struct elem) * 8); ctx->top = 0; ctx->capacity = 8; } else if(ctx->top == ctx->capacity) { int new_count = (ctx->capacity + ctx->capacity/2); void* renewed = ctx->realloc(ctx->stack, sizeof(struct elem) * new_count); if(renewed == NULL) { return -18; } else { ctx->stack = renewed; ctx->capacity = new_count; } } ctx->stack[ctx->top] = value; ctx->top++; return 0; } int ink_push_fn(struct context* ctx, struct stack_frame value) { if(ctx->function_stack == NULL) { ctx->function_stack = ctx->malloc(sizeof(struct stack_frame) * 8); ctx->function_stack_top = 0; ctx->function_stack_capacity = 8; } else if(ctx->function_stack_top == ctx->function_stack_capacity) { int new_count = (ctx->function_stack_capacity + ctx->function_stack_capacity/2); void* renewed = ctx->realloc(ctx->function_stack, sizeof(struct stack_frame) * new_count); if(renewed == NULL) { return -9; } else { ctx->function_stack = renewed; ctx->function_stack_capacity = new_count; } } ctx->function_stack[ctx->function_stack_top] = value; ctx->function_stack_top++; return 0; } static void ink_pop_fn(struct context* ctx) { if(ctx->function_stack == NULL) return; if(ctx->function_stack_top == 0) return; ctx->function_stack_top--; } static void ink_pop(struct context* ctx) { if(ctx->stack == NULL) return; if(ctx->top == 0) return; ctx->top--; } struct context* ink_make_context(void*(*malloc)(size_t), void*(*realloc)(void*, size_t), void(*free)(void*), int(*putchar)(int)) { struct context* ctx = (struct context*)malloc(sizeof(struct context)); ctx->malloc = malloc; ctx->realloc = realloc; ctx->free = free; ctx->putchar = putchar; ctx->panic = 0; ctx->stack = NULL; ctx->capacity = 0; ctx->top = 0; ctx->function_stack = NULL; ctx->function_stack_capacity = 0; ctx->function_stack_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; return ctx; } /** * 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 */ static char* ink_itoa(struct context* _, int cpy) { char* n = _->malloc(16); n[15] = 0; char* it = n+15; do { it--; *it = (cpy % 10) + '0'; cpy = cpy / 10; } while(cpy); memmove(n, it, 16 - (it-n)); return n; } #ifndef NOSTDLIB struct context* ink_make_default_context() { struct context* ctx = ink_make_context(malloc, realloc, free, putchar); ink_std_library(ctx); return ctx; } #endif static int ink_consume_one(int* end, struct context* pContext, char** buffer, char* r) { int i; if(*end == 0) { return 0; } r[*end] = 0; int done = 0; struct elem value; if (strcmp(r, _KEYWORD_INK_FUNCTION) == 0) { value.value = 0; value.type = INK_FUNCTION_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) { int err; 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; int err; 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; int err; 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; int err; 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; } int err; err = ink_push(pContext, value); if(err < 0) { return -23; } } *end = 0; return 0; } static int ink_lex(struct context *pContext, char* buffer) { int i; char r[128]; int end = 0; int err; while(*buffer != 0) { if(isspace(*buffer)) { err = ink_consume_one(&end, pContext, &buffer, r); if(err < 0) { pContext->panic = 1; return -8; } } else { r[end] = *buffer; ++end; } ++buffer; } err = ink_consume_one(&end, pContext, &buffer, r); if(err < 0) { pContext->panic = 1; return -9; } 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; } /** * * @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) { int i; #define LABEL_BUFFER 128 #define FUNCTION_BUFFER 256 struct label labels[LABEL_BUFFER]; struct elem function_buffer[FUNCTION_BUFFER]; int function_buffer_top = 0; int function_name = -1; #define MODE_EXECUTABLE 0 #define MODE_FUNCTION 1 #define MODE_DO 2 int mode = 0; memset(labels, 0, sizeof(struct label)*LABEL_BUFFER); for(i = 0; i < pContext->top; ++i) { struct elem current; current = pContext->stack[i]; switch (mode) { case MODE_EXECUTABLE: switch(current.type) { case INK_FUNCTION_KW: mode = MODE_FUNCTION; function_name = -1; goto next_token; case INK_DO_KW: case INK_END_KW: return -26; default: executable_buffer[*executable_buffer_top] = current; *executable_buffer_top += 1; } break; case MODE_FUNCTION: if(current.type == INK_DO_KW) { if(function_name == -1) { return -27; } else { mode = MODE_DO; memset(labels, 0, sizeof(struct label)*128); goto next_token; } } if(function_name != -1) { return -28; } if(current.type != INK_RESERVED) { return -29; } 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) { if(strcmp(labels[k].name, pContext->lex_reserved_words[pt.value]) == 0) { labels[k].dest = j; return -30; break; } } 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) { const char* str = pContext->lex_reserved_words[pt.value]; int k; for(k = 0; k < LABEL_BUFFER; k++) { if(labels[k].active) { const char* lbl = labels[k].name; int 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; } } } int err; err = ink_add_indigenous(pContext, pContext->lex_reserved_words[function_name], function_buffer, function_buffer_top); if(err < 0) { pContext->panic = 1; return -33; } 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; } if(mode == MODE_FUNCTION || mode == MODE_DO) { return -32; } return 0; #undef MODE_EXECUTABLE #undef MODE_FUNCTION #undef MODE_DO #undef LABEL_BUFFER #undef FUNCTION_BUFFER } int ink_step(struct context *pContext) { if(pContext->function_stack_top == 0) return 0; if(pContext->panic) { return -1; } struct stack_frame frame; struct stack_frame* top; struct elem next; int t; top = &pContext->function_stack[pContext->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++; if(pContext->native_words_top <= top->executing.value) { pContext->panic = 1; return -1; } pContext->native_words[top->executing.value].value(pContext); } break; case INK_FUNCTION: if(pContext->words_top <= top->executing.value) { pContext->panic = 1; return -1; } 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); if(t < 0) { pContext->panic = 1; return -11; } top->index++; } break; default: t = ink_push(pContext, top->executing); if(t < 0) { pContext->panic = 1; return -25; } ink_pop_fn(pContext); break; } return 1; } void ink_run(struct context *pContext, char* buffer) { pContext->free(pContext->stack); pContext->stack = NULL; pContext->top = 0; pContext->capacity = 0; int err; err = ink_lex(pContext, buffer); if(err < 0) { pContext->panic = 1; return; } int i = 0; struct elem executable_buffer[256]; int executable_buffer_top = 0; err = ink_parse(pContext, executable_buffer, &executable_buffer_top); if(err < 0) { pContext->panic = 1; return; } struct stack_frame frame; frame.executing.value = ink_add_indigenous(pContext, "__-MAIN-__", executable_buffer, executable_buffer_top); if(frame.executing.value < 0) { pContext->panic = 1; return; } frame.executing.type = INK_FUNCTION; frame.index = 0; err = ink_push_fn(pContext, frame); if(err < 0) { pContext->panic = 1; return; } int out; do { out = ink_step(pContext); } while(out > 0); } /**********************************************************************************************************************/ static void print_stacktrace(struct context* _) { int i = 0; for(; i < _->function_stack_top; ++i) { struct elem thing; thing = _->function_stack[i].executing; switch(thing.type) { case INK_NATIVE_FUNCTION: { char *n = _->native_words[thing.value].name; while (*n) { _->putchar(*n); ++n; } _->putchar(10); break; } case INK_FUNCTION:{ char *n = _->native_words[thing.value].name; while (*n) { _->putchar(*n); ++n; } _->putchar(':'); n = ink_itoa(_, _->function_stack[i].index); while (*n) { _->putchar(*n); ++n; } _->free(n); _->putchar(10); break; } default: break; } } } static void add_int(struct context* ctx) { if(ctx->top < 2) { ctx->panic = 1; return; } struct elem a; struct elem b; a = ctx->stack[ctx->top-1]; b = ctx->stack[ctx->top-2]; if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { ctx->panic = 1; return; } ink_pop(ctx); ctx->stack[ctx->top-1].value = a.value + b.value; } static void sub_int(struct context* ctx) { if(ctx->top < 2) { ctx->panic = 1; return; } struct elem a; struct elem b; a = ctx->stack[ctx->top-1]; b = ctx->stack[ctx->top-2]; if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { ctx->panic = 1; return; } ink_pop(ctx); ctx->stack[ctx->top-1].value = b.value - a.value; } static void mult_int(struct context* ctx) { if(ctx->top < 2) { ctx->panic = 1; return; } struct elem a; struct elem b; a = ctx->stack[ctx->top-1]; b = ctx->stack[ctx->top-2]; if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { ctx->panic = 1; return; } ink_pop(ctx); ctx->stack[ctx->top-1].value = b.value * a.value; } static void div_int(struct context* ctx) { if(ctx->top < 2) { ctx->panic = 1; return; } struct elem a; struct elem b; a = ctx->stack[ctx->top-1]; b = ctx->stack[ctx->top-2]; if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { ctx->panic = 1; return; } ink_pop(ctx); ctx->stack[ctx->top-1].value = b.value / a.value; } static void rem_int(struct context* ctx) { if(ctx->top < 2) { ctx->panic = 1; return; } struct elem a; struct elem b; a = ctx->stack[ctx->top-1]; b = ctx->stack[ctx->top-2]; if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) { ctx->panic = 1; return; } ink_pop(ctx); ctx->stack[ctx->top-1].value = b.value % a.value; } static void dupe_elem(struct context* ctx) { if(ctx->top < 1) { ctx->panic = 1; return; } struct elem a; a = ctx->stack[ctx->top-1]; int err; err = ink_push(ctx, a); if(err < 0) ctx->panic; } static void drop_elem(struct context* ctx) { if(ctx->top < 1) { ctx->panic = 1; return; } ink_pop(ctx); } static void pluck_elem(struct context* ctx) { if(ctx->top < 1) { ctx->panic = 1; return; } struct elem a; a = ctx->stack[ctx->top-1]; if(a.type != INK_INTEGER) { ctx->panic = 1; return; } int position = ctx->top - (a.value + 1); if(position >= ctx->top || position < 0) { ctx->panic = 1; return; } ink_pop(ctx); int err; err = ink_push(ctx, ctx->stack[position]); if(err < 0) ctx->panic; } static void swap_elem(struct context* ctx) { if(ctx->top < 2) { ctx->panic = 1; return; } struct elem a; struct elem b; a = ctx->stack[ctx->top-1]; b = ctx->stack[ctx->top-2]; ctx->stack[ctx->top-2] = a; ctx->stack[ctx->top-1] = b; } static void return_if(struct context* ctx) { if(ctx->top < 1) { ctx->panic = 1; return; } struct elem a; a = ctx->stack[ctx->top-1]; if(a.type != INK_INTEGER) { ctx->panic = 1; return; } if(a.value) { ink_pop_fn(ctx); ink_pop_fn(ctx); } ink_pop(ctx); return; } static void jump_if(struct context* ctx) { if(ctx->top < 1) { ctx->panic = 1; return; } struct elem a; a = ctx->stack[ctx->top-1]; if(a.type != INK_INTEGER) { ctx->panic = 1; return; } ink_pop(ctx); if(a.value) { ink_pop_fn(ctx); a = ctx->stack[ctx->top-1]; ctx->function_stack[ctx->function_stack_top - 1].index += a.value - 3; ink_pop(ctx); //printf("\t*%d\n", ctx->function_stack[ctx->function_stack_top - 1].index); } return; } static void print_int(struct context* ctx) { if(ctx->top < 1 || ctx->stack[ctx->top-1].type != INK_INTEGER) { ctx->panic = 1; return; } struct elem a; a = ctx->stack[ctx->top-1]; ink_pop(ctx); char* n = ink_itoa(ctx, a.value); char* str = n; while (*str) { ctx->putchar(*str); ++str; } ctx->free(n); } static void print_as_utf8(struct context* ctx) { if(ctx->top < 1 || ctx->stack[ctx->top-1].type != INK_INTEGER) { ctx->panic = 1; return; } struct elem a; a = ctx->stack[ctx->top-1]; if(a.value <= 0x7F) { ctx->putchar(a.value); } else if(a.value <= 0x7FF) { ctx->putchar(((a.value & 0xFC0) >> 6) | 192); ctx->putchar((a.value & 0x3F) | 128); } else if(a.value <= 0xFFFF) { ctx->putchar(((a.value & 0x3F000) >> 12) | 224); ctx->putchar(((a.value & 0xFC0) >> 6) | 128); ctx->putchar((a.value & 0x3F) | 128); } else if(a.value <= 0x10FFFF) { ctx->putchar(((a.value & 0x3C0000) >> 18) | 240); ctx->putchar(((a.value & 0x3F000) >> 12) | 128); ctx->putchar(((a.value & 0xFC0) >> 6) | 128); ctx->putchar((a.value & 0x3F) | 128); } else { ctx->panic = 1; return; } ink_pop(ctx); } int ink_std_library(struct context* ctx) { int v; v += ink_add_native(ctx, "trace", print_stacktrace); 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, "%", rem_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, "pluck", pluck_elem); v += ink_add_native(ctx, "return_if", return_if); v += ink_add_native(ctx, "jump_if", jump_if); return v; }