Browse Source

Initial commit

main
Ludovic 'Archivist' Lagouardette 7 months ago
commit
ef32bb1d81
7 changed files with 646 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +8
    -0
      CMakeLists.txt
  3. +64
    -0
      ink.h
  4. +549
    -0
      lib.c
  5. +20
    -0
      main.c
  6. +3
    -0
      test/test01.nk
  7. +1
    -0
      test/test02.nk

+ 1
- 0
.gitignore View File

@ -0,0 +1 @@
cmake-build-*

+ 8
- 0
CMakeLists.txt View File

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

+ 64
- 0
ink.h View File

@ -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);

+ 549
- 0
lib.c View File

@ -0,0 +1,549 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#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);
}

+ 20
- 0
main.c View File

@ -0,0 +1,20 @@
#include "ink.h"
#include <stdio.h>
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;
}

+ 3
- 0
test/test01.nk View File

@ -0,0 +1,3 @@
fn potato do
label: trace trace label print_int 10 print_utf8 endfn print_int 10 print_utf8 endfn:
end

+ 1
- 0
test/test02.nk View File

@ -0,0 +1 @@
potato potato

Loading…
Cancel
Save