diff --git a/examples/cat.grr b/examples/cat.grr deleted file mode 100644 index 663e885..0000000 --- a/examples/cat.grr +++ /dev/null @@ -1,4 +0,0 @@ -#load("std.grr") - -while: [stdin fgetline] - [print]; diff --git a/examples/fibonacci.grr b/examples/fibonacci.grr index dfa5e09..027ce15 100644 --- a/examples/fibonacci.grr +++ b/examples/fibonacci.grr @@ -1,4 +1,4 @@ -#load("std.grr") +load "../std.grr" def fib { 0 1 dig [dup [+] dip swap] times drop diff --git a/examples/fizzbuzz.grr b/examples/fizzbuzz.grr index ad5a72e..efc6cca 100644 --- a/examples/fizzbuzz.grr +++ b/examples/fizzbuzz.grr @@ -1,13 +1,4 @@ -def print { file/stdout file/write } -def when { [] if } -def keep { over [call] dip } -def bi { [keep] dip call } - -def times { - if: over 0 = - [drop drop] - [swap over >r >r call r> 1 - r> times]; -} +load "../std.grr" def fizzbuzz? { [3 % 0 =] [5 % 0 =] bi or } def fizz { when: 3 % 0 = ["Fizz" print]; } diff --git a/meson.build b/meson.build index 8f9a9fd..1b22cf0 100644 --- a/meson.build +++ b/meson.build @@ -17,6 +17,7 @@ growl_sources = [ 'src/core/compiler.c', 'src/core/dictionary.c', 'src/core/disasm.c', + 'src/core/dynarray.c', 'src/core/file.c', 'src/core/gc.c', 'src/core/hash.c', diff --git a/src/core/callable.c b/src/core/callable.c index db79bd5..216277a 100644 --- a/src/core/callable.c +++ b/src/core/callable.c @@ -10,6 +10,10 @@ int growl_callable(Growl obj) { case GROWL_TYPE_COMPOSE: case GROWL_TYPE_CURRY: return 1; + case GROWL_TYPE_ALIEN: { + GrowlAlien *alien = (GrowlAlien *)(hdr + 1); + return alien->type && alien->type->call != NULL; + } default: return 0; } diff --git a/src/core/compiler.c b/src/core/compiler.c index a751f00..18163ac 100644 --- a/src/core/compiler.c +++ b/src/core/compiler.c @@ -4,9 +4,13 @@ #include #include +#include "dynarray.h" #include "opcodes.h" #include "sleb128.h" -#include "dynarray.h" + +#include +#include +#include #define COMPILER_DEBUG 0 @@ -73,7 +77,7 @@ Primitive primitives[] = { // clang-format on static void emit_byte(GrowlVM *vm, Chunk *chunk, uint8_t byte) { - *push(chunk, &vm->scratch) = byte; + *growl_dynarray_push(chunk, &vm->scratch) = byte; } static void emit_sleb128(GrowlVM *vm, Chunk *chunk, intptr_t num) { @@ -95,7 +99,7 @@ static size_t add_constant(GrowlVM *vm, Chunk *chunk, Growl value) { if (chunk->constants.data[i] == value) return i; } - *push(&chunk->constants, &vm->scratch) = value; + *growl_dynarray_push(&chunk->constants, &vm->scratch) = value; return chunk->constants.count - 1; } @@ -110,9 +114,9 @@ static int is_integer(const char *str, long *out) { } __attribute__((format(printf, 2, 3))) static void -compile_error(GrowlLexer *lexer, const char *fmt, ...) { - fprintf(stderr, "%d:%d: compile error: ", lexer->start_row + 1, - lexer->start_col + 1); +compile_error(GrowlCompileContext *ctx, const char *fmt, ...) { + fprintf(stderr, "%s:%d:%d: compile error: ", ctx->file_path, + ctx->lexer->start_row + 1, ctx->lexer->start_col + 1); va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); @@ -140,85 +144,84 @@ static void optimize_tail_calls(Chunk *chunk) { } } -static int compile_token(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk); +static int compile_token(GrowlCompileContext *ctx, Chunk *chunk); -static int compile_quotation(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) { - growl_lexer_next(lexer); // skip '[' +static int compile_quotation(GrowlCompileContext *ctx, Chunk *chunk) { + growl_lexer_next(ctx->lexer); // skip '[' Chunk quot_chunk = {0}; - while (lexer->kind != ']' && lexer->kind != GTOK_EOF && - lexer->kind != GTOK_INVALID) { - if (compile_token(vm, lexer, "_chunk)) { + while (ctx->lexer->kind != ']' && ctx->lexer->kind != GTOK_EOF && + ctx->lexer->kind != GTOK_INVALID) { + if (compile_token(ctx, "_chunk)) { return 1; } } - if (lexer->kind != ']') { - compile_error(lexer, "expected ']' to close quotation"); + if (ctx->lexer->kind != ']') { + compile_error(ctx, "expected ']' to close quotation"); return 1; } - emit_byte(vm, "_chunk, GOP_RETURN); + emit_byte(ctx->vm, "_chunk, GOP_RETURN); optimize_tail_calls("_chunk); - Growl quot = growl_make_quotation(vm, quot_chunk.data, quot_chunk.count, + Growl quot = growl_make_quotation(ctx->vm, quot_chunk.data, quot_chunk.count, quot_chunk.constants.data, quot_chunk.constants.count); - size_t idx = add_constant(vm, chunk, quot); - emit_byte(vm, chunk, GOP_PUSH_CONSTANT); - emit_sleb128(vm, chunk, (intptr_t)idx); - growl_lexer_next(lexer); + size_t idx = add_constant(ctx->vm, chunk, quot); + emit_byte(ctx->vm, chunk, GOP_PUSH_CONSTANT); + emit_sleb128(ctx->vm, chunk, (intptr_t)idx); + growl_lexer_next(ctx->lexer); return 0; } -static int compile_string(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) { - Growl str = growl_wrap_string_tenured(vm, lexer->buffer); - size_t const_idx = add_constant(vm, chunk, str); - emit_byte(vm, chunk, GOP_PUSH_CONSTANT); - emit_sleb128(vm, chunk, (intptr_t)const_idx); - growl_lexer_next(lexer); +static int compile_string(GrowlCompileContext *ctx, Chunk *chunk) { + Growl str = growl_wrap_string_tenured(ctx->vm, ctx->lexer->buffer); + size_t const_idx = add_constant(ctx->vm, chunk, str); + emit_byte(ctx->vm, chunk, GOP_PUSH_CONSTANT); + emit_sleb128(ctx->vm, chunk, (intptr_t)const_idx); + growl_lexer_next(ctx->lexer); return 0; } -static int compile_def(GrowlVM *vm, GrowlLexer *lexer) { - growl_lexer_next(lexer); - if (lexer->kind != GTOK_WORD) { - compile_error(lexer, "expected name after 'def'"); +static int compile_def(GrowlCompileContext *ctx) { + growl_lexer_next(ctx->lexer); + if (ctx->lexer->kind != GTOK_WORD) { + compile_error(ctx, "expected name after 'def'"); return 1; } - char *name = growl_arena_strdup(&vm->scratch, lexer->buffer); - growl_lexer_next(lexer); - if (lexer->kind != GTOK_LBRACE) { - compile_error(lexer, "expected '{' after def name '%s'", name); + char *name = growl_arena_strdup(&ctx->vm->scratch, ctx->lexer->buffer); + growl_lexer_next(ctx->lexer); + if (ctx->lexer->kind != GTOK_LBRACE) { + compile_error(ctx, "expected '{' after def name '%s'", name); return 1; } - // Add a forward declaration to the dictionary so the word can reference itself GrowlDictionary *entry = - growl_dictionary_upsert(&vm->dictionary, name, &vm->arena); - GrowlDefinition *def = push(&vm->defs, &vm->arena); - def->name = growl_arena_strdup(&vm->arena, name); - def->callable = GROWL_NIL; // Placeholder, will be filled in after compilation + growl_dictionary_upsert(&ctx->vm->dictionary, name, &ctx->vm->arena); + GrowlDefinition *def = growl_dynarray_push(&ctx->vm->defs, &ctx->vm->arena); + def->name = growl_arena_strdup(&ctx->vm->arena, name); + def->callable = GROWL_NIL; entry->callable = GROWL_NIL; - entry->index = vm->defs.count - 1; + entry->index = ctx->vm->defs.count - 1; - growl_lexer_next(lexer); + growl_lexer_next(ctx->lexer); Chunk fn_chunk = {0}; - while (lexer->kind != GTOK_RBRACE && lexer->kind != GTOK_EOF && - lexer->kind != GTOK_INVALID) { - if (compile_token(vm, lexer, &fn_chunk)) { + while (ctx->lexer->kind != GTOK_RBRACE && ctx->lexer->kind != GTOK_EOF && + ctx->lexer->kind != GTOK_INVALID) { + if (compile_token(ctx, &fn_chunk)) { return 1; } } - if (lexer->kind != GTOK_RBRACE) { - compile_error(lexer, "expected '}' to close def '%s'", name); + if (ctx->lexer->kind != GTOK_RBRACE) { + compile_error(ctx, "expected '}' to close def '%s'", name); return 1; } - emit_byte(vm, &fn_chunk, GOP_RETURN); + emit_byte(ctx->vm, &fn_chunk, GOP_RETURN); optimize_tail_calls(&fn_chunk); Growl fn = - growl_make_quotation(vm, fn_chunk.data, fn_chunk.count, + growl_make_quotation(ctx->vm, fn_chunk.data, fn_chunk.count, fn_chunk.constants.data, fn_chunk.constants.count); #if COMPILER_DEBUG @@ -227,118 +230,206 @@ static int compile_def(GrowlVM *vm, GrowlLexer *lexer) { growl_disassemble(vm, quot); #endif - // Now update the definition with the compiled quotation def->callable = fn; entry->callable = fn; - growl_lexer_next(lexer); + growl_lexer_next(ctx->lexer); return 0; } -static int compile_call(GrowlVM *vm, GrowlLexer *lexer, const char *name, - Chunk *chunk) { +static int compile_call(GrowlCompileContext *ctx, Chunk *chunk, + const char *name) { for (size_t i = 0; primitives[i].name != NULL; i++) { if (strcmp(name, primitives[i].name) == 0) { for (size_t j = 0; primitives[i].opcodes[j] != 0; j++) - emit_byte(vm, chunk, primitives[i].opcodes[j]); - growl_lexer_next(lexer); + emit_byte(ctx->vm, chunk, primitives[i].opcodes[j]); + growl_lexer_next(ctx->lexer); return 0; } } - GrowlDictionary *entry = growl_dictionary_upsert(&vm->dictionary, name, NULL); + GrowlDictionary *entry = + growl_dictionary_upsert(&ctx->vm->dictionary, name, NULL); if (entry == NULL) { - compile_error(lexer, "undefined word '%s'", name); + compile_error(ctx, "undefined word '%s'", name); return 1; } - emit_byte(vm, chunk, GOP_WORD); - emit_sleb128(vm, chunk, entry->index); - growl_lexer_next(lexer); + emit_byte(ctx->vm, chunk, GOP_WORD); + emit_sleb128(ctx->vm, chunk, entry->index); + growl_lexer_next(ctx->lexer); return 0; } -static int compile_command(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) { - char *name = growl_arena_strdup(&vm->scratch, lexer->buffer); +static char *resolve_module_path(GrowlCompileContext *ctx, const char *path) { + char buf[PATH_MAX]; + + // relative to current file + if (ctx->file_dir && path[0] != '/') { + snprintf(buf, sizeof(buf), "%s/%s", ctx->file_dir, path); + char *resolved = realpath(buf, NULL); + if (resolved && access(resolved, R_OK) == 0) + return resolved; + free(resolved); + } + + // TODO: search paths + + // absolute path + if (path[0] == '/') { + char *resolved = realpath(path, NULL); + if (resolved && access(resolved, R_OK) == 0) + return resolved; + free(resolved); + } + + return NULL; +} + +static int compile_load(GrowlCompileContext *ctx) { + growl_lexer_next(ctx->lexer); + + if (ctx->lexer->kind != GTOK_STRING) { + compile_error(ctx, "expected string after 'load'"); + return 1; + } + + const char *path = ctx->lexer->buffer; + char *resolved = resolve_module_path(ctx, path); + if (!resolved) { + compile_error(ctx, "cannot find module '%s'", path); + return 1; + } + + FILE *file = fopen(resolved, "r"); + if (!file) { + compile_error(ctx, "cannot open '%s'", resolved); + free(resolved); + return 1; + } + + GrowlLexer mod_lexer = {0}; + mod_lexer.file = file; + + char *dir = dirname(strdup(resolved)); + + // I'd like to understand why clang-format does 4 spaces for aggregate + // initialization. + GrowlCompileContext mod_ctx = { + .file_path = resolved, + .file_dir = dir, + .parent = ctx, + .lexer = &mod_lexer, + .vm = ctx->vm, + }; + + int result = 0; + Growl obj = growl_compile_with_context(&mod_ctx); + if (obj == GROWL_NIL) { + return 1; + } + + GrowlQuotation *q = growl_unwrap_quotation(obj); + if (growl_vm_execute(ctx->vm, q) != 0) + result = 1; + + fclose(file); + free(resolved); + free(dir); + growl_lexer_next(ctx->lexer); + + return result; +} + +static int compile_command(GrowlCompileContext *ctx, Chunk *chunk) { + char *name = growl_arena_strdup(&ctx->vm->scratch, ctx->lexer->buffer); name[strlen(name) - 1] = '\0'; - growl_lexer_next(lexer); - while (lexer->kind != GTOK_SEMICOLON && lexer->kind != GTOK_EOF && - lexer->kind != GTOK_INVALID) { - if (compile_token(vm, lexer, chunk)) { + growl_lexer_next(ctx->lexer); + while (ctx->lexer->kind != GTOK_SEMICOLON && ctx->lexer->kind != GTOK_EOF && + ctx->lexer->kind != GTOK_INVALID) { + if (compile_token(ctx, chunk)) { return 1; } } - if (lexer->kind != GTOK_SEMICOLON) { - compile_error(lexer, "expected ';' to close command '%s:'", name); + if (ctx->lexer->kind != GTOK_SEMICOLON) { + compile_error(ctx, "expected ';' to close command '%s:'", name); return 1; } - return compile_call(vm, lexer, name, chunk); + return compile_call(ctx, chunk, name); } -static int compile_word(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) { - char *text = lexer->buffer; - size_t len = strlen(text); +static int compile_word(GrowlCompileContext *ctx, Chunk *chunk) { + char *name = ctx->lexer->buffer; + size_t len = strlen(name); - if (strcmp(text, "load") == 0) { - // TODO: loading source files - compile_error(lexer, "'load' nyi"); - return 1; + if (strcmp(name, "load") == 0) { + return compile_load(ctx); } // Compile a definition - if (strcmp(text, "def") == 0) { - return compile_def(vm, lexer); + if (strcmp(name, "def") == 0) { + return compile_def(ctx); } // Compile a command: word: args... ; - if (len > 1 && text[len - 1] == ':') { - return compile_command(vm, lexer, chunk); + if (len > 1 && name[len - 1] == ':') { + return compile_command(ctx, chunk); } // Compile an integer value long value; - if (is_integer(text, &value)) { - size_t idx = add_constant(vm, chunk, GROWL_NUM(value)); - emit_byte(vm, chunk, GOP_PUSH_CONSTANT); - emit_sleb128(vm, chunk, (intptr_t)idx); - growl_lexer_next(lexer); + if (is_integer(name, &value)) { + size_t idx = add_constant(ctx->vm, chunk, GROWL_NUM(value)); + emit_byte(ctx->vm, chunk, GOP_PUSH_CONSTANT); + emit_sleb128(ctx->vm, chunk, (intptr_t)idx); + growl_lexer_next(ctx->lexer); return 0; } - return compile_call(vm, lexer, text, chunk); + return compile_call(ctx, chunk, name); } -static int compile_token(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) { - switch (lexer->kind) { +static int compile_token(GrowlCompileContext *ctx, Chunk *chunk) { + switch (ctx->lexer->kind) { case GTOK_WORD: - return compile_word(vm, lexer, chunk); + return compile_word(ctx, chunk); case GTOK_STRING: - return compile_string(vm, lexer, chunk); + return compile_string(ctx, chunk); case GTOK_LBRACKET: - return compile_quotation(vm, lexer, chunk); + return compile_quotation(ctx, chunk); case GTOK_SEMICOLON: case GTOK_RPAREN: case GTOK_RBRACKET: case GTOK_RBRACE: - compile_error(lexer, "unexpected token '%c'", lexer->kind); + compile_error(ctx, "unexpected token '%c'", ctx->lexer->kind); return 1; case GTOK_INVALID: - compile_error(lexer, "invalid token"); + compile_error(ctx, "invalid token"); return 1; default: - compile_error(lexer, "unhandled token type '%c'", lexer->kind); + compile_error(ctx, "unhandled token type '%c'", ctx->lexer->kind); return 1; } } -Growl growl_compile(GrowlVM *vm, GrowlLexer *lexer) { +Growl growl_compile_with_context(GrowlCompileContext *ctx) { Chunk chunk = {0}; - growl_lexer_next(lexer); - while (lexer->kind != GTOK_EOF) { - if (compile_token(vm, lexer, &chunk)) + growl_lexer_next(ctx->lexer); + while (ctx->lexer->kind != GTOK_EOF) { + if (compile_token(ctx, &chunk)) return GROWL_NIL; } - emit_byte(vm, &chunk, GOP_RETURN); + emit_byte(ctx->vm, &chunk, GOP_RETURN); optimize_tail_calls(&chunk); - return growl_make_quotation(vm, chunk.data, chunk.count, chunk.constants.data, - chunk.constants.count); + return growl_make_quotation(ctx->vm, chunk.data, chunk.count, + chunk.constants.data, chunk.constants.count); +} + +Growl growl_compile(GrowlVM *vm, GrowlLexer *lexer, const char *path, + const char *dirname) { + GrowlCompileContext ctx = {0}; + ctx.vm = vm; + ctx.lexer = lexer; + ctx.file_path = path; + ctx.file_dir = dirname; + return growl_compile_with_context(&ctx); } diff --git a/src/core/dynarray.c b/src/core/dynarray.c new file mode 100644 index 0000000..31412c7 --- /dev/null +++ b/src/core/dynarray.c @@ -0,0 +1,24 @@ +#include "dynarray.h" +#include + +void growl_dynarray_grow(void *slice, ptrdiff_t size, ptrdiff_t align, + GrowlArena *a) { + struct { + uint8_t *data; + ptrdiff_t len; + ptrdiff_t cap; + } replica; + memcpy(&replica, slice, sizeof(replica)); + if (!replica.data) { + replica.cap = 1; + replica.data = growl_arena_alloc(a, 2 * size, align, replica.cap); + } else if (a->free == replica.data + size * replica.cap) { + growl_arena_alloc(a, size, 1, replica.cap); + } else { + void *data = growl_arena_alloc(a, 2 * size, align, replica.cap); + memcpy(data, replica.data, size * replica.len); + replica.data = data; + } + replica.cap *= 2; + memcpy(slice, &replica, sizeof(replica)); +} diff --git a/src/core/dynarray.h b/src/core/dynarray.h index 1a2a463..82b9cd2 100644 --- a/src/core/dynarray.h +++ b/src/core/dynarray.h @@ -5,38 +5,18 @@ #include #include -#include -#include -#define push(s, a) \ +#define growl_dynarray_push(s, a) \ ({ \ typeof(s) s_ = (s); \ typeof(a) a_ = (a); \ if (s_->count >= s_->capacity) { \ - __grow(s_, sizeof(*s_->data), _Alignof(*s_->data), a_); \ + growl_dynarray_grow(s_, sizeof(*s_->data), _Alignof(*s_->data), a_); \ } \ s_->data + s_->count++; \ }) -static void __grow(void *slice, ptrdiff_t size, ptrdiff_t align, GrowlArena *a) { - struct { - uint8_t *data; - ptrdiff_t len; - ptrdiff_t cap; - } replica; - memcpy(&replica, slice, sizeof(replica)); - if (!replica.data) { - replica.cap = 1; - replica.data = growl_arena_alloc(a, 2 * size, align, replica.cap); - } else if (a->free == replica.data + size * replica.cap) { - growl_arena_alloc(a, size, 1, replica.cap); - } else { - void *data = growl_arena_alloc(a, 2 * size, align, replica.cap); - memcpy(data, replica.data, size * replica.len); - replica.data = data; - } - replica.cap *= 2; - memcpy(slice, &replica, sizeof(replica)); -} +void growl_dynarray_grow(void *slice, ptrdiff_t size, ptrdiff_t align, + GrowlArena *a); #endif // GROWL_DYNARRAY_H diff --git a/src/core/gc.c b/src/core/gc.c index 87056e9..cca8bed 100644 --- a/src/core/gc.c +++ b/src/core/gc.c @@ -9,7 +9,7 @@ #include #include -#define GC_DEBUG 0 +#define GC_DEBUG 1 #define ALIGN(n) (((n) + 7) & ~7) static int in_from(GrowlVM *vm, void *ptr) { @@ -139,14 +139,15 @@ void growl_gc_collect(GrowlVM *vm) { gc_print_stats(vm, "before GC"); #endif - // Forward work stack - for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) { - vm->wst[i] = forward(vm, vm->wst[i]); + // Forward stacks + for (Growl *obj = vm->wst; obj < vm->sp; ++obj) { + *obj = forward(vm, *obj); } - - // Forward retain stack - for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) { - vm->rst[i] = forward(vm, vm->rst[i]); + for (Growl *obj = vm->rst; obj < vm->rsp; ++obj) { + *obj = forward(vm, *obj); + } + for (GrowlFrame *frame = vm->cst; frame < vm->csp; ++frame) { + frame->next = forward(vm, frame->next); } // Forward GC roots @@ -154,10 +155,10 @@ void growl_gc_collect(GrowlVM *vm) { *vm->roots[i] = forward(vm, *vm->roots[i]); } - // Forward word definitions - for (size_t i = 0; i < vm->defs.count; ++i) { - vm->defs.data[i].callable = forward(vm, vm->defs.data[i].callable); - } + // // Forward word definitions + // for (size_t i = 0; i < vm->defs.count; ++i) { + // vm->defs.data[i].callable = forward(vm, vm->defs.data[i].callable); + // } uint8_t *tenured_scan = vm->tenured.start; while (tenured_scan < vm->tenured.free) { diff --git a/src/core/native.c b/src/core/native.c index 3541f50..43598b9 100644 --- a/src/core/native.c +++ b/src/core/native.c @@ -21,7 +21,7 @@ void growl_register_native(GrowlVM *vm, const char *name, Growl alien = growl_make_alien_tenured(vm, &native_type, (void *)fn); GrowlDictionary *entry = growl_dictionary_upsert(&vm->dictionary, name, &vm->arena); - GrowlDefinition *def = push(&vm->defs, &vm->arena); + GrowlDefinition *def = growl_dynarray_push(&vm->defs, &vm->arena); def->name = growl_arena_strdup(&vm->arena, name); def->callable = alien; entry->callable = alien; diff --git a/src/core/vm.c b/src/core/vm.c index 88d5262..3596fb3 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -117,8 +117,7 @@ static GrowlFrame callstack_pop(GrowlVM *vm) { return *--vm->csp; } -static inline void dispatch(GrowlVM *vm, Growl obj, - int tail __attribute__((unused))) { +static inline void dispatch(GrowlVM *vm, Growl obj) { for (;;) { switch (growl_type(obj)) { case GROWL_TYPE_QUOTATION: { @@ -144,24 +143,21 @@ static inline void dispatch(GrowlVM *vm, Growl obj, GrowlAlien *alien = (GrowlAlien *)(GROWL_UNBOX(obj) + 1); if (alien->type && alien->type->call) { alien->type->call(vm, alien->data); - // After calling a native function, we need to return to the caller if (vm->csp != vm->cst) { GrowlFrame frame = callstack_pop(vm); vm->current_quotation = frame.quot; vm->ip = frame.ip; vm->next = frame.next; } else { - // No frames on call stack, use return trampoline to exit vm->current_quotation = vm->return_trampoline; vm->ip = vm->return_trampoline->data; } return; } - growl_vm_error(vm, "attempt to call non-callable alien"); + __attribute__((fallthrough)); } default: - growl_vm_error(vm, "attempt to call non-callable (type=%d)", - growl_type(obj)); + growl_vm_error(vm, "attempt to call non-callable"); } } } @@ -203,7 +199,7 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) { growl_vm_error(vm, "constant index %" PRIdPTR " out of bounds", idx); } } else { - growl_vm_error(vm, "attempt to index nil constant table"); + growl_vm_error(vm, "attempt to index invalid constant table"); } VM_NEXT(); } @@ -298,18 +294,18 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) { VM_OP(CALL) { Growl obj = growl_pop(vm); callstack_push(vm, vm->current_quotation, vm->ip); - dispatch(vm, obj, 0); + dispatch(vm, obj); VM_NEXT(); } VM_OP(CALL_NEXT) { Growl callable = vm->next; vm->next = GROWL_NIL; - dispatch(vm, callable, 1); + dispatch(vm, callable); VM_NEXT(); } VM_OP(TAIL_CALL) { Growl obj = growl_pop(vm); - dispatch(vm, obj, 1); + dispatch(vm, obj); VM_NEXT(); } VM_OP(WORD) { @@ -317,14 +313,14 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) { GrowlDefinition *def = &vm->defs.data[idx]; Growl word = def->callable; callstack_push(vm, vm->current_quotation, vm->ip); - dispatch(vm, word, 0); + dispatch(vm, word); VM_NEXT(); } VM_OP(TAIL_WORD) { intptr_t idx = growl_sleb128_decode(&vm->ip); GrowlDefinition *def = &vm->defs.data[idx]; Growl word = def->callable; - dispatch(vm, word, 1); + dispatch(vm, word); VM_NEXT(); } VM_OP(RETURN) { @@ -362,7 +358,7 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) { callstack_push(vm, vm->current_quotation, vm->ip); callstack_push(vm, vm->dip_trampoline, vm->dip_trampoline->data); vm->csp[-1].next = x; - dispatch(vm, callable, 0); + dispatch(vm, callable); VM_NEXT(); } VM_OP(PPRINT) { @@ -417,7 +413,7 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) { if (GROWL_IMM(a)) { growl_push(vm, GROWL_NUM(~GROWL_ORD(a))); } else { - growl_vm_error(vm, "arithmetic on non-numbers"); + growl_vm_error(vm, "numeric op on non-numbers"); } VM_NEXT(); } diff --git a/src/include/growl.h b/src/include/growl.h index bb6d2cc..e24b04b 100644 --- a/src/include/growl.h +++ b/src/include/growl.h @@ -175,7 +175,6 @@ char *growl_arena_strdup(GrowlArena *ar, const char *str); #define GROWL_STACK_SIZE 128 #define GROWL_CALL_STACK_SIZE 64 #define GROWL_HEAP_SIZE (4 * 1024 * 1024) -#define GROWL_ARENA_SIZE (2 * 1024 * 1024) #define GROWL_SCRATCH_SIZE (1024 * 1024) struct GrowlFrame { @@ -208,7 +207,11 @@ struct GrowlModule { struct GrowlCompileContext { GrowlCompileContext *parent; - const char *file_path, *file_dir; + GrowlVM *vm; + GrowlLexer *lexer; + const char *name; + const char *file_path; + const char *file_dir; }; GrowlDictionary *growl_dictionary_upsert(GrowlDictionary **dict, @@ -257,7 +260,9 @@ noreturn void growl_vm_error(GrowlVM *vm, const char *fmt, ...); int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot); /** Compiler */ -Growl growl_compile(GrowlVM *vm, GrowlLexer *lexer); +Growl growl_compile_with_context(GrowlCompileContext *ctx); +Growl growl_compile(GrowlVM *vm, GrowlLexer *lexer, const char *path, + const char *dirname); void growl_disassemble(GrowlVM *vm, GrowlQuotation *quot); /** Extra libraries */ diff --git a/src/main.c b/src/main.c index 4976e06..38049bd 100644 --- a/src/main.c +++ b/src/main.c @@ -1,13 +1,25 @@ #include -#include +#include +#include +#include + +int main(int argc, const char *argv[]) { + if (argc == 1) { + fprintf(stderr, "usage: %s file.grr\n", argv[0]); + exit(1); + } -int main(void) { GrowlVM *vm = growl_vm_init(); growl_register_file_library(vm); - GrowlLexer lexer = {0}; - lexer.file = stdin; - Growl obj = growl_compile(vm, &lexer); + GrowlLexer lexer = {0}; + const char *filename = argv[1]; + char *dirname_ = strdup(filename); + dirname_ = dirname(dirname_); + + lexer.file = fopen(argv[1], "r"); + + Growl obj = growl_compile(vm, &lexer, filename, dirname_); if (obj != GROWL_NIL) { GrowlQuotation *quot = growl_unwrap_quotation(obj); if (!growl_vm_execute(vm, quot)) { @@ -24,5 +36,6 @@ int main(void) { growl_gc_collect(vm); growl_vm_free(vm); + fclose(lexer.file); return 0; } diff --git a/std.grr b/std.grr index d89b60d..7e08fdd 100644 --- a/std.grr +++ b/std.grr @@ -1,19 +1,13 @@ -def print { stdout fprint } -def println { stdout fprint "\n" stdout fprint } -def nl { "\n" stdout fprint } - -def eprint { stderr fprint } -def eprintln { stderr fprint "\n" stderr fprint } +def print { file/stdout file/write } +def println { print "\n" print } def when { [] if } def unless { swap when } def 2dip { swap [dip] dip } -def 3dip { swap [2dip] dip } def keep { over [call] dip } def 2keep { [2dup] dip 2dip } -def 3keep { [dup 2over dig] dip 3dip } def bi { [keep] dip call } def tri { [[keep] dip keep] dip call }