This commit is contained in:
Lobo 2026-02-09 10:42:23 -03:00
parent 1746d12266
commit d279bf1d31
17 changed files with 407 additions and 137 deletions

View file

@ -1,4 +1,13 @@
#load("std.grr") 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];
}
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]; }
@ -6,7 +15,7 @@ def buzz { when: 5 % 0 = ["Buzz" print]; }
def fizzbuzz1 { def fizzbuzz1 {
if: fizzbuzz? if: fizzbuzz?
[ [fizz] keep buzz nl ] [ [fizz] keep buzz "\n" print ]
[ . ]; [ . ];
} }

View file

@ -51,10 +51,12 @@ growlnext_sources = [
'next/core/compiler.c', 'next/core/compiler.c',
'next/core/dictionary.c', 'next/core/dictionary.c',
'next/core/disasm.c', 'next/core/disasm.c',
'next/core/file.c',
'next/core/gc.c', 'next/core/gc.c',
'next/core/hash.c', 'next/core/hash.c',
'next/core/lexer.c', 'next/core/lexer.c',
'next/core/list.c', 'next/core/list.c',
'next/core/native.c',
'next/core/print.c', 'next/core/print.c',
'next/core/sleb128.c', 'next/core/sleb128.c',
'next/core/string.c', 'next/core/string.c',
@ -65,9 +67,13 @@ growlnext_sources = [
'next/main.c', 'next/main.c',
] ]
cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required: false)
growlnext = executable( growlnext = executable(
'growlnext', 'growlnext',
growlnext_sources, growlnext_sources,
dependencies: [m_dep],
include_directories: ['next/include'], include_directories: ['next/include'],
install: true, install: true,
) )

View file

@ -10,6 +10,16 @@ Growl growl_make_alien(GrowlVM *vm, GrowlAlienType *type, void *data) {
return GROWL_BOX(hdr); return GROWL_BOX(hdr);
} }
Growl growl_make_alien_tenured(GrowlVM *vm, GrowlAlienType *type, void *data) {
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlAlien);
GrowlObjectHeader *hdr = growl_gc_alloc_tenured(vm, size);
hdr->type = GROWL_TYPE_ALIEN;
GrowlAlien *alien = (GrowlAlien *)(hdr + 1);
alien->type = type;
alien->data = data;
return GROWL_BOX(hdr);
}
GrowlAlien *growl_unwrap_alien(Growl obj, GrowlAlienType *type) { GrowlAlien *growl_unwrap_alien(Growl obj, GrowlAlienType *type) {
if (obj == GROWL_NIL || GROWL_IMM(obj)) if (obj == GROWL_NIL || GROWL_IMM(obj))
return NULL; return NULL;

View file

@ -6,6 +6,9 @@
#include "opcodes.h" #include "opcodes.h"
#include "sleb128.h" #include "sleb128.h"
#include "dynarray.h"
#define COMPILER_DEBUG 0
typedef struct { typedef struct {
Growl *data; Growl *data;
@ -46,12 +49,15 @@ Primitive primitives[] = {
{"call", {GOP_CALL, 0}}, {"call", {GOP_CALL, 0}},
{"compose", {GOP_COMPOSE, 0}}, {"compose", {GOP_COMPOSE, 0}},
{"curry", {GOP_CURRY, 0}}, {"curry", {GOP_CURRY, 0}},
{"dip", {GOP_DIP, 0}},
{".", {GOP_PPRINT, 0}}, {".", {GOP_PPRINT, 0}},
{"+", {GOP_ADD, 0}}, {"+", {GOP_ADD, 0}},
{"*", {GOP_MUL, 0}}, {"*", {GOP_MUL, 0}},
{"-", {GOP_SUB, 0}}, {"-", {GOP_SUB, 0}},
{"/", {GOP_DIV, 0}}, {"/", {GOP_DIV, 0}},
{"%", {GOP_MOD, 0}}, {"%", {GOP_MOD, 0}},
{"and", {GOP_AND, 0}},
{"or", {GOP_OR, 0}},
{"=", {GOP_EQ, 0}}, {"=", {GOP_EQ, 0}},
{"!=", {GOP_NEQ, 0}}, {"!=", {GOP_NEQ, 0}},
{"<", {GOP_LT, 0}}, {"<", {GOP_LT, 0}},
@ -66,40 +72,6 @@ Primitive primitives[] = {
}; };
// clang-format on // clang-format on
// See https://nullprogram.com/blog/2023/10/05/
#define 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_); \
} \
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));
}
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; *push(chunk, &vm->scratch) = byte;
} }
@ -160,10 +132,8 @@ static void optimize_tail_calls(Chunk *chunk) {
} }
if (i < chunk->count && chunk->data[i] == GOP_RETURN) { if (i < chunk->count && chunk->data[i] == GOP_RETURN) {
if (opcode == GOP_CALL) { if (opcode == GOP_CALL) {
chunk->data[i] = GOP_NOP;
chunk->data[start] = GOP_TAIL_CALL; chunk->data[start] = GOP_TAIL_CALL;
} else if (opcode == GOP_WORD) { } else if (opcode == GOP_WORD) {
chunk->data[i] = GOP_NOP;
chunk->data[start] = GOP_TAIL_WORD; chunk->data[start] = GOP_TAIL_WORD;
} }
} }
@ -200,7 +170,7 @@ static int compile_quotation(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) {
} }
static int compile_string(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) { static int compile_string(GrowlVM *vm, GrowlLexer *lexer, Chunk *chunk) {
Growl str = growl_wrap_string(vm, lexer->buffer); Growl str = growl_wrap_string_tenured(vm, lexer->buffer);
size_t const_idx = add_constant(vm, chunk, str); size_t const_idx = add_constant(vm, chunk, str);
emit_byte(vm, chunk, GOP_PUSH_CONSTANT); emit_byte(vm, chunk, GOP_PUSH_CONSTANT);
emit_sleb128(vm, chunk, (intptr_t)const_idx); emit_sleb128(vm, chunk, (intptr_t)const_idx);
@ -222,6 +192,15 @@ static int compile_def(GrowlVM *vm, GrowlLexer *lexer) {
return 1; 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
entry->callable = GROWL_NIL;
entry->index = vm->defs.count - 1;
growl_lexer_next(lexer); growl_lexer_next(lexer);
Chunk fn_chunk = {0}; Chunk fn_chunk = {0};
while (lexer->kind != GTOK_RBRACE && lexer->kind != GTOK_EOF && while (lexer->kind != GTOK_RBRACE && lexer->kind != GTOK_EOF &&
@ -241,14 +220,16 @@ static int compile_def(GrowlVM *vm, GrowlLexer *lexer) {
Growl fn = Growl fn =
growl_make_quotation(vm, fn_chunk.data, fn_chunk.count, growl_make_quotation(vm, fn_chunk.data, fn_chunk.count,
fn_chunk.constants.data, fn_chunk.constants.count); fn_chunk.constants.data, fn_chunk.constants.count);
GrowlQuotation *quot = (GrowlQuotation *)(GROWL_UNBOX(fn) + 1);
GrowlDictionary *entry = #if COMPILER_DEBUG
growl_dictionary_upsert(&vm->dictionary, name, &vm->arena); GrowlQuotation *quot = growl_unwrap_quotation(fn);
GrowlDefinition *def = push(&vm->defs, &vm->arena); fprintf(stderr, "=== %s ===\n", def->name);
def->name = growl_arena_strdup(&vm->arena, name); growl_disassemble(vm, quot);
def->quotation = quot; #endif
entry->quotation = quot;
entry->index = vm->defs.count - 1; // Now update the definition with the compiled quotation
def->callable = fn;
entry->callable = fn;
growl_lexer_next(lexer); growl_lexer_next(lexer);
return 0; return 0;

View file

@ -7,15 +7,15 @@ static void disassemble(GrowlVM *vm, GrowlQuotation *quot, int indent);
static size_t disassemble_instr(GrowlVM *vm, GrowlQuotation *quot, static size_t disassemble_instr(GrowlVM *vm, GrowlQuotation *quot,
size_t offset, int indent) { size_t offset, int indent) {
for (int i = 0; i < indent; i++) { for (int i = 0; i < indent; i++) {
printf(" "); fprintf(stderr, " ");
} }
printf("%04zu ", offset); fprintf(stderr, "%04zu ", offset);
uint8_t opcode = quot->data[offset++]; uint8_t opcode = quot->data[offset++];
// clang-format off // clang-format off
#define OPCODE(name) case GOP_## name: #define OPCODE(name) case GOP_## name:
#define OPCODE1(name) case GOP_## name: printf(#name "\n"); return offset; #define OPCODE1(name) case GOP_## name: fprintf(stderr, #name "\n"); return offset;
// clang-format on // clang-format on
switch (opcode) { switch (opcode) {
@ -24,26 +24,26 @@ static size_t disassemble_instr(GrowlVM *vm, GrowlQuotation *quot,
OPCODE(PUSH_CONSTANT) { OPCODE(PUSH_CONSTANT) {
intptr_t idx; intptr_t idx;
size_t bytes_read = growl_sleb128_peek(&quot->data[offset], &idx); size_t bytes_read = growl_sleb128_peek(&quot->data[offset], &idx);
printf("PUSH_CONSTANT %ld", idx); fprintf(stderr, "PUSH_CONSTANT %ld", idx);
if (quot->constants != GROWL_NIL && if (quot->constants != GROWL_NIL &&
growl_type(quot->constants) == GROWL_TYPE_TUPLE) { growl_type(quot->constants) == GROWL_TYPE_TUPLE) {
GrowlTuple *constants = growl_unwrap_tuple(quot->constants); GrowlTuple *constants = growl_unwrap_tuple(quot->constants);
if (idx >= 0 && (size_t)idx < constants->count) { if (idx >= 0 && (size_t)idx < constants->count) {
Growl constant = constants->data[idx]; Growl constant = constants->data[idx];
printf(" ("); fprintf(stderr, " (");
growl_print(constant); growl_print_to(stderr, constant);
printf(")"); fprintf(stderr, ")");
if (!GROWL_IMM(constant) && constant != GROWL_NIL && if (!GROWL_IMM(constant) && constant != GROWL_NIL &&
growl_type(constant) == GROWL_TYPE_QUOTATION) { growl_type(constant) == GROWL_TYPE_QUOTATION) {
putchar('\n'); putc('\n', stderr);
GrowlQuotation *inner = growl_unwrap_quotation(constant); GrowlQuotation *inner = growl_unwrap_quotation(constant);
disassemble(vm, inner, indent + 1); disassemble(vm, inner, indent + 1);
return offset + bytes_read; return offset + bytes_read;
} }
} }
} }
putchar('\n'); putc('\n', stderr);
return offset + bytes_read; return offset + bytes_read;
} }
OPCODE1(DROP); OPCODE1(DROP);
@ -61,22 +61,24 @@ static size_t disassemble_instr(GrowlVM *vm, GrowlQuotation *quot,
OPCODE1(CHOOSE); OPCODE1(CHOOSE);
OPCODE1(CALL); OPCODE1(CALL);
OPCODE1(CALL_NEXT); OPCODE1(CALL_NEXT);
OPCODE1(PUSH_NEXT);
OPCODE1(TAIL_CALL); OPCODE1(TAIL_CALL);
OPCODE(WORD) { OPCODE(WORD) {
intptr_t idx; intptr_t idx;
size_t bytes_read = growl_sleb128_peek(&quot->data[offset], &idx); size_t bytes_read = growl_sleb128_peek(&quot->data[offset], &idx);
printf("WORD %s\n", vm->defs.data[idx].name); fprintf(stderr, "WORD %s\n", vm->defs.data[idx].name);
return offset + bytes_read; return offset + bytes_read;
} }
OPCODE(TAIL_WORD) { OPCODE(TAIL_WORD) {
intptr_t idx; intptr_t idx;
size_t bytes_read = growl_sleb128_peek(&quot->data[offset], &idx); size_t bytes_read = growl_sleb128_peek(&quot->data[offset], &idx);
printf("TAIL_WORD %s\n", vm->defs.data[idx].name); fprintf(stderr, "TAIL_WORD %s\n", vm->defs.data[idx].name);
return offset + bytes_read; return offset + bytes_read;
} }
OPCODE1(RETURN); OPCODE1(RETURN);
OPCODE1(COMPOSE); OPCODE1(COMPOSE);
OPCODE1(CURRY); OPCODE1(CURRY);
OPCODE1(DIP);
OPCODE1(PPRINT); OPCODE1(PPRINT);
OPCODE1(ADD); OPCODE1(ADD);
OPCODE1(MUL); OPCODE1(MUL);

42
next/core/dynarray.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef GROWL_DYNARRAY_H
#define GROWL_DYNARRAY_H
// See https://nullprogram.com/blog/2023/10/05/
#include <growl.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#define 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_); \
} \
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));
}
#endif // GROWL_DYNARRAY_H

49
next/core/file.c Normal file
View file

@ -0,0 +1,49 @@
#include <growl.h>
static void file_finalize(void *data) {
FILE *f = data;
if (f && f != stdin && f != stdout && f != stderr)
fclose(f);
}
// clang-format off
static GrowlAlienType alien_file_type = {
.name = "file",
.finalizer = file_finalize,
.call = NULL,
};
// clang-format on
static Growl stdout_obj = GROWL_NIL;
static void native_file_stdout(GrowlVM *vm) {
if (stdout_obj == GROWL_NIL) {
GrowlObjectHeader *hdr = growl_gc_alloc_tenured(
vm, sizeof(GrowlObjectHeader) + sizeof(GrowlAlien));
hdr->type = GROWL_TYPE_ALIEN;
GrowlAlien *stdout_alien = (GrowlAlien *)(hdr + 1);
stdout_alien->data = stdout;
stdout_alien->type = &alien_file_type;
stdout_obj = GROWL_BOX(hdr);
}
growl_push(vm, stdout_obj);
}
static void native_file_write(GrowlVM *vm) {
Growl file_obj = growl_pop(vm);
Growl string_obj = growl_pop(vm);
GrowlAlien *file_alien = growl_unwrap_alien(file_obj, &alien_file_type);
if (file_alien == NULL)
growl_vm_error(vm, "expected file object");
GrowlString *str = growl_unwrap_string(string_obj);
if (str == NULL)
growl_vm_error(vm, "expected string");
fwrite(str->data, sizeof(char), str->len, file_alien->data);
}
void growl_register_file_library(GrowlVM *vm) {
growl_register_native(vm, "file/stdout", native_file_stdout);
growl_register_native(vm, "file/write", native_file_write);
}

View file

@ -139,14 +139,26 @@ void growl_gc_collect(GrowlVM *vm) {
gc_print_stats(vm, "before GC"); gc_print_stats(vm, "before GC");
#endif #endif
// Forward work stack
for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) { for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) {
vm->wst[i] = forward(vm, vm->wst[i]); vm->wst[i] = forward(vm, vm->wst[i]);
} }
// Forward retain stack
for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) {
vm->rst[i] = forward(vm, vm->rst[i]);
}
// Forward GC roots
for (size_t i = 0; i < vm->root_count; ++i) { for (size_t i = 0; i < vm->root_count; ++i) {
*vm->roots[i] = forward(vm, *vm->roots[i]); *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);
}
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) {
GrowlObjectHeader *hdr = (GrowlObjectHeader *)tenured_scan; GrowlObjectHeader *hdr = (GrowlObjectHeader *)tenured_scan;

View file

@ -134,7 +134,7 @@ int growl_lexer_next(GrowlLexer *lexer) {
switch (next) { switch (next) {
case '\\': case '\\':
for (; next != '\n'; next = getc_ws(lexer)) for (; next != '\n'; next = lexer_getc(lexer))
; ;
return growl_lexer_next(lexer); return growl_lexer_next(lexer);
case '(': case '(':

29
next/core/native.c Normal file
View file

@ -0,0 +1,29 @@
#include <growl.h>
#include <string.h>
#include "dynarray.h"
static void call_native(GrowlVM *vm, void *data) {
void (*fn)(GrowlVM *) = data;
fn(vm);
}
// clang-format off
static GrowlAlienType native_type = {
.name = "native",
.finalizer = NULL,
.call = call_native,
};
// clang-format on
void growl_register_native(GrowlVM *vm, const char *name,
void (*fn)(GrowlVM *)) {
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);
def->name = growl_arena_strdup(&vm->arena, name);
def->callable = alien;
entry->callable = alien;
entry->index = vm->defs.count - 1;
}

View file

@ -5,6 +5,7 @@ enum GrowlOpcode {
GOP_NOP = 0, GOP_NOP = 0,
GOP_PUSH_NIL, GOP_PUSH_NIL,
GOP_PUSH_CONSTANT, GOP_PUSH_CONSTANT,
GOP_PUSH_NEXT,
GOP_DROP, GOP_DROP,
GOP_DUP, GOP_DUP,
GOP_SWAP, GOP_SWAP,
@ -26,6 +27,7 @@ enum GrowlOpcode {
GOP_RETURN, GOP_RETURN,
GOP_COMPOSE, GOP_COMPOSE,
GOP_CURRY, GOP_CURRY,
GOP_DIP,
GOP_PPRINT, GOP_PPRINT,
GOP_ADD, GOP_ADD,
GOP_MUL, GOP_MUL,
@ -36,6 +38,8 @@ enum GrowlOpcode {
GOP_BOR, GOP_BOR,
GOP_BXOR, GOP_BXOR,
GOP_BNOT, GOP_BNOT,
GOP_AND,
GOP_OR,
GOP_EQ, GOP_EQ,
GOP_NEQ, GOP_NEQ,
GOP_LT, GOP_LT,

View file

@ -8,57 +8,56 @@ void growl_println(Growl value) {
putchar('\n'); putchar('\n');
} }
static void print_escaped(const char *data, size_t len) { static void print_escaped(FILE *file, const char *data, size_t len) {
putchar('"'); putc('"', file);
for (size_t i = 0; i < len; ++i) { for (size_t i = 0; i < len; ++i) {
switch (data[i]) { switch (data[i]) {
case '\0': case '\0':
putchar('\\'); putc('\\', file);
putchar('0'); putc('0', file);
break; break;
case '\t': case '\t':
putchar('\\'); putc('\\', file);
putchar('t'); putc('t', file);
break; break;
case '\n': case '\n':
putchar('\\'); putc('\\', file);
putchar('n'); putc('n', file);
break; break;
case '\r': case '\r':
putchar('\\'); putc('\\', file);
putchar('r'); putc('r', file);
break; break;
case '\b': case '\b':
putchar('\\'); putc('\\', file);
putchar('b'); putc('b', file);
break; break;
case '\v': case '\v':
putchar('\\'); putc('\\', file);
putchar('v'); putc('v', file);
break; break;
case '\f': case '\f':
putchar('\\'); putc('\\', file);
putchar('f'); putc('f', file);
break; break;
case '\x1b': case '\x1b':
putchar('\\'); putc('\\', file);
putchar('e'); putc('e', file);
break; break;
case '\\': case '\\':
putchar('\\'); putc('\\', file);
putchar('\\'); putc('\\', file);
break; break;
case '"': case '"':
putchar('\\'); putc('\\', file);
putchar('"'); putc('"', file);
break; break;
default: default:
putchar(data[i]); putc(data[i], file);
break; break;
} }
} }
putchar('"'); putc('"', file);
} }
void growl_print_to(FILE *file, Growl value) { void growl_print_to(FILE *file, Growl value) {
@ -71,7 +70,7 @@ void growl_print_to(FILE *file, Growl value) {
switch (hdr->type) { switch (hdr->type) {
case GROWL_TYPE_STRING: { case GROWL_TYPE_STRING: {
GrowlString *str = (GrowlString *)(hdr + 1); GrowlString *str = (GrowlString *)(hdr + 1);
print_escaped(str->data, str->len); print_escaped(file, str->data, str->len);
break; break;
} }
default: default:

View file

@ -23,6 +23,18 @@ Growl growl_wrap_string(GrowlVM *vm, const char *cstr) {
return GROWL_BOX(hdr); return GROWL_BOX(hdr);
} }
Growl growl_wrap_string_tenured(GrowlVM *vm, const char *cstr) {
size_t len = strlen(cstr);
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlString) + len + 1;
GrowlObjectHeader *hdr = growl_gc_alloc_tenured(vm, size);
hdr->type = GROWL_TYPE_STRING;
GrowlString *str = (GrowlString *)(hdr + 1);
str->len = len;
memcpy(str->data, cstr, len);
str->data[len] = 0;
return GROWL_BOX(hdr);
}
GrowlString *growl_unwrap_string(Growl obj) { GrowlString *growl_unwrap_string(Growl obj) {
if (obj == 0 || GROWL_IMM(obj)) if (obj == 0 || GROWL_IMM(obj))
return NULL; return NULL;

View file

@ -31,9 +31,17 @@ GrowlVM *growl_vm_init(void) {
vm->root_count = 0; vm->root_count = 0;
vm->root_capacity = 0; vm->root_capacity = 0;
static uint8_t trampoline_code[] = {GOP_CALL_NEXT}; static uint8_t compose_code[] = {GOP_CALL_NEXT};
Growl trampoline = growl_make_quotation(vm, trampoline_code, 1, NULL, 0); Growl compose_tramp = growl_make_quotation(vm, compose_code, 1, NULL, 0);
vm->compose_trampoline = (GrowlQuotation *)(GROWL_UNBOX(trampoline) + 1); vm->compose_trampoline = (GrowlQuotation *)(GROWL_UNBOX(compose_tramp) + 1);
static uint8_t return_code[] = {GOP_RETURN};
Growl return_tramp = growl_make_quotation(vm, return_code, 1, NULL, 0);
vm->return_trampoline = (GrowlQuotation *)(GROWL_UNBOX(return_tramp) + 1);
static uint8_t dip_code[] = {GOP_PUSH_NEXT, GOP_RETURN};
Growl dip_tramp = growl_make_quotation(vm, dip_code, 2, NULL, 0);
vm->dip_trampoline = (GrowlQuotation *)(GROWL_UNBOX(dip_tramp) + 1);
return vm; return vm;
} }
@ -49,8 +57,8 @@ void growl_vm_free(GrowlVM *vm) {
free(vm); free(vm);
} }
__attribute__((format(printf, 2, 3))) static noreturn void __attribute__((format(printf, 2, 3))) noreturn void
vm_error(GrowlVM *vm, const char *fmt, ...) { growl_vm_error(GrowlVM *vm, const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
fprintf(stderr, "vm: "); fprintf(stderr, "vm: ");
@ -62,19 +70,19 @@ vm_error(GrowlVM *vm, const char *fmt, ...) {
void growl_push(GrowlVM *vm, Growl obj) { void growl_push(GrowlVM *vm, Growl obj) {
if (vm->sp >= vm->wst + GROWL_STACK_SIZE) if (vm->sp >= vm->wst + GROWL_STACK_SIZE)
vm_error(vm, "work stack overflow"); growl_vm_error(vm, "work stack overflow");
*vm->sp++ = obj; *vm->sp++ = obj;
} }
Growl growl_peek(GrowlVM *vm, size_t depth) { Growl growl_peek(GrowlVM *vm, size_t depth) {
if (vm->sp <= vm->wst + depth) if (vm->sp <= vm->wst + depth)
vm_error(vm, "work stack underflow"); growl_vm_error(vm, "work stack underflow");
return vm->sp[-(depth + 1)]; return vm->sp[-(depth + 1)];
} }
Growl growl_pop(GrowlVM *vm) { Growl growl_pop(GrowlVM *vm) {
if (vm->sp <= vm->wst) if (vm->sp <= vm->wst)
vm_error(vm, "work stack underflow"); growl_vm_error(vm, "work stack underflow");
Growl obj = *--vm->sp; Growl obj = *--vm->sp;
*vm->sp = GROWL_NIL; *vm->sp = GROWL_NIL;
return obj; return obj;
@ -82,13 +90,13 @@ Growl growl_pop(GrowlVM *vm) {
void growl_rpush(GrowlVM *vm, Growl obj) { void growl_rpush(GrowlVM *vm, Growl obj) {
if (vm->rsp >= vm->rst + GROWL_STACK_SIZE) if (vm->rsp >= vm->rst + GROWL_STACK_SIZE)
vm_error(vm, "work stack overflow"); growl_vm_error(vm, "work stack overflow");
*vm->rsp++ = obj; *vm->rsp++ = obj;
} }
Growl growl_rpop(GrowlVM *vm) { Growl growl_rpop(GrowlVM *vm) {
if (vm->rsp <= vm->rst) if (vm->rsp <= vm->rst)
vm_error(vm, "work stack underflow"); growl_vm_error(vm, "work stack underflow");
Growl obj = *--vm->rsp; Growl obj = *--vm->rsp;
*vm->rsp = GROWL_NIL; *vm->rsp = GROWL_NIL;
return obj; return obj;
@ -96,7 +104,7 @@ Growl growl_rpop(GrowlVM *vm) {
static void callstack_push(GrowlVM *vm, GrowlQuotation *q, uint8_t *ip) { static void callstack_push(GrowlVM *vm, GrowlQuotation *q, uint8_t *ip) {
if (vm->csp >= vm->cst + GROWL_CALL_STACK_SIZE) if (vm->csp >= vm->cst + GROWL_CALL_STACK_SIZE)
vm_error(vm, "call stack overflow"); growl_vm_error(vm, "call stack overflow");
vm->csp->quot = q; vm->csp->quot = q;
vm->csp->ip = ip; vm->csp->ip = ip;
vm->csp->next = GROWL_NIL; vm->csp->next = GROWL_NIL;
@ -105,11 +113,12 @@ static void callstack_push(GrowlVM *vm, GrowlQuotation *q, uint8_t *ip) {
static GrowlFrame callstack_pop(GrowlVM *vm) { static GrowlFrame callstack_pop(GrowlVM *vm) {
if (vm->csp <= vm->cst) if (vm->csp <= vm->cst)
vm_error(vm, "call stack underflow"); growl_vm_error(vm, "call stack underflow");
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: {
@ -131,8 +140,28 @@ static inline void dispatch(GrowlVM *vm, Growl obj) {
obj = c->callable; obj = c->callable;
continue; continue;
} }
case GROWL_TYPE_ALIEN: {
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");
}
default: default:
vm_error(vm, "attempt to call non-callable"); growl_vm_error(vm, "attempt to call non-callable (type=%d)",
growl_type(obj));
} }
} }
} }
@ -171,13 +200,18 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
if (idx >= 0 && (size_t)idx < constants->count) { if (idx >= 0 && (size_t)idx < constants->count) {
growl_push(vm, constants->data[idx]); growl_push(vm, constants->data[idx]);
} else { } else {
vm_error(vm, "constant index %" PRIdPTR " out of bounds", idx); growl_vm_error(vm, "constant index %" PRIdPTR " out of bounds", idx);
} }
} else { } else {
vm_error(vm, "attempt to index nil constant table"); growl_vm_error(vm, "attempt to index nil constant table");
} }
VM_NEXT(); VM_NEXT();
} }
VM_OP(PUSH_NEXT) {
growl_push(vm, vm->next);
vm->next = GROWL_NIL;
VM_NEXT();
}
VM_OP(DROP) { VM_OP(DROP) {
(void)growl_pop(vm); (void)growl_pop(vm);
VM_NEXT(); VM_NEXT();
@ -251,9 +285,9 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
VM_NEXT(); VM_NEXT();
} }
VM_OP(CHOOSE) { VM_OP(CHOOSE) {
Growl cond = growl_pop(vm);
Growl f = growl_pop(vm); Growl f = growl_pop(vm);
Growl t = growl_pop(vm); Growl t = growl_pop(vm);
Growl cond = growl_pop(vm);
if (cond != GROWL_NIL) { if (cond != GROWL_NIL) {
growl_push(vm, t); growl_push(vm, t);
} else { } else {
@ -264,32 +298,33 @@ 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); dispatch(vm, obj, 0);
VM_NEXT(); VM_NEXT();
} }
VM_OP(CALL_NEXT) { VM_OP(CALL_NEXT) {
growl_push(vm, vm->compose_next); Growl callable = vm->next;
vm->compose_next = GROWL_NIL; vm->next = GROWL_NIL;
__attribute__((__fallthrough__)); dispatch(vm, callable, 1);
VM_NEXT();
} }
VM_OP(TAIL_CALL) { VM_OP(TAIL_CALL) {
Growl obj = growl_pop(vm); Growl obj = growl_pop(vm);
dispatch(vm, obj); dispatch(vm, obj, 1);
VM_NEXT(); VM_NEXT();
} }
VM_OP(WORD) { VM_OP(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 = GROWL_BOX((GrowlObjectHeader *)(def->quotation) - 1); Growl word = def->callable;
callstack_push(vm, vm->current_quotation, vm->ip); callstack_push(vm, vm->current_quotation, vm->ip);
dispatch(vm, word); dispatch(vm, word, 0);
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 = GROWL_BOX((GrowlObjectHeader *)(def->quotation) - 1); Growl word = def->callable;
dispatch(vm, word); dispatch(vm, word, 1);
VM_NEXT(); VM_NEXT();
} }
VM_OP(RETURN) { VM_OP(RETURN) {
@ -297,7 +332,7 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
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->compose_next = frame.next; vm->next = frame.next;
} else { } else {
goto done; goto done;
} }
@ -308,7 +343,7 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
Growl first = growl_pop(vm); Growl first = growl_pop(vm);
Growl composed = growl_compose(vm, first, second); Growl composed = growl_compose(vm, first, second);
if (composed == GROWL_NIL) if (composed == GROWL_NIL)
vm_error(vm, "attempt to compose with a non-callable"); growl_vm_error(vm, "attempt to compose with a non-callable");
growl_push(vm, composed); growl_push(vm, composed);
VM_NEXT(); VM_NEXT();
} }
@ -317,10 +352,19 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
Growl value = growl_pop(vm); Growl value = growl_pop(vm);
Growl curried = growl_curry(vm, value, callable); Growl curried = growl_curry(vm, value, callable);
if (curried == GROWL_NIL) if (curried == GROWL_NIL)
vm_error(vm, "attempt to curry with a non-callable"); growl_vm_error(vm, "attempt to curry with a non-callable");
growl_push(vm, curried); growl_push(vm, curried);
VM_NEXT(); VM_NEXT();
} }
VM_OP(DIP) {
Growl callable = growl_pop(vm);
Growl x = growl_pop(vm);
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);
VM_NEXT();
}
VM_OP(PPRINT) { VM_OP(PPRINT) {
growl_println(growl_pop(vm)); growl_println(growl_pop(vm));
VM_NEXT(); VM_NEXT();
@ -333,7 +377,7 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
if (GROWL_IMM(b) && GROWL_IMM(a)) { \ if (GROWL_IMM(b) && GROWL_IMM(a)) { \
growl_push(vm, GROWL_NUM(GROWL_ORD(a) op GROWL_ORD(b))); \ growl_push(vm, GROWL_NUM(GROWL_ORD(a) op GROWL_ORD(b))); \
} else { \ } else { \
vm_error(vm, "numeric op on non-numbers"); \ growl_vm_error(vm, "numeric op on non-numbers"); \
} \ } \
VM_NEXT(); \ VM_NEXT(); \
} }
@ -346,10 +390,10 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
Growl a = growl_pop(vm); Growl a = growl_pop(vm);
if (GROWL_IMM(b) && GROWL_IMM(a)) { if (GROWL_IMM(b) && GROWL_IMM(a)) {
if (GROWL_ORD(b) == 0) if (GROWL_ORD(b) == 0)
vm_error(vm, "division by zero"); growl_vm_error(vm, "division by zero");
growl_push(vm, GROWL_NUM(GROWL_ORD(a) / GROWL_ORD(b))); growl_push(vm, GROWL_NUM(GROWL_ORD(a) / GROWL_ORD(b)));
} else { } else {
vm_error(vm, "numeric op on non-numbers"); growl_vm_error(vm, "numeric op on non-numbers");
}; };
VM_NEXT(); VM_NEXT();
} }
@ -358,10 +402,10 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
Growl a = growl_pop(vm); Growl a = growl_pop(vm);
if (GROWL_IMM(b) && GROWL_IMM(a)) { if (GROWL_IMM(b) && GROWL_IMM(a)) {
if (GROWL_ORD(b) == 0) if (GROWL_ORD(b) == 0)
vm_error(vm, "division by zero"); growl_vm_error(vm, "division by zero");
growl_push(vm, GROWL_NUM(GROWL_ORD(a) % GROWL_ORD(b))); growl_push(vm, GROWL_NUM(GROWL_ORD(a) % GROWL_ORD(b)));
} else { } else {
vm_error(vm, "numeric op on non-numbers"); growl_vm_error(vm, "numeric op on non-numbers");
}; };
VM_NEXT(); VM_NEXT();
} }
@ -373,7 +417,27 @@ 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 {
vm_error(vm, "arithmetic on non-numbers"); growl_vm_error(vm, "arithmetic on non-numbers");
}
VM_NEXT();
}
VM_OP(AND) {
Growl b = growl_pop(vm);
Growl a = growl_pop(vm);
if (a == GROWL_NIL) {
growl_push(vm, a);
} else {
growl_push(vm, b);
}
VM_NEXT();
}
VM_OP(OR) {
Growl b = growl_pop(vm);
Growl a = growl_pop(vm);
if (a == GROWL_NIL) {
growl_push(vm, b);
} else {
growl_push(vm, a);
} }
VM_NEXT(); VM_NEXT();
} }
@ -399,7 +463,29 @@ int growl_vm_execute(GrowlVM *vm, GrowlQuotation *quot) {
} }
VM_NEXT(); VM_NEXT();
} }
VM_DEFAULT() { vm_error(vm, "unknown opcode %d", opcode); }
#define VM_CMPOP(name, op) \
case GOP_##name: { \
Growl b = growl_pop(vm); \
Growl a = growl_pop(vm); \
if (GROWL_IMM(b) && GROWL_IMM(a)) { \
if (GROWL_ORD(a) op GROWL_ORD(b)) { \
growl_push(vm, GROWL_NUM(1)); \
} else { \
growl_push(vm, GROWL_NIL); \
} \
} else { \
growl_vm_error(vm, "comparison on non-numbers"); \
} \
VM_NEXT(); \
}
VM_CMPOP(LT, <);
VM_CMPOP(LTE, <=);
VM_CMPOP(GT, >);
VM_CMPOP(GTE, >=);
VM_DEFAULT() { growl_vm_error(vm, "unknown opcode %d", opcode); }
VM_END() VM_END()
done: done:

View file

@ -5,6 +5,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdnoreturn.h>
typedef uintptr_t Growl; typedef uintptr_t Growl;
@ -28,6 +29,8 @@ typedef struct GrowlAlien GrowlAlien;
typedef struct GrowlLexer GrowlLexer; typedef struct GrowlLexer GrowlLexer;
typedef struct GrowlArena GrowlArena; typedef struct GrowlArena GrowlArena;
typedef struct GrowlFrame GrowlFrame; typedef struct GrowlFrame GrowlFrame;
typedef struct GrowlModule GrowlModule;
typedef struct GrowlCompileContext GrowlCompileContext;
typedef struct GrowlDictionary GrowlDictionary; typedef struct GrowlDictionary GrowlDictionary;
typedef struct GrowlDefinition GrowlDefinition; typedef struct GrowlDefinition GrowlDefinition;
typedef struct GrowlDefinitionTable GrowlDefinitionTable; typedef struct GrowlDefinitionTable GrowlDefinitionTable;
@ -52,6 +55,8 @@ struct GrowlObjectHeader {
}; };
uint32_t growl_type(Growl obj); uint32_t growl_type(Growl obj);
int growl_equals(Growl a, Growl b);
uint64_t growl_hash_combine(uint64_t a, uint64_t b); uint64_t growl_hash_combine(uint64_t a, uint64_t b);
uint64_t growl_hash_bytes(const uint8_t *data, size_t len); uint64_t growl_hash_bytes(const uint8_t *data, size_t len);
uint64_t growl_hash(Growl obj); uint64_t growl_hash(Growl obj);
@ -60,8 +65,6 @@ void growl_print_to(FILE *file, Growl value);
void growl_print(Growl value); void growl_print(Growl value);
void growl_println(Growl value); void growl_println(Growl value);
int growl_equals(Growl a, Growl b);
struct GrowlString { struct GrowlString {
size_t len; size_t len;
char data[]; char data[];
@ -69,6 +72,7 @@ struct GrowlString {
Growl growl_make_string(GrowlVM *vm, size_t len); Growl growl_make_string(GrowlVM *vm, size_t len);
Growl growl_wrap_string(GrowlVM *vm, const char *cstr); Growl growl_wrap_string(GrowlVM *vm, const char *cstr);
Growl growl_wrap_string_tenured(GrowlVM *vm, const char *cstr);
GrowlString *growl_unwrap_string(Growl obj); GrowlString *growl_unwrap_string(Growl obj);
struct GrowlList { struct GrowlList {
@ -85,7 +89,6 @@ GrowlTuple *growl_unwrap_tuple(Growl obj);
struct GrowlTable {}; struct GrowlTable {};
GrowlTable *growl_unwrap_table(Growl obj); GrowlTable *growl_unwrap_table(Growl obj);
GrowlTable *growl_table_upsert(GrowlVM *vm, GrowlTable **table, Growl key);
struct GrowlQuotation { struct GrowlQuotation {
size_t count; size_t count;
@ -112,6 +115,7 @@ GrowlCurry *growl_unwrap_curry(Growl obj);
struct GrowlAlienType { struct GrowlAlienType {
const char *name; const char *name;
void (*call)(GrowlVM *, void *);
void (*finalizer)(void *); void (*finalizer)(void *);
}; };
@ -121,7 +125,10 @@ struct GrowlAlien {
}; };
Growl growl_make_alien(GrowlVM *vm, GrowlAlienType *type, void *data); Growl growl_make_alien(GrowlVM *vm, GrowlAlienType *type, void *data);
Growl growl_make_alien_tenured(GrowlVM *vm, GrowlAlienType *type, void *data);
GrowlAlien *growl_unwrap_alien(Growl obj, GrowlAlienType *type); GrowlAlien *growl_unwrap_alien(Growl obj, GrowlAlienType *type);
void growl_register_native(GrowlVM *vm, const char *name,
void (*fn)(GrowlVM *));
/** Lexer */ /** Lexer */
enum { enum {
@ -179,7 +186,7 @@ struct GrowlFrame {
struct GrowlDefinition { struct GrowlDefinition {
const char *name; const char *name;
GrowlQuotation *quotation; Growl callable;
}; };
struct GrowlDefinitionTable { struct GrowlDefinitionTable {
@ -190,10 +197,20 @@ struct GrowlDefinitionTable {
struct GrowlDictionary { struct GrowlDictionary {
GrowlDictionary *child[4]; GrowlDictionary *child[4];
const char *name; const char *name;
GrowlQuotation *quotation; Growl callable;
size_t index; size_t index;
}; };
struct GrowlModule {
char *resolved_path;
GrowlModule *next;
};
struct GrowlCompileContext {
GrowlCompileContext *parent;
const char *file_path, *file_dir;
};
GrowlDictionary *growl_dictionary_upsert(GrowlDictionary **dict, GrowlDictionary *growl_dictionary_upsert(GrowlDictionary **dict,
const char *name, GrowlArena *perm); const char *name, GrowlArena *perm);
@ -213,7 +230,9 @@ struct GrowlVM {
GrowlFrame cst[GROWL_CALL_STACK_SIZE], *csp; GrowlFrame cst[GROWL_CALL_STACK_SIZE], *csp;
GrowlQuotation *compose_trampoline; GrowlQuotation *compose_trampoline;
Growl compose_next; GrowlQuotation *return_trampoline;
GrowlQuotation *dip_trampoline;
Growl next;
Growl **roots; Growl **roots;
size_t root_count, root_capacity; size_t root_count, root_capacity;
@ -229,10 +248,19 @@ void growl_gc_collect(GrowlVM *vm);
void growl_gc_root(GrowlVM *vm, Growl *ptr); void growl_gc_root(GrowlVM *vm, Growl *ptr);
size_t growl_gc_mark(GrowlVM *vm); size_t growl_gc_mark(GrowlVM *vm);
void growl_gc_reset(GrowlVM *vm, size_t mark); void growl_gc_reset(GrowlVM *vm, size_t mark);
void growl_push(GrowlVM *vm, Growl obj);
Growl growl_peek(GrowlVM *vm, size_t depth);
Growl growl_pop(GrowlVM *vm);
void growl_rpush(GrowlVM *vm, Growl obj);
Growl growl_rpop(GrowlVM *vm);
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(GrowlVM *vm, GrowlLexer *lexer);
void growl_disassemble(GrowlVM *vm, GrowlQuotation *quot); void growl_disassemble(GrowlVM *vm, GrowlQuotation *quot);
/** Extra libraries */
void growl_register_file_library(GrowlVM *vm);
#endif // GROWL_H #endif // GROWL_H

View file

@ -1,24 +1,26 @@
#include "core/opcodes.h"
#include <growl.h> #include <growl.h>
#include <math.h>
int main(void) { int main(void) {
GrowlVM *vm = growl_vm_init(); GrowlVM *vm = growl_vm_init();
growl_register_file_library(vm);
GrowlLexer lexer = {0}; GrowlLexer lexer = {0};
lexer.file = stdin; lexer.file = stdin;
Growl obj = growl_compile(vm, &lexer); Growl obj = growl_compile(vm, &lexer);
if (obj != GROWL_NIL) { if (obj != GROWL_NIL) {
GrowlQuotation *quot = growl_unwrap_quotation(obj); GrowlQuotation *quot = growl_unwrap_quotation(obj);
growl_disassemble(vm, quot); if (!growl_vm_execute(vm, quot)) {
growl_vm_execute(vm, quot); if (vm->sp != vm->wst) {
fprintf(stderr, "Stack:");
printf("Stack:");
for (Growl *p = vm->wst; p < vm->sp; p++) { for (Growl *p = vm->wst; p < vm->sp; p++) {
putchar(' '); putc(' ', stderr);
growl_print(*p); growl_print_to(stderr, *p);
} }
putchar('\n'); putchar('\n');
} }
}
}
growl_gc_collect(vm); growl_gc_collect(vm);
growl_vm_free(vm); growl_vm_free(vm);

View file

@ -8,7 +8,6 @@ def eprintln { stderr fprint "\n" stderr fprint }
def when { [] if } def when { [] if }
def unless { swap when } def unless { swap when }
def dip { swap [] curry compose call }
def 2dip { swap [dip] dip } def 2dip { swap [dip] dip }
def 3dip { swap [2dip] dip } def 3dip { swap [2dip] dip }