Lots of changes with long commit message again
1. (list ...) 2. string-* functions 3. (native str) to resolve native functions at runtime 4. Memory fuckups in compiler core unit loading 5. Now can call non-identifiers in compiler
This commit is contained in:
parent
e15327e2e5
commit
91961bcec5
1
Makefile
1
Makefile
@ -9,6 +9,7 @@ VM_OBJS=$(O)/vm/main.o \
|
|||||||
$(O)/vm/stack.o \
|
$(O)/vm/stack.o \
|
||||||
$(O)/vm/error.o \
|
$(O)/vm/error.o \
|
||||||
$(O)/vm/debug.o \
|
$(O)/vm/debug.o \
|
||||||
|
$(O)/vm/libcore.o \
|
||||||
$(O)/core/vector.o \
|
$(O)/core/vector.o \
|
||||||
$(O)/core/hash.o
|
$(O)/core/hash.o
|
||||||
COMPILER_OBJS=$(O)/compiler/main.o \
|
COMPILER_OBJS=$(O)/compiler/main.o \
|
||||||
|
@ -9,6 +9,17 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static void emit_rev_cons(struct function *fn,
|
||||||
|
struct context *ctx,
|
||||||
|
struct node *args) {
|
||||||
|
if (null_q(args)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit_rev_cons(fn, ctx, cdr(args));
|
||||||
|
emit(fn, ctx, car(args));
|
||||||
|
emit_insn(fn, OP(OP_CONS, 0));
|
||||||
|
}
|
||||||
|
|
||||||
int emit_builtin_syntax(struct function *fn,
|
int emit_builtin_syntax(struct function *fn,
|
||||||
struct context *ctx,
|
struct context *ctx,
|
||||||
const char *name,
|
const char *name,
|
||||||
@ -154,6 +165,10 @@ int emit_builtin_syntax(struct function *fn,
|
|||||||
cond_jmp = vector_ref(&fn->bytecode, cond_jmp_loc);
|
cond_jmp = vector_ref(&fn->bytecode, cond_jmp_loc);
|
||||||
*cond_jmp |= (end_loc - cond_jmp_loc) & 0xFFFFFF;
|
*cond_jmp |= (end_loc - cond_jmp_loc) & 0xFFFFFF;
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (!strcmp(name, "list")) {
|
||||||
|
emit_insn(fn, OP(OP_LDNIL, 0));
|
||||||
|
emit_rev_cons(fn, ctx, tail);
|
||||||
|
return 0;
|
||||||
} else if (!strcmp(name, "use") ||
|
} else if (!strcmp(name, "use") ||
|
||||||
!strcmp(name, "export")) {
|
!strcmp(name, "export")) {
|
||||||
// Ignore
|
// Ignore
|
||||||
|
@ -22,6 +22,11 @@ void emit_function(struct context *ctx, struct function *fn) {
|
|||||||
struct unit *root = ctx->root;
|
struct unit *root = ctx->root;
|
||||||
assert(ctx->owner == fn);
|
assert(ctx->owner == fn);
|
||||||
|
|
||||||
|
fn->argc = 0;
|
||||||
|
for (struct node *node = fn->args; node; node = cdr(node)) {
|
||||||
|
++fn->argc;
|
||||||
|
}
|
||||||
|
|
||||||
// Pass 0 - extract global scope
|
// Pass 0 - extract global scope
|
||||||
for (struct node *node = body; node; node = cdr(node)) {
|
for (struct node *node = body; node; node = cdr(node)) {
|
||||||
struct node *expr = car(node);
|
struct node *expr = car(node);
|
||||||
@ -124,6 +129,7 @@ void emit(struct function *fn, struct context *ctx, struct node *expr) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct node *n0;
|
struct node *n0;
|
||||||
|
size_t index;
|
||||||
int c0;
|
int c0;
|
||||||
|
|
||||||
switch (expr->type) {
|
switch (expr->type) {
|
||||||
@ -135,32 +141,39 @@ void emit(struct function *fn, struct context *ctx, struct node *expr) {
|
|||||||
fprintf(stderr, "Call to nil\n");
|
fprintf(stderr, "Call to nil\n");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
if (n0->type != N_IDENT) {
|
|
||||||
fprintf(stderr, "TODO non-identifier calls\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try builtin syntax construct
|
if (n0->type == N_IDENT) {
|
||||||
if (emit_builtin_syntax(fn, ctx, n0->n_ident, cdr(expr)) == 0) {
|
// Try builtin syntax construct
|
||||||
|
if (emit_builtin_syntax(fn, ctx, n0->n_ident, cdr(expr)) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
c0 = emit_arg_list(fn, ctx, expr->n_cons.cdr);
|
||||||
|
|
||||||
|
// Try builtin operator
|
||||||
|
if (emit_builtin_insn(fn, n0->n_ident, c0) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If everything else failed, try to resolve this as a function
|
||||||
|
// call
|
||||||
|
emit_call(fn, ctx, n0->n_ident);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
c0 = emit_arg_list(fn, ctx, expr->n_cons.cdr);
|
||||||
|
emit(fn, ctx, n0);
|
||||||
|
emit_insn(fn, OP(OP_CALL, 0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case N_STRING:
|
||||||
c0 = emit_arg_list(fn, ctx, expr->n_cons.cdr);
|
assert(unit_insert_string(ctx->root, expr->n_string, &index) == 0);
|
||||||
|
emit_insn(fn, OP(OP_LDS, index));
|
||||||
// Try builtin operator
|
|
||||||
if (emit_builtin_insn(fn, n0->n_ident, c0) == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If everything else failed, try to resolve this as a function
|
|
||||||
// call
|
|
||||||
emit_call(fn, ctx, n0->n_ident);
|
|
||||||
return;
|
return;
|
||||||
case N_INTEGER:
|
case N_INTEGER:
|
||||||
if (expr->n_integer < 0x7FFFFF && expr->n_integer > -0x7FFFFF) {
|
if (expr->n_integer < 0x7FFFFF && expr->n_integer > -0x7FFFFF) {
|
||||||
emit_insn(fn, OP(OP_LDI, expr->n_integer));
|
emit_insn(fn, OP(OP_LDI, expr->n_integer));
|
||||||
} else {
|
} else {
|
||||||
printf("ldc ...\n");
|
assert(0 && "TODO constant pool lmao");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case N_IDENT:
|
case N_IDENT:
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
enum node_type {
|
enum node_type {
|
||||||
N_CONS,
|
N_CONS,
|
||||||
N_IDENT,
|
N_IDENT,
|
||||||
N_INTEGER
|
N_INTEGER,
|
||||||
|
N_STRING
|
||||||
};
|
};
|
||||||
|
|
||||||
struct node {
|
struct node {
|
||||||
@ -16,6 +17,7 @@ struct node {
|
|||||||
struct node *car, *cdr;
|
struct node *car, *cdr;
|
||||||
} n_cons;
|
} n_cons;
|
||||||
char *n_ident;
|
char *n_ident;
|
||||||
|
char *n_string;
|
||||||
intmax_t n_integer;
|
intmax_t n_integer;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -40,6 +42,10 @@ static inline int integer_q(struct node *n) {
|
|||||||
return n && n->type == N_INTEGER;
|
return n && n->type == N_INTEGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int string_q(struct node *n) {
|
||||||
|
return n && n->type == N_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct node *car(struct node *n) {
|
static inline struct node *car(struct node *n) {
|
||||||
assert(pair_q(n));
|
assert(pair_q(n));
|
||||||
return n->n_cons.car;
|
return n->n_cons.car;
|
||||||
@ -62,6 +68,7 @@ static inline struct node *cddr(struct node *n) {
|
|||||||
return cdr(cdr(n));
|
return cdr(cdr(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct node *string_from_owned(char *ptr);
|
||||||
struct node *cons(struct node *a, struct node *b);
|
struct node *cons(struct node *a, struct node *b);
|
||||||
struct node *ident(const char *i);
|
struct node *ident(const char *i);
|
||||||
struct node *integer(intmax_t v);
|
struct node *integer(intmax_t v);
|
||||||
|
@ -35,6 +35,7 @@ struct ext_unit {
|
|||||||
struct function {
|
struct function {
|
||||||
size_t index;
|
size_t index;
|
||||||
size_t local_count;
|
size_t local_count;
|
||||||
|
size_t argc;
|
||||||
struct node *args, *body;
|
struct node *args, *body;
|
||||||
struct vector bytecode;
|
struct vector bytecode;
|
||||||
};
|
};
|
||||||
@ -47,6 +48,7 @@ struct export {
|
|||||||
struct unit {
|
struct unit {
|
||||||
struct context global;
|
struct context global;
|
||||||
|
|
||||||
|
struct vector strtab;
|
||||||
struct vector functions;
|
struct vector functions;
|
||||||
struct vector exports;
|
struct vector exports;
|
||||||
struct vector ext_refs;
|
struct vector ext_refs;
|
||||||
@ -59,5 +61,6 @@ struct function *unit_lambda(struct unit *u);
|
|||||||
int unit_ext_load(struct unit *self, const char *unit);
|
int unit_ext_load(struct unit *self, const char *unit);
|
||||||
int ctx_lookup_name(struct context *ctx, const char *name, int *kind, size_t *index);
|
int ctx_lookup_name(struct context *ctx, const char *name, int *kind, size_t *index);
|
||||||
int unit_lookup_global(struct unit *unit, const char *name, size_t *index);
|
int unit_lookup_global(struct unit *unit, const char *name, size_t *index);
|
||||||
|
int unit_insert_string(struct unit *unit, const char *text, size_t *index);
|
||||||
|
|
||||||
void emit_insn(struct function *fn, uint32_t insn);
|
void emit_insn(struct function *fn, uint32_t insn);
|
||||||
|
@ -47,8 +47,10 @@ static void write_unit(FILE *fp, struct unit *u) {
|
|||||||
hdr.func_table_size = u->functions.size;
|
hdr.func_table_size = u->functions.size;
|
||||||
for (size_t i = 0; i < u->functions.size; ++i) {
|
for (size_t i = 0; i < u->functions.size; ++i) {
|
||||||
struct function *fn = vector_ref(&u->functions, i);
|
struct function *fn = vector_ref(&u->functions, i);
|
||||||
offset += sizeof(struct bin_func_entry) + fn->bytecode.size;
|
offset += sizeof(struct bin_func_entry) + fn->bytecode.size * sizeof(uint32_t);
|
||||||
}
|
}
|
||||||
|
hdr.string_table_offset = offset;
|
||||||
|
hdr.string_table_size = u->strtab.size;
|
||||||
|
|
||||||
fwrite(&hdr, 1, sizeof(struct bin_header), fp);
|
fwrite(&hdr, 1, sizeof(struct bin_header), fp);
|
||||||
for (size_t i = 0; i < u->ext_units.size; ++i) {
|
for (size_t i = 0; i < u->ext_units.size; ++i) {
|
||||||
@ -77,16 +79,19 @@ static void write_unit(FILE *fp, struct unit *u) {
|
|||||||
for (size_t i = 0; i < u->functions.size; ++i) {
|
for (size_t i = 0; i < u->functions.size; ++i) {
|
||||||
struct function *fn = vector_ref(&u->functions, i);
|
struct function *fn = vector_ref(&u->functions, i);
|
||||||
struct bin_func_entry bin_func;
|
struct bin_func_entry bin_func;
|
||||||
size_t argc = 0;
|
bin_func.argc = fn->argc;
|
||||||
for (struct node *arg = fn->args; arg; arg = cdr(arg)) {
|
|
||||||
++argc;
|
|
||||||
}
|
|
||||||
bin_func.argc = argc;
|
|
||||||
bin_func.local_count = fn->local_count;
|
bin_func.local_count = fn->local_count;
|
||||||
bin_func.len = fn->bytecode.size * sizeof(uint32_t);
|
bin_func.len = fn->bytecode.size * sizeof(uint32_t);
|
||||||
fwrite(&bin_func, 1, sizeof(struct bin_func_entry), fp);
|
fwrite(&bin_func, 1, sizeof(struct bin_func_entry), fp);
|
||||||
fwrite(fn->bytecode.data, 1, fn->bytecode.size * sizeof(uint32_t), fp);
|
fwrite(fn->bytecode.data, 1, fn->bytecode.size * sizeof(uint32_t), fp);
|
||||||
}
|
}
|
||||||
|
for (size_t i = 0; i < u->strtab.size; ++i) {
|
||||||
|
char **ref = vector_ref(&u->strtab, i);
|
||||||
|
const char *str = *ref;
|
||||||
|
uint32_t len = strlen(str);
|
||||||
|
fwrite(&len, 1, sizeof(uint32_t), fp);
|
||||||
|
fwrite(str, 1, len + 1, fp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
@ -5,6 +5,13 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct node *string_from_owned(char *ptr) {
|
||||||
|
struct node *n = malloc(sizeof(struct node));
|
||||||
|
n->type = N_STRING;
|
||||||
|
n->n_string = ptr;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
struct node *cons(struct node *a, struct node *b) {
|
struct node *cons(struct node *a, struct node *b) {
|
||||||
struct node *n = malloc(sizeof(struct node));
|
struct node *n = malloc(sizeof(struct node));
|
||||||
n->type = N_CONS;
|
n->type = N_CONS;
|
||||||
@ -57,6 +64,9 @@ void vm_print(const struct node *node, int depth) {
|
|||||||
case N_INTEGER:
|
case N_INTEGER:
|
||||||
printf("%zd", node->n_integer);
|
printf("%zd", node->n_integer);
|
||||||
break;
|
break;
|
||||||
|
case N_STRING:
|
||||||
|
printf("\"%s\"", node->n_string);
|
||||||
|
break;
|
||||||
case N_CONS:
|
case N_CONS:
|
||||||
if (!depth) {
|
if (!depth) {
|
||||||
printf("(");
|
printf("(");
|
||||||
|
@ -202,6 +202,39 @@ int vm_parse(struct vm_parser *in, struct node **out) {
|
|||||||
buf[i] = 0;
|
buf[i] = 0;
|
||||||
*out = ident(buf);
|
*out = ident(buf);
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (ch == '"') {
|
||||||
|
char *buf;
|
||||||
|
size_t cap, len;
|
||||||
|
cap = 32;
|
||||||
|
len = 0;
|
||||||
|
buf = malloc(cap);
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(in->pop(in) == ch);
|
||||||
|
while (1) {
|
||||||
|
ch = in->peek(in);
|
||||||
|
if (!ch) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
assert(ch != '\\' && "Not implemented");
|
||||||
|
assert(in->pop(in) == ch);
|
||||||
|
if (ch == '"') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == cap - 1) {
|
||||||
|
cap += 32;
|
||||||
|
buf = realloc(buf, cap);
|
||||||
|
assert(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[len++] = ch;
|
||||||
|
}
|
||||||
|
buf[len] = 0;
|
||||||
|
*out = string_from_owned(buf);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Unrecognized character: #%c (%d)\n", ch, ch);
|
fprintf(stderr, "Unrecognized character: #%c (%d)\n", ch, ch);
|
||||||
|
@ -9,13 +9,17 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
static struct ext_unit_entry u_core_entries[] = {
|
static struct ext_unit_entry u_core_entries[] = {
|
||||||
|
{ "println" },
|
||||||
{ "print" },
|
{ "print" },
|
||||||
{ "file-open" },
|
{ "putc" },
|
||||||
{ "file-read" },
|
|
||||||
{ "file-write" },
|
{ "string-new" },
|
||||||
{ "file-close" },
|
{ "string-length" },
|
||||||
{ "file-seek" },
|
{ "string-ref" },
|
||||||
{ "exit" }
|
{ "string-set!" },
|
||||||
|
{ "string-append" },
|
||||||
|
|
||||||
|
{ "native" },
|
||||||
};
|
};
|
||||||
|
|
||||||
void unit_init(struct unit *u) {
|
void unit_init(struct unit *u) {
|
||||||
@ -27,6 +31,7 @@ void unit_init(struct unit *u) {
|
|||||||
vector_init(&u->ext_refs, sizeof(struct ext_ref));
|
vector_init(&u->ext_refs, sizeof(struct ext_ref));
|
||||||
vector_init(&u->ext_units, sizeof(struct ext_unit));
|
vector_init(&u->ext_units, sizeof(struct ext_unit));
|
||||||
vector_init(&u->exports, sizeof(struct export));
|
vector_init(&u->exports, sizeof(struct export));
|
||||||
|
vector_init(&u->strtab, sizeof(char *));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctx_init(struct context *ctx, struct context *parent) {
|
void ctx_init(struct context *ctx, struct context *parent) {
|
||||||
@ -128,9 +133,13 @@ static int parse_exports(FILE *fp, struct ext_unit *unit) {
|
|||||||
int unit_ext_load(struct unit *self, const char *name) {
|
int unit_ext_load(struct unit *self, const char *name) {
|
||||||
if (!strcmp(name, "core")) {
|
if (!strcmp(name, "core")) {
|
||||||
struct ext_unit *unit = vector_append(&self->ext_units);
|
struct ext_unit *unit = vector_append(&self->ext_units);
|
||||||
unit->exports.size = sizeof(u_core_entries) / sizeof(u_core_entries[0]);
|
vector_init(&unit->exports, sizeof(struct ext_unit_entry));
|
||||||
unit->exports.cap = unit->exports.size;
|
for (size_t i = 0; i < sizeof(u_core_entries) / sizeof(u_core_entries[0]); ++i) {
|
||||||
unit->exports.data = u_core_entries;
|
struct ext_unit_entry *from, *to;
|
||||||
|
from = &u_core_entries[i];
|
||||||
|
to = vector_append(&unit->exports);
|
||||||
|
memcpy(to, from, sizeof(struct ext_unit_entry));
|
||||||
|
}
|
||||||
strcpy(unit->name, name);
|
strcpy(unit->name, name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -148,6 +157,20 @@ int unit_ext_load(struct unit *self, const char *name) {
|
|||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int unit_insert_string(struct unit *unit, const char *text, size_t *index) {
|
||||||
|
// TODO find identical strings?
|
||||||
|
*index = unit->strtab.size;
|
||||||
|
char **ref = vector_append(&unit->strtab);
|
||||||
|
if (!ref) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
*ref = strdup(text);
|
||||||
|
if (!*ref) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO external references
|
// TODO external references
|
||||||
int ctx_lookup_name(struct context *ctx, const char *name, int *kind, size_t *index) {
|
int ctx_lookup_name(struct context *ctx, const char *name, int *kind, size_t *index) {
|
||||||
// 1. Lookup local
|
// 1. Lookup local
|
||||||
|
@ -16,6 +16,8 @@ struct bin_header {
|
|||||||
uint32_t export_table_size;
|
uint32_t export_table_size;
|
||||||
uint32_t func_table_offset;
|
uint32_t func_table_offset;
|
||||||
uint32_t func_table_size;
|
uint32_t func_table_size;
|
||||||
|
uint32_t string_table_offset;
|
||||||
|
uint32_t string_table_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bin_unit_entry {
|
struct bin_unit_entry {
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#define OP_LDC 0x30
|
#define OP_LDC 0x30
|
||||||
#define OP_LDNIL 0x31
|
#define OP_LDNIL 0x31
|
||||||
#define OP_LDI 0x32
|
#define OP_LDI 0x32
|
||||||
|
#define OP_LDS 0x33
|
||||||
|
|
||||||
#define OP_CAR 0x40
|
#define OP_CAR 0x40
|
||||||
#define OP_CDR 0x41
|
#define OP_CDR 0x41
|
||||||
|
18
mod1.vml
18
mod1.vml
@ -1,15 +1,11 @@
|
|||||||
(use core)
|
(use core)
|
||||||
|
|
||||||
(define (f2 x)
|
(define (string-chars s)
|
||||||
(define (f1 x y)
|
(define len (string-length s))
|
||||||
(define (f0 x y z)
|
(define out nil)
|
||||||
(+ x y z)
|
(while (/= len 0)
|
||||||
)
|
(setq len (- len 1))
|
||||||
|
(setq out (cons (string-ref s len) out))
|
||||||
(f0 x y 1)
|
|
||||||
)
|
)
|
||||||
|
out
|
||||||
(f1 x 2)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
(print (f2 3))
|
|
||||||
|
4
vm/include/libcore.h
Normal file
4
vm/include/libcore.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct vm_unit_info;
|
||||||
|
int load_core(struct vm_unit_info *info);
|
@ -21,6 +21,7 @@ struct vm_func_entry {
|
|||||||
struct vm_unit {
|
struct vm_unit {
|
||||||
struct vector ref_table;
|
struct vector ref_table;
|
||||||
struct vector functions;
|
struct vector functions;
|
||||||
|
struct vector strtab;
|
||||||
|
|
||||||
uint64_t *global_pool;
|
uint64_t *global_pool;
|
||||||
size_t global_pool_size;
|
size_t global_pool_size;
|
||||||
@ -28,6 +29,7 @@ struct vm_unit {
|
|||||||
int is_loaded;
|
int is_loaded;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char **unit_add_string(struct vm_unit *u);
|
||||||
struct vm_func_entry *unit_add_function(struct vm_unit *u);
|
struct vm_func_entry *unit_add_function(struct vm_unit *u);
|
||||||
struct vm_ref_entry *unit_add_ref(struct vm_unit *u);
|
struct vm_ref_entry *unit_add_ref(struct vm_unit *u);
|
||||||
void unit_free(struct vm_unit *u);
|
void unit_free(struct vm_unit *u);
|
||||||
|
@ -11,6 +11,7 @@ enum vm_type {
|
|||||||
VT_CONS,
|
VT_CONS,
|
||||||
VT_STRING,
|
VT_STRING,
|
||||||
VT_FUNC,
|
VT_FUNC,
|
||||||
|
VT_CFUNC,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vm_value {
|
struct vm_value {
|
||||||
@ -24,6 +25,7 @@ struct vm_value {
|
|||||||
size_t lib_index, fn_index;
|
size_t lib_index, fn_index;
|
||||||
} v_func;
|
} v_func;
|
||||||
struct vm_string v_string;
|
struct vm_string v_string;
|
||||||
|
uintptr_t v_cfunc;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,6 +33,10 @@ static inline int ref_q(uint64_t w) {
|
|||||||
return (w & FLAG_REF) != 0;
|
return (w & FLAG_REF) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int integer_q(uint64_t w) {
|
||||||
|
return !(w & FLAG_REF);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int null_q(uint64_t w) {
|
static inline int null_q(uint64_t w) {
|
||||||
return w == FLAG_REF;
|
return w == FLAG_REF;
|
||||||
}
|
}
|
||||||
@ -40,6 +46,10 @@ static inline struct vm_value *getref(uint64_t w) {
|
|||||||
return (struct vm_value *) (w << 1);
|
return (struct vm_value *) (w << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int string_q(uint64_t w) {
|
||||||
|
return !null_q(w) && ref_q(w) && getref(w)->type == VT_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int cons_q(uint64_t w) {
|
static inline int cons_q(uint64_t w) {
|
||||||
if (ref_q(w)) {
|
if (ref_q(w)) {
|
||||||
return null_q(w) || getref(w)->type == VT_CONS;
|
return null_q(w) || getref(w)->type == VT_CONS;
|
||||||
@ -52,6 +62,10 @@ static inline int func_q(uint64_t w) {
|
|||||||
return ref_q(w) && !null_q(w) && getref(w)->type == VT_FUNC;
|
return ref_q(w) && !null_q(w) && getref(w)->type == VT_FUNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int cfunc_q(uint64_t w) {
|
||||||
|
return ref_q(w) && !null_q(w) && getref(w)->type == VT_CFUNC;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int pair_q(uint64_t w) {
|
static inline int pair_q(uint64_t w) {
|
||||||
return ref_q(w) && (!null_q(w) && getref(w)->type == VT_CONS);
|
return ref_q(w) && (!null_q(w) && getref(w)->type == VT_CONS);
|
||||||
}
|
}
|
||||||
@ -76,5 +90,6 @@ void vm_value_free(struct vm_value *val);
|
|||||||
struct vm_value *vm_cons(uint64_t w0, uint64_t w1);
|
struct vm_value *vm_cons(uint64_t w0, uint64_t w1);
|
||||||
struct vm_value *vm_makestr(const char *str);
|
struct vm_value *vm_makestr(const char *str);
|
||||||
struct vm_value *vm_func(size_t lib_index, size_t fn_index);
|
struct vm_value *vm_func(size_t lib_index, size_t fn_index);
|
||||||
|
struct vm_value *vm_cfunc(uintptr_t address);
|
||||||
|
|
||||||
void vm_print(FILE *dst, uint64_t w);
|
void vm_print(FILE *dst, uint64_t w);
|
||||||
|
137
vm/libcore.c
Normal file
137
vm/libcore.c
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#include "vmstate.h"
|
||||||
|
#include "vmval.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "load.h"
|
||||||
|
#include "unit.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define ARG(name) \
|
||||||
|
uint64_t name; \
|
||||||
|
if ((res = vm_pop(vm, &name)) != 0) { \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
#define ARG_INTEGER(name) \
|
||||||
|
int64_t name; \
|
||||||
|
if ((res = vm_pop_integer(vm, &name)) != 0) { \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
// I/O stuff
|
||||||
|
static int c_print(struct vm *vm) {
|
||||||
|
int res;
|
||||||
|
ARG(w);
|
||||||
|
if (string_q(w)) {
|
||||||
|
fputs(getref(w)->v_string.data, stdout);
|
||||||
|
} else {
|
||||||
|
vm_print(stdout, w);
|
||||||
|
}
|
||||||
|
vm_unref(w);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int c_println(struct vm *vm) {
|
||||||
|
int res;
|
||||||
|
if ((res = c_print(vm)) != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
putc('\n', stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int c_putc(struct vm *vm) {
|
||||||
|
int res;
|
||||||
|
ARG_INTEGER(c);
|
||||||
|
putc(c, stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// String stuff
|
||||||
|
static int c_string_length(struct vm *vm) {
|
||||||
|
int res;
|
||||||
|
ARG(w);
|
||||||
|
|
||||||
|
if (!string_q(w)) {
|
||||||
|
return -ERR_OPERAND_TYPE;
|
||||||
|
}
|
||||||
|
struct vm_value *v = getref(w);
|
||||||
|
size_t len = vm_strlen(&v->v_string);
|
||||||
|
vm_value_unref(v);
|
||||||
|
return vm_push_integer(vm, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int c_string_ref(struct vm *vm) {
|
||||||
|
int res;
|
||||||
|
ARG(s);
|
||||||
|
ARG_INTEGER(i);
|
||||||
|
if (!string_q(s)) {
|
||||||
|
return -ERR_OPERAND_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vm_value *v = getref(s);
|
||||||
|
char c;
|
||||||
|
if (i < 0 || (size_t) i >= vm_strlen(&v->v_string)) {
|
||||||
|
vm_value_unref(v);
|
||||||
|
return -ERR_RANGE;
|
||||||
|
}
|
||||||
|
c = v->v_string.data[i];
|
||||||
|
vm_value_unref(v);
|
||||||
|
return vm_push_integer(vm, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int c_extra_func(struct vm *vm) {
|
||||||
|
int res;
|
||||||
|
ARG(w);
|
||||||
|
printf("Runtime-resolved function was called!\n");
|
||||||
|
vm_unref(w);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int c_native(struct vm *vm) {
|
||||||
|
int res;
|
||||||
|
ARG(w);
|
||||||
|
if (!string_q(w)) {
|
||||||
|
return -ERR_OPERAND_TYPE;
|
||||||
|
}
|
||||||
|
struct vm_value *name = getref(w);
|
||||||
|
struct vm_value *sym;
|
||||||
|
if (!strcmp(name->v_string.data, "extra-func")) {
|
||||||
|
sym = vm_cfunc((uintptr_t) c_extra_func);
|
||||||
|
} else {
|
||||||
|
return -ERR_SYMBOL_UNDEFINED;
|
||||||
|
}
|
||||||
|
vm_value_unref(name);
|
||||||
|
vm_value_ref(sym);
|
||||||
|
vm_push_ref(vm, sym);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct vm_export_entry c_unit_core_exports[] = {
|
||||||
|
{ 1, { (uintptr_t) c_println }, "println" },
|
||||||
|
{ 1, { (uintptr_t) c_print }, "print" },
|
||||||
|
{ 1, { (uintptr_t) c_putc }, "putc" },
|
||||||
|
|
||||||
|
//{ 1, { (uintptr_t) c_string_length }, "string-new" },
|
||||||
|
{ 1, { (uintptr_t) c_string_length }, "string-length" },
|
||||||
|
{ 1, { (uintptr_t) c_string_ref }, "string-ref" },
|
||||||
|
//{ 1, { (uintptr_t) c_string_length }, "string-ref" },
|
||||||
|
|
||||||
|
{ 1, { (uintptr_t) c_native }, "native" },
|
||||||
|
};
|
||||||
|
|
||||||
|
int load_core(struct vm_unit_info *info) {
|
||||||
|
struct vm_export_entry *ent;
|
||||||
|
assert(info);
|
||||||
|
vector_init(&info->exports, sizeof(struct vm_export_entry));
|
||||||
|
for (size_t i = 0; i < sizeof(c_unit_core_exports) / sizeof(struct vm_export_entry); ++i) {
|
||||||
|
ent = vector_append(&info->exports);
|
||||||
|
if (!ent) {
|
||||||
|
return -ERR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
memcpy(ent, &c_unit_core_exports[i], sizeof(struct vm_export_entry));
|
||||||
|
}
|
||||||
|
info->unit = NULL;
|
||||||
|
info->index = 0xFFFFFFFF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
60
vm/load.c
60
vm/load.c
@ -10,41 +10,9 @@
|
|||||||
#include "load.h"
|
#include "load.h"
|
||||||
#include "unit.h"
|
#include "unit.h"
|
||||||
#include "vmstate.h"
|
#include "vmstate.h"
|
||||||
|
#include "libcore.h"
|
||||||
#include "vmval.h"
|
#include "vmval.h"
|
||||||
|
|
||||||
static int c_print(struct vm *vm) {
|
|
||||||
uint64_t w;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
if ((res = vm_pop(vm, &w)) != 0) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
vm_print(stdout, w);
|
|
||||||
printf("\n");
|
|
||||||
vm_unref(w);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct vm_export_entry c_unit_core_exports[] = {
|
|
||||||
{ 1, { (uintptr_t) c_print }, "print" }
|
|
||||||
};
|
|
||||||
|
|
||||||
static int load_core(struct vm_unit_info *info) {
|
|
||||||
struct vm_export_entry *ent;
|
|
||||||
assert(info);
|
|
||||||
vector_init(&info->exports, sizeof(struct vm_export_entry));
|
|
||||||
for (size_t i = 0; i < sizeof(c_unit_core_exports) / sizeof(struct vm_export_entry); ++i) {
|
|
||||||
ent = vector_append(&info->exports);
|
|
||||||
if (!ent) {
|
|
||||||
return -ERR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
memcpy(ent, &c_unit_core_exports[i], sizeof(struct vm_export_entry));
|
|
||||||
}
|
|
||||||
info->unit = NULL;
|
|
||||||
info->index = 0xFFFFFFFF;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FILE *unit_try_open_in(const char *paths, const char *name) {
|
static FILE *unit_try_open_in(const char *paths, const char *name) {
|
||||||
const char *head;
|
const char *head;
|
||||||
char path[256];
|
char path[256];
|
||||||
@ -183,6 +151,28 @@ static int vm_load_refs(struct vm_unit *unit,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vm_load_strings(struct vm_unit *unit,
|
||||||
|
struct bin_header *hdr,
|
||||||
|
FILE *fp) {
|
||||||
|
uint32_t len;
|
||||||
|
fseek(fp, hdr->string_table_offset, SEEK_SET);
|
||||||
|
for (size_t i = 0; i < hdr->string_table_size; ++i) {
|
||||||
|
// TODO optimize this crap
|
||||||
|
char **ref = unit_add_string(unit);
|
||||||
|
if (!ref) {
|
||||||
|
return -ERR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
fread(&len, 1, sizeof(uint32_t), fp);
|
||||||
|
*ref = malloc(len + 1);
|
||||||
|
if (!*ref) {
|
||||||
|
return -ERR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
fread(*ref, 1, len + 1, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int vm_load_exports(struct vm_unit_info *info,
|
static int vm_load_exports(struct vm_unit_info *info,
|
||||||
struct bin_header *hdr,
|
struct bin_header *hdr,
|
||||||
FILE *fp) {
|
FILE *fp) {
|
||||||
@ -244,6 +234,10 @@ int vm_load_unit_file(struct vm *vm,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((res = vm_load_strings(unit, &hdr, fp)) != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
info->unit = unit;
|
info->unit = unit;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@ struct vm_func_entry *unit_add_function(struct vm_unit *u) {
|
|||||||
return vector_append(&u->functions);
|
return vector_append(&u->functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char **unit_add_string(struct vm_unit *u) {
|
||||||
|
return vector_append(&u->strtab);
|
||||||
|
}
|
||||||
|
|
||||||
void unit_free(struct vm_unit *u) {
|
void unit_free(struct vm_unit *u) {
|
||||||
struct vm_func_entry *func;
|
struct vm_func_entry *func;
|
||||||
|
|
||||||
@ -21,6 +25,11 @@ void unit_free(struct vm_unit *u) {
|
|||||||
free(func->bytecode);
|
free(func->bytecode);
|
||||||
}
|
}
|
||||||
vector_free(&u->functions);
|
vector_free(&u->functions);
|
||||||
|
for (size_t i = 0; i < u->strtab.size; ++i) {
|
||||||
|
char **ref = vector_ref(&u->strtab, i);
|
||||||
|
free(*ref);
|
||||||
|
}
|
||||||
|
vector_free(&u->strtab);
|
||||||
|
|
||||||
for (size_t i = 0; i < u->global_pool_size; ++i) {
|
for (size_t i = 0; i < u->global_pool_size; ++i) {
|
||||||
vm_unref(u->global_pool[i]);
|
vm_unref(u->global_pool[i]);
|
||||||
|
47
vm/vmstate.c
47
vm/vmstate.c
@ -95,6 +95,7 @@ struct vm_unit *vm_add_unit(struct vm *vm, size_t global_pool_size) {
|
|||||||
|
|
||||||
vector_init(&unit->ref_table, sizeof(struct vm_ref_entry));
|
vector_init(&unit->ref_table, sizeof(struct vm_ref_entry));
|
||||||
vector_init(&unit->functions, sizeof(struct vm_func_entry));
|
vector_init(&unit->functions, sizeof(struct vm_func_entry));
|
||||||
|
vector_init(&unit->strtab, sizeof(char *));
|
||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,10 +153,20 @@ static uint64_t *vm_arg_ref(struct vm *vm, size_t index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int vm_call_ref(struct vm *vm, struct vm_value *ref) {
|
int vm_call_ref(struct vm *vm, struct vm_value *ref) {
|
||||||
if (ref->type != VT_FUNC) {
|
switch (ref->type) {
|
||||||
|
case VT_CFUNC:
|
||||||
|
{
|
||||||
|
int (*func) (struct vm *) = (void *) ref->v_cfunc;
|
||||||
|
if (!func) {
|
||||||
|
return -ERR_OPERAND_TYPE;
|
||||||
|
}
|
||||||
|
return func(vm);
|
||||||
|
}
|
||||||
|
case VT_FUNC:
|
||||||
|
return vm_call_unit_index(vm, ref->v_func.lib_index, ref->v_func.fn_index);
|
||||||
|
default:
|
||||||
return -ERR_OPERAND_TYPE;
|
return -ERR_OPERAND_TYPE;
|
||||||
}
|
}
|
||||||
return vm_call_unit_index(vm, ref->v_func.lib_index, ref->v_func.fn_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vm_read_ext_ref(struct vm *vm, struct vm_ref_entry *ref, uint64_t *w) {
|
static int vm_read_ext_ref(struct vm *vm, struct vm_ref_entry *ref, uint64_t *w) {
|
||||||
@ -265,6 +276,21 @@ int vm_eval_step(struct vm *vm) {
|
|||||||
vm_unref(w0);
|
vm_unref(w0);
|
||||||
vm_unref(w1);
|
vm_unref(w1);
|
||||||
return res;
|
return res;
|
||||||
|
case OP_NEQ:
|
||||||
|
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if ((res = stack_pop(&vm->data_stack, &w1)) != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (ref_q(w0) || ref_q(w1)) {
|
||||||
|
// TODO what do?
|
||||||
|
return -ERR_OPERAND_TYPE;
|
||||||
|
}
|
||||||
|
res = vm_push_bool(vm, w0 != w1);
|
||||||
|
vm_unref(w0);
|
||||||
|
vm_unref(w1);
|
||||||
|
return res;
|
||||||
//
|
//
|
||||||
case OP_LDNIL:
|
case OP_LDNIL:
|
||||||
return stack_push(&vm->data_stack, FLAG_REF);
|
return stack_push(&vm->data_stack, FLAG_REF);
|
||||||
@ -345,6 +371,17 @@ int vm_eval_step(struct vm *vm) {
|
|||||||
v0 = vm_func(vm->lp, i0);
|
v0 = vm_func(vm->lp, i0);
|
||||||
vm_value_ref(v0);
|
vm_value_ref(v0);
|
||||||
return vm_push_ref(vm, v0);
|
return vm_push_ref(vm, v0);
|
||||||
|
case OP_LDS:
|
||||||
|
i0 = opcode & 0xFFFFFF;
|
||||||
|
if (i0 >= unit->strtab.size) {
|
||||||
|
return -ERR_RANGE;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char **ref = vector_ref(&unit->strtab, i0);
|
||||||
|
v0 = vm_makestr(*ref);
|
||||||
|
vm_value_ref(v0);
|
||||||
|
return vm_push_ref(vm, v0);
|
||||||
|
}
|
||||||
case OP_STL:
|
case OP_STL:
|
||||||
i0 = opcode & 0xFFFFFF;
|
i0 = opcode & 0xFFFFFF;
|
||||||
if (i0 >= func->local_count) {
|
if (i0 >= func->local_count) {
|
||||||
@ -386,7 +423,7 @@ int vm_eval_step(struct vm *vm) {
|
|||||||
if ((res = vm_read_ext_ref(vm, r0, &w0)) != 0) {
|
if ((res = vm_read_ext_ref(vm, r0, &w0)) != 0) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (!func_q(w0)) {
|
if (!ref_q(w0) || null_q(w0)) {
|
||||||
return -ERR_OPERAND_TYPE;
|
return -ERR_OPERAND_TYPE;
|
||||||
}
|
}
|
||||||
return vm_call_ref(vm, getref(w0));
|
return vm_call_ref(vm, getref(w0));
|
||||||
@ -397,7 +434,7 @@ int vm_eval_step(struct vm *vm) {
|
|||||||
return -ERR_RANGE;
|
return -ERR_RANGE;
|
||||||
}
|
}
|
||||||
w0 = unit->global_pool[i0];
|
w0 = unit->global_pool[i0];
|
||||||
if (!func_q(w0)) {
|
if (!ref_q(w0) || null_q(w0)) {
|
||||||
return -ERR_OPERAND_TYPE;
|
return -ERR_OPERAND_TYPE;
|
||||||
}
|
}
|
||||||
return vm_call_ref(vm, getref(w0));
|
return vm_call_ref(vm, getref(w0));
|
||||||
@ -405,7 +442,7 @@ int vm_eval_step(struct vm *vm) {
|
|||||||
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
|
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (!func_q(w0)) {
|
if (!ref_q(w0) || null_q(w0)) {
|
||||||
return -ERR_OPERAND_TYPE;
|
return -ERR_OPERAND_TYPE;
|
||||||
}
|
}
|
||||||
res = vm_call_ref(vm, getref(w0));
|
res = vm_call_ref(vm, getref(w0));
|
||||||
|
13
vm/vmval.c
13
vm/vmval.c
@ -34,6 +34,9 @@ static void vm_print_ref(FILE *fp, struct vm_value *value, int cdepth) {
|
|||||||
fprintf(fp, ")");
|
fprintf(fp, ")");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case VT_CFUNC:
|
||||||
|
fprintf(fp, "<native function 0x%zx>", value->v_cfunc);
|
||||||
|
break;
|
||||||
case VT_FUNC:
|
case VT_FUNC:
|
||||||
fprintf(fp, "<function %zu:%zu>", value->v_func.lib_index, value->v_func.fn_index);
|
fprintf(fp, "<function %zu:%zu>", value->v_func.lib_index, value->v_func.fn_index);
|
||||||
break;
|
break;
|
||||||
@ -73,6 +76,7 @@ void vm_value_free(struct vm_value *v) {
|
|||||||
break;
|
break;
|
||||||
case VT_STRING:
|
case VT_STRING:
|
||||||
// TODO
|
// TODO
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -100,6 +104,15 @@ int vm_value_unref(struct vm_value *v) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct vm_value *vm_cfunc(uintptr_t ptr) {
|
||||||
|
struct vm_value *v = vm_value_create(VT_CFUNC);
|
||||||
|
if (!v) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
v->v_cfunc = ptr;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
struct vm_value *vm_cons(uint64_t w0, uint64_t w1) {
|
struct vm_value *vm_cons(uint64_t w0, uint64_t w1) {
|
||||||
struct vm_value *v = vm_value_create(VT_CONS);
|
struct vm_value *v = vm_value_create(VT_CONS);
|
||||||
if (!v) {
|
if (!v) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user