lama_byterun/byterun/src/interpreter.c

486 lines
12 KiB
C
Raw Normal View History

2024-10-31 00:54:04 +03:00
#include "interpreter.h"
2024-10-31 21:08:48 +03:00
#include "../../runtime/gc.h"
#include "../../runtime/runtime.h"
2024-10-31 21:08:48 +03:00
#include "runtime_externs.h"
#include "stack.h"
#include "types.h"
#include "utils.h"
2024-11-12 02:38:26 +03:00
struct State s;
static inline int ip_read_int(char **ip) {
*ip += sizeof(int);
return *(int *)((*ip) - sizeof(int));
}
2024-11-12 02:38:26 +03:00
static inline char ip_read_byte(char **ip) { return *(*ip)++; }
2024-11-12 02:38:26 +03:00
static inline char *ip_read_string(char **ip, bytefile *bf) {
2024-10-20 16:42:57 +03:00
return get_string(bf, ip_read_int(ip));
}
2024-11-12 02:12:28 +03:00
const size_t BUFFER_SIZE = 1000;
2024-11-07 19:31:25 +03:00
void run(bytefile *bf, int argc, char **argv) {
2024-11-12 21:08:41 +03:00
size_t stack[STACK_SIZE];
2024-11-12 02:12:28 +03:00
void *buffer[BUFFER_SIZE];
2024-11-12 21:08:41 +03:00
construct_state(bf, &s, (void**)stack);
2024-11-07 19:07:26 +03:00
#ifdef DEBUG_VERSION
printf("--- interpreter run ---\n");
2024-11-07 19:07:26 +03:00
#endif
const size_t OPS_SIZE = 13;
const char *ops[] = {
"+", "-", "*", "/", "%", "<", "<=", ">", ">=", "==", "!=", "&&", "!!"};
aint (*ops_func[])(void *, void *) = {
&Ls__Infix_43, // +
&Ls__Infix_45, // -
&Ls__Infix_42, // *
&Ls__Infix_47, // /
&Ls__Infix_37, // %
&Ls__Infix_60, // <
&Ls__Infix_6061, // <=
&Ls__Infix_62, // >
&Ls__Infix_6261, // >=
&Ls__Infix_6161, // ==
&Ls__Infix_3361, // !=
&Ls__Infix_3838, // &&
&Ls__Infix_3333, // !!
2024-10-31 21:08:48 +03:00
};
const size_t PATS_SIZE = 7;
const char *pats[] = {"=str", "#string", "#array", "#sexp",
"#ref", "#val", "#fun"};
2024-11-07 19:31:25 +03:00
// argc, argv
{
2024-11-12 02:38:26 +03:00
s_push_i(BOX(argc));
2024-11-07 19:31:25 +03:00
void **argv_strs = calloc(argc, sizeof(void *));
for (size_t i = 0; i < argc; ++i) {
argv_strs[i] = Bstring((aint *)&argv[i]);
}
2024-11-12 02:38:26 +03:00
s_push(Barray((aint *)&argv_strs, argc));
2024-11-07 19:31:25 +03:00
free(argv_strs);
}
2024-11-07 19:07:26 +03:00
#ifdef DEBUG_VERSION
printf("- loop start\n");
2024-11-07 19:07:26 +03:00
#endif
do {
// char *before_op_ip = s.ip; // save to set s.prev_ip
bool call_happened = false;
2024-11-12 21:08:41 +03:00
if (s.ip >= bf->code_ptr + bf->code_size) {
s_failure(&s, "instruction pointer is out of range (>= size)");
}
if (s.ip < bf->code_ptr) {
s_failure(&s, "instruction pointer is out of range (< 0)");
}
s.instr_ip = s.ip;
char x = ip_read_byte(&s.ip), h = (x & 0xF0) >> 4, l = x & 0x0F;
2024-11-07 19:07:26 +03:00
#ifdef DEBUG_VERSION
printf("0x%.8x\n", s.ip - bf->code_ptr - 1);
2024-11-07 19:07:26 +03:00
#endif
switch (h) {
case 15:
goto stop;
/* BINOP */
2024-10-12 00:37:28 +03:00
case 0: // BINOP ops[l-1]
if (l > OPS_SIZE) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "undefined binop type index");
}
if (l < 1) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "negative binop type index");
}
2024-11-12 02:38:26 +03:00
void *left = s_pop();
void *right = s_pop();
s_push((void *)ops_func[l - 1](right, left));
break;
case 1:
switch (l) {
case 0: // CONST %d
2024-11-12 02:38:26 +03:00
s_push_i(BOX(ip_read_int(&s.ip)));
break;
case 1: { // STRING %s
void *str = ip_read_string(&s.ip, bf);
2024-11-12 02:38:26 +03:00
s_push(Bstring((aint *)&str));
break;
2024-11-02 01:19:54 +03:00
}
case 2: { // SEXP %s %d // create sexpr with tag=%s and %d elements from
// stack
// params read from stack
2024-11-07 01:14:57 +03:00
const char *name = ip_read_string(&s.ip, bf);
aint args_count = ip_read_int(&s.ip);
2024-11-07 19:07:26 +03:00
#ifdef DEBUG_VERSION
2024-11-07 19:31:25 +03:00
printf("tag hash is %i, n is %i\n", UNBOX(LtagHash((char *)name)),
2024-11-07 01:14:57 +03:00
args_count);
2024-11-07 19:07:26 +03:00
#endif
if (args_count < 0) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "args count should be >= 0");
}
2024-11-12 02:12:28 +03:00
void **opr_buffer = args_count >= BUFFER_SIZE ? calloc(args_count + 1, sizeof(void *)) : buffer;
2024-11-07 19:31:25 +03:00
for (size_t i = 1; i <= args_count; ++i) {
2024-11-12 02:38:26 +03:00
opr_buffer[args_count - i] = s_pop();
}
2024-11-12 02:12:28 +03:00
opr_buffer[args_count] = (void *)LtagHash((char *)name);
2024-11-12 02:12:28 +03:00
void *sexp = Bsexp((aint *)opr_buffer, BOX(args_count + 1));
push_extra_root(sexp);
2024-11-12 02:38:26 +03:00
s_push(sexp);
pop_extra_root(sexp);
2024-11-12 02:12:28 +03:00
if (args_count >= BUFFER_SIZE) {
free(opr_buffer);
}
break;
}
2024-11-07 01:14:57 +03:00
case 3: { // STI - write by ref (?)
2024-11-07 19:31:25 +03:00
// NOTE: example not found, no checks done
2024-11-12 02:38:26 +03:00
void *elem = s_pop();
void **addr = (void **)s_pop();
2024-11-07 01:14:57 +03:00
*addr = elem;
2024-11-12 02:38:26 +03:00
s_push(elem);
break;
2024-11-07 01:14:57 +03:00
}
case 4: { // STA - write to array elem
2024-11-12 02:38:26 +03:00
void *elem = s_pop();
aint index = s_pop_i();
void *data = s_pop();
s_push(Bsta(data, index, elem));
break;
}
2024-11-07 01:14:57 +03:00
case 5: { // JMP 0x%.8x
int jmp_p = ip_read_int(&s.ip);
if (jmp_p < 0) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "negative file offset jumps are not allowed");
}
s.ip = bf->code_ptr + jmp_p;
break;
}
case 6: // END
2024-11-12 02:38:26 +03:00
if (!s_is_empty() && s.fp->prev_fp != 0) {
s.fp->ret = *s_peek();
s_pop();
}
2024-11-12 02:38:26 +03:00
s_exit_f();
break;
case 7: // RET
2024-11-12 02:38:26 +03:00
if (!s_is_empty() && s.fp->prev_fp != 0) {
s.fp->ret = *s_peek();
s_pop();
}
2024-11-12 02:38:26 +03:00
s_exit_f();
break;
case 8: // DROP
2024-11-12 02:38:26 +03:00
s_pop();
break;
case 9: // DUP
{
2024-11-12 02:38:26 +03:00
s_push(*s_peek());
break;
}
2024-10-12 00:37:28 +03:00
case 10: // SWAP
{
2024-11-12 02:38:26 +03:00
void* x = s_pop();
void* y = s_pop();
s_push(y);
s_push(x);
2024-11-12 00:10:02 +03:00
// if (s.sp + 1 >= s.stack + STACK_SIZE ||
// (s.fp != NULL && s.sp + 1 >= f_locals(s.fp))) {
// s_failure(&s, "can't SWAP: < 2 values on stack");
// }
// void *v = *s.sp;
// push_extra_root(v);
// *s.sp = *(s.sp + 1);
// *(s.sp + 1) = v;
// pop_extra_root(v);
} break;
2024-10-12 00:37:28 +03:00
case 11: // ELEM
{
2024-11-12 02:38:26 +03:00
aint index = s_pop_i();
void *data = s_pop();
s_push(Belem(data, index));
} break;
default:
2024-11-12 00:10:02 +03:00
s_failure(&s, "invalid opcode"); // %d-%d\n", h, l);
}
break;
case 2: { // LD %d
void **var_ptr =
2024-11-12 02:38:26 +03:00
var_by_category(to_var_category(l), ip_read_int(&s.ip));
s_push(*var_ptr);
break;
}
case 3: { // LDA %d
void **var_ptr =
2024-11-12 02:38:26 +03:00
var_by_category(to_var_category(l), ip_read_int(&s.ip));
s_push(*var_ptr);
break;
}
case 4: { // ST %d
void **var_ptr =
2024-11-12 02:38:26 +03:00
var_by_category(to_var_category(l), ip_read_int(&s.ip));
*var_ptr = *s_peek();
break;
}
case 5:
switch (l) {
case 0: { // CJMPz 0x%.8x
int jmp_p = ip_read_int(&s.ip);
if (jmp_p < 0) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "negative file offset jumps are not allowed");
}
2024-11-12 02:38:26 +03:00
if (UNBOX(s_pop_i()) == 0) {
s.ip = bf->code_ptr + jmp_p;
2024-10-12 00:37:28 +03:00
}
break;
2024-10-12 00:37:28 +03:00
}
2024-10-12 00:37:28 +03:00
case 1: { // CJMPnz 0x%.8x
int jmp_p = ip_read_int(&s.ip);
if (jmp_p < 0) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "negative file offset jumps are not allowed");
}
2024-11-12 02:38:26 +03:00
if (UNBOX(s_pop_i()) != 0) {
s.ip = bf->code_ptr + jmp_p;
2024-10-12 00:37:28 +03:00
}
break;
2024-10-12 00:37:28 +03:00
}
case 2: { // BEGIN %d %d // function begin
int args_sz = ip_read_int(&s.ip);
int locals_sz = ip_read_int(&s.ip);
if (s.fp != NULL && s.call_ip == NULL) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "begin should only be called after call");
}
2024-11-12 02:38:26 +03:00
s_enter_f(s.call_ip /*ip from call*/, s.is_closure_call, args_sz,
2024-11-07 01:14:57 +03:00
locals_sz);
break;
}
2024-11-07 19:31:25 +03:00
case 3: { // CBEGIN %d %d
// NOTE: example not found, no checks done
int args_sz = ip_read_int(&s.ip);
int locals_sz = ip_read_int(&s.ip);
if (s.fp != NULL && s.call_ip == NULL) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "begin should only be called after call");
}
2024-11-12 02:38:26 +03:00
s_enter_f(s.call_ip /*ip from call*/, s.is_closure_call, args_sz,
2024-11-07 01:14:57 +03:00
locals_sz);
break;
}
2024-11-07 01:14:57 +03:00
case 4: // CLOSURE 0x%.8x
{
aint call_offset = ip_read_int(&s.ip);
aint args_count = ip_read_int(&s.ip);
for (aint i = 0; i < args_count; i++) {
aint arg_type = ip_read_byte(&s.ip);
aint arg_id = ip_read_int(&s.ip);
2024-11-07 01:14:57 +03:00
void **var_ptr =
2024-11-12 02:38:26 +03:00
var_by_category(to_var_category(l), ip_read_int(&s.ip));
s_push(*var_ptr);
2024-11-07 01:14:57 +03:00
}
2024-11-12 02:38:26 +03:00
s_push(bf->code_ptr + call_offset);
2024-11-07 01:14:57 +03:00
void *closure = Bclosure((aint *)s.sp, args_count);
2024-11-07 01:14:57 +03:00
push_extra_root(closure);
2024-11-12 02:38:26 +03:00
s_popn(args_count + 1);
s_push(closure);
2024-11-07 01:14:57 +03:00
pop_extra_root(closure);
break;
}
2024-11-07 01:14:57 +03:00
case 5: { // CALLC %d // call clojure
2024-11-07 00:56:21 +03:00
aint args_count = ip_read_int(&s.ip); // args count
call_happened = true;
s.is_closure_call = true;
s.call_ip = s.ip;
2024-11-12 02:38:26 +03:00
s.ip = Belem(*s_nth(args_count), BOX(0)); // use offset instead ??
break;
2024-11-07 00:56:21 +03:00
}
2024-11-07 01:14:57 +03:00
case 6: { // CALL 0x%.8x %d // call function
int call_p = ip_read_int(&s.ip);
ip_read_int(&s.ip); // args count
call_happened = true;
s.is_closure_call = false;
s.call_ip = s.ip;
if (call_p < 0) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "negative file offset jumps are not allowed");
}
s.ip = bf->code_ptr + call_p;
break;
}
case 7: { // TAG %s %d
2024-11-07 01:14:57 +03:00
const char *name = ip_read_string(&s.ip, bf);
aint args_count = ip_read_int(&s.ip);
2024-11-07 19:07:26 +03:00
#ifdef DEBUG_VERSION
2024-11-07 01:14:57 +03:00
printf("tag hash is %i, n is %i, peek is %i\n",
UNBOX(LtagHash((char *)name)), args_count, s_peek(&s));
2024-11-07 19:07:26 +03:00
#endif
2024-11-12 02:38:26 +03:00
s_push_i(Btag(s_pop(), LtagHash((char *)name), BOX(args_count)));
break;
}
case 8: // ARRAY %d
2024-11-12 02:38:26 +03:00
s_push_i(Barray_patt(s_pop(), BOX(ip_read_int(&s.ip))));
break;
2024-11-09 23:32:09 +03:00
case 9: { // FAIL %d %d
int line = ip_read_int(&s.ip); // ??
int col = ip_read_int(&s.ip); // ??
2024-11-12 02:38:26 +03:00
Bmatch_failure(s_pop(), argv[0], BOX(line), BOX(col));
break;
2024-11-09 23:32:09 +03:00
}
2024-10-12 00:37:28 +03:00
case 10: // LINE %d
2024-11-09 23:32:09 +03:00
s.current_line = ip_read_int(&s.ip);
2024-10-12 00:37:28 +03:00
// maybe some metainfo should be collected
break;
default:
2024-11-12 00:10:02 +03:00
s_failure(&s, "invalid opcode"); // %d-%d\n", h, l);
}
break;
case 6: // PATT pats[l]
2024-11-02 01:19:54 +03:00
// {"=str", "#string", "#array", "#sexp", "#ref", "#val", "#fun"}
switch (l) {
case 0: // =str
2024-11-12 02:38:26 +03:00
s_push_i(Bstring_patt(s_pop(), s_pop()));
2024-11-02 01:19:54 +03:00
break;
case 1: // #string
2024-11-12 02:38:26 +03:00
s_push_i(Bstring_tag_patt(s_pop()));
2024-11-02 01:19:54 +03:00
break;
case 2: // #array
2024-11-12 02:38:26 +03:00
s_push_i(Barray_tag_patt(s_pop()));
2024-11-02 01:19:54 +03:00
break;
case 3: // #sexp
2024-11-12 02:38:26 +03:00
s_push_i(Bsexp_tag_patt(s_pop()));
2024-11-02 01:19:54 +03:00
break;
2024-11-07 19:31:25 +03:00
case 4: // #ref
2024-11-12 02:38:26 +03:00
s_push_i(Bunboxed_patt(s_pop()));
2024-11-02 01:19:54 +03:00
break;
2024-11-07 19:31:25 +03:00
case 5: // #val
2024-11-12 02:38:26 +03:00
s_push_i(Bboxed_patt(s_pop()));
2024-11-02 01:19:54 +03:00
break;
case 6: // #fun
2024-11-12 02:38:26 +03:00
s_push_i(Bclosure_tag_patt(s_pop()));
2024-11-02 01:19:54 +03:00
break;
default:
2024-11-12 00:10:02 +03:00
s_failure(&s, "invalid opcode"); // %d-%d\n", h, l);
2024-11-02 01:19:54 +03:00
}
break;
case 7: {
switch (l) {
2024-10-12 00:37:28 +03:00
case 0: // CALL Lread
2024-11-12 02:38:26 +03:00
s_push_i(Lread());
break;
2024-10-12 00:37:28 +03:00
case 1: // CALL Lwrite
2024-11-12 02:38:26 +03:00
Lwrite(*s_peek_i());
break;
2024-10-12 00:37:28 +03:00
case 2: // CALL Llength
2024-11-12 02:38:26 +03:00
s_push_i(Llength(s_pop()));
break;
2024-11-02 01:19:54 +03:00
case 3: { // CALL Lstring
2024-11-12 02:38:26 +03:00
void *val = s_pop();
2024-11-07 01:14:57 +03:00
void *str = Lstring((aint *)&val);
2024-11-12 02:38:26 +03:00
s_push(str);
break;
2024-11-02 01:19:54 +03:00
}
2024-11-02 01:19:54 +03:00
case 4: { // CALL Barray %d
2024-11-09 23:32:09 +03:00
size_t elem_count = ip_read_int(&s.ip);
if (elem_count < 0) {
2024-11-12 00:10:02 +03:00
s_failure(&s, "elements count should be >= 0");
2024-11-09 23:32:09 +03:00
}
2024-11-12 02:12:28 +03:00
void **opr_buffer = elem_count > BUFFER_SIZE ? calloc(elem_count, sizeof(void *)) : buffer;
2024-11-09 23:32:09 +03:00
for (size_t i = 0; i < elem_count; ++i) {
2024-11-12 02:38:26 +03:00
opr_buffer[elem_count - i - 1] = s_pop();
2024-11-09 23:32:09 +03:00
}
2024-11-07 19:31:25 +03:00
void *array =
2024-11-12 02:12:28 +03:00
Barray((aint *)opr_buffer, BOX(elem_count)); // NOTE: not shure if elems should be added
2024-11-12 02:38:26 +03:00
s_push(array);
2024-11-09 23:32:09 +03:00
2024-11-12 02:12:28 +03:00
if (elem_count > BUFFER_SIZE) {
free(opr_buffer);
}
break;
2024-11-02 01:19:54 +03:00
}
default:
2024-11-12 00:10:02 +03:00
s_failure(&s, "invalid opcode"); // %d-%d\n", h, l);
}
} break;
default:
2024-11-12 00:10:02 +03:00
s_failure(&s, "invalid opcode"); // %d-%d\n", h, l);
}
if (!call_happened) {
s.is_closure_call = false;
s.call_ip = NULL;
}
if (s.fp == NULL) {
break;
}
2024-11-07 19:07:26 +03:00
#ifdef DEBUG_VERSION
print_stack(&s);
2024-11-07 19:07:26 +03:00
#endif
} while (1);
2024-10-12 00:37:28 +03:00
stop:;
2024-11-07 19:07:26 +03:00
#ifdef DEBUG_VERSION
printf("--- run end ---\n");
2024-11-07 19:07:26 +03:00
#endif
2024-10-31 21:08:48 +03:00
cleanup_state(&s);
}