commit ef32bb1d8177745e1a260d7cf88fd2f01478582d Author: Ludovic 'Archivist' Lagouardette Date: Mon May 13 13:36:26 2024 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d0826a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +cmake-build-* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4500333 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.27) +project(ink C) + +set(CMAKE_C_STANDARD 90) + +add_library(ink lib.c ink.h) +add_executable(ink_exe main.c) +target_link_libraries(ink_exe PUBLIC ink) diff --git a/ink.h b/ink.h new file mode 100644 index 0000000..22842cd --- /dev/null +++ b/ink.h @@ -0,0 +1,64 @@ +#pragma once +#include "stddef.h" + +#define INK_INTEGER 0 +#define INK_NATIVE_FUNCTION 1 +#define INK_FUNCTION 2 + +struct elem { + int type; + int value; +}; + +struct stack_frame { + struct elem executing; + int index; +}; + +struct fn { + char* name; + struct elem* things; + int size; +}; + +struct context; + +struct native_fn { + char* name; + void(*value)(struct context*); +}; + +struct context { + int panic; + void*(*malloc)(size_t); + void*(*realloc)(void*, size_t); + void(*free)(void*); + + struct elem* stack; + int capacity; + int top; + + struct stack_frame* function_stack; + int function_stack_capacity; + int function_stack_top; + + struct native_fn* native_words; + int native_words_capacity; + int native_words_top; + + struct fn* words; + int words_capacity; + int words_top; + + char** lex_reserved_words; + int lex_reserved_words_capacity; + int lex_reserved_words_top; +}; + +void ink_add_native(struct context* ctx, const char* name, void(*value)(struct context*)); +void ink_push(struct context* ctx, struct elem value); +void ink_push_fn(struct context* ctx, struct stack_frame value); +struct context* ink_make_context(void*(*malloc)(size_t), void*(*realloc)(void*, size_t), void(*free)(void*)); +struct context* ink_make_default_context(); +int ink_step(struct context *pContext); +void ink_run(struct context *pContext, char* buffer); diff --git a/lib.c b/lib.c new file mode 100644 index 0000000..de520d4 --- /dev/null +++ b/lib.c @@ -0,0 +1,549 @@ +#include +#include +#include +#include +#include "ink.h" + +#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" + +struct label { + int active; + int dest; + char* name; +}; + +void 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) { + // TODO: error + } else { + ctx->native_words = renewed; + ctx->native_words_capacity = new_count; + } + } + int len = strlen(name); + char* copy = ctx->malloc(len+1); + if(copy == NULL) { + // TODO: error + } + 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++; +} + +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) { + // TODO: error + } 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 = 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) { + // TODO: error + } + memcpy(copy, name, len); + copy[len] = 0; + ctx->words[ctx->words_top].things = 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) { + // TODO: error + } 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] = 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; +} + +void 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) { + // TODO: error + } else { + ctx->stack = renewed; + ctx->capacity = new_count; + } + } + ctx->stack[ctx->top] = value; + ctx->top++; +} + +void 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) { + // TODO: error + } else { + ctx->function_stack = renewed; + ctx->function_stack_capacity = new_count; + } + } + ctx->function_stack[ctx->function_stack_top] = value; + ctx->function_stack_top++; +} + +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*)) { + struct context* ctx = (struct context*)malloc(sizeof(struct context)); + ctx->malloc = malloc; + ctx->realloc = realloc; + ctx->free = free; + 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; +} + +static void print_stacktrace(struct context* _) { + int i = 0; + for(; i < _->function_stack_top; ++i) { + struct elem thing = _->function_stack[i].executing; + switch(thing.type) { + case INK_NATIVE_FUNCTION: + printf("%s\n", _->native_words[thing.value].name); + break; + case INK_FUNCTION: + printf("%s:%d\n", _->words[thing.value].name, _->function_stack[i].index); + break; + default: + break; + } + } +} + +static void add_int(struct context* ctx) { + if(ctx->top < 2) { + ctx->panic = 1; + return; + } + struct elem a = ctx->stack[ctx->top-1]; + struct elem 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 print_int(struct context* ctx) { + if(ctx->top < 1 || ctx->stack[ctx->top-1].type != INK_INTEGER) { + ctx->panic = 1; + return; + } + struct elem a = ctx->stack[ctx->top-1]; + ink_pop(ctx); + printf("%d", a.value); +} + +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 = ctx->stack[ctx->top-1]; + if(a.value <= 0x7F) { + putc(a.value, stdout); + } else if(a.value <= 0x7FF) { + putc(((a.value & 0xFC0) >> 6) | 192, stdout); + putc((a.value & 0x3F) | 128, stdout); + } else if(a.value <= 0xFFFF) { + putc(((a.value & 0x3F000) >> 12) | 224, stdout); + putc(((a.value & 0xFC0) >> 6) | 128, stdout); + putc((a.value & 0x3F) | 128, stdout); + } else if(a.value <= 0x10FFFF) { + putc(((a.value & 0x3C0000) >> 18) | 240, stdout); + putc(((a.value & 0x3F000) >> 12) | 128, stdout); + putc(((a.value & 0xFC0) >> 6) | 128, stdout); + putc((a.value & 0x3F) | 128, stdout); + } else { + ctx->panic = 1; + return; + } + ink_pop(ctx); +} + +struct context* ink_make_default_context() { + struct context* ctx = ink_make_context(malloc, realloc, free); + ink_add_native(ctx, "trace", print_stacktrace); + ink_add_native(ctx, "print_int", print_int); + ink_add_native(ctx, "print_utf8", print_as_utf8); + ink_add_native(ctx, "+", add_int); + return ctx; +} + +static void ink_consume_one(int* end, struct context* pContext, char** buffer, char* r) { + int i; + if(*end == 0) { + return; + } + r[*end] = 0; + int done = 0; + if (strcmp(r, _KEYWORD_INK_FUNCTION) == 0) { + struct elem value; + value.value = 0; + value.type = INK_FUNCTION_KW; + ink_push(pContext, value); + done = 1; + } + if (!done && strcmp(r, _KEYWORD_INK_DO) == 0) { + struct elem value; + value.value = 0; + value.type = INK_DO_KW; + ink_push(pContext, value); + done = 1; + } + if (!done && strcmp(r, _KEYWORD_INK_END) == 0) { + struct elem value; + value.value = 0; + value.type = INK_END_KW; + ink_push(pContext, value); + done = 1; + } + if (!done) { + for (i = 0; i < pContext->words_top; ++i) { + if (strcmp(r, pContext->words[i].name) == 0) { + struct elem value; + value.value = i; + value.type = INK_FUNCTION; + ink_push(pContext, value); + done = 1; + break; + } + } + } + if (!done) { + for (i = 0; i < pContext->native_words_top; ++i) { + if (strcmp(r, pContext->native_words[i].name) == 0) { + struct elem value; + value.value = i; + value.type = INK_NATIVE_FUNCTION; + ink_push(pContext, value); + done = 1; + break; + } + } + } + if (!done) { + for(i = (r[0] == '-'); i < *end; i++) { + if(!isdigit(r[i])){ + goto not_an_int; + } + } + struct elem value; + value.value = atoi(r); + value.type = INK_INTEGER; + ink_push(pContext, value); + done = 1; + } + not_an_int: if (!done) { + i = ink_add_lex_string(pContext, r); + struct elem value; + value.value = i; + if(r[strlen(r) - 1] == ':') { + value.type = INK_LABEL; + } else { + value.type = INK_RESERVED; + } + ink_push(pContext, value); + } + *end = 0; +} + +static void ink_lex(struct context *pContext, char* buffer) { + int i; + char r[128]; + int end = 0; + + while(*buffer != 0) { + if(isspace(*buffer)) { + ink_consume_one(&end, pContext, &buffer, r); + } else { + r[end] = *buffer; + ++end; + } + ++buffer; + } + ink_consume_one(&end, pContext, &buffer, r); +} + +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; +} + +static void ink_parse(struct context* pContext, struct elem* executable_buffer, int* executable_buffer_top) { + int i; + struct label labels[128]; + struct elem function_buffer[256]; + 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)*128); + for(i = 0; i < pContext->top; ++i) { + struct elem current = pContext->stack[i]; + switch (mode) { + case MODE_EXECUTABLE: + switch(current.type) { + case INK_FUNCTION_KW: + mode = MODE_FUNCTION; + goto next_token; + case INK_DO_KW: + case INK_END_KW: + // TODO: error + 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) { + // TODO: error (function name was not supplied) + } else { + mode = MODE_DO; + memset(labels, 0, sizeof(struct label)*128); + goto next_token; + } + } + if(function_name != -1) { + // TODO: error (function name supplied already) + } + if(current.type != INK_RESERVED) { + // TODO: error + } + 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 = function_buffer[j]; + if(pt.type == INK_LABEL) { + int k; + for(k = 0; k < 128; k++) { + if(labels[k].active) { + if(strcmp(labels[k].name, pContext->lex_reserved_words[pt.value]) == 0) { + labels[k].dest = j; + // TODO: error + 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 = function_buffer[j]; + if(pt.type == INK_RESERVED) { + const char* str = pContext->lex_reserved_words[pt.value]; + int k; + for(k = 0; k < 128; 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; + } + } + } + ink_add_indigenous(pContext, pContext->lex_reserved_words[function_name], function_buffer, function_buffer_top); + 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) { + // error, missing an end + } +#undef MODE_EXECUTABLE +#undef MODE_FUNCTION +#undef MODE_DO +} + +int ink_step(struct context *pContext) { + if(pContext->function_stack_top == 0) return 0; + if(pContext->panic) { + perror("PANIC!!!\n"); + return -1; + } + struct stack_frame* top = &pContext->function_stack[pContext->function_stack_top-1]; + switch(top->executing.type) { + 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 { + struct stack_frame frame; + frame.executing = pContext->words[top->executing.value].things[top->index]; + frame.index = 0; + ink_push_fn(pContext, frame); + top->index++; + } + break; + default: + ink_push(pContext, top->executing); + 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; + ink_lex(pContext, buffer); + int i = 0; + struct elem executable_buffer[256]; + int executable_buffer_top = 0; + ink_parse(pContext, executable_buffer, &executable_buffer_top); + struct stack_frame frame; + frame.executing.value = ink_add_indigenous(pContext, "__-MAIN-__", executable_buffer, executable_buffer_top); + frame.executing.type = INK_FUNCTION; + frame.index = 0; + ink_push_fn(pContext, frame); + + int out; + do { + out = ink_step(pContext); + } while(out > 0); +} \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..766ce7f --- /dev/null +++ b/main.c @@ -0,0 +1,20 @@ +#include "ink.h" +#include + +int main(int argc, char** argv) { + char read_buffer[2048]; + struct context* ctx = ink_make_default_context(); + char** end_argv = argv + argc; + for(argv+=1; argv != end_argv; argv++) { + FILE* file = fopen(*argv, "r"); + size_t cnt = fread(read_buffer, 1, 2047, file); + if(cnt == 0) { + + } + read_buffer[cnt] = 0; + ink_run(ctx, read_buffer); + fclose(file); + } + + return ctx->panic; +} \ No newline at end of file diff --git a/test/test01.nk b/test/test01.nk new file mode 100644 index 0000000..cc27c58 --- /dev/null +++ b/test/test01.nk @@ -0,0 +1,3 @@ +fn potato do + label: trace trace label print_int 10 print_utf8 endfn print_int 10 print_utf8 endfn: +end \ No newline at end of file diff --git a/test/test02.nk b/test/test02.nk new file mode 100644 index 0000000..d64e5a3 --- /dev/null +++ b/test/test02.nk @@ -0,0 +1 @@ +potato potato \ No newline at end of file