remove old interpreter from source tree
This commit is contained in:
parent
d279bf1d31
commit
e18681b309
91 changed files with 45 additions and 11539 deletions
80
meson.build
80
meson.build
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
28
next/main.c
28
next/main.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
31
src/arena.c
31
src/arena.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
18
src/arena.h
18
src/arena.h
|
|
@ -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
|
|
||||||
91
src/chunk.c
91
src/chunk.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
50
src/chunk.h
50
src/chunk.h
|
|
@ -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
|
|
||||||
16
src/common.h
16
src/common.h
|
|
@ -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
|
|
||||||
332
src/compile.c
332
src/compile.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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 *);
|
|
||||||
146
src/debug.c
146
src/debug.c
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
#include "chunk.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "dictionary.h"
|
|
||||||
|
|
||||||
V disassemble(Bc *, const char*, Dt **);
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
83
src/file.c
83
src/file.c
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
@ -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
215
src/gc.c
|
|
@ -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);
|
|
||||||
}
|
|
||||||
38
src/gc.h
38
src/gc.h
|
|
@ -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
|
|
||||||
217
src/lexer.c
217
src/lexer.c
|
|
@ -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);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
36
src/lexer.h
36
src/lexer.h
|
|
@ -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
|
|
||||||
107
src/main.c
107
src/main.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
10
src/object.c
10
src/object.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
48
src/object.h
48
src/object.h
|
|
@ -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
|
|
||||||
156
src/parser.c
156
src/parser.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
35
src/parser.h
35
src/parser.h
|
|
@ -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
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
91
src/print.c
91
src/print.c
|
|
@ -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');
|
|
||||||
}
|
|
||||||
10
src/print.h
10
src/print.h
|
|
@ -1,10 +0,0 @@
|
||||||
#ifndef PRINT_H
|
|
||||||
#define PRINT_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "object.h"
|
|
||||||
|
|
||||||
V print(O);
|
|
||||||
V println(O);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
39
src/stream.c
39
src/stream.c
|
|
@ -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
|
|
||||||
30
src/stream.h
30
src/stream.h
|
|
@ -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
|
|
||||||
51
src/string.c
51
src/string.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
13
src/string.h
13
src/string.h
|
|
@ -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);
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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
1763
src/vendor/linenoise.c
vendored
File diff suppressed because it is too large
Load diff
114
src/vendor/linenoise.h
vendored
114
src/vendor/linenoise.h
vendored
|
|
@ -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
4128
src/vendor/mpc.c
vendored
File diff suppressed because it is too large
Load diff
391
src/vendor/mpc.h
vendored
391
src/vendor/mpc.h
vendored
|
|
@ -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
2
src/vendor/yar.c
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
#define YAR_IMPLEMENTATION
|
|
||||||
#include "yar.h"
|
|
||||||
229
src/vendor/yar.h
vendored
229
src/vendor/yar.h
vendored
|
|
@ -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
509
src/vm.c
|
|
@ -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
103
src/vm.h
|
|
@ -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
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
@ -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])
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue