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/error.o \
|
||||
$(O)/vm/debug.o \
|
||||
$(O)/vm/libcore.o \
|
||||
$(O)/core/vector.o \
|
||||
$(O)/core/hash.o
|
||||
COMPILER_OBJS=$(O)/compiler/main.o \
|
||||
|
@ -9,6 +9,17 @@
|
||||
#include <string.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,
|
||||
struct context *ctx,
|
||||
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 |= (end_loc - cond_jmp_loc) & 0xFFFFFF;
|
||||
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") ||
|
||||
!strcmp(name, "export")) {
|
||||
// Ignore
|
||||
|
@ -22,6 +22,11 @@ void emit_function(struct context *ctx, struct function *fn) {
|
||||
struct unit *root = ctx->root;
|
||||
assert(ctx->owner == fn);
|
||||
|
||||
fn->argc = 0;
|
||||
for (struct node *node = fn->args; node; node = cdr(node)) {
|
||||
++fn->argc;
|
||||
}
|
||||
|
||||
// Pass 0 - extract global scope
|
||||
for (struct node *node = body; node; node = cdr(node)) {
|
||||
struct node *expr = car(node);
|
||||
@ -124,6 +129,7 @@ void emit(struct function *fn, struct context *ctx, struct node *expr) {
|
||||
return;
|
||||
}
|
||||
struct node *n0;
|
||||
size_t index;
|
||||
int c0;
|
||||
|
||||
switch (expr->type) {
|
||||
@ -135,32 +141,39 @@ void emit(struct function *fn, struct context *ctx, struct node *expr) {
|
||||
fprintf(stderr, "Call to nil\n");
|
||||
abort();
|
||||
}
|
||||
if (n0->type != N_IDENT) {
|
||||
fprintf(stderr, "TODO non-identifier calls\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
// Try builtin syntax construct
|
||||
if (emit_builtin_syntax(fn, ctx, n0->n_ident, cdr(expr)) == 0) {
|
||||
if (n0->type == N_IDENT) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
case N_STRING:
|
||||
assert(unit_insert_string(ctx->root, expr->n_string, &index) == 0);
|
||||
emit_insn(fn, OP(OP_LDS, index));
|
||||
return;
|
||||
case N_INTEGER:
|
||||
if (expr->n_integer < 0x7FFFFF && expr->n_integer > -0x7FFFFF) {
|
||||
emit_insn(fn, OP(OP_LDI, expr->n_integer));
|
||||
} else {
|
||||
printf("ldc ...\n");
|
||||
assert(0 && "TODO constant pool lmao");
|
||||
}
|
||||
return;
|
||||
case N_IDENT:
|
||||
|
@ -6,7 +6,8 @@
|
||||
enum node_type {
|
||||
N_CONS,
|
||||
N_IDENT,
|
||||
N_INTEGER
|
||||
N_INTEGER,
|
||||
N_STRING
|
||||
};
|
||||
|
||||
struct node {
|
||||
@ -16,6 +17,7 @@ struct node {
|
||||
struct node *car, *cdr;
|
||||
} n_cons;
|
||||
char *n_ident;
|
||||
char *n_string;
|
||||
intmax_t n_integer;
|
||||
};
|
||||
};
|
||||
@ -40,6 +42,10 @@ static inline int integer_q(struct node *n) {
|
||||
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) {
|
||||
assert(pair_q(n));
|
||||
return n->n_cons.car;
|
||||
@ -62,6 +68,7 @@ static inline struct node *cddr(struct node *n) {
|
||||
return cdr(cdr(n));
|
||||
}
|
||||
|
||||
struct node *string_from_owned(char *ptr);
|
||||
struct node *cons(struct node *a, struct node *b);
|
||||
struct node *ident(const char *i);
|
||||
struct node *integer(intmax_t v);
|
||||
|
@ -35,6 +35,7 @@ struct ext_unit {
|
||||
struct function {
|
||||
size_t index;
|
||||
size_t local_count;
|
||||
size_t argc;
|
||||
struct node *args, *body;
|
||||
struct vector bytecode;
|
||||
};
|
||||
@ -47,6 +48,7 @@ struct export {
|
||||
struct unit {
|
||||
struct context global;
|
||||
|
||||
struct vector strtab;
|
||||
struct vector functions;
|
||||
struct vector exports;
|
||||
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 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_insert_string(struct unit *unit, const char *text, size_t *index);
|
||||
|
||||
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;
|
||||
for (size_t i = 0; i < u->functions.size; ++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);
|
||||
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) {
|
||||
struct function *fn = vector_ref(&u->functions, i);
|
||||
struct bin_func_entry bin_func;
|
||||
size_t argc = 0;
|
||||
for (struct node *arg = fn->args; arg; arg = cdr(arg)) {
|
||||
++argc;
|
||||
}
|
||||
bin_func.argc = argc;
|
||||
bin_func.argc = fn->argc;
|
||||
bin_func.local_count = fn->local_count;
|
||||
bin_func.len = fn->bytecode.size * sizeof(uint32_t);
|
||||
fwrite(&bin_func, 1, sizeof(struct bin_func_entry), 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) {
|
||||
|
@ -5,6 +5,13 @@
|
||||
#include <stdlib.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 *n = malloc(sizeof(struct node));
|
||||
n->type = N_CONS;
|
||||
@ -57,6 +64,9 @@ void vm_print(const struct node *node, int depth) {
|
||||
case N_INTEGER:
|
||||
printf("%zd", node->n_integer);
|
||||
break;
|
||||
case N_STRING:
|
||||
printf("\"%s\"", node->n_string);
|
||||
break;
|
||||
case N_CONS:
|
||||
if (!depth) {
|
||||
printf("(");
|
||||
|
@ -202,6 +202,39 @@ int vm_parse(struct vm_parser *in, struct node **out) {
|
||||
buf[i] = 0;
|
||||
*out = ident(buf);
|
||||
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);
|
||||
|
@ -9,13 +9,17 @@
|
||||
#include <stdio.h>
|
||||
|
||||
static struct ext_unit_entry u_core_entries[] = {
|
||||
{ "println" },
|
||||
{ "print" },
|
||||
{ "file-open" },
|
||||
{ "file-read" },
|
||||
{ "file-write" },
|
||||
{ "file-close" },
|
||||
{ "file-seek" },
|
||||
{ "exit" }
|
||||
{ "putc" },
|
||||
|
||||
{ "string-new" },
|
||||
{ "string-length" },
|
||||
{ "string-ref" },
|
||||
{ "string-set!" },
|
||||
{ "string-append" },
|
||||
|
||||
{ "native" },
|
||||
};
|
||||
|
||||
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_units, sizeof(struct ext_unit));
|
||||
vector_init(&u->exports, sizeof(struct export));
|
||||
vector_init(&u->strtab, sizeof(char *));
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!strcmp(name, "core")) {
|
||||
struct ext_unit *unit = vector_append(&self->ext_units);
|
||||
unit->exports.size = sizeof(u_core_entries) / sizeof(u_core_entries[0]);
|
||||
unit->exports.cap = unit->exports.size;
|
||||
unit->exports.data = u_core_entries;
|
||||
vector_init(&unit->exports, sizeof(struct ext_unit_entry));
|
||||
for (size_t i = 0; i < sizeof(u_core_entries) / sizeof(u_core_entries[0]); ++i) {
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
@ -148,6 +157,20 @@ int unit_ext_load(struct unit *self, const char *name) {
|
||||
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
|
||||
int ctx_lookup_name(struct context *ctx, const char *name, int *kind, size_t *index) {
|
||||
// 1. Lookup local
|
||||
|
@ -16,6 +16,8 @@ struct bin_header {
|
||||
uint32_t export_table_size;
|
||||
uint32_t func_table_offset;
|
||||
uint32_t func_table_size;
|
||||
uint32_t string_table_offset;
|
||||
uint32_t string_table_size;
|
||||
};
|
||||
|
||||
struct bin_unit_entry {
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define OP_LDC 0x30
|
||||
#define OP_LDNIL 0x31
|
||||
#define OP_LDI 0x32
|
||||
#define OP_LDS 0x33
|
||||
|
||||
#define OP_CAR 0x40
|
||||
#define OP_CDR 0x41
|
||||
|
18
mod1.vml
18
mod1.vml
@ -1,15 +1,11 @@
|
||||
(use core)
|
||||
|
||||
(define (f2 x)
|
||||
(define (f1 x y)
|
||||
(define (f0 x y z)
|
||||
(+ x y z)
|
||||
)
|
||||
|
||||
(f0 x y 1)
|
||||
(define (string-chars s)
|
||||
(define len (string-length s))
|
||||
(define out nil)
|
||||
(while (/= len 0)
|
||||
(setq len (- len 1))
|
||||
(setq out (cons (string-ref s len) out))
|
||||
)
|
||||
|
||||
(f1 x 2)
|
||||
out
|
||||
)
|
||||
|
||||
(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 vector ref_table;
|
||||
struct vector functions;
|
||||
struct vector strtab;
|
||||
|
||||
uint64_t *global_pool;
|
||||
size_t global_pool_size;
|
||||
@ -28,6 +29,7 @@ struct vm_unit {
|
||||
int is_loaded;
|
||||
};
|
||||
|
||||
char **unit_add_string(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);
|
||||
void unit_free(struct vm_unit *u);
|
||||
|
@ -11,6 +11,7 @@ enum vm_type {
|
||||
VT_CONS,
|
||||
VT_STRING,
|
||||
VT_FUNC,
|
||||
VT_CFUNC,
|
||||
};
|
||||
|
||||
struct vm_value {
|
||||
@ -24,6 +25,7 @@ struct vm_value {
|
||||
size_t lib_index, fn_index;
|
||||
} v_func;
|
||||
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;
|
||||
}
|
||||
|
||||
static inline int integer_q(uint64_t w) {
|
||||
return !(w & FLAG_REF);
|
||||
}
|
||||
|
||||
static inline int null_q(uint64_t w) {
|
||||
return w == FLAG_REF;
|
||||
}
|
||||
@ -40,6 +46,10 @@ static inline struct vm_value *getref(uint64_t w) {
|
||||
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) {
|
||||
if (ref_q(w)) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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_makestr(const char *str);
|
||||
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);
|
||||
|
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 "unit.h"
|
||||
#include "vmstate.h"
|
||||
#include "libcore.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) {
|
||||
const char *head;
|
||||
char path[256];
|
||||
@ -183,6 +151,28 @@ static int vm_load_refs(struct vm_unit *unit,
|
||||
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,
|
||||
struct bin_header *hdr,
|
||||
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) {
|
||||
info->unit = unit;
|
||||
}
|
||||
|
@ -12,6 +12,10 @@ struct vm_func_entry *unit_add_function(struct vm_unit *u) {
|
||||
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) {
|
||||
struct vm_func_entry *func;
|
||||
|
||||
@ -21,6 +25,11 @@ void unit_free(struct vm_unit *u) {
|
||||
free(func->bytecode);
|
||||
}
|
||||
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) {
|
||||
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->functions, sizeof(struct vm_func_entry));
|
||||
vector_init(&unit->strtab, sizeof(char *));
|
||||
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) {
|
||||
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 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) {
|
||||
@ -265,6 +276,21 @@ int vm_eval_step(struct vm *vm) {
|
||||
vm_unref(w0);
|
||||
vm_unref(w1);
|
||||
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:
|
||||
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);
|
||||
vm_value_ref(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:
|
||||
i0 = opcode & 0xFFFFFF;
|
||||
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) {
|
||||
return res;
|
||||
}
|
||||
if (!func_q(w0)) {
|
||||
if (!ref_q(w0) || null_q(w0)) {
|
||||
return -ERR_OPERAND_TYPE;
|
||||
}
|
||||
return vm_call_ref(vm, getref(w0));
|
||||
@ -397,7 +434,7 @@ int vm_eval_step(struct vm *vm) {
|
||||
return -ERR_RANGE;
|
||||
}
|
||||
w0 = unit->global_pool[i0];
|
||||
if (!func_q(w0)) {
|
||||
if (!ref_q(w0) || null_q(w0)) {
|
||||
return -ERR_OPERAND_TYPE;
|
||||
}
|
||||
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) {
|
||||
return res;
|
||||
}
|
||||
if (!func_q(w0)) {
|
||||
if (!ref_q(w0) || null_q(w0)) {
|
||||
return -ERR_OPERAND_TYPE;
|
||||
}
|
||||
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, ")");
|
||||
}
|
||||
break;
|
||||
case VT_CFUNC:
|
||||
fprintf(fp, "<native function 0x%zx>", value->v_cfunc);
|
||||
break;
|
||||
case VT_FUNC:
|
||||
fprintf(fp, "<function %zu:%zu>", value->v_func.lib_index, value->v_func.fn_index);
|
||||
break;
|
||||
@ -73,6 +76,7 @@ void vm_value_free(struct vm_value *v) {
|
||||
break;
|
||||
case VT_STRING:
|
||||
// TODO
|
||||
break;
|
||||
default:
|
||||
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 *v = vm_value_create(VT_CONS);
|
||||
if (!v) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user