This commit is contained in:
Lobo 2026-02-09 14:44:00 -03:00
parent e18681b309
commit 20da6dd16d
14 changed files with 279 additions and 183 deletions

View file

@ -1,4 +0,0 @@
#load("std.grr")
while: [stdin fgetline]
[print];

View file

@ -1,4 +1,4 @@
#load("std.grr") load "../std.grr"
def fib { def fib {
0 1 dig [dup [+] dip swap] times drop 0 1 dig [dup [+] dip swap] times drop

View file

@ -1,13 +1,4 @@
def print { file/stdout file/write } load "../std.grr"
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];
}
def fizzbuzz? { [3 % 0 =] [5 % 0 =] bi or } def fizzbuzz? { [3 % 0 =] [5 % 0 =] bi or }
def fizz { when: 3 % 0 = ["Fizz" print]; } def fizz { when: 3 % 0 = ["Fizz" print]; }

View file

@ -17,6 +17,7 @@ growl_sources = [
'src/core/compiler.c', 'src/core/compiler.c',
'src/core/dictionary.c', 'src/core/dictionary.c',
'src/core/disasm.c', 'src/core/disasm.c',
'src/core/dynarray.c',
'src/core/file.c', 'src/core/file.c',
'src/core/gc.c', 'src/core/gc.c',
'src/core/hash.c', 'src/core/hash.c',

View file

@ -10,6 +10,10 @@ int growl_callable(Growl obj) {
case GROWL_TYPE_COMPOSE: case GROWL_TYPE_COMPOSE:
case GROWL_TYPE_CURRY: case GROWL_TYPE_CURRY:
return 1; return 1;
case GROWL_TYPE_ALIEN: {
GrowlAlien *alien = (GrowlAlien *)(hdr + 1);
return alien->type && alien->type->call != NULL;
}
default: default:
return 0; return 0;
} }

View file

@ -4,9 +4,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "dynarray.h"
#include "opcodes.h" #include "opcodes.h"
#include "sleb128.h" #include "sleb128.h"
#include "dynarray.h"
#include <libgen.h>
#include <limits.h>
#include <unistd.h>
#define COMPILER_DEBUG 0 #define COMPILER_DEBUG 0
@ -73,7 +77,7 @@ Primitive primitives[] = {
// clang-format on // clang-format on
static void emit_byte(GrowlVM *vm, Chunk *chunk, uint8_t byte) { 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) { 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) if (chunk->constants.data[i] == value)
return i; return i;
} }
*push(&chunk->constants, &vm->scratch) = value; *growl_dynarray_push(&chunk->constants, &vm->scratch) = value;
return chunk->constants.count - 1; 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 __attribute__((format(printf, 2, 3))) static void
compile_error(GrowlLexer *lexer, const char *fmt, ...) { compile_error(GrowlCompileContext *ctx, const char *fmt, ...) {
fprintf(stderr, "%d:%d: compile error: ", lexer->start_row + 1, fprintf(stderr, "%s:%d:%d: compile error: ", ctx->file_path,
lexer->start_col + 1); ctx->lexer->start_row + 1, ctx->lexer->start_col + 1);
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vfprintf(stderr, fmt, args); 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) { static int compile_quotation(GrowlCompileContext *ctx, Chunk *chunk) {
growl_lexer_next(lexer); // skip '[' growl_lexer_next(ctx->lexer); // skip '['
Chunk quot_chunk = {0}; Chunk quot_chunk = {0};
while (lexer->kind != ']' && lexer->kind != GTOK_EOF && while (ctx->lexer->kind != ']' && ctx->lexer->kind != GTOK_EOF &&
lexer->kind != GTOK_INVALID) { ctx->lexer->kind != GTOK_INVALID) {
if (compile_token(vm, lexer, &quot_chunk)) { if (compile_token(ctx, &quot_chunk)) {
return 1; return 1;
} }
} }
if (lexer->kind != ']') { if (ctx->lexer->kind != ']') {
compile_error(lexer, "expected ']' to close quotation"); compile_error(ctx, "expected ']' to close quotation");
return 1; return 1;
} }
emit_byte(vm, &quot_chunk, GOP_RETURN); emit_byte(ctx->vm, &quot_chunk, GOP_RETURN);
optimize_tail_calls(&quot_chunk); optimize_tail_calls(&quot_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.data,
quot_chunk.constants.count); quot_chunk.constants.count);
size_t idx = add_constant(vm, chunk, quot); size_t idx = add_constant(ctx->vm, chunk, quot);
emit_byte(vm, chunk, GOP_PUSH_CONSTANT); emit_byte(ctx->vm, chunk, GOP_PUSH_CONSTANT);
emit_sleb128(vm, chunk, (intptr_t)idx); emit_sleb128(ctx->vm, chunk, (intptr_t)idx);
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
return 0; return 0;
} }
static int compile_string(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) { static int compile_string(GrowlCompileContext *ctx, Chunk *chunk) {
Growl str = growl_wrap_string_tenured(vm, lexer->buffer); Growl str = growl_wrap_string_tenured(ctx->vm, ctx->lexer->buffer);
size_t const_idx = add_constant(vm, chunk, str); size_t const_idx = add_constant(ctx->vm, chunk, str);
emit_byte(vm, chunk, GOP_PUSH_CONSTANT); emit_byte(ctx->vm, chunk, GOP_PUSH_CONSTANT);
emit_sleb128(vm, chunk, (intptr_t)const_idx); emit_sleb128(ctx->vm, chunk, (intptr_t)const_idx);
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
return 0; return 0;
} }
static int compile_def(GrowlVM *vm, GrowlLexer *lexer) { static int compile_def(GrowlCompileContext *ctx) {
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
if (lexer->kind != GTOK_WORD) { if (ctx->lexer->kind != GTOK_WORD) {
compile_error(lexer, "expected name after 'def'"); compile_error(ctx, "expected name after 'def'");
return 1; return 1;
} }
char *name = growl_arena_strdup(&vm->scratch, lexer->buffer); char *name = growl_arena_strdup(&ctx->vm->scratch, ctx->lexer->buffer);
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
if (lexer->kind != GTOK_LBRACE) { if (ctx->lexer->kind != GTOK_LBRACE) {
compile_error(lexer, "expected '{' after def name '%s'", name); compile_error(ctx, "expected '{' after def name '%s'", name);
return 1; return 1;
} }
// Add a forward declaration to the dictionary so the word can reference itself
GrowlDictionary *entry = GrowlDictionary *entry =
growl_dictionary_upsert(&vm->dictionary, name, &vm->arena); growl_dictionary_upsert(&ctx->vm->dictionary, name, &ctx->vm->arena);
GrowlDefinition *def = push(&vm->defs, &vm->arena); GrowlDefinition *def = growl_dynarray_push(&ctx->vm->defs, &ctx->vm->arena);
def->name = growl_arena_strdup(&vm->arena, name); def->name = growl_arena_strdup(&ctx->vm->arena, name);
def->callable = GROWL_NIL; // Placeholder, will be filled in after compilation def->callable = GROWL_NIL;
entry->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}; Chunk fn_chunk = {0};
while (lexer->kind != GTOK_RBRACE && lexer->kind != GTOK_EOF && while (ctx->lexer->kind != GTOK_RBRACE && ctx->lexer->kind != GTOK_EOF &&
lexer->kind != GTOK_INVALID) { ctx->lexer->kind != GTOK_INVALID) {
if (compile_token(vm, lexer, &fn_chunk)) { if (compile_token(ctx, &fn_chunk)) {
return 1; return 1;
} }
} }
if (lexer->kind != GTOK_RBRACE) { if (ctx->lexer->kind != GTOK_RBRACE) {
compile_error(lexer, "expected '}' to close def '%s'", name); compile_error(ctx, "expected '}' to close def '%s'", name);
return 1; return 1;
} }
emit_byte(vm, &fn_chunk, GOP_RETURN); emit_byte(ctx->vm, &fn_chunk, GOP_RETURN);
optimize_tail_calls(&fn_chunk); optimize_tail_calls(&fn_chunk);
Growl fn = 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); fn_chunk.constants.data, fn_chunk.constants.count);
#if COMPILER_DEBUG #if COMPILER_DEBUG
@ -227,118 +230,206 @@ static int compile_def(GrowlVM *vm, GrowlLexer *lexer) {
growl_disassemble(vm, quot); growl_disassemble(vm, quot);
#endif #endif
// Now update the definition with the compiled quotation
def->callable = fn; def->callable = fn;
entry->callable = fn; entry->callable = fn;
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
return 0; return 0;
} }
static int compile_call(GrowlVM *vm, GrowlLexer *lexer, const char *name, static int compile_call(GrowlCompileContext *ctx, Chunk *chunk,
Chunk *chunk) { const char *name) {
for (size_t i = 0; primitives[i].name != NULL; i++) { for (size_t i = 0; primitives[i].name != NULL; i++) {
if (strcmp(name, primitives[i].name) == 0) { if (strcmp(name, primitives[i].name) == 0) {
for (size_t j = 0; primitives[i].opcodes[j] != 0; j++) for (size_t j = 0; primitives[i].opcodes[j] != 0; j++)
emit_byte(vm, chunk, primitives[i].opcodes[j]); emit_byte(ctx->vm, chunk, primitives[i].opcodes[j]);
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
return 0; return 0;
} }
} }
GrowlDictionary *entry = growl_dictionary_upsert(&vm->dictionary, name, NULL); GrowlDictionary *entry =
growl_dictionary_upsert(&ctx->vm->dictionary, name, NULL);
if (entry == NULL) { if (entry == NULL) {
compile_error(lexer, "undefined word '%s'", name); compile_error(ctx, "undefined word '%s'", name);
return 1; return 1;
} }
emit_byte(vm, chunk, GOP_WORD); emit_byte(ctx->vm, chunk, GOP_WORD);
emit_sleb128(vm, chunk, entry->index); emit_sleb128(ctx->vm, chunk, entry->index);
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
return 0; return 0;
} }
static int compile_command(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) { static char *resolve_module_path(GrowlCompileContext *ctx, const char *path) {
char *name = growl_arena_strdup(&vm->scratch, lexer->buffer); 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'; name[strlen(name) - 1] = '\0';
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
while (lexer->kind != GTOK_SEMICOLON && lexer->kind != GTOK_EOF && while (ctx->lexer->kind != GTOK_SEMICOLON && ctx->lexer->kind != GTOK_EOF &&
lexer->kind != GTOK_INVALID) { ctx->lexer->kind != GTOK_INVALID) {
if (compile_token(vm, lexer, chunk)) { if (compile_token(ctx, chunk)) {
return 1; return 1;
} }
} }
if (lexer->kind != GTOK_SEMICOLON) { if (ctx->lexer->kind != GTOK_SEMICOLON) {
compile_error(lexer, "expected ';' to close command '%s:'", name); compile_error(ctx, "expected ';' to close command '%s:'", name);
return 1; 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) { static int compile_word(GrowlCompileContext *ctx, Chunk *chunk) {
char *text = lexer->buffer; char *name = ctx->lexer->buffer;
size_t len = strlen(text); size_t len = strlen(name);
if (strcmp(text, "load") == 0) { if (strcmp(name, "load") == 0) {
// TODO: loading source files return compile_load(ctx);
compile_error(lexer, "'load' nyi");
return 1;
} }
// Compile a definition // Compile a definition
if (strcmp(text, "def") == 0) { if (strcmp(name, "def") == 0) {
return compile_def(vm, lexer); return compile_def(ctx);
} }
// Compile a command: word: args... ; // Compile a command: word: args... ;
if (len > 1 && text[len - 1] == ':') { if (len > 1 && name[len - 1] == ':') {
return compile_command(vm, lexer, chunk); return compile_command(ctx, chunk);
} }
// Compile an integer value // Compile an integer value
long value; long value;
if (is_integer(text, &value)) { if (is_integer(name, &value)) {
size_t idx = add_constant(vm, chunk, GROWL_NUM(value)); size_t idx = add_constant(ctx->vm, chunk, GROWL_NUM(value));
emit_byte(vm, chunk, GOP_PUSH_CONSTANT); emit_byte(ctx->vm, chunk, GOP_PUSH_CONSTANT);
emit_sleb128(vm, chunk, (intptr_t)idx); emit_sleb128(ctx->vm, chunk, (intptr_t)idx);
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
return 0; 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) { static int compile_token(GrowlCompileContext *ctx, Chunk *chunk) {
switch (lexer->kind) { switch (ctx->lexer->kind) {
case GTOK_WORD: case GTOK_WORD:
return compile_word(vm, lexer, chunk); return compile_word(ctx, chunk);
case GTOK_STRING: case GTOK_STRING:
return compile_string(vm, lexer, chunk); return compile_string(ctx, chunk);
case GTOK_LBRACKET: case GTOK_LBRACKET:
return compile_quotation(vm, lexer, chunk); return compile_quotation(ctx, chunk);
case GTOK_SEMICOLON: case GTOK_SEMICOLON:
case GTOK_RPAREN: case GTOK_RPAREN:
case GTOK_RBRACKET: case GTOK_RBRACKET:
case GTOK_RBRACE: case GTOK_RBRACE:
compile_error(lexer, "unexpected token '%c'", lexer->kind); compile_error(ctx, "unexpected token '%c'", ctx->lexer->kind);
return 1; return 1;
case GTOK_INVALID: case GTOK_INVALID:
compile_error(lexer, "invalid token"); compile_error(ctx, "invalid token");
return 1; return 1;
default: default:
compile_error(lexer, "unhandled token type '%c'", lexer->kind); compile_error(ctx, "unhandled token type '%c'", ctx->lexer->kind);
return 1; return 1;
} }
} }
Growl growl_compile(GrowlVM *vm, GrowlLexer *lexer) { Growl growl_compile_with_context(GrowlCompileContext *ctx) {
Chunk chunk = {0}; Chunk chunk = {0};
growl_lexer_next(lexer); growl_lexer_next(ctx->lexer);
while (lexer->kind != GTOK_EOF) { while (ctx->lexer->kind != GTOK_EOF) {
if (compile_token(vm, lexer, &chunk)) if (compile_token(ctx, &chunk))
return GROWL_NIL; return GROWL_NIL;
} }
emit_byte(vm, &chunk, GOP_RETURN); emit_byte(ctx->vm, &chunk, GOP_RETURN);
optimize_tail_calls(&chunk); optimize_tail_calls(&chunk);
return growl_make_quotation(vm, chunk.data, chunk.count, chunk.constants.data, return growl_make_quotation(ctx->vm, chunk.data, chunk.count,
chunk.constants.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);
} }

24
src/core/dynarray.c Normal file
View file

@ -0,0 +1,24 @@
#include "dynarray.h"
#include <string.h>
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));
}

View file

@ -5,38 +5,18 @@
#include <growl.h> #include <growl.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <string.h>
#define push(s, a) \ #define growl_dynarray_push(s, a) \
({ \ ({ \
typeof(s) s_ = (s); \ typeof(s) s_ = (s); \
typeof(a) a_ = (a); \ typeof(a) a_ = (a); \
if (s_->count >= s_->capacity) { \ 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++; \ s_->data + s_->count++; \
}) })
static void __grow(void *slice, ptrdiff_t size, ptrdiff_t align, GrowlArena *a) { void growl_dynarray_grow(void *slice, ptrdiff_t size, ptrdiff_t align,
struct { GrowlArena *a);
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));
}
#endif // GROWL_DYNARRAY_H #endif // GROWL_DYNARRAY_H

View file

@ -9,7 +9,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define GC_DEBUG 0 #define GC_DEBUG 1
#define ALIGN(n) (((n) + 7) & ~7) #define ALIGN(n) (((n) + 7) & ~7)
static int in_from(GrowlVM *vm, void *ptr) { static int in_from(GrowlVM *vm, void *ptr) {
@ -139,14 +139,15 @@ void growl_gc_collect(GrowlVM *vm) {
gc_print_stats(vm, "before GC"); gc_print_stats(vm, "before GC");
#endif #endif
// Forward work stack // Forward stacks
for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) { for (Growl *obj = vm->wst; obj < vm->sp; ++obj) {
vm->wst[i] = forward(vm, vm->wst[i]); *obj = forward(vm, *obj);
} }
for (Growl *obj = vm->rst; obj < vm->rsp; ++obj) {
// Forward retain stack *obj = forward(vm, *obj);
for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) { }
vm->rst[i] = forward(vm, vm->rst[i]); for (GrowlFrame *frame = vm->cst; frame < vm->csp; ++frame) {
frame->next = forward(vm, frame->next);
} }
// Forward GC roots // Forward GC roots
@ -154,10 +155,10 @@ void growl_gc_collect(GrowlVM *vm) {
*vm->roots[i] = forward(vm, *vm->roots[i]); *vm->roots[i] = forward(vm, *vm->roots[i]);
} }
// Forward word definitions // // Forward word definitions
for (size_t i = 0; i < vm->defs.count; ++i) { // for (size_t i = 0; i < vm->defs.count; ++i) {
vm->defs.data[i].callable = forward(vm, vm->defs.data[i].callable); // vm->defs.data[i].callable = forward(vm, vm->defs.data[i].callable);
} // }
uint8_t *tenured_scan = vm->tenured.start; uint8_t *tenured_scan = vm->tenured.start;
while (tenured_scan < vm->tenured.free) { while (tenured_scan < vm->tenured.free) {

View file

@ -21,7 +21,7 @@ void growl_register_native(GrowlVM *vm, const char *name,
Growl alien = growl_make_alien_tenured(vm, &native_type, (void *)fn); Growl alien = growl_make_alien_tenured(vm, &native_type, (void *)fn);
GrowlDictionary *entry = GrowlDictionary *entry =
growl_dictionary_upsert(&vm->dictionary, name, &vm->arena); 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->name = growl_arena_strdup(&vm->arena, name);
def->callable = alien; def->callable = alien;
entry->callable = alien; entry->callable = alien;

View file

@ -117,8 +117,7 @@ static GrowlFrame callstack_pop(GrowlVM *vm) {
return *--vm->csp; return *--vm->csp;
} }
static inline void dispatch(GrowlVM *vm, Growl obj, static inline void dispatch(GrowlVM *vm, Growl obj) {
int tail __attribute__((unused))) {
for (;;) { for (;;) {
switch (growl_type(obj)) { switch (growl_type(obj)) {
case GROWL_TYPE_QUOTATION: { case GROWL_TYPE_QUOTATION: {
@ -144,24 +143,21 @@ static inline void dispatch(GrowlVM *vm, Growl obj,
GrowlAlien *alien = (GrowlAlien *)(GROWL_UNBOX(obj) + 1); GrowlAlien *alien = (GrowlAlien *)(GROWL_UNBOX(obj) + 1);
if (alien->type && alien->type->call) { if (alien->type && alien->type->call) {
alien->type->call(vm, alien->data); alien->type->call(vm, alien->data);
// After calling a native function, we need to return to the caller
if (vm->csp != vm->cst) { if (vm->csp != vm->cst) {
GrowlFrame frame = callstack_pop(vm); GrowlFrame frame = callstack_pop(vm);
vm->current_quotation = frame.quot; vm->current_quotation = frame.quot;
vm->ip = frame.ip; vm->ip = frame.ip;
vm->next = frame.next; vm->next = frame.next;
} else { } else {
// No frames on call stack, use return trampoline to exit
vm->current_quotation = vm->return_trampoline; vm->current_quotation = vm->return_trampoline;
vm->ip = vm->return_trampoline->data; vm->ip = vm->return_trampoline->data;
} }
return; return;
} }
growl_vm_error(vm, "attempt to call non-callable alien"); __attribute__((fallthrough));
} }
default: default:
growl_vm_error(vm, "attempt to call non-callable (type=%d)", growl_vm_error(vm, "attempt to call non-callable");
growl_type(obj));
} }
} }
} }
@ -203,7 +199,7 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
growl_vm_error(vm, "constant index %" PRIdPTR " out of bounds", idx); growl_vm_error(vm, "constant index %" PRIdPTR " out of bounds", idx);
} }
} else { } else {
growl_vm_error(vm, "attempt to index nil constant table"); growl_vm_error(vm, "attempt to index invalid constant table");
} }
VM_NEXT(); VM_NEXT();
} }
@ -298,18 +294,18 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
VM_OP(CALL) { VM_OP(CALL) {
Growl obj = growl_pop(vm); Growl obj = growl_pop(vm);
callstack_push(vm, vm->current_quotation, vm->ip); callstack_push(vm, vm->current_quotation, vm->ip);
dispatch(vm, obj, 0); dispatch(vm, obj);
VM_NEXT(); VM_NEXT();
} }
VM_OP(CALL_NEXT) { VM_OP(CALL_NEXT) {
Growl callable = vm->next; Growl callable = vm->next;
vm->next = GROWL_NIL; vm->next = GROWL_NIL;
dispatch(vm, callable, 1); dispatch(vm, callable);
VM_NEXT(); VM_NEXT();
} }
VM_OP(TAIL_CALL) { VM_OP(TAIL_CALL) {
Growl obj = growl_pop(vm); Growl obj = growl_pop(vm);
dispatch(vm, obj, 1); dispatch(vm, obj);
VM_NEXT(); VM_NEXT();
} }
VM_OP(WORD) { VM_OP(WORD) {
@ -317,14 +313,14 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
GrowlDefinition *def = &vm->defs.data[idx]; GrowlDefinition *def = &vm->defs.data[idx];
Growl word = def->callable; Growl word = def->callable;
callstack_push(vm, vm->current_quotation, vm->ip); callstack_push(vm, vm->current_quotation, vm->ip);
dispatch(vm, word, 0); dispatch(vm, word);
VM_NEXT(); VM_NEXT();
} }
VM_OP(TAIL_WORD) { VM_OP(TAIL_WORD) {
intptr_t idx = growl_sleb128_decode(&vm->ip); intptr_t idx = growl_sleb128_decode(&vm->ip);
GrowlDefinition *def = &vm->defs.data[idx]; GrowlDefinition *def = &vm->defs.data[idx];
Growl word = def->callable; Growl word = def->callable;
dispatch(vm, word, 1); dispatch(vm, word);
VM_NEXT(); VM_NEXT();
} }
VM_OP(RETURN) { 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->current_quotation, vm->ip);
callstack_push(vm, vm->dip_trampoline, vm->dip_trampoline->data); callstack_push(vm, vm->dip_trampoline, vm->dip_trampoline->data);
vm->csp[-1].next = x; vm->csp[-1].next = x;
dispatch(vm, callable, 0); dispatch(vm, callable);
VM_NEXT(); VM_NEXT();
} }
VM_OP(PPRINT) { VM_OP(PPRINT) {
@ -417,7 +413,7 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
if (GROWL_IMM(a)) { if (GROWL_IMM(a)) {
growl_push(vm, GROWL_NUM(~GROWL_ORD(a))); growl_push(vm, GROWL_NUM(~GROWL_ORD(a)));
} else { } else {
growl_vm_error(vm, "arithmetic on non-numbers"); growl_vm_error(vm, "numeric op on non-numbers");
} }
VM_NEXT(); VM_NEXT();
} }

View file

@ -175,7 +175,6 @@ char *growl_arena_strdup(GrowlArena *ar, const char *str);
#define GROWL_STACK_SIZE 128 #define GROWL_STACK_SIZE 128
#define GROWL_CALL_STACK_SIZE 64 #define GROWL_CALL_STACK_SIZE 64
#define GROWL_HEAP_SIZE (4 * 1024 * 1024) #define GROWL_HEAP_SIZE (4 * 1024 * 1024)
#define GROWL_ARENA_SIZE (2 * 1024 * 1024)
#define GROWL_SCRATCH_SIZE (1024 * 1024) #define GROWL_SCRATCH_SIZE (1024 * 1024)
struct GrowlFrame { struct GrowlFrame {
@ -208,7 +207,11 @@ struct GrowlModule {
struct GrowlCompileContext { struct GrowlCompileContext {
GrowlCompileContext *parent; 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, 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); int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot);
/** Compiler */ /** 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); void growl_disassemble(GrowlVM *vm, GrowlQuotation *quot);
/** Extra libraries */ /** Extra libraries */

View file

@ -1,13 +1,25 @@
#include <growl.h> #include <growl.h>
#include <math.h> #include <libgen.h>
#include <stdlib.h>
#include <string.h>
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(); GrowlVM *vm = growl_vm_init();
growl_register_file_library(vm); 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) { if (obj != GROWL_NIL) {
GrowlQuotation *quot = growl_unwrap_quotation(obj); GrowlQuotation *quot = growl_unwrap_quotation(obj);
if (!growl_vm_execute(vm, quot)) { if (!growl_vm_execute(vm, quot)) {
@ -24,5 +36,6 @@ int main(void) {
growl_gc_collect(vm); growl_gc_collect(vm);
growl_vm_free(vm); growl_vm_free(vm);
fclose(lexer.file);
return 0; return 0;
} }

10
std.grr
View file

@ -1,19 +1,13 @@
def print { stdout fprint } def print { file/stdout file/write }
def println { stdout fprint "\n" stdout fprint } def println { print "\n" print }
def nl { "\n" stdout fprint }
def eprint { stderr fprint }
def eprintln { stderr fprint "\n" stderr fprint }
def when { [] if } def when { [] if }
def unless { swap when } def unless { swap when }
def 2dip { swap [dip] dip } def 2dip { swap [dip] dip }
def 3dip { swap [2dip] dip }
def keep { over [call] dip } def keep { over [call] dip }
def 2keep { [2dup] dip 2dip } def 2keep { [2dup] dip 2dip }
def 3keep { [dup 2over dig] dip 3dip }
def bi { [keep] dip call } def bi { [keep] dip call }
def tri { [[keep] dip keep] dip call } def tri { [[keep] dip keep] dip call }