remove old interpreter from source tree

This commit is contained in:
Lobo 2026-02-09 10:48:16 -03:00
parent d279bf1d31
commit e18681b309
91 changed files with 45 additions and 11539 deletions

View file

@ -1,79 +1,45 @@
project( project(
'growl', 'growl',
'c', 'c',
'cpp',
meson_version: '>= 1.3.0', meson_version: '>= 1.3.0',
version: '0.1', version: '0.1',
default_options: [ default_options: [
'buildtype=debugoptimized', 'buildtype=debugoptimized',
'c_std=gnu11', 'c_std=gnu11',
'cpp_std=c++20',
'warning_level=2', 'warning_level=2',
], ],
) )
libutf = subproject('libutf')
libutf_dep = libutf.get_variable('libutf_dep')
growl_sources = [ growl_sources = [
'src/arena.c', 'src/core/alien.c',
'src/chunk.c', 'src/core/arena.c',
'src/compile.c', 'src/core/callable.c',
'src/debug.c', 'src/core/compiler.c',
'src/dictionary.c', 'src/core/dictionary.c',
'src/file.c', 'src/core/disasm.c',
'src/lexer.c', 'src/core/file.c',
'src/object.c', 'src/core/gc.c',
'src/gc.c', 'src/core/hash.c',
'src/parser.c', 'src/core/lexer.c',
'src/primitive.c', 'src/core/list.c',
'src/print.c', 'src/core/native.c',
'src/stream.c', 'src/core/print.c',
'src/string.c', 'src/core/sleb128.c',
'src/userdata.c', 'src/core/string.c',
'src/vm.c', 'src/core/table.c',
'src/vendor/linenoise.c', 'src/core/tuple.c',
'src/vendor/yar.c', 'src/core/value.c',
] 'src/core/vm.c',
growl = executable(
'growl',
'src/main.c', 'src/main.c',
growl_sources,
dependencies: [libutf_dep],
install: true,
)
growlnext_sources = [
'next/core/alien.c',
'next/core/arena.c',
'next/core/callable.c',
'next/core/compiler.c',
'next/core/dictionary.c',
'next/core/disasm.c',
'next/core/file.c',
'next/core/gc.c',
'next/core/hash.c',
'next/core/lexer.c',
'next/core/list.c',
'next/core/native.c',
'next/core/print.c',
'next/core/sleb128.c',
'next/core/string.c',
'next/core/table.c',
'next/core/tuple.c',
'next/core/value.c',
'next/core/vm.c',
'next/main.c',
] ]
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required: false) m_dep = cc.find_library('m', required: false)
growlnext = executable( growl = executable(
'growlnext', 'growl',
growlnext_sources, growl_sources,
dependencies: [m_dep], dependencies: [m_dep],
include_directories: ['next/include'], include_directories: ['src/include'],
install: true, install: true,
) )

View file

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

View file

@ -1,31 +0,0 @@
#include "arena.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
V *_arena_alloc(Ar *ar, I count, I size, I align) {
I pad = -(U)ar->start & (align - 1);
assert(count < (ar->end - ar->start - pad) / size);
V *r = ar->start + pad;
ar->start += pad + count * size;
return memset(r, 0, count * size);
}
V arena_init(Ar *ar, Z size) {
ar->data = malloc(size);
ar->start = ar->data;
ar->end = ar->start + size;
}
V arena_free(Ar *ar) {
free(ar->data);
ar->data = ar->start = ar->end = NULL;
}
char *arena_strdup(Ar *ar, const char *str) {
Z len = strlen(str) + 1;
char *copy = arena_alloc(ar, len, char);
memcpy(copy, str, len);
return copy;
}

View file

@ -1,18 +0,0 @@
#ifndef ARENA_H
#define ARENA_H
#include "common.h"
typedef struct Ar {
U8 *data;
U8 *start, *end;
} Ar;
#define arena_alloc(a, n, t) (t *)_arena_alloc(a, n, sizeof(t), _Alignof(t))
V *_arena_alloc(Ar *, ptrdiff_t, ptrdiff_t, ptrdiff_t);
V arena_init(Ar *, Z);
V arena_free(Ar *);
char *arena_strdup(Ar *, const char *);
#endif

View file

@ -1,91 +0,0 @@
#include <stdlib.h>
#include "chunk.h"
#include "vendor/yar.h"
#if CHUNK_DEBUG
#include <stdio.h>
#endif
Bc *chunk_new(const char *name) {
Bc *chunk = calloc(1, sizeof(Bc));
chunk->name = name;
chunk->ref = 1;
#if CHUNK_DEBUG
fprintf(stderr, "DEBUG: created chunk %s at %p\n", chunk->name, (V *)chunk);
#endif
return chunk;
}
V chunk_acquire(Bc *chunk) {
#if CHUNK_DEBUG
fprintf(stderr, "DEBUG: acquiring chunk %s at %p\n", chunk->name, (V *)chunk);
#endif
chunk->ref++;
}
V chunk_release(Bc *chunk) {
#if CHUNK_DEBUG
fprintf(stderr, "DEBUG: releasing chunk %s at %p\n", chunk->name, (V *)chunk);
#endif
if (--chunk->ref == 0) {
#if CHUNK_DEBUG
fprintf(stderr, "DEBUG: freeing chunk %s at %p\n", chunk->name, (V *)chunk);
#endif
yar_free(&chunk->constants);
yar_free(&chunk->lines);
yar_free(&chunk->symbols);
yar_free(chunk);
free(chunk);
}
}
V chunk_emit_byte(Bc *chunk, U8 byte) { *yar_append(chunk) = byte; }
V chunk_emit_sleb128(Bc *chunk, I num) {
I more = 1;
while (more) {
U8 byte = num & 0x7f;
num >>= 7;
if ((num == 0 && !(byte & 0x40)) || (num == -1 && (byte & 0x40))) {
more = 0;
} else {
byte |= 0x80;
}
chunk_emit_byte(chunk, byte);
}
}
I chunk_add_constant(Bc *chunk, O value) {
I mark = chunk->constants.count;
*yar_append(&chunk->constants) = value;
return mark;
}
V chunk_emit_byte_with_line(Bc *chunk, U8 byte, I line, I col) {
*yar_append(chunk) = byte;
if (chunk->lines.count == 0 ||
chunk->lines.items[chunk->lines.count - 1].row != line ||
chunk->lines.items[chunk->lines.count - 1].col != col) {
Bl *entry = yar_append(&chunk->lines);
entry->offset = chunk->count - 1;
entry->row = line;
entry->col = col;
}
}
I chunk_get_line(Bc *chunk, Z offset, I *out_col) {
if (chunk->lines.count == 0)
return -1;
Z left = 0, right = chunk->lines.count - 1;
while (left < right) {
Z mid = left + (right - left + 1) / 2;
if (chunk->lines.items[mid].offset <= offset)
left = mid;
else
right = mid - 1;
}
if (out_col)
*out_col = chunk->lines.items[left].col;
return chunk->lines.items[left].row;
}

View file

@ -1,50 +0,0 @@
#ifndef CHUNK_H
#define CHUNK_H
#define CHUNK_DEBUG 0
#include "common.h"
#include "object.h"
typedef struct Bl {
Z offset;
I row;
I col;
} Bl;
typedef struct Bs {
const char *name;
struct Dt *resolved;
} Bs;
typedef struct Bc {
I ref;
const char *name;
U8 *items;
Z count, capacity;
struct {
O *items;
Z count, capacity;
} constants;
struct {
Bl *items;
Z count, capacity;
} lines;
struct {
Bs *items;
Z count, capacity;
} symbols;
} Bc;
Bc *chunk_new(const char *);
V chunk_acquire(Bc *);
V chunk_release(Bc *);
V chunk_emit_byte(Bc *, U8);
V chunk_emit_sleb128(Bc *, I);
I chunk_add_constant(Bc *, O);
V chunk_emit_byte_with_line(Bc *, U8, I, I);
I chunk_get_line(Bc *, Z, I*);
#endif

View file

@ -1,16 +0,0 @@
#ifndef COMMON_H
#define COMMON_H
#include <stdint.h>
#include <stddef.h>
typedef void V;
typedef intptr_t I;
typedef uintptr_t U;
typedef double F;
typedef size_t Z;
typedef uint8_t U8;
typedef uint32_t U32;
typedef uint64_t U64;
#endif

View file

@ -1,332 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "chunk.h"
#include "compile.h"
#include "debug.h"
#include "gc.h"
#include "object.h"
#include "parser.h"
#include "src/primitive.h"
#include "string.h"
#include "vendor/yar.h"
#include "vm.h"
// clang-format off
struct {
const char *name;
U8 opcode[8];
} primitives[] = {
{"nil", {OP_NIL, 0}},
{"dup", {OP_DUP, 0}},
{"drop", {OP_DROP, 0}},
{"swap", {OP_SWAP, 0}},
{"2dup", {OP_2DUP, 0}},
{"2drop", {OP_2DROP, 0}},
{"2swap", {OP_2SWAP, 0}},
{"2over", {OP_2TOR, OP_2DUP, OP_2FROMR, OP_2SWAP, 0}},
{"over", {OP_OVER, 0}},
{"nip", {OP_NIP, 0}},
{"bury", {OP_BURY, 0}},
{"dig", {OP_DIG, 0}},
{">r", {OP_TOR, 0}},
{"r>", {OP_FROMR, 0}},
{"2>r", {OP_2TOR, 0}},
{"2r>", {OP_2FROMR, 0}},
{"if", {OP_CHOOSE, OP_CALL, 0}},
{"call", {OP_CALL, 0}},
{"compose", {OP_COMPOSE, 0}},
{"curry", {OP_CURRY, 0}},
{"?", {OP_CHOOSE, 0}},
{"+", {OP_ADD, 0}},
{"-", {OP_SUB, 0}},
{"*", {OP_MUL, 0}},
{"/", {OP_DIV, 0}},
{"%", {OP_MOD, 0}},
{"logand", {OP_LOGAND, 0}},
{"logor", {OP_LOGOR, 0}},
{"logxor", {OP_LOGXOR, 0}},
{"lognot", {OP_LOGNOT, 0}},
{"=", {OP_EQ, 0}},
{"<>", {OP_NEQ, 0}},
{"<", {OP_LT, 0}},
{">", {OP_GT, 0}},
{"<=", {OP_LTE, 0}},
{">=", {OP_GTE, 0}},
{"and", {OP_AND, 0}},
{"or", {OP_OR, 0}},
{"^", {OP_CONCAT, 0}},
{NULL, {0}},
};
// clang-format on
V compiler_init(Cm *cm, Vm *vm, const char *name) {
cm->vm = vm;
cm->arena = &vm->arena;
cm->dictionary = &vm->dictionary;
cm->chunk = chunk_new(name);
}
V compiler_deinit(Cm *cm) { cm->dictionary = NULL; }
static I peek_sleb128(U8 *ptr, I *out_value) {
I result = 0;
I shift = 0;
U8 byte;
I bytes = 0;
do {
byte = ptr[bytes];
bytes++;
result |= (I)(byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
if ((shift < 64) && (byte & 0x40)) {
result |= -(1LL << shift);
}
if (out_value)
*out_value = result;
return bytes;
}
static V optim_tailcall(Bc *chunk) {
Z i = 0;
while (i < chunk->count) {
U8 opcode = chunk->items[i];
if (opcode == OP_DOWORD) {
I ofs = peek_sleb128(&chunk->items[i + 1], NULL);
Z next = i + 1 + ofs;
if (next < chunk->count && chunk->items[next] == OP_RETURN) {
chunk->items[i] = OP_TAIL_DOWORD;
}
i++;
} else if (opcode == OP_CALL) {
Z ofs = i + 1;
if (ofs < chunk->count && chunk->items[ofs] == OP_RETURN) {
chunk->items[i] = OP_TAIL_CALL;
}
i++;
} else if (opcode == OP_CONST) {
I ofs = peek_sleb128(&chunk->items[i + 1], NULL);
i += 1 + ofs;
} else {
i++;
}
}
}
static I compile_expr(Cm *cm, Ast *node);
static I compile_constant(Cm *cm, O value, I line, I col) {
I idx = chunk_add_constant(cm->chunk, value);
chunk_emit_byte_with_line(cm->chunk, OP_CONST, line, col);
chunk_emit_sleb128(cm->chunk, idx);
return 1;
}
static I add_sym(Bc *chunk, const char *name, Dt *word) {
for (Z i = 0; i < chunk->symbols.count; i++) {
if (strcmp(chunk->symbols.items[i].name, name) == 0)
return i;
}
Z idx = chunk->symbols.count;
Bs *sym = yar_append(&chunk->symbols);
sym->name = name;
sym->resolved = word;
return idx;
}
static I compile_call(Cm *cm, const char *name, I line, I col) {
for (Z i = 0; primitives[i].name != NULL; i++) {
if (strcmp(name, primitives[i].name) == 0) {
for (Z j = 0; primitives[i].opcode[j] != 0; j++)
chunk_emit_byte_with_line(cm->chunk, primitives[i].opcode[j], line,
col);
return 1;
}
}
I prim_idx = prim_find(name);
if (prim_idx != -1) {
chunk_emit_byte_with_line(cm->chunk, OP_PRIM, line, col);
chunk_emit_sleb128(cm->chunk, prim_idx);
return 1;
}
Dt *word = upsert(cm->dictionary, name, NULL);
if (!word) {
fprintf(stderr, "compiler error at %ld:%ld: undefined word '%s'\n",
line + 1, col + 1, name);
return 0;
}
I idx = add_sym(cm->chunk, name, word);
chunk_emit_byte_with_line(cm->chunk, OP_DOWORD, line, col);
chunk_emit_sleb128(cm->chunk, idx);
return 1;
}
static I compile_command(Cm *cm, Ast *node) {
for (size_t i = 0; i < node->children.count; i++) {
if (!compile_expr(cm, node->children.items[i]))
return 0;
}
return compile_call(cm, node->name, node->line, node->col);
}
static I compile_definition(Cm *cm, Ast *node) {
const char *name = arena_strdup(cm->arena, node->name);
Dt *entry = upsert(cm->dictionary, name, cm->arena);
Cm inner = {0};
inner.arena = cm->arena;
inner.chunk = chunk_new(name);
inner.vm = cm->vm;
inner.dictionary = cm->dictionary;
for (size_t i = 0; i < node->children.count; i++) {
if (!compile_expr(&inner, node->children.items[i])) {
chunk_release(inner.chunk);
return 0;
}
}
chunk_emit_byte_with_line(inner.chunk, OP_RETURN, node->line, node->col);
optim_tailcall(inner.chunk);
entry->chunk = inner.chunk;
#if COMPILER_DEBUG
disassemble(inner.chunk, name, cm->dictionary);
#endif
return 1;
}
static O compile_quotation_obj(Cm *cm, Ast *node) {
Cm inner = {0};
inner.arena = cm->arena;
inner.chunk = chunk_new("<quotation>");
inner.vm = cm->vm;
inner.dictionary = cm->dictionary;
for (size_t i = 0; i < node->children.count; i++) {
if (!compile_expr(&inner, node->children.items[i])) {
chunk_release(inner.chunk);
return NIL;
}
}
chunk_emit_byte_with_line(inner.chunk, OP_RETURN, node->line, node->col);
optim_tailcall(inner.chunk);
Hd *hd = gc_alloc(cm->vm, sizeof(Hd) + sizeof(Bc *));
hd->type = OBJ_QUOT;
Bc **chunk_ptr = (Bc **)(hd + 1);
*chunk_ptr = inner.chunk;
return BOX(hd);
}
static I compile_quotation(Cm *cm, Ast *node) {
O obj = compile_quotation_obj(cm, node);
if (obj == NIL)
return 0;
return compile_constant(cm, obj, node->line, node->col);
}
static I compile_pragma(Cm *cm, Ast *node) {
if (strcmp(node->name, "#load") == 0) {
if (node->children.count == 0) {
fprintf(stderr, "compiler error: #load requires argument\n");
return 0;
}
Ast *arg = node->children.items[0];
if (arg->type != AST_STR) {
fprintf(stderr, "compiler error: #load requires string\n");
return 0;
}
char *fname = arg->name;
FILE *f = fopen(fname, "rb");
if (!f) {
fprintf(stderr, "compiler error: cannot open file '%s'\n", fname);
return 0;
}
Stream s = {filestream_vtable, f};
Lx *lx = lexer_make(&s);
Ast *root = parser_parse(lx);
I success = 1;
for (size_t i = 0; i < root->children.count; i++) {
if (!compile_expr(cm, root->children.items[i])) {
success = 0;
break;
}
}
ast_free(root);
lexer_free(lx);
fclose(f);
return success;
}
fprintf(stderr, "compiler warning: unknown pragma \"%s\"\n", node->name);
return 1;
}
static I compile_expr(Cm *cm, Ast *node) {
if (!node)
return 0;
switch (node->type) {
case AST_INT: {
O num = NUM(node->int_val);
return compile_constant(cm, num, node->line, node->col);
}
case AST_STR: {
O obj = string_make(cm->vm, node->name, -1);
return compile_constant(cm, obj, node->line, node->col);
}
case AST_WORD:
return compile_call(cm, node->name, node->line, node->col);
case AST_QUOTE:
return compile_quotation(cm, node);
case AST_DEF:
return compile_definition(cm, node);
case AST_CMD:
return compile_command(cm, node);
case AST_PRAGMA:
return compile_pragma(cm, node);
case AST_PROGRAM:
for (size_t i = 0; i < node->children.count; i++) {
if (!compile_expr(cm, node->children.items[i]))
return 0;
}
return 1;
default:
fprintf(stderr, "compiler error: nyi ast type %d\n", (int)node->type);
return 0;
}
}
Bc *compile_program(Cm *cm, Ast *ast) {
if (ast->type == AST_PROGRAM) {
for (size_t i = 0; i < ast->children.count; i++) {
if (!compile_expr(cm, ast->children.items[i])) {
chunk_release(cm->chunk);
return NULL;
}
}
} else {
if (!compile_expr(cm, ast)) {
chunk_release(cm->chunk);
return NULL;
}
}
chunk_emit_byte(cm->chunk, OP_RETURN);
optim_tailcall(cm->chunk);
return cm->chunk;
}

View file

@ -1,21 +0,0 @@
#include "common.h"
#include "arena.h"
#include "chunk.h"
#include "gc.h"
#include "vm.h"
#include "parser.h"
#define COMPILER_DEBUG 0
/** Compiler context */
typedef struct Cm {
Vm *vm; // Parent context
Ar *arena;
Bc *chunk;
Dt **dictionary;
} Cm;
V compiler_init(Cm *, Vm *, const char *);
V compiler_deinit(Cm *);
Bc *compile_program(Cm *, Ast *);

View file

@ -1,146 +0,0 @@
#include <stdio.h>
#include "chunk.h"
#include "debug.h"
#include "dictionary.h"
#include "primitive.h"
#include "print.h"
#include "vm.h"
static I decode_sleb128(U8 *ptr, Z *bytes_read) {
I result = 0;
I shift = 0;
U8 byte;
Z count = 0;
do {
byte = ptr[count++];
result |= (I)(byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
if ((shift < 64) && (byte & 0x40))
result |= -(1LL << shift);
*bytes_read = count;
return result;
}
static Z dis_instr(Bc *chunk, Z offset, Dt **dictionary, I indent);
static V dis(Bc *chunk, Dt **dictionary, I indent) {
Z offset = 0;
while (offset < chunk->count)
offset = dis_instr(chunk, offset, dictionary, indent);
}
V disassemble(Bc *chunk, const char *name, Dt **dictionary) {
printf("=== %s ===\n", name);
dis(chunk, dictionary, 0);
}
static Z dis_instr(Bc *chunk, Z offset, Dt **dictionary, I indent) {
for (I i = 0; i < indent; i++)
printf(" ");
printf("%04zu ", offset);
I col = -1;
I line = chunk_get_line(chunk, offset, &col);
if (line >= 0) {
printf("%4ld:%-3ld ", line + 1, col + 1);
} else {
printf(" ");
}
U8 opcode = chunk->items[offset++];
#define CASE(name) case OP_##name:
#define SIMPLE(name) \
case OP_##name: \
printf(#name "\n"); \
return offset;
switch (opcode) {
SIMPLE(NOP);
SIMPLE(NIL);
CASE(CONST) {
Z bytes_read;
I idx = decode_sleb128(&chunk->items[offset], &bytes_read);
printf("CONST %ld", idx);
if (idx >= 0 && idx < (I)chunk->constants.count) {
O obj = chunk->constants.items[idx];
printf(" (");
print(obj);
printf(")");
if (!IMM(obj) && obj != NIL && type(obj) == OBJ_QUOT) {
putchar('\n');
Hd *hdr = UNBOX(obj);
Bc **chunk_ptr = (Bc **)(hdr + 1);
Bc *quot_chunk = *chunk_ptr;
dis(quot_chunk, dictionary, indent + 1);
return offset + bytes_read;
}
}
printf("\n");
return offset + bytes_read;
}
SIMPLE(DROP);
SIMPLE(DUP);
SIMPLE(SWAP);
SIMPLE(NIP);
SIMPLE(OVER);
SIMPLE(BURY);
SIMPLE(DIG);
SIMPLE(TOR);
SIMPLE(FROMR);
CASE(DOWORD) {
Z bytes_read;
I idx = decode_sleb128(&chunk->items[offset], &bytes_read);
Dt *word = chunk->symbols.items[idx].resolved;
printf("DOWORD \"%s\"\n", word->name);
return offset + bytes_read;
}
SIMPLE(CALL);
CASE(TAIL_DOWORD) {
Z bytes_read;
I idx = decode_sleb128(&chunk->items[offset], &bytes_read);
Dt *word = chunk->symbols.items[idx].resolved;
printf("TAIL_DOWORD \"%s\"\n", word->name);
return offset + bytes_read;
}
SIMPLE(TAIL_CALL);
CASE(PRIM) {
Z bytes_read;
I idx = decode_sleb128(&chunk->items[offset], &bytes_read);
Pr prim = primitives_table[idx];
printf("PRIM \"%s\"\n", prim.name);
return offset + bytes_read;
}
SIMPLE(COMPOSE);
SIMPLE(CURRY);
SIMPLE(RETURN);
SIMPLE(CHOOSE);
SIMPLE(ADD);
SIMPLE(SUB);
SIMPLE(MUL);
SIMPLE(DIV);
SIMPLE(MOD);
SIMPLE(LOGAND);
SIMPLE(LOGOR);
SIMPLE(LOGXOR);
SIMPLE(LOGNOT);
SIMPLE(EQ);
SIMPLE(NEQ);
SIMPLE(LT);
SIMPLE(GT);
SIMPLE(LTE);
SIMPLE(GTE);
SIMPLE(AND);
SIMPLE(OR);
SIMPLE(CONCAT);
default:
printf("??? (%d)\n", opcode);
return offset;
}
#undef SIMPLE
#undef CASE
}

View file

@ -1,5 +0,0 @@
#include "chunk.h"
#include "common.h"
#include "dictionary.h"
V disassemble(Bc *, const char*, Dt **);

View file

@ -1,39 +0,0 @@
#include <string.h>
#include "arena.h"
#include "common.h"
#include "dictionary.h"
U64 hash64(const char *str) {
I len = strlen(str);
U64 h = 0x100;
for (I i = 0; i < len; i++) {
h ^= str[i] & 255;
h *= 1111111111111111111;
}
return h;
}
Dt *upsert(Dt **env, const char *key, Ar *a) {
U64 hash = hash64(key);
for (U64 h = hash; *env; h <<= 2) {
if (hash == (*env)->hash)
return *env;
env = &(*env)->child[h >> 62];
}
if (!a)
return 0;
*env = arena_alloc(a, 1, Dt);
(*env)->name = key;
(*env)->hash = hash;
return *env;
}
Dt *lookup_hash(Dt **env, U64 hash) {
for (U64 h = hash; *env; h <<= 2) {
if ((*env)->hash == hash)
return *env;
env = &(*env)->child[h >> 62];
}
return NULL;
}

View file

@ -1,19 +0,0 @@
#ifndef DICTIONARY_H
#define DICTIONARY_H
#include "arena.h"
#include "chunk.h"
typedef struct Dt Dt;
struct Dt {
Dt *child[4];
const char *name;
U64 hash;
Bc *chunk;
};
U64 hash64(const char *);
Dt *upsert(Dt **, const char *, Ar *);
Dt *lookup_hash(Dt **, U64);
#endif

View file

@ -1,83 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "src/gc.h"
#include "src/object.h"
#include "string.h"
#include "userdata.h"
#include "vm.h"
static V finalizer(V *data);
// clang-format off
Ut userdata_file = {
.name = "file",
.finalizer = finalizer
};
// clang-format on
I prim_file_stdin(Vm *vm) {
vm_push(vm, vm->stdin);
return 0;
}
I prim_file_stdout(Vm *vm) {
vm_push(vm, vm->stdout);
return 0;
}
I prim_file_stderr(Vm *vm) {
vm_push(vm, vm->stderr);
return 0;
}
I prim_file_fprint(Vm *vm) {
O file_obj = vm_pop(vm);
O string_obj = vm_pop(vm);
Ud *file_ud = userdata_unwrap(file_obj, &userdata_file);
if (file_ud == NULL) {
fprintf(stderr, "expected file object\n");
return VM_ERR_TYPE;
};
Str *str = string_unwrap(string_obj);
if (str == NULL) {
fprintf(stderr, "expected string\n");
return VM_ERR_TYPE;
}
fwrite(str->data, sizeof(char), str->len, (FILE *)file_ud->data);
return 0;
}
I prim_file_fgetline(Vm *vm) {
O file_obj = vm_pop(vm);
I mark = gc_mark(&vm->gc);
gc_addroot(&vm->gc, &file_obj);
Ud *file_ud = userdata_unwrap(file_obj, &userdata_file);
if (file_ud == NULL) {
fprintf(stderr, "expected file object\n");
return VM_ERR_TYPE;
}
char *lineptr = NULL;
size_t size;
I len = getline(&lineptr, &size, (FILE *)file_ud->data);
if (len == -1) {
vm_push(vm, NIL);
} else {
vm_push(vm, string_make(vm, lineptr, len));
}
free(lineptr);
gc_reset(&vm->gc, mark);
return 0;
}
static V finalizer(V *data) {
FILE *f = (FILE *)data;
if (f && f != stdin && f != stdout && f != stderr)
fclose(f);
}

View file

@ -1,9 +0,0 @@
#include "userdata.h"
extern Ut userdata_file;
I prim_file_stdin(Vm *);
I prim_file_stdout(Vm *);
I prim_file_stderr(Vm *);
I prim_file_fprint(Vm *);
I prim_file_fgetline(Vm *vm);

215
src/gc.c
View file

@ -1,215 +0,0 @@
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "chunk.h"
#include "gc.h"
#include "object.h"
#include "userdata.h"
#include "vendor/yar.h"
#include "vm.h"
#define ALIGN(n) (((n) + 7) & ~7)
static inline int infrom(Gc *gc, V *ptr) {
const U8 *x = (const U8 *)ptr;
return (x >= gc->from.start && x < gc->from.end);
}
V gc_addroot(Gc *gc, O *ptr) { *yar_append(&gc->roots) = ptr; }
I gc_mark(Gc *gc) { return gc->roots.count; }
V gc_reset(Gc *gc, I mark) { gc->roots.count = mark; }
static O copy(Gc *gc, Hd *hdr) {
assert(infrom(gc, hdr));
assert(hdr->type != OBJ_FWD);
Z sz = ALIGN(hdr->size);
Hd *new = (Hd *)gc->to.free;
gc->to.free += sz;
memcpy(new, hdr, sz);
hdr->type = OBJ_FWD;
O *obj = (O *)(hdr + 1);
*obj = BOX(new);
return *obj;
}
static O forward(Gc *gc, O obj) {
if (obj == 0)
return 0;
if (IMM(obj))
return obj;
if (!infrom(gc, (V *)obj))
return obj;
Hd *hdr = UNBOX(obj);
if (hdr->type == OBJ_FWD) {
O *o = (O *)(hdr + 1);
return *o;
} else {
return copy(gc, hdr);
}
}
#if GC_DEBUG
static V printstats(Gc *gc, const char *label) {
Z used = (Z)(gc->from.free - gc->from.start);
fprintf(stderr, "[%s] used=%zu/%zu bytes (%.1f%%)\n", label, used,
(Z)HEAP_BYTES, (F)used / (F)HEAP_BYTES * 100.0);
}
#endif
V gc_collect(Vm *vm, I final) {
Gc *gc = &vm->gc;
uint8_t *scan = gc->to.free;
#if GC_DEBUG
printstats(gc, "before GC");
#endif
if (!final) {
// Final GC ignores roots.
for (Z i = 0; i < gc->roots.count; i++) {
O *o = gc->roots.items[i];
*o = forward(gc, *o);
}
Dt *dstack[256];
Dt **dsp = dstack;
*dsp++ = vm->dictionary;
// Forward constants referenced by dictionary entries
while (dsp > dstack) {
Dt *node = *--dsp;
if (!node)
continue;
if (node->chunk != NULL) {
for (Z i = 0; i < node->chunk->constants.count; i++) {
node->chunk->constants.items[i] =
forward(gc, node->chunk->constants.items[i]);
}
}
for (I i = 0; i < 4; i++) {
if (node->child[i] != NULL)
*dsp++ = node->child[i];
}
}
}
while (scan < gc->to.free) {
if (scan >= gc->to.end) {
fprintf(stderr, "fatal GC error: out of memory\n");
abort();
}
Hd *hdr = (Hd *)scan;
switch (hdr->type) {
case OBJ_STR:
break;
case OBJ_QUOT: {
Bc **chunk_ptr = (Bc **)(hdr + 1);
Bc *chunk = *chunk_ptr;
for (Z i = 0; i < chunk->constants.count; i++)
chunk->constants.items[i] = forward(gc, chunk->constants.items[i]);
break;
}
case OBJ_COMPOSE: {
Qo *comp = (Qo *)(hdr + 1);
comp->first = forward(gc, comp->first);
comp->second = forward(gc, comp->second);
break;
};
case OBJ_CURRY: {
Qc *curry = (Qc *)(hdr + 1);
curry->value = forward(gc, curry->value);
curry->callable = forward(gc, curry->callable);
break;
};
case OBJ_USERDATA:
break;
case OBJ_FWD:
fprintf(stderr, "fatal GC error: forwarding pointer in to-space\n");
abort();
default:
fprintf(stderr, "GC warning: junk object type %" PRId32 "\n", hdr->type);
}
scan += ALIGN(hdr->size);
}
scan = gc->from.start;
while (scan < gc->from.free) {
Hd *hdr = (Hd *)scan;
if (hdr->type != OBJ_FWD) {
switch (hdr->type) {
case OBJ_QUOT: {
Bc **chunk_ptr = (Bc **)(hdr + 1);
chunk_release(*chunk_ptr);
break;
}
case OBJ_USERDATA: {
Ud *ud = (Ud *)(hdr + 1);
if (ud->kind->finalizer != NULL)
ud->kind->finalizer(ud->data);
break;
}
default:
break;
}
}
scan += ALIGN(hdr->size);
}
Gs tmp = gc->from;
gc->from = gc->to;
gc->to = tmp;
gc->to.free = gc->to.start;
#if GC_DEBUG
printstats(gc, "after GC");
#endif
}
Hd *gc_alloc(Vm *vm, Z sz) {
Gc *gc = &vm->gc;
sz = ALIGN(sz);
if (gc->from.free + sz > gc->from.end) {
gc_collect(vm, 0);
if (gc->from.free + sz > gc->from.end) {
fprintf(stderr, "out of memory (requested %" PRIdPTR "bytes\n", sz);
abort();
}
}
Hd *hdr = (Hd *)gc->from.free;
gc->from.free += sz;
hdr->size = sz;
return hdr;
}
V gc_init(Gc *gc) {
gc->from.start = malloc(HEAP_BYTES);
if (!gc->from.start)
goto fatal;
gc->from.end = gc->from.start + HEAP_BYTES;
gc->from.free = gc->from.start;
gc->to.start = malloc(HEAP_BYTES);
if (!gc->to.start)
goto fatal;
gc->to.end = gc->to.start + HEAP_BYTES;
gc->to.free = gc->to.start;
gc->roots.capacity = 0;
gc->roots.count = 0;
gc->roots.items = NULL;
return;
fatal:
fprintf(stderr, "failed to allocate heap space\n");
abort();
}
V gc_deinit(Gc *gc) {
free(gc->from.start);
free(gc->to.start);
yar_free(&gc->roots);
}

View file

@ -1,38 +0,0 @@
#ifndef GC_H
#define GC_H
#include "common.h"
#include "object.h"
#define GC_DEBUG 1
#if GC_DEBUG
#define HEAP_BYTES (8 * 1024)
#else
#define HEAP_BYTES (4 * 1024 * 1024)
#endif
typedef struct Gs {
U8 *start, *end;
U8 *free;
} Gs;
typedef struct Gc {
Gs from, to;
struct {
O **items;
Z count, capacity;
} roots;
} Gc;
V gc_addroot(Gc *, O *);
I gc_mark(Gc *);
V gc_reset(Gc *, I);
V gc_init(Gc *);
V gc_deinit(Gc *);
typedef struct Vm Vm;
V gc_collect(Vm *, I);
Hd *gc_alloc(Vm *, Z);
#endif

View file

@ -1,217 +0,0 @@
#include <ctype.h>
#include <err.h>
#include <stdlib.h>
#include <utf.h>
#include "lexer.h"
#include "vendor/yar.h"
Lx *lexer_make(Stream *s) {
Lx *lx = calloc(1, sizeof(Lx));
lx->stream = s;
return lx;
}
V lexer_free(Lx *lx) {
yar_free(lx);
free(lx);
}
static int lx_getc(Lx *lx) {
int c = ST_GETC(lx->stream);
if (c == '\n') {
lx->curr_line++;
lx->curr_col = 0;
} else if (c != -1) {
lx->curr_col++;
}
return c;
}
static void lx_ungetc(Lx *lx, int c) {
ST_UNGETC(c, lx->stream);
if (c == '\n') {
lx->curr_line--;
} else if (c != -1) {
lx->curr_col--;
}
}
static inline int is_delimiter(int i) {
return i == '(' || i == ')' || i == '[' || i == ']' || i == '{' || i == '}' ||
i == ';' || i == '\\' || i == '"';
}
static inline void appendrune(Lx *lx, Rune rn) {
char data[5];
I len = runetochar(data, &rn);
yar_append_many(lx, data, len);
}
static inline void appendbyte(Lx *lx, char byte) { *yar_append(lx) = byte; }
static int getc_ws(Lx *lx) {
if (ST_EOF(lx->stream))
return -1;
for (;;) {
int ch = lx_getc(lx);
if (isspace(ch))
continue;
return ch;
}
}
static int scanword(Lx *lx) {
int next = lx_getc(lx);
for (;;) {
if (next == -1) {
if (lx->count == 0)
lx->kind = TOK_EOF;
appendbyte(lx, 0);
return lx->kind;
} else if (is_delimiter(next) || isspace(next)) {
lx_ungetc(lx, next);
appendbyte(lx, 0);
return lx->kind;
} else {
appendbyte(lx, next);
next = lx_getc(lx);
continue;
}
}
}
static void scanescape(Lx *lx) {
char escbuf[7], *escptr = escbuf;
int next;
Rune tmp;
for (;;) {
next = lx_getc(lx);
if (next == -1) {
errx(1, "unterminated hex sequence '%s'", escbuf);
} else if (next == ';') {
*escptr = 0;
break;
} else if (!isxdigit(next)) {
errx(1, "invalid hex digit '%c'", next);
}
if (escptr - escbuf >= 6) {
errx(1, "hex sequence too long (6 chars max.)");
} else {
*(escptr++) = next;
}
}
tmp = strtol(escbuf, &escptr, 16);
if (*escptr == '\0') {
if (tmp < 256) {
appendbyte(lx, (U8)(tmp & 255));
} else {
appendrune(lx, tmp);
}
} else {
errx(1, "invalid hex sequence '%s'", escbuf);
}
}
static int scanstring(Lx *lx) {
int next;
for (;;) {
next = lx_getc(lx);
switch (next) {
case -1:
goto eof;
case '\\':
next = lx_getc(lx);
if (next == -1)
goto eof;
switch (next) {
case 't':
appendbyte(lx, '\t');
break;
case 'n':
appendbyte(lx, '\n');
break;
case 'r':
appendbyte(lx, '\r');
break;
case 'b':
appendbyte(lx, '\b');
break;
case 'v':
appendbyte(lx, '\v');
break;
case 'f':
appendbyte(lx, '\f');
break;
case '0':
appendbyte(lx, '\0');
break;
case 'e':
appendbyte(lx, '\x1b');
break;
case '\\':
case '"':
appendbyte(lx, next);
break;
case 'x':
scanescape(lx);
break;
default:
return (lx->kind = TOK_INVALID);
}
break;
case '"':
appendbyte(lx, 0);
return (lx->kind = TOK_STRING);
default:
appendbyte(lx, next);
}
}
eof:
return (lx->kind = TOK_INVALID);
}
I lexer_next(Lx *lx) {
int next;
lx->cursor = 0;
lx->count = 0;
if (ST_EOF(lx->stream)) {
lx->kind = TOK_EOF;
return 0;
}
next = getc_ws(lx);
lx->start_line = lx->curr_line;
lx->start_col = (lx->curr_col > 0) ? lx->curr_col - 1 : 0;
switch (next) {
case '\\':
for (; next != '\n'; next = lx_getc(lx))
;
return lexer_next(lx);
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case ';':
return (lx->kind = next);
case '"':
return scanstring(lx);
default:
lx_ungetc(lx, next);
lx->kind = TOK_WORD;
return scanword(lx);
};
}

View file

@ -1,36 +0,0 @@
#ifndef LEXER_H
#define LEXER_H
#include "common.h"
#include "stream.h"
enum {
TOK_INVALID = -1,
TOK_EOF = 0,
TOK_WORD = 'a',
TOK_STRING = '"',
TOK_SEMICOLON = ';',
TOK_LPAREN = '(',
TOK_RPAREN = ')',
TOK_LBRACKET = '[',
TOK_RBRACKET = ']',
TOK_LBRACE = '{',
TOK_RBRACE = '}',
TOK_COMMENT = '\\',
};
typedef struct Lx {
I kind;
I cursor;
I curr_line, curr_col;
I start_line, start_col;
Stream *stream;
char *items;
Z count, capacity;
} Lx;
Lx *lexer_make(Stream *);
V lexer_free(Lx *lx);
I lexer_next(Lx *);
#endif

View file

@ -1,91 +1,28 @@
#include <stdio.h> #include <growl.h>
#include <stdlib.h> #include <math.h>
#include <string.h>
#include "chunk.h" int main(void) {
#include "compile.h" GrowlVM *vm = growl_vm_init();
#include "debug.h" growl_register_file_library(vm);
#include "parser.h" GrowlLexer lexer = {0};
#include "vm.h" lexer.file = stdin;
#include "vendor/linenoise.h" Growl obj = growl_compile(vm, &lexer);
if (obj != GROWL_NIL) {
#define REPL_BUFFER_SIZE 4096 GrowlQuotation *quot = growl_unwrap_quotation(obj);
if (!growl_vm_execute(vm, quot)) {
I repl(void) { if (vm->sp != vm->wst) {
Vm vm = {0}; fprintf(stderr, "Stack:");
vm_init(&vm); for (Growl *p = vm->wst; p < vm->sp; p++) {
putc(' ', stderr);
char *line; growl_print_to(stderr, *p);
while ((line = linenoise("growl> ")) != NULL) { }
Buf b = { line, (int)strlen(line), 0, -1 }; putchar('\n');
Stream s = { bufstream_vtable, &b }; }
Lx *lx = lexer_make(&s);
Ast *root = parser_parse(lx);
Cm cm = {0};
compiler_init(&cm, &vm, "<repl>");
Bc *chunk = compile_program(&cm, root);
ast_free(root);
lexer_free(lx);
if (chunk != NULL) {
vm_run(&vm, chunk, 0);
chunk_release(chunk);
linenoiseHistoryAdd(line);
} }
compiler_deinit(&cm);
linenoiseFree(line);
} }
vm_deinit(&vm);
growl_gc_collect(vm);
growl_vm_free(vm);
return 0; return 0;
} }
I loadfile(const char *fname) {
Vm vm = {0};
vm_init(&vm);
FILE *f = fopen(fname, "rb");
if (!f) {
fprintf(stderr, "error: cannot open file '%s'\n", fname);
return 1;
}
Stream s = { filestream_vtable, f };
Lx *lx = lexer_make(&s);
Ast *root = parser_parse(lx);
Cm cm = {0};
compiler_init(&cm, &vm, fname);
Bc *chunk = compile_program(&cm, root);
ast_free(root);
lexer_free(lx);
fclose(f);
if (chunk != NULL) {
#if COMPILER_DEBUG
disassemble(chunk, fname, &vm.dictionary);
#endif
I res = vm_run(&vm, chunk, 0);
chunk_release(chunk);
vm_deinit(&vm);
return !res;
} else {
vm_deinit(&vm);
return 1;
}
}
int main(int argc, const char *argv[]) {
switch (argc) {
case 1:
return repl();
case 2:
return loadfile(argv[1]);
default:
fprintf(stderr, "usage: growl [file]\n");
return 64;
}
}

View file

@ -1,10 +0,0 @@
#include "object.h"
I type(O o) {
if (o == NIL)
return OBJ_NIL;
if (IMM(o))
return OBJ_NUM;
Hd *h = UNBOX(o);
return h->type;
}

View file

@ -1,48 +0,0 @@
#ifndef OBJECT_H
#define OBJECT_H
#include "common.h"
#define NIL ((O)0)
#define BOX(x) ((O)(x))
#define UNBOX(x) ((Hd *)(x))
#define IMM(x) ((O)(x) & (O)1)
#define NUM(x) (((O)((intptr_t)(x) << 1)) | (O)1)
#define ORD(x) ((intptr_t)(x) >> 1)
enum {
OBJ_NIL = 0,
OBJ_NUM = 1,
OBJ_FWD = 2,
OBJ_QUOT,
OBJ_COMPOSE,
OBJ_CURRY,
OBJ_STR,
OBJ_ARRAY,
OBJ_USERDATA,
};
typedef uintptr_t O;
/** Object header */
typedef struct Hd {
U32 size, type;
} Hd;
/** Composition */
typedef struct Qo {
O first, second;
} Qo;
/** Curry */
typedef struct Qc {
O value, callable;
} Qc; //
I type(O);
static inline I callable(O o) {
I t = type(o);
return t == OBJ_QUOT || t == OBJ_COMPOSE || t == OBJ_CURRY;
}
#endif

View file

@ -1,156 +0,0 @@
#include "parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static Ast *ast_new(I type, I line, I col) {
Ast *node = calloc(1, sizeof(Ast));
node->type = type;
node->line = line;
node->col = col;
return node;
}
void ast_free(Ast *ast) {
if (!ast)
return;
if (ast->name)
free(ast->name);
for (size_t i = 0; i < ast->children.count; i++) {
ast_free(ast->children.items[i]);
}
yar_free(&ast->children);
free(ast);
}
static Ast *parse_expr_at(Lx *lx);
static void parse_block(Lx *lx, Ast *parent, int close_token) {
while (1) {
if (lx->kind == TOK_EOF) {
if (close_token != TOK_EOF)
fprintf(stderr, "syntax error: unexpected EOF, expected '%c'\n",
close_token);
break;
}
if (lx->kind == close_token) {
lexer_next(lx);
break;
}
Ast *expr = parse_expr_at(lx);
*yar_append(&parent->children) = expr;
}
}
static Ast *parse_expr_at(Lx *lx) {
int kind = lx->kind;
I line = lx->start_line;
I col = lx->start_col;
if (kind == TOK_WORD) {
char *text = lx->items;
if (strcmp(text, "def") == 0) {
Ast *node = ast_new(AST_DEF, line, col);
lexer_next(lx);
if (lx->kind != TOK_WORD) {
fprintf(stderr, "syntax error: expected word after 'def' at %ld:%ld\n",
(long)line + 1, (long)col + 1);
return node;
}
node->name = strdup(lx->items);
lexer_next(lx);
if (lx->kind != '{') {
fprintf(stderr,
"syntax error: expected '{' after def name at %ld:%ld\n",
(long)lx->start_line + 1, (long)lx->start_col + 1);
return node;
}
lexer_next(lx);
parse_block(lx, node, '}');
return node;
}
size_t len = strlen(text);
if (len > 0 && text[len - 1] == ':') {
Ast *node = ast_new(AST_CMD, line, col);
node->name = strndup(text, len - 1);
lexer_next(lx);
parse_block(lx, node, ';');
return node;
}
if (text[0] == '#') {
Ast *node = ast_new(AST_PRAGMA, line, col);
node->name = strdup(text);
lexer_next(lx);
if (lx->kind == '(') {
lexer_next(lx);
parse_block(lx, node, ')');
}
return node;
}
char *end;
long val = strtol(text, &end, 0);
if (*end == '\0') {
Ast *node = ast_new(AST_INT, line, col);
node->int_val = val;
lexer_next(lx);
return node;
}
Ast *node = ast_new(AST_WORD, line, col);
node->name = strdup(text);
lexer_next(lx);
return node;
}
if (kind == TOK_STRING) {
Ast *node = ast_new(AST_STR, line, col);
node->name = strdup(lx->items);
lexer_next(lx);
return node;
}
if (kind == '[') {
Ast *node = ast_new(AST_QUOTE, line, col);
lexer_next(lx);
parse_block(lx, node, ']');
return node;
}
if (kind == '{') {
Ast *node = ast_new(AST_TABLE, line, col);
lexer_next(lx);
parse_block(lx, node, '}');
return node;
}
if (kind == '(') {
Ast *node = ast_new(AST_LIST, line, col);
lexer_next(lx);
parse_block(lx, node, ')');
return node;
}
if (kind == TOK_INVALID) {
fprintf(stderr, "syntax error: invalid token at %ld:%ld\n", (long)line + 1,
(long)col + 1);
} else {
fprintf(stderr, "syntax error: unexpected token '%c' (%d) at %ld:%ld\n",
kind, kind, (long)line + 1, (long)col + 1);
}
lexer_next(lx);
return NULL;
}
Ast *parser_parse(Lx *lx) {
Ast *root = ast_new(AST_PROGRAM, 0, 0);
lexer_next(lx);
parse_block(lx, root, TOK_EOF);
return root;
}

View file

@ -1,35 +0,0 @@
#ifndef PARSER_H
#define PARSER_H
#include "common.h"
#include "lexer.h"
#include "vendor/yar.h"
enum {
AST_PROGRAM,
AST_INT,
AST_STR,
AST_WORD,
AST_LIST,
AST_TABLE,
AST_QUOTE,
AST_DEF,
AST_CMD,
AST_PRAGMA,
};
typedef struct Ast {
I type;
char *name;
I int_val;
struct {
struct Ast **items;
Z count, capacity;
} children;
I line, col;
} Ast;
Ast *parser_parse(Lx *lx);
void ast_free(Ast *ast);
#endif

View file

@ -1,46 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "primitive.h"
#include "print.h"
#include "string.h"
#include "vm.h"
#include "file.h"
// Pretty-printing primitives
static I prim_pprint(Vm *vm) {
println(vm_pop(vm));
return 0;
}
static I prim_printstack(Vm *vm) {
printf("Stk:");
for (O *p = vm->stack; p < vm->sp; p++) {
putchar(' ');
print(*p);
}
putchar('\n');
return 0;
}
// clang-format off
Pr primitives_table[] = {
{".", prim_pprint},
{".s", prim_printstack},
{"stdin", prim_file_stdin},
{"stdout", prim_file_stdout},
{"stderr", prim_file_stderr},
{"fprint", prim_file_fprint},
{"fgetline", prim_file_fgetline},
{NULL, NULL},
};
// clang-format on
I prim_find(const char *name) {
for (Z i = 0; primitives_table[i].name != NULL; i++) {
if (strcmp(primitives_table[i].name, name) == 0)
return i;
}
return -1;
}

View file

@ -1,14 +0,0 @@
#ifndef PRIMITIVE_H
#define PRIMITIVE_H
#include "vm.h"
typedef struct Pr {
const char *name;
I (*fn)(Vm *);
} Pr;
extern Pr primitives_table[];
I prim_find(const char *name);
#endif

View file

@ -1,91 +0,0 @@
#include <inttypes.h>
#include <stdio.h>
#include "object.h"
#include "print.h"
#include "string.h"
#include "userdata.h"
static V print_string(Str *s) {
putchar('"');
for (Z i = 0; i < s->len; i++) {
unsigned char c = s->data[i];
switch (c) {
case '\t':
printf("\\t");
break;
case '\n':
printf("\\n");
break;
case '\r':
printf("\\r");
break;
case '\b':
printf("\\b");
break;
case '\v':
printf("\\v");
break;
case '\f':
printf("\\f");
break;
case '\0':
printf("\\0");
break;
case '\x1b':
printf("\\e");
break;
case '\\':
printf("\\\\");
break;
case '\"':
printf("\\\"");
break;
default:
if (c < 32 || c > 126) {
printf("\\x%02x;", c);
} else {
putchar(c);
}
}
}
putchar('"');
}
V print(O o) {
if (o == NIL) {
printf("nil");
} else if (IMM(o)) {
printf("%" PRIdPTR, ORD(o));
} else {
Hd *hdr = UNBOX(o);
switch (hdr->type) {
case OBJ_QUOT:
printf("<quotation>");
break;
case OBJ_COMPOSE:
printf("<composed>");
break;
case OBJ_CURRY:
printf("<curried>");
break;
case OBJ_STR: {
Str *s = string_unwrap(o);
print_string(s);
break;
}
case OBJ_USERDATA: {
Ud *ud = (Ud *)(hdr + 1);
printf("<#userdata %s@%p>", ud->kind->name, ud->data);
break;
}
default:
printf("<#obj type=%ld ptr=%p>", type(o), (void *)o);
}
}
}
V println(O o) {
print(o);
putchar('\n');
}

View file

@ -1,10 +0,0 @@
#ifndef PRINT_H
#define PRINT_H
#include "common.h"
#include "object.h"
V print(O);
V println(O);
#endif

View file

@ -1,39 +0,0 @@
#include "stream.h"
#include <stdio.h>
static int filestream_getc(void *f) { return fgetc((FILE *)f); }
static int filestream_ungetc(int c, void *f) { return ungetc(c, (FILE *)f); }
static int filestream_eof(void *f) { return feof((FILE *)f); }
static int bufstream_getc(void *f) {
Buf *b = f;
if (b->unread != -1) {
int c = b->unread;
b->unread = -1;
return c;
} else if (b->pos >= b->len) {
return -1;
}
return b->data[b->pos++];
}
static int bufstream_ungetc(int c, void *f) { return ((Buf *)f)->unread = c; }
static int bufstream_eof(void *f) {
Buf *b = f;
if (b->unread != -1)
return 0;
return b->pos >= b->len;
}
// clang-format off
static const StreamVtable _filestream_vtable = {
filestream_getc, filestream_ungetc, filestream_eof
};
const StreamVtable *filestream_vtable = &_filestream_vtable;
static const StreamVtable _bufstream_vtable = {
bufstream_getc, bufstream_ungetc, bufstream_eof
};
const StreamVtable *bufstream_vtable = &_bufstream_vtable;
// clang-format on

View file

@ -1,30 +0,0 @@
#ifndef STREAM_H
#define STREAM_H
typedef struct StreamVtable {
int (*__sgetc)(void *);
int (*__sungetc)(int, void *);
int (*__seof)(void *);
} StreamVtable;
typedef struct Stream {
const StreamVtable *vtable;
void *data;
} Stream;
typedef struct Buf {
const char *data;
int len, pos;
int unread;
} Buf;
#define ST_GETC(R) ((R)->vtable->__sgetc((R)->data))
#define ST_UNGETC(C, R) ((R)->vtable->__sungetc(C, (R)->data))
#define ST_EOF(R) ((R)->vtable->__seof((R)->data))
#define BUF(s) ((Buf){s, sizeof(s)-1, 0, -1})
extern const StreamVtable *filestream_vtable;
extern const StreamVtable *bufstream_vtable;
#endif

View file

@ -1,51 +0,0 @@
#include <string.h>
#include "string.h"
#include "src/gc.h"
O string_make(Vm *vm, const char *str, I len) {
if (len < 0)
len = strlen(str);
Z size = sizeof(Hd) + sizeof(Str) + len + 1;
Hd *hdr = gc_alloc(vm, size);
hdr->type = OBJ_STR;
Str *s = (Str *)(hdr + 1);
s->len = len;
memcpy(s->data, str, len);
s->data[len] = 0;
return BOX(hdr);
}
Str *string_unwrap(O o) {
if (o == NIL || IMM(o))
return NULL;
Hd *hdr = UNBOX(o);
if (hdr->type != OBJ_STR)
return NULL;
return (Str *)(hdr + 1);
}
O string_concat(Vm *vm, O a_obj, O b_obj) {
I mark = gc_mark(&vm->gc);
gc_addroot(&vm->gc, &a_obj);
gc_addroot(&vm->gc, &b_obj);
Str *as = string_unwrap(a_obj);
Str *bs = string_unwrap(b_obj);
I a_len = as->len;
I b_len = bs->len;
O new = string_make(vm, "", a_len + b_len);
as = string_unwrap(a_obj);
bs = string_unwrap(b_obj);
Str *news = (Str *)(UNBOX(new) + 1);
memcpy(news->data, as->data, a_len);
memcpy(news->data + a_len, bs->data, b_len);
news->data[a_len + b_len] = 0;
gc_reset(&vm->gc, mark);
return new;
}

View file

@ -1,13 +0,0 @@
#include "common.h"
#include "object.h"
#include "vm.h"
/** String */
typedef struct Str {
Z len;
char data[];
} Str;
O string_make(Vm *, const char *, I);
Str *string_unwrap(O);
O string_concat(Vm *, O, O);

View file

@ -1,24 +0,0 @@
#include "userdata.h"
#include "gc.h"
O userdata_make(Vm *vm, V *data, Ut *kind) {
Z size = sizeof(Hd) + sizeof(Ud);
Hd *hdr = gc_alloc(vm, size);
hdr->type = OBJ_USERDATA;
Ud *ud = (Ud *)(hdr + 1);
ud->kind = kind;
ud->data = data;
return BOX(hdr);
}
Ud *userdata_unwrap(O o, Ut *kind) {
if (o == NIL || IMM(o))
return NULL;
Hd *hdr = UNBOX(o);
if (hdr->type != OBJ_USERDATA)
return NULL;
Ud *ud = (Ud *)(hdr + 1);
if (ud->kind != kind)
return NULL;
return ud;
}

View file

@ -1,21 +0,0 @@
#ifndef USERDATA_H
#define USERDATA_H
#include "common.h"
#include "object.h"
#include "vm.h"
typedef struct Ut {
const char *name;
V (*finalizer)(V *);
} Ut;
typedef struct Ud {
Ut *kind;
V *data;
} Ud;
O userdata_make(Vm *, V *, Ut *);
Ud *userdata_unwrap(O, Ut *);
#endif

1763
src/vendor/linenoise.c vendored

File diff suppressed because it is too large Load diff

114
src/vendor/linenoise.h vendored
View file

@ -1,114 +0,0 @@
/* linenoise.h -- VERSION 1.0
*
* Guerrilla line editing library against the idea that a line editing lib
* needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LINENOISE_H
#define __LINENOISE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h> /* For size_t. */
extern char *linenoiseEditMore;
/* The linenoiseState structure represents the state during line editing.
* We pass this state to functions implementing specific editing
* functionalities. */
struct linenoiseState {
int in_completion; /* The user pressed TAB and we are now in completion
* mode, so input is handled by completeLine(). */
size_t completion_idx; /* Index of next completion to propose. */
int ifd; /* Terminal stdin file descriptor. */
int ofd; /* Terminal stdout file descriptor. */
char *buf; /* Edited line buffer. */
size_t buflen; /* Edited line buffer size. */
const char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
size_t len; /* Current edited line length. */
size_t cols; /* Number of columns in terminal. */
size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */
int oldrpos; /* Cursor row from last refresh (for multiline clearing). */
int history_index; /* The history index we are currently editing. */
};
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
/* Non blocking API. */
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt);
char *linenoiseEditFeed(struct linenoiseState *l);
void linenoiseEditStop(struct linenoiseState *l);
void linenoiseHide(struct linenoiseState *l);
void linenoiseShow(struct linenoiseState *l);
/* Blocking API. */
char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
/* Completion API. */
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
typedef void(linenoiseFreeHintsCallback)(void *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
/* History API. */
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
/* Other utilities. */
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);
void linenoiseMaskModeEnable(void);
void linenoiseMaskModeDisable(void);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_H */

4128
src/vendor/mpc.c vendored

File diff suppressed because it is too large Load diff

391
src/vendor/mpc.h vendored
View file

@ -1,391 +0,0 @@
/*
** mpc - Micro Parser Combinator library for C
**
** https://github.com/orangeduck/mpc
**
** Daniel Holden - contact@daniel-holden.com
** Licensed under BSD3
*/
#ifndef mpc_h
#define mpc_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <ctype.h>
/*
** State Type
*/
typedef struct {
long pos;
long row;
long col;
int term;
} mpc_state_t;
/*
** Error Type
*/
typedef struct {
mpc_state_t state;
int expected_num;
char *filename;
char *failure;
char **expected;
char received;
} mpc_err_t;
void mpc_err_delete(mpc_err_t *e);
char *mpc_err_string(mpc_err_t *e);
void mpc_err_print(mpc_err_t *e);
void mpc_err_print_to(mpc_err_t *e, FILE *f);
/*
** Parsing
*/
typedef void mpc_val_t;
typedef union {
mpc_err_t *error;
mpc_val_t *output;
} mpc_result_t;
struct mpc_parser_t;
typedef struct mpc_parser_t mpc_parser_t;
int mpc_parse(const char *filename, const char *string, mpc_parser_t *p, mpc_result_t *r);
int mpc_nparse(const char *filename, const char *string, size_t length, mpc_parser_t *p, mpc_result_t *r);
int mpc_parse_file(const char *filename, FILE *file, mpc_parser_t *p, mpc_result_t *r);
int mpc_parse_pipe(const char *filename, FILE *pipe, mpc_parser_t *p, mpc_result_t *r);
int mpc_parse_contents(const char *filename, mpc_parser_t *p, mpc_result_t *r);
/*
** Function Types
*/
typedef void(*mpc_dtor_t)(mpc_val_t*);
typedef mpc_val_t*(*mpc_ctor_t)(void);
typedef mpc_val_t*(*mpc_apply_t)(mpc_val_t*);
typedef mpc_val_t*(*mpc_apply_to_t)(mpc_val_t*,void*);
typedef mpc_val_t*(*mpc_fold_t)(int,mpc_val_t**);
typedef int(*mpc_check_t)(mpc_val_t**);
typedef int(*mpc_check_with_t)(mpc_val_t**,void*);
/*
** Building a Parser
*/
mpc_parser_t *mpc_new(const char *name);
mpc_parser_t *mpc_copy(mpc_parser_t *a);
mpc_parser_t *mpc_define(mpc_parser_t *p, mpc_parser_t *a);
mpc_parser_t *mpc_undefine(mpc_parser_t *p);
void mpc_delete(mpc_parser_t *p);
void mpc_cleanup(int n, ...);
/*
** Basic Parsers
*/
mpc_parser_t *mpc_any(void);
mpc_parser_t *mpc_char(char c);
mpc_parser_t *mpc_range(char s, char e);
mpc_parser_t *mpc_oneof(const char *s);
mpc_parser_t *mpc_noneof(const char *s);
mpc_parser_t *mpc_satisfy(int(*f)(char));
mpc_parser_t *mpc_string(const char *s);
/*
** Other Parsers
*/
mpc_parser_t *mpc_pass(void);
mpc_parser_t *mpc_fail(const char *m);
mpc_parser_t *mpc_failf(const char *fmt, ...);
mpc_parser_t *mpc_lift(mpc_ctor_t f);
mpc_parser_t *mpc_lift_val(mpc_val_t *x);
mpc_parser_t *mpc_anchor(int(*f)(char,char));
mpc_parser_t *mpc_state(void);
/*
** Combinator Parsers
*/
mpc_parser_t *mpc_expect(mpc_parser_t *a, const char *e);
mpc_parser_t *mpc_expectf(mpc_parser_t *a, const char *fmt, ...);
mpc_parser_t *mpc_apply(mpc_parser_t *a, mpc_apply_t f);
mpc_parser_t *mpc_apply_to(mpc_parser_t *a, mpc_apply_to_t f, void *x);
mpc_parser_t *mpc_check(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *e);
mpc_parser_t *mpc_check_with(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *e);
mpc_parser_t *mpc_checkf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *fmt, ...);
mpc_parser_t *mpc_check_withf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *fmt, ...);
mpc_parser_t *mpc_not(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_not_lift(mpc_parser_t *a, mpc_dtor_t da, mpc_ctor_t lf);
mpc_parser_t *mpc_maybe(mpc_parser_t *a);
mpc_parser_t *mpc_maybe_lift(mpc_parser_t *a, mpc_ctor_t lf);
mpc_parser_t *mpc_many(mpc_fold_t f, mpc_parser_t *a);
mpc_parser_t *mpc_many1(mpc_fold_t f, mpc_parser_t *a);
mpc_parser_t *mpc_count(int n, mpc_fold_t f, mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_or(int n, ...);
mpc_parser_t *mpc_and(int n, mpc_fold_t f, ...);
mpc_parser_t *mpc_predictive(mpc_parser_t *a);
/*
** Common Parsers
*/
mpc_parser_t *mpc_eoi(void);
mpc_parser_t *mpc_soi(void);
mpc_parser_t *mpc_boundary(void);
mpc_parser_t *mpc_boundary_newline(void);
mpc_parser_t *mpc_whitespace(void);
mpc_parser_t *mpc_whitespaces(void);
mpc_parser_t *mpc_blank(void);
mpc_parser_t *mpc_newline(void);
mpc_parser_t *mpc_tab(void);
mpc_parser_t *mpc_escape(void);
mpc_parser_t *mpc_digit(void);
mpc_parser_t *mpc_hexdigit(void);
mpc_parser_t *mpc_octdigit(void);
mpc_parser_t *mpc_digits(void);
mpc_parser_t *mpc_hexdigits(void);
mpc_parser_t *mpc_octdigits(void);
mpc_parser_t *mpc_lower(void);
mpc_parser_t *mpc_upper(void);
mpc_parser_t *mpc_alpha(void);
mpc_parser_t *mpc_underscore(void);
mpc_parser_t *mpc_alphanum(void);
mpc_parser_t *mpc_int(void);
mpc_parser_t *mpc_hex(void);
mpc_parser_t *mpc_oct(void);
mpc_parser_t *mpc_number(void);
mpc_parser_t *mpc_real(void);
mpc_parser_t *mpc_float(void);
mpc_parser_t *mpc_char_lit(void);
mpc_parser_t *mpc_string_lit(void);
mpc_parser_t *mpc_regex_lit(void);
mpc_parser_t *mpc_ident(void);
/*
** Useful Parsers
*/
mpc_parser_t *mpc_startwith(mpc_parser_t *a);
mpc_parser_t *mpc_endwith(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_whole(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_stripl(mpc_parser_t *a);
mpc_parser_t *mpc_stripr(mpc_parser_t *a);
mpc_parser_t *mpc_strip(mpc_parser_t *a);
mpc_parser_t *mpc_tok(mpc_parser_t *a);
mpc_parser_t *mpc_sym(const char *s);
mpc_parser_t *mpc_total(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_between(mpc_parser_t *a, mpc_dtor_t ad, const char *o, const char *c);
mpc_parser_t *mpc_parens(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_braces(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_brackets(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_squares(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_tok_between(mpc_parser_t *a, mpc_dtor_t ad, const char *o, const char *c);
mpc_parser_t *mpc_tok_parens(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_tok_braces(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_tok_brackets(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_tok_squares(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_sepby1(mpc_fold_t f, mpc_parser_t *sep, mpc_parser_t *a);
/*
** Common Function Parameters
*/
void mpcf_dtor_null(mpc_val_t *x);
mpc_val_t *mpcf_ctor_null(void);
mpc_val_t *mpcf_ctor_str(void);
mpc_val_t *mpcf_free(mpc_val_t *x);
mpc_val_t *mpcf_int(mpc_val_t *x);
mpc_val_t *mpcf_hex(mpc_val_t *x);
mpc_val_t *mpcf_oct(mpc_val_t *x);
mpc_val_t *mpcf_float(mpc_val_t *x);
mpc_val_t *mpcf_strtriml(mpc_val_t *x);
mpc_val_t *mpcf_strtrimr(mpc_val_t *x);
mpc_val_t *mpcf_strtrim(mpc_val_t *x);
mpc_val_t *mpcf_escape(mpc_val_t *x);
mpc_val_t *mpcf_escape_regex(mpc_val_t *x);
mpc_val_t *mpcf_escape_string_raw(mpc_val_t *x);
mpc_val_t *mpcf_escape_char_raw(mpc_val_t *x);
mpc_val_t *mpcf_unescape(mpc_val_t *x);
mpc_val_t *mpcf_unescape_regex(mpc_val_t *x);
mpc_val_t *mpcf_unescape_string_raw(mpc_val_t *x);
mpc_val_t *mpcf_unescape_char_raw(mpc_val_t *x);
mpc_val_t *mpcf_null(int n, mpc_val_t** xs);
mpc_val_t *mpcf_fst(int n, mpc_val_t** xs);
mpc_val_t *mpcf_snd(int n, mpc_val_t** xs);
mpc_val_t *mpcf_trd(int n, mpc_val_t** xs);
mpc_val_t *mpcf_fst_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_snd_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_trd_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_all_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_freefold(int n, mpc_val_t** xs);
mpc_val_t *mpcf_strfold(int n, mpc_val_t** xs);
/*
** Regular Expression Parsers
*/
enum {
MPC_RE_DEFAULT = 0,
MPC_RE_M = 1,
MPC_RE_S = 2,
MPC_RE_MULTILINE = 1,
MPC_RE_DOTALL = 2
};
mpc_parser_t *mpc_re(const char *re);
mpc_parser_t *mpc_re_mode(const char *re, int mode);
/*
** AST
*/
typedef struct mpc_ast_t {
char *tag;
char *contents;
mpc_state_t state;
int children_num;
struct mpc_ast_t** children;
} mpc_ast_t;
mpc_ast_t *mpc_ast_new(const char *tag, const char *contents);
mpc_ast_t *mpc_ast_build(int n, const char *tag, ...);
mpc_ast_t *mpc_ast_add_root(mpc_ast_t *a);
mpc_ast_t *mpc_ast_add_child(mpc_ast_t *r, mpc_ast_t *a);
mpc_ast_t *mpc_ast_add_tag(mpc_ast_t *a, const char *t);
mpc_ast_t *mpc_ast_add_root_tag(mpc_ast_t *a, const char *t);
mpc_ast_t *mpc_ast_tag(mpc_ast_t *a, const char *t);
mpc_ast_t *mpc_ast_state(mpc_ast_t *a, mpc_state_t s);
void mpc_ast_delete(mpc_ast_t *a);
void mpc_ast_print(mpc_ast_t *a);
void mpc_ast_print_to(mpc_ast_t *a, FILE *fp);
int mpc_ast_get_index(mpc_ast_t *ast, const char *tag);
int mpc_ast_get_index_lb(mpc_ast_t *ast, const char *tag, int lb);
mpc_ast_t *mpc_ast_get_child(mpc_ast_t *ast, const char *tag);
mpc_ast_t *mpc_ast_get_child_lb(mpc_ast_t *ast, const char *tag, int lb);
typedef enum {
mpc_ast_trav_order_pre,
mpc_ast_trav_order_post
} mpc_ast_trav_order_t;
typedef struct mpc_ast_trav_t {
mpc_ast_t *curr_node;
struct mpc_ast_trav_t *parent;
int curr_child;
mpc_ast_trav_order_t order;
} mpc_ast_trav_t;
mpc_ast_trav_t *mpc_ast_traverse_start(mpc_ast_t *ast,
mpc_ast_trav_order_t order);
mpc_ast_t *mpc_ast_traverse_next(mpc_ast_trav_t **trav);
void mpc_ast_traverse_free(mpc_ast_trav_t **trav);
/*
** Warning: This function currently doesn't test for equality of the `state` member!
*/
int mpc_ast_eq(mpc_ast_t *a, mpc_ast_t *b);
mpc_val_t *mpcf_fold_ast(int n, mpc_val_t **as);
mpc_val_t *mpcf_str_ast(mpc_val_t *c);
mpc_val_t *mpcf_state_ast(int n, mpc_val_t **xs);
mpc_parser_t *mpca_tag(mpc_parser_t *a, const char *t);
mpc_parser_t *mpca_add_tag(mpc_parser_t *a, const char *t);
mpc_parser_t *mpca_root(mpc_parser_t *a);
mpc_parser_t *mpca_state(mpc_parser_t *a);
mpc_parser_t *mpca_total(mpc_parser_t *a);
mpc_parser_t *mpca_not(mpc_parser_t *a);
mpc_parser_t *mpca_maybe(mpc_parser_t *a);
mpc_parser_t *mpca_many(mpc_parser_t *a);
mpc_parser_t *mpca_many1(mpc_parser_t *a);
mpc_parser_t *mpca_count(int n, mpc_parser_t *a);
mpc_parser_t *mpca_or(int n, ...);
mpc_parser_t *mpca_and(int n, ...);
enum {
MPCA_LANG_DEFAULT = 0,
MPCA_LANG_PREDICTIVE = 1,
MPCA_LANG_WHITESPACE_SENSITIVE = 2
};
mpc_parser_t *mpca_grammar(int flags, const char *grammar, ...);
mpc_err_t *mpca_lang(int flags, const char *language, ...);
mpc_err_t *mpca_lang_file(int flags, FILE *f, ...);
mpc_err_t *mpca_lang_pipe(int flags, FILE *f, ...);
mpc_err_t *mpca_lang_contents(int flags, const char *filename, ...);
/*
** Misc
*/
void mpc_print(mpc_parser_t *p);
void mpc_optimise(mpc_parser_t *p);
void mpc_stats(mpc_parser_t *p);
int mpc_test_pass(mpc_parser_t *p, const char *s, const void *d,
int(*tester)(const void*, const void*),
mpc_dtor_t destructor,
void(*printer)(const void*));
int mpc_test_fail(mpc_parser_t *p, const char *s, const void *d,
int(*tester)(const void*, const void*),
mpc_dtor_t destructor,
void(*printer)(const void*));
#ifdef __cplusplus
}
#endif
#endif

2
src/vendor/yar.c vendored
View file

@ -1,2 +0,0 @@
#define YAR_IMPLEMENTATION
#include "yar.h"

229
src/vendor/yar.h vendored
View file

@ -1,229 +0,0 @@
/* yar - dynamic arrays in C - public domain Nicholas Rixson 2025
*
* https://github.com/segcore/yar
*
* Licence: see end of file
Sample usage:
#define YAR_IMPLEMENTATION
#include "yar.h"
int main() {
// struct { double *items; size_t count; size_t capacity; } numbers = {0};
yar(double) numbers = {0};
*yar_append(&numbers) = 3.14159;
*yar_append(&numbers) = 2.71828;
*yar_append(&numbers) = 1.61803;
for(size_t i = 0; i < numbers.count; i++) {
printf("%f\n", numbers.items[i]);
}
yar_free(&numbers);
}
*/
#ifndef YAR_H
#define YAR_H
#include <stddef.h> // size_t
#include <string.h> // strlen
/*
* yar(type) - Declare a new basic dynamic array
*
* yar_append(array) - Add a new item at the end of the array, and return a pointer to it
*
* yar_reserve(array, extra) - Reserve space for `extra` count of items
*
* yar_append_many(array, data, num) - Append a copy of existing data
*
* yar_append_cstr(array, data) - Append a C string (nul-terminated char array)
*
* yar_insert(array, index, num) - Insert items somewhere within the array. Moves items to higher indexes as required. Returns &array[index]
*
* yar_remove(array, index, num) - Remove items from somewhere within the array. Moves items to lower indexes as required.
*
* yar_reset(array) - Reset the count of elements to 0, to re-use the memory. Does not free the memory.
*
* yar_init(array) - Set items, count, and capacity to 0. Can usually be avoided with <declaration> = {0};
*
* yar_free(array) - Free items memory, and set the items, count, and capacity to 0.
*/
#define yar(type) struct { type *items; size_t count; size_t capacity; }
#define yar_append(array) ((_yar_append((void**)&(array)->items, &(array)->count, &(array)->capacity, sizeof((array)->items[0])) ? \
&(array)->items[(array)->count - 1] : NULL))
#define yar_reserve(array, extra) ((_yar_reserve((void**)&(array)->items, &(array)->count, &(array)->capacity, sizeof((array)->items[0]), (extra)) ? \
&(array)->items[(array)->count] : NULL))
#define yar_append_many(array, data, num) ((_yar_append_many((void**)&(array)->items, &(array)->count, &(array)->capacity, sizeof((array)->items[0]), 1 ? (data) : ((array)->items), (num)) ))
#define yar_append_cstr(array, data) yar_append_many(array, data, strlen(data))
#define yar_insert(array, index, num) ((_yar_insert((void**)&(array)->items, &(array)->count, &(array)->capacity, sizeof((array)->items[0]), index, num) ))
#define yar_remove(array, index, num) ((_yar_remove((void**)&(array)->items, &(array)->count, sizeof((array)->items[0]), index, num) ))
#define yar_reset(array) (((array)->count = 0))
#define yar_init(array) ((array)->items = NULL, (array)->count = 0, (array)->capacity = 0)
#define yar_free(array) ((_yar_free((array)->items)), (array)->items = NULL, (array)->count = 0, (array)->capacity = 0)
#ifndef YARAPI
#define YARAPI // nothing; overridable if needed.
#endif
#ifdef __cplusplus
extern "C" {
#endif
// Implementation functions
YARAPI void* _yar_append(void** items_pointer, size_t* count, size_t* capacity, size_t item_size);
YARAPI void* _yar_append_many(void** items_pointer, size_t* count, size_t* capacity, size_t item_size, void* data, size_t extra);
YARAPI void* _yar_reserve(void** items_pointer, size_t* count, size_t* capacity, size_t item_size, size_t extra);
YARAPI void* _yar_insert(void** items_pointer, size_t* count, size_t* capacity, size_t item_size, size_t index, size_t extra);
YARAPI void* _yar_remove(void** items_pointer, size_t* count, size_t item_size, size_t index, size_t remove);
YARAPI void* _yar_realloc(void* p, size_t new_size);
YARAPI void _yar_free(void* p);
#ifdef __cplusplus
}
#endif
#endif // YAR_H
#if defined(YAR_IMPLEMENTATION)
#ifndef YAR_MIN_CAP
#define YAR_MIN_CAP 16
#endif
#ifndef YAR_REALLOC
#define YAR_REALLOC realloc
#endif
#ifndef YAR_FREE
#define YAR_FREE free
#endif
#include <string.h> // mem* functions
YARAPI void* _yar_append(void** items_pointer, size_t* count, size_t* capacity, size_t item_size)
{
void* result = _yar_reserve(items_pointer, count, capacity, item_size, 1);
if (result != NULL) *count += 1;
return result;
}
YARAPI void* _yar_append_many(void** items_pointer, size_t* count, size_t* capacity, size_t item_size, void* data, size_t extra)
{
void* result = _yar_reserve(items_pointer, count, capacity, item_size, extra);
if (result != NULL) {
memcpy(result, data, item_size * extra);
*count += extra;
}
return result;
}
YARAPI void* _yar_reserve(void** items_pointer, size_t* count, size_t* capacity, size_t item_size, size_t extra)
{
char* items = *items_pointer;
size_t newcount = *count + extra;
if (newcount > *capacity) {
size_t newcap = (*capacity < YAR_MIN_CAP) ? YAR_MIN_CAP : *capacity * 8 / 5;
if (newcap < newcount) newcap = newcount;
void* next = _yar_realloc(items, newcap * item_size);
if (next == NULL) return NULL;
items = next;
*items_pointer = next;
*capacity = newcap;
}
void* result = items + (*count * item_size);
if (extra && result) memset(result, 0, item_size * extra);
return result;
}
YARAPI void* _yar_insert(void** items_pointer, size_t* count, size_t* capacity, size_t item_size, size_t index, size_t extra)
{
void* next = _yar_reserve(items_pointer, count, capacity, item_size, extra);
if(next == NULL) return NULL;
char* items = *items_pointer;
if (index < *count)
{
memmove(&items[item_size * (index + extra)], &items[item_size * index], (*count - index) * item_size);
memset(&items[item_size * index], 0, extra * item_size);
}
*count += extra;
return items + index * item_size;
}
YARAPI void* _yar_remove(void** items_pointer, size_t* count, size_t item_size, size_t index, size_t remove)
{
if(remove >= *count) {
*count = 0;
return *items_pointer;
}
if (index >= *count) {
return *items_pointer;
}
char* items = *items_pointer;
memmove(&items[item_size * index], &items[item_size * (index + remove)], item_size * (*count - (index + remove)));
*count -= remove;
return items + item_size * index;
}
YARAPI void* _yar_realloc(void* p, size_t new_size)
{
// Declaration, so we can call it if the definition is overridden
extern void* YAR_REALLOC(void *ptr, size_t size);
return YAR_REALLOC(p, new_size);
}
YARAPI void _yar_free(void* p)
{
extern void YAR_FREE(void *ptr);
YAR_FREE(p);
}
#endif // YAR_IMPLEMENTATION
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2025 Nicholas Rixson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

509
src/vm.c
View file

@ -1,509 +0,0 @@
#include <setjmp.h>
#include <stdio.h>
#include "arena.h"
#include "chunk.h"
#include "compile.h"
#include "dictionary.h"
#include "file.h"
#include "gc.h"
#include "object.h"
#include "primitive.h"
#include "string.h"
#include "userdata.h"
#include "vm.h"
static I decode_sleb128(U8 **ptr) {
I result = 0;
I shift = 0;
U8 byte;
do {
byte = **ptr;
(*ptr)++;
result |= (I)(byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
if ((shift < 64) && (byte & 0x40)) {
result |= -(1LL << shift);
}
return result;
}
V vm_init(Vm *vm) {
vm->sp = vm->stack;
vm->rsp = vm->rstack;
vm->tsp = vm->tstack;
vm->chunk = NULL;
vm->dictionary = NULL;
gc_init(&vm->gc);
arena_init(&vm->arena, 1024 * 1024);
for (Z i = 0; i < STACK_SIZE; i++) {
vm->stack[i] = NIL;
vm->tstack[i] = NIL;
vm->rstack[i].obj = NIL;
gc_addroot(&vm->gc, &vm->stack[i]);
gc_addroot(&vm->gc, &vm->tstack[i]);
gc_addroot(&vm->gc, &vm->rstack[i].obj);
}
vm->next_call = NIL;
gc_addroot(&vm->gc, &vm->next_call);
vm->trampoline = chunk_new("<trampoline>");
chunk_emit_byte(vm->trampoline, OP_CALL_NEXT);
vm->stdin = userdata_make(vm, (void *)stdin, &userdata_file);
vm->stdout = userdata_make(vm, (void *)stdout, &userdata_file);
vm->stderr = userdata_make(vm, (void *)stderr, &userdata_file);
gc_addroot(&vm->gc, &vm->stdin);
gc_addroot(&vm->gc, &vm->stdout);
gc_addroot(&vm->gc, &vm->stderr);
}
V vm_deinit(Vm *vm) {
chunk_release(vm->trampoline);
// Free all definitions
Dt *dstack[256];
Dt **dsp = dstack;
*dsp++ = vm->dictionary;
while (dsp > dstack) {
Dt *node = *--dsp;
if (!node)
continue;
if (node->chunk != NULL)
chunk_release(node->chunk);
for (I i = 0; i < 4; i++) {
if (node->child[i] != NULL)
*dsp++ = node->child[i];
}
}
arena_free(&vm->arena);
vm->dictionary = NULL;
// Run final GC pass
gc_collect(vm, 1);
gc_deinit(&vm->gc);
}
static V vm_error(Vm *vm, I error, const char *message) {
I col = -1;
I line = chunk_get_line(vm->chunk, vm->ip - vm->chunk->items, &col);
fprintf(stderr, "error at %ld:%ld: %s\n", line + 1, col + 1, message);
longjmp(vm->error, error);
}
V vm_push(Vm *vm, O o) {
if (vm->sp >= vm->stack + STACK_SIZE)
vm_error(vm, VM_ERR_STACK_OVERFLOW, "data stack overflow");
*vm->sp++ = o;
}
O vm_pop(Vm *vm) {
if (vm->sp <= vm->stack)
vm_error(vm, VM_ERR_STACK_UNDERFLOW, "data stack underflow");
O o = *--vm->sp;
*vm->sp = NIL;
return o;
}
V vm_tpush(Vm *vm, O o) {
if (vm->tsp >= vm->tstack + STACK_SIZE)
vm_error(vm, VM_ERR_STACK_OVERFLOW, "retain stack overflow");
*vm->tsp++ = o;
}
O vm_tpop(Vm *vm) {
if (vm->tsp <= vm->tstack)
vm_error(vm, VM_ERR_STACK_UNDERFLOW, "retain stack underflow");
O o = *--vm->tsp;
*vm->tsp = NIL;
return o;
}
V vm_rpush(Vm *vm, Bc *chunk, U8 *ip) {
if (vm->rsp >= vm->rstack + STACK_SIZE)
vm_error(vm, VM_ERR_STACK_OVERFLOW, "return stack overflow");
vm->rsp->chunk = chunk;
vm->rsp->ip = ip;
vm->rsp->obj = NIL;
vm->rsp++;
}
Fr vm_rpop(Vm *vm) {
if (vm->rsp <= vm->rstack)
vm_error(vm, VM_ERR_STACK_UNDERFLOW, "return stack underflow");
return *--vm->rsp;
}
I vm_run(Vm *vm, Bc *chunk, I offset) {
I mark = gc_mark(&vm->gc);
if (setjmp(vm->error) != 0) {
gc_reset(&vm->gc, mark);
return 0;
}
for (Z i = 0; i < chunk->constants.count; i++)
gc_addroot(&vm->gc, &chunk->constants.items[i]);
#define BINOP(op) \
{ \
O b = vm_pop(vm); \
O a = vm_pop(vm); \
if (!IMM(a) || !IMM(b)) \
vm_error(vm, VM_ERR_TYPE, "numop on non-numeric objects"); \
vm_push(vm, NUM(ORD(a) op ORD(b))); \
break; \
}
#define CMPOP(op) \
{ \
O b = vm_pop(vm); \
O a = vm_pop(vm); \
if (!IMM(a) || !IMM(b)) \
vm_error(vm, VM_ERR_TYPE, "comparison on non-numeric objects"); \
vm_push(vm, (ORD(a) op ORD(b)) ? NUM(1) : NIL); \
break; \
}
vm->ip = chunk->items + offset;
vm->chunk = chunk;
for (;;) {
U8 opcode;
switch (opcode = *vm->ip++) {
case OP_NOP:
continue;
case OP_NIL:
vm_push(vm, NIL);
break;
case OP_CONST: {
I idx = decode_sleb128(&vm->ip);
vm_push(vm, vm->chunk->constants.items[idx]);
break;
}
case OP_DROP: {
(void)vm_pop(vm);
break;
}
case OP_2DROP: {
(void)vm_pop(vm);
(void)vm_pop(vm);
break;
}
case OP_DUP: {
O obj = vm_pop(vm);
vm_push(vm, obj);
vm_push(vm, obj);
break;
}
case OP_2DUP: {
O obj2 = vm_pop(vm);
O obj1 = vm_pop(vm);
vm_push(vm, obj1);
vm_push(vm, obj2);
vm_push(vm, obj1);
vm_push(vm, obj2);
break;
}
case OP_SWAP: {
O b = vm_pop(vm);
O a = vm_pop(vm);
vm_push(vm, b);
vm_push(vm, a);
break;
}
case OP_2SWAP: {
O d = vm_pop(vm);
O c = vm_pop(vm);
O b = vm_pop(vm);
O a = vm_pop(vm);
vm_push(vm, c);
vm_push(vm, d);
vm_push(vm, a);
vm_push(vm, b);
break;
}
case OP_NIP: {
/* a b -> b */
O b = vm_pop(vm);
(void)vm_pop(vm);
vm_push(vm, b);
break;
}
case OP_OVER: {
/* a b -> a b a */
O b = vm_pop(vm);
O a = vm_pop(vm);
vm_push(vm, a);
vm_push(vm, b);
vm_push(vm, a);
break;
}
case OP_BURY: {
/* a b c - c a b */
O c = vm_pop(vm);
O b = vm_pop(vm);
O a = vm_pop(vm);
vm_push(vm, c);
vm_push(vm, a);
vm_push(vm, b);
break;
}
case OP_DIG: {
/* a b c - b c a */
O c = vm_pop(vm);
O b = vm_pop(vm);
O a = vm_pop(vm);
vm_push(vm, b);
vm_push(vm, c);
vm_push(vm, a);
break;
}
case OP_TOR: {
vm_tpush(vm, vm_pop(vm));
break;
}
case OP_2TOR: {
O obj2 = vm_pop(vm);
O obj1 = vm_pop(vm);
vm_tpush(vm, obj1);
vm_tpush(vm, obj2);
break;
}
case OP_FROMR: {
vm_push(vm, vm_tpop(vm));
break;
}
case OP_2FROMR: {
O obj2 = vm_tpop(vm);
O obj1 = vm_tpop(vm);
vm_push(vm, obj1);
vm_push(vm, obj2);
break;
}
case OP_DOWORD: {
I idx = decode_sleb128(&vm->ip);
Dt *word = vm->chunk->symbols.items[idx].resolved;
if (!word)
vm_error(vm, VM_ERR_RUNTIME, "word not found");
vm_rpush(vm, vm->chunk, vm->ip);
vm->chunk = word->chunk;
vm->ip = word->chunk->items;
break;
}
case OP_CALL: {
O quot = vm_pop(vm);
vm_rpush(vm, vm->chunk, vm->ip);
do_call:
switch (type(quot)) {
case OBJ_QUOT: {
Bc **ptr = (Bc **)(UNBOX(quot) + 1);
Bc *chunk = *ptr;
vm->chunk = chunk;
vm->ip = chunk->items;
break;
}
case OBJ_COMPOSE: {
Qo *comp = (Qo *)(UNBOX(quot) + 1);
vm_rpush(vm, vm->trampoline, vm->trampoline->items);
vm->rsp[-1].obj = comp->second;
quot = comp->first;
goto do_call;
}
case OBJ_CURRY: {
Qc *curry = (Qc *)(UNBOX(quot) + 1);
vm_push(vm, curry->value);
quot = curry->callable;
goto do_call;
break;
}
default:
vm_error(vm, VM_ERR_TYPE, "attempt to call non-quotation object");
}
break;
}
case OP_TAIL_DOWORD: {
I idx = decode_sleb128(&vm->ip);
Dt *word = vm->chunk->symbols.items[idx].resolved;
if (!word)
vm_error(vm, VM_ERR_RUNTIME, "word not found");
vm->chunk = word->chunk;
vm->ip = word->chunk->items;
break;
}
case OP_CALL_NEXT:
vm_push(vm, vm->next_call);
vm->next_call = NIL;
// fallthrough
case OP_TAIL_CALL: {
O quot = vm_pop(vm);
do_tail_call:
switch (type(quot)) {
case OBJ_QUOT: {
Bc **ptr = (Bc **)(UNBOX(quot) + 1);
Bc *chunk = *ptr;
vm->chunk = chunk;
vm->ip = chunk->items;
break;
}
case OBJ_COMPOSE: {
Qo *comp = (Qo *)(UNBOX(quot) + 1);
vm_rpush(vm, vm->trampoline, vm->trampoline->items);
vm->rsp[-1].obj = comp->second;
quot = comp->first;
goto do_tail_call;
}
case OBJ_CURRY: {
Qc *curry = (Qc *)(UNBOX(quot) + 1);
vm_push(vm, curry->value);
quot = curry->callable;
goto do_tail_call;
break;
}
default:
vm_error(vm, VM_ERR_TYPE, "attempt to call non-quotation object");
}
break;
}
case OP_PRIM: {
I idx = decode_sleb128(&vm->ip);
Pr prim = primitives_table[idx];
I err = prim.fn(vm);
if (err != 0)
vm_error(vm, err, "primitive call failed");
break;
}
case OP_COMPOSE: {
I mark = gc_mark(&vm->gc);
O c1 = vm_pop(vm);
O c2 = vm_pop(vm);
gc_addroot(&vm->gc, &c2);
gc_addroot(&vm->gc, &c1);
if (!callable(c2) || !callable(c1))
vm_error(vm, VM_ERR_TYPE, "non-callable arguments to compose");
Hd *hd = gc_alloc(vm, sizeof(Hd) + sizeof(Qo));
hd->type = OBJ_COMPOSE;
Qo *comp = (Qo *)(hd + 1);
comp->first = c2;
comp->second = c1;
vm_push(vm, BOX(hd));
gc_reset(&vm->gc, mark);
break;
}
case OP_CURRY: {
I mark = gc_mark(&vm->gc);
O cble = vm_pop(vm);
O value = vm_pop(vm);
gc_addroot(&vm->gc, &cble);
gc_addroot(&vm->gc, &value);
if (!callable(cble))
vm_error(vm, VM_ERR_TYPE, "non-callable argument to curry");
Hd *hd = gc_alloc(vm, sizeof(Hd) + sizeof(Qc));
hd->type = OBJ_CURRY;
Qc *curry = (Qc *)(hd + 1);
curry->value = value;
curry->callable = cble;
vm_push(vm, BOX(hd));
gc_reset(&vm->gc, mark);
break;
}
case OP_RETURN:
if (vm->rsp != vm->rstack) {
Fr frame = vm_rpop(vm);
vm->next_call = frame.obj;
vm->chunk = frame.chunk;
vm->ip = frame.ip;
} else {
goto done;
}
break;
case OP_CHOOSE: {
O fals = vm_pop(vm);
O tru = vm_pop(vm);
O cond = vm_pop(vm);
if (cond == NIL) {
vm_push(vm, fals);
} else {
vm_push(vm, tru);
}
break;
}
case OP_ADD:
BINOP(+);
case OP_SUB:
BINOP(-);
case OP_MUL:
BINOP(*);
case OP_DIV:
BINOP(/);
case OP_MOD:
BINOP(%);
case OP_LOGAND:
BINOP(&);
case OP_LOGOR:
BINOP(|);
case OP_LOGXOR:
BINOP(^);
case OP_LOGNOT: {
O o = vm_pop(vm);
if (!IMM(o))
vm_error(vm, VM_ERR_TYPE, "numop on non-number");
vm_push(vm, NUM(~ORD(o)));
break;
}
case OP_EQ:
CMPOP(==);
case OP_NEQ:
CMPOP(!=);
case OP_LT:
CMPOP(<);
case OP_GT:
CMPOP(>);
case OP_LTE:
CMPOP(<=);
case OP_GTE:
CMPOP(>=);
case OP_AND: {
O b = vm_pop(vm);
O a = vm_pop(vm);
if (a == NIL) {
vm_push(vm, NIL);
} else {
vm_push(vm, b);
}
break;
}
case OP_OR: {
O b = vm_pop(vm);
O a = vm_pop(vm);
if (a == NIL) {
vm_push(vm, b);
} else {
vm_push(vm, a);
}
break;
}
case OP_CONCAT: {
O b = vm_pop(vm);
if (type(b) != OBJ_STR)
vm_error(vm, VM_ERR_TYPE, "expected string");
O a = vm_pop(vm);
if (type(a) != OBJ_STR)
vm_error(vm, VM_ERR_TYPE, "expected string");
vm_push(vm, string_concat(vm, a, b));
break;
}
default:
vm_error(vm, VM_ERR_RUNTIME, "unknown opcode");
}
}
done:
gc_reset(&vm->gc, mark);
return 1;
}

103
src/vm.h
View file

@ -1,103 +0,0 @@
#ifndef VM_H
#define VM_H
#include <setjmp.h>
#include "common.h"
#include "arena.h"
#include "chunk.h"
#include "dictionary.h"
#include "gc.h"
#include "object.h"
enum {
OP_NOP = 0,
OP_CONST,
OP_NIL,
OP_DROP,
OP_2DROP,
OP_DUP,
OP_2DUP,
OP_SWAP,
OP_2SWAP,
OP_NIP,
OP_OVER,
OP_BURY,
OP_DIG,
OP_TOR,
OP_2TOR,
OP_FROMR,
OP_2FROMR,
OP_DOWORD,
OP_CALL,
OP_TAIL_DOWORD,
OP_TAIL_CALL,
OP_PRIM,
OP_COMPOSE,
OP_CURRY,
OP_RETURN,
OP_CHOOSE,
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV,
OP_MOD,
OP_EQ,
OP_NEQ,
OP_LOGAND,
OP_LOGOR,
OP_LOGXOR,
OP_LOGNOT,
OP_LT,
OP_GT,
OP_LTE,
OP_GTE,
OP_AND,
OP_OR,
OP_CONCAT,
OP_CALL_NEXT,
};
#define STACK_SIZE 256
typedef struct Fr {
Bc *chunk;
U8 *ip;
O obj;
} Fr;
typedef struct Vm {
Gc gc;
O stack[STACK_SIZE], *sp;
O tstack[STACK_SIZE], *tsp;
Fr rstack[STACK_SIZE], *rsp;
U8 *ip;
Bc *chunk;
Dt *dictionary;
Ar arena;
jmp_buf error;
Bc *trampoline;
O next_call;
// These objects need to stay as roots!
O stdin, stdout, stderr;
} Vm;
enum {
VM_ERR_STACK_OVERFLOW = 1,
VM_ERR_STACK_UNDERFLOW,
VM_ERR_TYPE,
VM_ERR_RUNTIME
};
V vm_init(Vm *);
V vm_deinit(Vm *);
I vm_run(Vm *, Bc *, I);
V vm_push(Vm *, O);
O vm_pop(Vm *);
V vm_tpush(Vm *, O);
O vm_tpop(Vm *);
#endif

View file

View file

@ -1,22 +0,0 @@
This is a Unix port of the Plan 9 UTF-8 library, by Rob Pike and Ken Thompson.
Please send comments about the packaging to Russ Cox <rsc@swtch.com>.
Copyright © 2021 Plan 9 Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -1,41 +0,0 @@
project('libutf', 'c')
add_project_arguments(
'-Wno-missing-braces',
'-Wno-parentheses',
'-Wno-sign-compare',
language: 'c'
)
inc = include_directories('.')
libutf = static_library(
'utf',
[
'rune.c',
'runestrcat.c',
'runestrchr.c',
'runestrcmp.c',
'runestrcpy.c',
'runestrdup.c',
'runestrecpy.c',
'runestrlen.c',
'runestrncat.c',
'runestrncmp.c',
'runestrncpy.c',
'runestrrchr.c',
'runestrstr.c',
'runetype.c',
'utfecpy.c',
'utflen.c',
'utfnlen.c',
'utfrrune.c',
'utfrune.c',
'utfutf.c',
],
)
libutf_dep = declare_dependency(
include_directories: inc,
link_with: libutf
)

View file

@ -1,28 +0,0 @@
/*
* compiler directive on Plan 9
*/
#ifndef USED
#define USED(x) if(x);else
#endif
/*
* easiest way to make sure these are defined
*/
#define uchar _utfuchar
#define ushort _utfushort
#define uint _utfuint
#define ulong _utfulong
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
/*
* nil cannot be ((void*)0) on ANSI C,
* because it is used for function pointers
*/
#undef nil
#define nil 0
#undef nelem
#define nelem(x) (sizeof (x)/sizeof (x)[0])

View file

@ -1,217 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
enum
{
Bit1 = 7,
Bitx = 6,
Bit2 = 5,
Bit3 = 4,
Bit4 = 3,
Bit5 = 2,
T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */
Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0000 0000 0111 1111 */
Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0000 0000 0111 1111 1111 */
Rune3 = (1<<(Bit3+2*Bitx))-1, /* 0000 0000 1111 1111 1111 1111 */
Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0011 1111 1111 1111 1111 1111 */
Maskx = (1<<Bitx)-1, /* 0011 1111 */
Testx = Maskx ^ 0xFF, /* 1100 0000 */
Bad = Runeerror
};
int
chartorune(Rune *rune, char *str)
{
int c, c1, c2, c3;
long l;
/*
* one character sequence
* 00000-0007F => T1
*/
c = *(uchar*)str;
if(c < Tx) {
*rune = c;
return 1;
}
/*
* two character sequence
* 0080-07FF => T2 Tx
*/
c1 = *(uchar*)(str+1) ^ Tx;
if(c1 & Testx)
goto bad;
if(c < T3) {
if(c < T2)
goto bad;
l = ((c << Bitx) | c1) & Rune2;
if(l <= Rune1)
goto bad;
*rune = l;
return 2;
}
/*
* three character sequence
* 0800-FFFF => T3 Tx Tx
*/
c2 = *(uchar*)(str+2) ^ Tx;
if(c2 & Testx)
goto bad;
if(c < T4) {
l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
if(l <= Rune2)
goto bad;
*rune = l;
return 3;
}
/*
* four character sequence
* 10000-10FFFF => T4 Tx Tx Tx
*/
if(UTFmax >= 4) {
c3 = *(uchar*)(str+3) ^ Tx;
if(c3 & Testx)
goto bad;
if(c < T5) {
l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4;
if(l <= Rune3)
goto bad;
if(l > Runemax)
goto bad;
*rune = l;
return 4;
}
}
/*
* bad decoding
*/
bad:
*rune = Bad;
return 1;
}
int
runetochar(char *str, Rune *rune)
{
long c;
/*
* one character sequence
* 00000-0007F => 00-7F
*/
c = *rune;
if(c <= Rune1) {
str[0] = c;
return 1;
}
/*
* two character sequence
* 00080-007FF => T2 Tx
*/
if(c <= Rune2) {
str[0] = T2 | (c >> 1*Bitx);
str[1] = Tx | (c & Maskx);
return 2;
}
/*
* three character sequence
* 00800-0FFFF => T3 Tx Tx
*/
if(c > Runemax)
c = Runeerror;
if(c <= Rune3) {
str[0] = T3 | (c >> 2*Bitx);
str[1] = Tx | ((c >> 1*Bitx) & Maskx);
str[2] = Tx | (c & Maskx);
return 3;
}
/*
* four character sequence
* 010000-1FFFFF => T4 Tx Tx Tx
*/
str[0] = T4 | (c >> 3*Bitx);
str[1] = Tx | ((c >> 2*Bitx) & Maskx);
str[2] = Tx | ((c >> 1*Bitx) & Maskx);
str[3] = Tx | (c & Maskx);
return 4;
}
int
runelen(long c)
{
Rune rune;
char str[10];
rune = c;
return runetochar(str, &rune);
}
int
runenlen(Rune *r, int nrune)
{
int nb, c;
nb = 0;
while(nrune--) {
c = *r++;
if(c <= Rune1)
nb++;
else
if(c <= Rune2)
nb += 2;
else
if(c <= Rune3 || c > Runemax)
nb += 3;
else
nb += 4;
}
return nb;
}
int
fullrune(char *str, int n)
{
int c;
if(n <= 0)
return 0;
c = *(uchar*)str;
if(c < Tx)
return 1;
if(c < T3)
return n >= 2;
if(UTFmax == 3 || c < T4)
return n >= 3;
return n >= 4;
}

View file

@ -1,25 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
Rune*
runestrcat(Rune *s1, Rune *s2)
{
runestrcpy(runestrchr(s1, 0), s2);
return s1;
}

View file

@ -1,35 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
Rune*
runestrchr(Rune *s, Rune c)
{
Rune c0 = c;
Rune c1;
if(c == 0) {
while(*s++)
;
return s-1;
}
while(c1 = *s++)
if(c1 == c0)
return s-1;
return 0;
}

View file

@ -1,35 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
int
runestrcmp(Rune *s1, Rune *s2)
{
Rune c1, c2;
for(;;) {
c1 = *s1++;
c2 = *s2++;
if(c1 != c2) {
if(c1 > c2)
return 1;
return -1;
}
if(c1 == 0)
return 0;
}
}

View file

@ -1,28 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
Rune*
runestrcpy(Rune *s1, Rune *s2)
{
Rune *os1;
os1 = s1;
while(*s1++ = *s2++)
;
return os1;
}

View file

@ -1,30 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include "plan9.h"
#include "utf.h"
Rune*
runestrdup(Rune *s)
{
Rune *ns;
ns = malloc(sizeof(Rune)*(runestrlen(s) + 1));
if(ns == 0)
return 0;
return runestrcpy(ns, s);
}

View file

@ -1,32 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
Rune*
runestrecpy(Rune *s1, Rune *es1, Rune *s2)
{
if(s1 >= es1)
return s1;
while(*s1++ = *s2++){
if(s1 == es1){
*--s1 = '\0';
break;
}
}
return s1;
}

View file

@ -1,24 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
long
runestrlen(Rune *s)
{
return runestrchr(s, 0) - s;
}

View file

@ -1,32 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
Rune*
runestrncat(Rune *s1, Rune *s2, long n)
{
Rune *os1;
os1 = s1;
s1 = runestrchr(s1, 0);
while(*s1++ = *s2++)
if(--n < 0) {
s1[-1] = 0;
break;
}
return os1;
}

View file

@ -1,37 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
int
runestrncmp(Rune *s1, Rune *s2, long n)
{
Rune c1, c2;
while(n > 0) {
c1 = *s1++;
c2 = *s2++;
n--;
if(c1 != c2) {
if(c1 > c2)
return 1;
return -1;
}
if(c1 == 0)
break;
}
return 0;
}

View file

@ -1,33 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
Rune*
runestrncpy(Rune *s1, Rune *s2, long n)
{
int i;
Rune *os1;
os1 = s1;
for(i = 0; i < n; i++)
if((*s1++ = *s2++) == 0) {
while(++i < n)
*s1++ = 0;
return os1;
}
return os1;
}

View file

@ -1,30 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
Rune*
runestrrchr(Rune *s, Rune c)
{
Rune *r;
if(c == 0)
return runestrchr(s, 0);
r = 0;
while(s = runestrchr(s, c))
r = s++;
return r;
}

View file

@ -1,44 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
/*
* Return pointer to first occurrence of s2 in s1,
* 0 if none
*/
Rune*
runestrstr(Rune *s1, Rune *s2)
{
Rune *p, *pa, *pb;
int c0, c;
c0 = *s2;
if(c0 == 0)
return s1;
s2++;
for(p=runestrchr(s1, c0); p; p=runestrchr(p+1, c0)) {
pa = p;
for(pb=s2;; pb++) {
c = *pb;
if(c == 0)
return p;
if(c != *++pa)
break;
}
}
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -1,54 +0,0 @@
#ifndef _UTF_H_
#define _UTF_H_ 1
#if defined(__cplusplus)
extern "C" {
#endif
typedef unsigned int Rune; /* 32 bits */
enum
{
UTFmax = 4, /* maximum bytes per rune */
Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
Runeself = 0x80, /* rune and UTF sequences are the same (<) */
Runeerror = 0xFFFD, /* decoding error in UTF */
Runemax = 0x10FFFF /* maximum rune value */
};
/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/utf/?*.c | grep -v static |grep -v __ */
int chartorune(Rune *rune, char *str);
int fullrune(char *str, int n);
int isalpharune(Rune c);
int islowerrune(Rune c);
int isspacerune(Rune c);
int istitlerune(Rune c);
int isupperrune(Rune c);
int runelen(long c);
int runenlen(Rune *r, int nrune);
Rune* runestrcat(Rune *s1, Rune *s2);
Rune* runestrchr(Rune *s, Rune c);
int runestrcmp(Rune *s1, Rune *s2);
Rune* runestrcpy(Rune *s1, Rune *s2);
Rune* runestrdup(Rune *s) ;
Rune* runestrecpy(Rune *s1, Rune *es1, Rune *s2);
long runestrlen(Rune *s);
Rune* runestrncat(Rune *s1, Rune *s2, long n);
int runestrncmp(Rune *s1, Rune *s2, long n);
Rune* runestrncpy(Rune *s1, Rune *s2, long n);
Rune* runestrrchr(Rune *s, Rune c);
Rune* runestrstr(Rune *s1, Rune *s2);
int runetochar(char *str, Rune *rune);
Rune tolowerrune(Rune c);
Rune totitlerune(Rune c);
Rune toupperrune(Rune c);
char* utfecpy(char *to, char *e, char *from);
int utflen(char *s);
int utfnlen(char *s, long m);
char* utfrrune(char *s, long c);
char* utfrune(char *s, long c);
char* utfutf(char *s1, char *s2);
#if defined(__cplusplus)
}
#endif
#endif

View file

@ -1,32 +0,0 @@
/*
* compiler directive on Plan 9
*/
#ifndef USED
#define USED(x) if(x);else
#endif
/*
* easiest way to make sure these are defined
*/
#define uchar _fmtuchar
#define ushort _fmtushort
#define uint _fmtuint
#define ulong _fmtulong
#define vlong _fmtvlong
#define uvlong _fmtuvlong
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned long long uvlong;
typedef long long vlong;
/*
* nil cannot be ((void*)0) on ANSI C,
* because it is used for function pointers
*/
#undef nil
#define nil 0
#undef nelem
#define nelem ((void*)0)

View file

@ -1,38 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#define _BSD_SOURCE 1 /* memccpy */
#define _DEFAULT_SOURCE 1
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
char*
utfecpy(char *to, char *e, char *from)
{
char *end;
if(to >= e)
return to;
end = memccpy(to, from, '\0', e - to);
if(end == nil){
end = e-1;
while(end>to && (*--end&0xC0)==0x80)
;
*end = '\0';
}else{
end--;
}
return end;
}

View file

@ -1,37 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
int
utflen(char *s)
{
int c;
long n;
Rune rune;
n = 0;
for(;;) {
c = *(uchar*)s;
if(c < Runeself) {
if(c == 0)
return n;
s++;
} else
s += chartorune(&rune, s);
n++;
}
}

View file

@ -1,41 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
int
utfnlen(char *s, long m)
{
int c;
long n;
Rune rune;
char *es;
es = s + m;
for(n = 0; s < es; n++) {
c = *(uchar*)s;
if(c < Runeself){
if(c == '\0')
break;
s++;
continue;
}
if(!fullrune(s, es-s))
break;
s += chartorune(&rune, s);
}
return n;
}

View file

@ -1,45 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
char*
utfrrune(char *s, long c)
{
long c1;
Rune r;
char *s1;
if(c < Runesync) /* not part of utf sequence */
return strrchr(s, c);
s1 = 0;
for(;;) {
c1 = *(uchar*)s;
if(c1 < Runeself) { /* one byte rune */
if(c1 == 0)
return s1;
if(c1 == c)
s1 = s;
s++;
continue;
}
c1 = chartorune(&r, s);
if(r == c)
s1 = s;
s += c1;
}
}

View file

@ -1,44 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
char*
utfrune(char *s, long c)
{
long c1;
Rune r;
int n;
if(c < Runesync) /* not part of utf sequence */
return strchr(s, c);
for(;;) {
c1 = *(uchar*)s;
if(c1 < Runeself) { /* one byte rune */
if(c1 == 0)
return 0;
if(c1 == c)
return s;
s++;
continue;
}
n = chartorune(&r, s);
if(r == c)
return s;
s += n;
}
}

View file

@ -1,41 +0,0 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"
/*
* Return pointer to first occurrence of s2 in s1,
* 0 if none
*/
char*
utfutf(char *s1, char *s2)
{
char *p;
long f, n1, n2;
Rune r;
n1 = chartorune(&r, s2);
f = r;
if(f <= Runesync) /* represents self */
return strstr(s1, s2);
n2 = strlen(s2);
for(p=s1; p=utfrune(p, f); p+=n1)
if(strncmp(p, s2, n2) == 0)
return p;
return 0;
}