some fixes to work with gc and one stack of pointers

This commit is contained in:
ProgramSnail 2024-10-25 19:38:45 +03:00
parent d782934f0c
commit 1c85bf553a
4 changed files with 258 additions and 185 deletions

View file

@ -33,3 +33,51 @@ inline void f_cons(struct State *s) {
// TODO
}
// TODO
inline void f_binop(struct State *s, const char *opr) {
size_t len = strlen(opr);
if (len < 1) {
failure("empty operation");
}
switch (opr[0]) {
case '+':
break;
case '-':
break;
case '*':
break;
case '/':
break;
case '%':
break;
case '<':
if (len == 1) { // <
} else { // <=
}
break;
case '>':
if (len == 1) { // >
} else { // >=
}
break;
case '=': // ==
break;
case '!':
if (len == 1) {
failure("'!...' opr len is 1");
}
if (opr[1] == '=') { // !=
} else { // !!
}
break;
case '&': // &&
break;
default:
failure("unknown operation");
}
}

View file

@ -1,15 +1,13 @@
#pragma once
#include "../../runtime/gc.h"
#include "../../runtime/runtime.h"
#include "types.h"
#include <cstdlib>
// TODO: use gc
#include "stdlib.h"
// ------ general ------
// TODO: use gc
inline void free_var_ptr(union VarT *var);
inline void free_var(union VarT var) {
@ -36,12 +34,12 @@ inline void free_var(union VarT var) {
for (size_t i = 0; i < dh_param(var.array.data_header); ++i) {
free_var_ptr(to_var(var.array.values[i]));
}
free(var.array.values);
// free(var.array.values); // FIXME
break;
case SEXP_T:
// tag is const string, no need to free
if (var.sexp.next != NULL) {
free(var.sexp.next);
// free(var.sexp.next); // FIXME
}
break;
case FUN_T:
@ -52,77 +50,32 @@ inline void free_var(union VarT var) {
// TODO: use gc
inline void free_var_ptr(union VarT *var) {
free_var(*var);
free((void *)var);
// free((void *)var); // FIXME
}
//
inline NilT clear_var() { return NilT{.data_header = NIL_T}; }
// usually not required, because frame is located on shared stack
inline struct Frame clear_frame() {
struct Frame frame = {
.ret_ip = NULL,
.rp = NULL,
.ret = NULL,
.params = NULL,
.locals = NULL,
.end = NULL,
};
return frame;
}
// TODO: not required ??
// inline struct Var deep_copy_var(struct Var var) {
// switch (var.type) {
// case INT_T:
// break;
// case CONST_STR_T:
// break;
// case STR_T: {
// char *old_str = var.value.str_v;
// var.value.str_v = calloc(var.size + 1, sizeof(char));
// strcpy(var.value.str_v, old_str);
// break;
// }
// case LIST_T:
// if (var.value.list_v.elem != NULL) {
// struct Var *old_elem = var.value.list_v.elem;
// var.value.list_v.elem = calloc(1, sizeof(struct Var));
// *var.value.list_v.elem = deep_copy_var(*old_elem);
// }
// if (var.value.list_v.next != NULL) {
// struct Var *old_next = var.value.list_v.next;
// var.value.list_v.next = calloc(1, sizeof(struct Var));
// *var.value.list_v.next = deep_copy_var(*old_next);
// }
// break;
// case ARRAY_T: {
// struct Var *old_array = var.value.array_v;
// var.value.array_v = calloc(var.size, sizeof(char));
// for (size_t i = 0; i < var.size; ++i) {
// var.value.array_v[i] = deep_copy_var(*(old_array + i));
// }
// break;
// }
// case FUN_T:
// break;
// case NIL_T:
// break;
// }
// return var;
// }
inline struct NilT clear_var() { return NilT{.data_header = NIL_T}; }
// ------ put on stack ---
inline void s_put_ptr(struct State *s, char *val) { // any var
*s->vp = (NilT *)val;
++s->vp;
}
inline void s_put_var_ptr(struct State *s, struct NilT **val) { // any var
*s->vp = (NilT *)val;
++s->vp;
}
inline void s_put_var(struct State *s, struct NilT *val) { // any var
*s->vp = val;
++s->vp;
}
inline void s_put_nil(struct State *s) {
struct NilT *var = alloc();
struct NilT *var = (NilT *)alloc(sizeof(NilT));
var->data_header = NIL_T; // no param
s_put_var(s, var);
}
@ -134,42 +87,52 @@ inline void s_putn_nil(struct State *s, size_t n) {
}
inline void s_put_i(struct State *s, int val) {
struct IntT *var = alloc();
struct IntT *var = (IntT *)alloc(sizeof(IntT));
var->data_header = INT_T; // no param
var->value = val;
s_put_var(s, (NilT *)var);
}
inline void s_put_const_str(struct State *s,
const char *val) { // memory controlled externally
struct ConstStrT *var = alloc();
inline void s_put_const_str(struct State *s, const char *val) {
struct ConstStrT *var = (ConstStrT *)alloc(sizeof(ConstStrT));
var->data_header = CONST_STR_T; // no param
var->value = val;
s_put_var(s, (NilT *)var);
}
inline void s_put_str(struct State *s, char *val) { // memory controlled by var
struct StrT *var = alloc();
inline void s_put_str(struct State *s, char *val) {
struct StrT *var = (StrT *)alloc(sizeof(StrT));
var->data_header = STR_T; // no param
var->value = val;
s_put_var(s, (NilT *)var);
}
// TODO
inline void s_put_array(struct State *s, int sz) { // memory controlled by var
struct Var var = {
.type = ARRAY_T,
.value.array_v = calloc(sz, sizeof(struct Var)),
.size = sz,
};
s_put_var(s, var);
// fill array with nils ?
inline void s_put_enum(struct State *s, const char *tag, int args_sz) {
// TODO FIXME
}
inline void s_put_list(struct State *s,
struct NilT *first_elem) { // memory controlled by var
struct ListT *var;
inline void s_put_array(struct State *s, int sz) {
struct ArrayT *var = (ArrayT *)alloc(sizeof(ArrayT));
if (sz < 0) {
failure("array size < 0");
}
if (sz > MAX_ARRAY_SIZE) {
failure("too big array size");
}
var->data_header = sz & ARRAY_T;
var->values = (NilT **)alloc(sizeof(NilT *) * sz);
for (size_t i = 0; i < sz; ++i) {
var->values[i] = NULL;
}
s_put_var(s, (NilT *)var);
}
inline void s_put_list(struct State *s, struct NilT *first_elem) {
struct ListT *var = (ListT *)alloc(sizeof(ListT));
var->data_header = LIST_T; // no param
var->value = first_elem;
var->next = NULL;
@ -182,12 +145,12 @@ inline void s_put_list(struct State *s,
// ------ take from stack ------
inline union VarT *s_take_var(struct State *s) {
if (s->vp == s->fp->end) {
if (s->vp == s->stack || (s->fp != NULL && s->vp == s->fp->end)) {
failure("take: no var");
}
--s->vp;
union VarT *ret = *s->vp;
union VarT *ret = (VarT *)*s->vp;
*s->vp = NULL; // clear top var
return ret;
}
@ -201,11 +164,11 @@ inline int s_take_i(struct State *s) {
}
inline void s_drop_var(struct State *s) {
if (s->vp == s->fp->end) {
if (s->vp == s->stack || (s->fp != NULL && s->vp == s->fp->end)) {
failure("drop: no var");
}
--s->vp;
free_var_ptr(*s->vp);
free_var_ptr((VarT *)*s->vp);
*s->vp = NULL;
}
@ -217,39 +180,63 @@ inline void s_dropn_var(struct State *s, size_t n) {
// ------ functions ------
// TODO
inline void s_exit_f(struct State *s) {
if (s->fp == (void *)s->stack) {
failure("exit: no func");
}
--s->fp;
s_dropn_var(s, s->vp - s->fp->locals);
// drop local var stack and locals // TODO: check +-1
union VarT *ret = *s->vp;
--s->vp;
s_dropn_var(s, s->vp - s->fp->params);
// drop params // TODO: check +-1
s_put_var(s, ret);
s->ip = s->fp->rp;
// *s->fp = clear_frame(); // clear top frame
}
// TODO
// |> param_0 ... param_n | frame[ ret rp prev_fp &params &locals &end ]
// |> local_0 ... local_m |> | ...
//
// where |> defines corresponding frame pointer, | is stack pointer location
// before / after new frame added
inline void s_enter_f(struct State *s, char *func_ip, size_t params_sz,
size_t locals_sz) {
if (params_sz > s->vp - s->stack or
(s->fp != NULL and params_sz > s->vp - s->fp->end)) {
failure("not enough parameters in stack");
}
size_t frame_sz_in_ptr = sizeof(Frame) / sizeof(void *);
struct Frame frame = {
.rp = s->ip, // ??
.ret = s->vp,
.params = s->vp - params_sz, // TODO: check +-1
.locals = s->vp + 1,
.end = s->vp + locals_sz + 1, // ??
.ret = NULL, // field in frame itself
.rp = s->ip,
.prev_fp = s->fp,
.params = s->vp - params_sz,
.locals = s->vp + frame_sz_in_ptr,
.end = s->vp + frame_sz_in_ptr + locals_sz,
};
// TODO:
s_put_nil(s); // ret
s_putn_nil(s, locals_sz); // locals
s->ip = func_ip;
// put frame on stack
s->fp = (Frame *)s->vp;
(*s->fp) = frame;
++s->fp;
// update stack pointer
s->vp = frame.end;
// go to function body
s->ip = func_ip;
}
inline void s_exit_f(struct State *s) {
if (s->fp == NULL) {
failure("exit: no func");
}
// drop stack entities and locals
s_dropn_var(s, s->vp - s->fp->locals);
// drop params
s->vp = (void **)s->fp;
s_dropn_var(s, s->vp - s->fp->params);
// s->vp = s->fp->params; // done automatically
// save ret_val;
if (s->fp->ret != NULL) {
(*s->vp) = s->fp->ret;
++s->vp;
}
s->ip = s->fp->rp;
s->fp = s->fp->prev_fp;
}
inline union VarT *var_by_category(struct State *s, enum VarCategory category,
int id) {
// TODO: FIXME
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "../../runtime/runtime.h"
#include "parser.h"
#include <stdint.h>
@ -18,43 +19,44 @@ enum Type {
};
struct NilT { // AnyVarT too
int32_t data_header;
uint32_t data_header;
};
struct IntT {
int32_t data_header;
uint32_t data_header;
int32_t value; // int value => size = 1;
};
struct ConstStrT {
int32_t data_header;
uint32_t data_header;
const char *value;
};
struct StrT {
int32_t data_header;
uint32_t data_header;
char *value;
};
struct ListT {
int32_t data_header;
uint32_t data_header;
struct NilT *value;
struct NilT *next;
};
struct ArrayT {
int32_t data_header;
uint32_t data_header;
struct NilT **values;
};
const size_t MAX_ARRAY_SIZE = 0x11111110;
struct SExpT {
int32_t data_header;
uint32_t data_header;
const char *tag;
struct NilT *next;
};
struct FunT {
int32_t data_header;
uint32_t data_header;
char *fun_ip;
};
@ -82,11 +84,12 @@ inline union VarT *to_var(struct NilT *var) { return (union VarT *)var; }
// ------ Frame ------
struct Frame {
char *rp; // ret instruction pointer
struct NilT **ret; // store returned value
struct NilT **params; // store arguments
struct NilT **locals; // store locals
struct NilT **end; // store locals
struct NilT *ret; // store returned value
char *rp; // ret instruction pointer
struct Frame *prev_fp; // ret function frame pointer
void **params; // store arguments
void **locals; // store locals
void **end; // store locals
};
inline uint64_t frame_locals_sz(struct Frame *frame) {
@ -101,16 +104,16 @@ inline uint64_t frame_params_sz(struct Frame *frame) {
union StackValue {
union VarT *var;
union VarT **var_ptr;
// struct Frame frame; // TODO
struct Frame frame; // ??
char *addr;
};
// inline StackValue *to_sv(void *var) { return (StackValue *)var; }
struct State {
union StackValue *stack; // vaid**
struct NilT **vp; // var pointer
struct Frame *fp; // function frame pointer
void **stack; // vaid**
void **vp; // stack pointer
struct Frame *fp; // function frame pointer
char *ip; // instruction pointer
char *prev_ip; // prev instruction pointer
@ -118,3 +121,19 @@ struct State {
struct State init_state(bytefile *bf);
void destruct_state(struct State *state);
// ------ VarCategory ------
enum VarCategory {
VAR_GLOBAL = 0,
VAR_LOCAL = 1,
VAR_A = 2, // TODO: ??
VAR_C = 3 // TODO: ??
};
inline enum VarCategory to_var_category(uint8_t category) {
if (category > 3) {
failure("unexpected variable category");
}
return (VarCategory)category;
}

View file

@ -1,6 +1,9 @@
#include "../include/interpreter.h"
#include "../include/types.h"
#include "../include/builtin.h"
#include "../include/operations.h"
#include "../../runtime/runtime.h"
#include "../../runtime/gc.h"
int ip_read_int(char** ip) {
*ip += sizeof(int);
@ -20,9 +23,11 @@ char* ip_read_string(char** ip, bytefile* bf) {
void run(bytefile *bf) {
struct State s = init_state(bf);
const size_t OPS_SIZE = 13;
const char *ops [] = {"+", "-", "*", "/", "%", "<", "<=", ">", ">=", "==", "!=", "&&", "!!"};
const size_t PATS_SIZE = 7;
const char *pats[] = {"=str", "#string", "#array", "#sexp", "#ref", "#val", "#fun"};
const char *lds [] = {"LD", "LDA", "ST"};
do {
char* before_op_ip = s.ip; // save to set s.prev_ip
@ -38,7 +43,13 @@ void run(bytefile *bf) {
/* BINOP */
case 0: // BINOP ops[l-1]
// TODO: all binops
if (l > OPS_SIZE) {
failure("BINOP: l > OPS_SIZE");
}
if (l < 1) {
failure("BINOP: l < 1");
}
f_binop(&s, ops[l-1]);
break;
case 1:
@ -51,8 +62,9 @@ void run(bytefile *bf) {
s_put_const_str(&s, ip_read_string(&s.ip, bf));
break;
case 2: // SEXP %s %d
// TODO: call sexp ??
case 2: // SEXP %s %d // create sexpr with tag=%s and %d elements from stack
// TODO: params??
s_put_sexp(&s, ip_read_string(&s.ip, bf), ip_read_int(&s.ip));
break;
case 3: // STI
@ -81,14 +93,17 @@ void run(bytefile *bf) {
case 9: // DUP
{ // guess
struct Var v = deep_copy_var(*s.vp);
s_put_var(&s, v);
if (s.vp == s.stack || (s.fp != NULL && s.vp == s.fp->end)) {
failure("can't DUP: no value on stack");
}
*s.vp = *(s.vp - 1);
++s.vp;
break;
}
case 10: // SWAP
{ // guess
struct Var v = *s.vp;
struct NilT* v = *s.vp;
*s.vp = *(s.vp - 1);
*(s.vp - 1) = v;
}
@ -96,67 +111,74 @@ void run(bytefile *bf) {
case 11: // ELEM
{
struct Var array = s_take_var(&s);
struct Var index = s_take_var(&s);
if (array.type != ARRAY_T) {
failure("ERROR: elem, previous element is not array", h, l);
union VarT* array = s_take_var(&s);
union VarT* index = s_take_var(&s);
if (dh_type(array->nil.data_header) != ARRAY_T) {
failure("ELEM: elem, previous element is not array");
}
if (index.type != INT_T) {
failure("ERROR: elem, last element is not int", h, l);
if (dh_type(index->nil.data_header) != INT_T) {
failure("ELEM: elem, last element is not int");
}
s_put_var(&s, array.value.array_v[index.value.int_v]);
if (index->int_t.value < 0) {
failure("ELEM: can't access by index < 0");
}
if (index->int_t.value >= dh_param(array->array.data_header)) {
failure("ELEM: array index is out of range");
}
s_put_var(&s, array->array.values[index->int_t.value]);
free_var(array);
free_var(index);
// FIXME: deal vith deletion of shallow copies of locals
free_var_ptr(array);
free_var_ptr(index);
}
break;
default:
failure("ERROR: invalid opcode %d-%d\n", h, l);
failure("invalid opcode %d-%d\n", h, l);
}
break;
case 2:
case 3:
case 4:
// fprintf (f, "%s\t", lds[h-2]);
switch (l) {
// case 0: fprintf (f, "G(%d)", INT); break;
// case 1: fprintf (f, "L(%d)", INT); break;
// case 2: fprintf (f, "A(%d)", INT); break;
// case 3: fprintf (f, "C(%d)", INT); break;
default:
failure("ERROR: invalid opcode %d-%d\n", h, l);
}
case 2: { // LD %d
int8_t category = ip_read_byte(&s.ip);
union VarT* var = var_by_category(&s, to_var_category(category), ip_read_int(&s.ip));
// TODO
break;
}
case 3: { // LDA %d
int8_t category = ip_read_byte(&s.ip);
union VarT* var = var_by_category(&s, to_var_category(category), ip_read_int(&s.ip));
// TODO
break;
}
case 4: { // ST %d
int8_t category = ip_read_byte(&s.ip);
union VarT* var = var_by_category(&s, to_var_category(category), ip_read_int(&s.ip));
// TODO
break;
}
case 5:
switch (l) {
case 0: { // CJMPz 0x%.8x
// FIXME: TODO: jump by top stack condition ??
char* new_ip = (char*)(long)ip_read_int(&s.ip); // TODO: check
if (s_take_i(&s) != 0) { // FIXME: bools ??, other vars ??
if (s_take_i(&s) != 0) {
s.ip = new_ip;
}
break;
}
case 1: { // CJMPnz 0x%.8x
// FIXME: TODO: jump by top stack condition ??i
// FIXME: TODO: jump by top stack condition ??
char* new_ip = (char*)(long)ip_read_int(&s.ip); // TODO: check
if (s_take_i(&s) == 0) { // FIXME: bools ??, other vars ??
if (s_take_i(&s) == 0) {
s.ip = new_ip;
}
break;
}
case 2: // BEGIN %d %d
case 2: // BEGIN %d %d // function begin
s_enter_f(&s, s.prev_ip/*ip from call*/, ip_read_int(&s.ip), ip_read_int(&s.ip));
// TODO: is func enter ?
break;
case 3: // CBEGIN %d %d
case 3: // CBEGIN %d %d // TODO: clojure begin ??
s_enter_f(&s, s.prev_ip/*ip from call*/, ip_read_int(&s.ip), ip_read_int(&s.ip));
// TODO: is func enter ?
break;
case 4: // CLOSURE 0x%.8x
@ -169,21 +191,19 @@ void run(bytefile *bf) {
// case 2: // A(%d)
// case 3: // C(%d)
default:
failure("ERROR: invalid opcode %d-%d\n", h, l);
failure("invalid opcode %d-%d\n", h, l);
}
}
};
break;
case 5: // CALLC %d
// TODO: no arguments given ??
// TODO: jump only ??
s.ip = (char*)(long)ip_read_int(&s.ip); // TODO: check
case 5: // CALLC %d // call clojure
// TODO FIXME: call clojure
// s.ip = (char*)(long)ip_read_int(&s.ip); // TODO: check
break;
case 6: // CALL 0x%.8x %d
// TODO: second arg is given params amount ??
// TODO: jump only ??
case 6: // CALL 0x%.8x %d // call function
// FIXME: second arg ??
s.ip = (char*)(long)ip_read_int(&s.ip); // TODO: check
break;
@ -196,8 +216,7 @@ void run(bytefile *bf) {
break;
case 9: // FAIL %d %d
// TODO: ??
failure("FAIL: %d-%d\n", ip_read_int(&s.ip), ip_read_int(&s.ip));
failure("[FAIL]: %d-%d\n", ip_read_int(&s.ip), ip_read_int(&s.ip));
break;
case 10: // LINE %d
@ -205,7 +224,7 @@ void run(bytefile *bf) {
break;
default:
failure("ERROR: invalid opcode %d-%d\n", h, l);
failure("invalid opcode %d-%d\n", h, l);
}
break;
@ -236,18 +255,18 @@ void run(bytefile *bf) {
break;
default:
failure("ERROR: invalid opcode %d-%d\n", h, l);
failure("invalid opcode %d-%d\n", h, l);
}
}
break;
default:
failure("ERROR: invalid opcode %d-%d\n", h, l);
failure("invalid opcode %d-%d\n", h, l);
}
s.prev_ip = before_op_ip;
}
while (1);
stop:;
free_state(&s);
destruct_state(&s);
}