A minimalistic programming language written in C89.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1090 lines
28 KiB

#include "ink.h"
#ifndef NOSTDLIB
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef INSTRUMENTION
#include <time.h>
#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 _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->inner_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->inner_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->inner_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->inner_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->inner_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->routine_current >= ctx->routines_top) return -65;
struct ink_routine* current = ctx->routines + ctx->routine_current;
if(current->stack == NULL) {
current->stack = ctx->malloc(sizeof(struct elem) * 8);
current->top = 0;
current->capacity = 8;
} else if(current->top == current->capacity) {
int new_count = (current->capacity + current->capacity/2);
void* renewed = ctx->realloc(current->stack, sizeof(struct elem) * new_count);
if(renewed == NULL) {
return -18;
} else {
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) {
if(ctx->routine_current >= ctx->routines_top) return -55;
struct ink_routine* current = ctx->routines + ctx->routine_current;
if(current->panic) return -56;
if(current->function_stack == NULL) {
current->function_stack = ctx->malloc(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 = (current->function_stack_capacity + current->function_stack_capacity/2);
void* renewed = ctx->realloc(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)(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->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->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->steps = 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
* @internal this function is slightly cursed
*/
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;
}
int ink_make_routine(struct context* ctx) {
if(ctx->routines == NULL) {
ctx->routines = ctx->inner_malloc(sizeof(struct ink_routine) * 8);
ctx->routines_top = 0;
ctx->routines_capacity = 8;
struct ink_routine* it = ctx->routines;
struct ink_routine* end = ctx->routines + 8;
for(;it != end;++it) {
it->stack = NULL;
it->function_stack = NULL;
it->panic = INK_ROUTINE_CAN_REUSE;
}
} else if(ctx->routines_top == ctx->routines_capacity) {
int new_count = (ctx->routines_capacity + ctx->routines_capacity/2);
void* renewed = ctx->inner_realloc(ctx->routines, sizeof(struct stack_frame) * new_count);
if(renewed == NULL) {
return -99;
} else {
ctx->routines = renewed;
struct ink_routine* it = ctx->routines + ctx->routines_capacity;
struct ink_routine* end = ctx->routines + new_count;
for(;it != end;++it) {
it->panic = INK_ROUTINE_CAN_REUSE;
}
ctx->routines_capacity = new_count;
}
}
struct ink_routine* it = ctx->routines;
struct ink_routine* end = ctx->routines + ctx->routines_capacity;
for(;it != end;++it) {
if(it->panic == INK_ROUTINE_CAN_REUSE) {
it->panic = 0;
it->stack = NULL;
it->top = 0;
it->capacity = 0;
it->function_stack = NULL;
it->function_stack_top = 0;
it->function_stack_capacity = 0;
int idx = it - ctx->routines;
if(idx >= ctx->routines_top) {
ctx->routines_top = idx + 1;
}
return idx;
}
}
}
int ink_kill_routine(struct context* ctx, int routine){
if(routine < 0 || routine >= ctx->routines_top) {
return 0;
}
struct ink_routine* curr = ctx->routines + routine;
if(curr->panic == INK_ROUTINE_CAN_REUSE) {
return 0;
}
if(curr->stack != NULL) {
ctx->free(curr->stack);
curr->stack = NULL;
}
if(curr->function_stack != NULL) {
ctx->free(curr->function_stack);
curr->function_stack = NULL;
}
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 = pContext->routines + pContext->routine_current;
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 < 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;
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) {
struct ink_routine* currentRoutine = pContext->routines + pContext->routine_current;
pContext->steps++;
if(currentRoutine->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 = &currentRoutine->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++;
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_compile(struct context *pContext, char* buffer) {
int routine = ink_make_routine(pContext);
int saved = pContext->routine_current;
pContext->routine_current = routine;
struct ink_routine* currentRoutine = pContext->routines + routine;
currentRoutine->stack = NULL;
currentRoutine->top = 0;
currentRoutine->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;
char main_fn[32] = "__-MAIN-__";
char* integer = ink_itoa(pContext, routine);
size_t integer_size = strlen(integer);
memcpy(main_fn+10, integer, integer_size);
pContext->free(integer);
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) {
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;
}
pContext->routine_current = saved;
return;
}
int ink_can_run(struct context* pContext) {
int it = 0;
for(;it < pContext->routines_top; ++it) {
if(pContext->routines[it].panic == 0) {
return 1;
}
}
return 0;
}
int ink_step_everyone(struct context* pContext) {
int out;
pContext->routine_current = -1;
for(;;) {
do{
++(pContext->routine_current);
} while(pContext->routine_current < pContext->routines_top && pContext->routines[pContext->routine_current].panic != 0);
if(pContext->routine_current >= pContext->routines_top) break;
if(pContext->routines[pContext->routine_current].panic == INK_ROUTINE_SUCCESS) {
ink_kill_routine(pContext, pContext->routine_current);
}
out = ink_step(pContext);
if(out == 0) {
pContext->routines[pContext->routine_current].panic = INK_ROUTINE_SUCCESS;
} else if(out < 0) {
pContext->routines[pContext->routine_current].panic = out;
}
}
return 0;
}
/**********************************************************************************************************************/
static void print_stacktrace(struct context* _) {
int i = 0;
struct ink_routine* currentRoutine = _->routines + _->routine_current;
for(; i < currentRoutine->function_stack_top; ++i) {
struct elem thing;
thing = currentRoutine->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(_, currentRoutine->function_stack[i].index);
while (*n) {
_->putchar(*n);
++n;
}
_->free(n);
_->putchar(10);
break;
}
default:
break;
}
}
}
static void add_int(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 2) {
currentRoutine->panic = 1;
return;
}
struct elem a;
struct elem b;
a = currentRoutine->stack[currentRoutine->top-1];
b = currentRoutine->stack[currentRoutine->top-2];
if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) {
ctx->panic = 1;
return;
}
ink_pop(ctx);
currentRoutine->stack[currentRoutine->top-1].value = a.value + b.value;
}
static void sub_int(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 2) {
currentRoutine->panic = 1;
return;
}
struct elem a;
struct elem b;
a = currentRoutine->stack[currentRoutine->top-1];
b = currentRoutine->stack[currentRoutine->top-2];
if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) {
currentRoutine->panic = 1;
return;
}
ink_pop(ctx);
currentRoutine->stack[currentRoutine->top-1].value = b.value - a.value;
}
static void mult_int(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 2) {
currentRoutine->panic = 1;
return;
}
struct elem a;
struct elem b;
a = currentRoutine->stack[currentRoutine->top-1];
b = currentRoutine->stack[currentRoutine->top-2];
if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) {
currentRoutine->panic = 1;
return;
}
ink_pop(ctx);
currentRoutine->stack[currentRoutine->top-1].value = b.value * a.value;
}
static void div_int(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 2) {
currentRoutine->panic = 1;
return;
}
struct elem a;
struct elem b;
a = currentRoutine->stack[currentRoutine->top-1];
b = currentRoutine->stack[currentRoutine->top-2];
if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) {
currentRoutine->panic = 1;
return;
}
ink_pop(ctx);
currentRoutine->stack[currentRoutine->top-1].value = b.value / a.value;
}
static void rem_int(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 2) {
currentRoutine->panic = 1;
return;
}
struct elem a;
struct elem b;
a = currentRoutine->stack[currentRoutine->top-1];
b = currentRoutine->stack[currentRoutine->top-2];
if(!(a.type == INK_INTEGER && b.type == INK_INTEGER)) {
currentRoutine->panic = 1;
return;
}
ink_pop(ctx);
currentRoutine->stack[currentRoutine->top-1].value = b.value % a.value;
}
static void dupe_elem(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 1) {
ctx->panic = 1;
return;
}
struct elem a;
a = currentRoutine->stack[currentRoutine->top-1];
int err;
err = ink_push(ctx, a);
if(err < 0) ctx->panic;
}
static void drop_elem(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 1) {
ctx->panic = 1;
return;
}
ink_pop(ctx);
}
static void pluck_elem(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 1) {
currentRoutine->panic = 1;
return;
}
struct elem a;
a = currentRoutine->stack[currentRoutine->top-1];
if(a.type != INK_INTEGER) {
ctx->panic = 1;
return;
}
int position = currentRoutine->top - (a.value + 1);
if(position >= currentRoutine->top || position < 0) {
ctx->panic = 1;
return;
}
ink_pop(ctx);
int err;
err = ink_push(ctx, currentRoutine->stack[position]);
if(err < 0) ctx->panic;
}
static void swap_elem(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 2) {
currentRoutine->panic = 1;
return;
}
struct elem a;
struct elem b;
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 = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 1) {
ctx->panic = 1;
return;
}
struct elem a;
a = currentRoutine->stack[currentRoutine->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) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 1) {
ctx->panic = 1;
return;
}
struct elem a;
a = currentRoutine->stack[currentRoutine->top-1];
if(a.type != INK_INTEGER) {
ctx->panic = 1;
return;
}
ink_pop(ctx);
if(a.value) {
ink_pop_fn(ctx);
a = currentRoutine->stack[currentRoutine->top-1];
currentRoutine->function_stack[currentRoutine->function_stack_top - 1].index += a.value - 3;
}
ink_pop(ctx);
return;
}
static void print_int(struct context* ctx) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 1 || currentRoutine->stack[currentRoutine->top-1].type != INK_INTEGER) {
currentRoutine->panic = 1;
return;
}
struct elem a;
a = currentRoutine->stack[currentRoutine->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) {
struct ink_routine* currentRoutine = ctx->routines + ctx->routine_current;
if(currentRoutine->top < 1 || currentRoutine->stack[currentRoutine->top-1].type != INK_INTEGER) {
ctx->panic = 1;
return;
}
struct elem a;
a = currentRoutine->stack[currentRoutine->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 = 0;
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;
}