next: *
This commit is contained in:
parent
1746d12266
commit
d279bf1d31
17 changed files with 407 additions and 137 deletions
|
|
@ -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 ]
|
||||||
[ . ];
|
[ . ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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("->data[offset], &idx);
|
size_t bytes_read = growl_sleb128_peek("->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("->data[offset], &idx);
|
size_t bytes_read = growl_sleb128_peek("->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("->data[offset], &idx);
|
size_t bytes_read = growl_sleb128_peek("->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
42
next/core/dynarray.h
Normal 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
49
next/core/file.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
29
next/core/native.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
158
next/core/vm.c
158
next/core/vm.c
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
20
next/main.c
20
next/main.c
|
|
@ -1,23 +1,25 @@
|
||||||
#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++) {
|
putc(' ', stderr);
|
||||||
putchar(' ');
|
growl_print_to(stderr, *p);
|
||||||
growl_print(*p);
|
}
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
putchar('\n');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
growl_gc_collect(vm);
|
growl_gc_collect(vm);
|
||||||
|
|
|
||||||
1
std.grr
1
std.grr
|
|
@ -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 }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue