part of migration to gc

This commit is contained in:
ProgramSnail 2024-10-20 16:42:57 +03:00
parent fb1ec1c7ae
commit d782934f0c
5 changed files with 457 additions and 337 deletions

35
byterun/include/builtin.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include <stdio.h>
#include "operations.h"
inline void f_read(struct State *s) {
int x = 0;
printf("> "); // TODO: ??
scanf("%i", &x);
s_put_i(s, x);
}
inline void f_write(struct State *s) {
int x = s_take_i(s);
printf("%i", x);
// put 0 ??
}
inline void f_length(struct State *s) {
// TODO
}
inline void f_string(struct State *s) {
// TODO
}
inline void f_array(struct State *s, int sz) {
// TODO
}
inline void f_cons(struct State *s) {
// TODO
}

View file

@ -0,0 +1,255 @@
#pragma once
#include "../../runtime/runtime.h"
#include "types.h"
#include <cstdlib>
// TODO: use gc
// ------ general ------
// TODO: use gc
inline void free_var_ptr(union VarT *var);
inline void free_var(union VarT var) {
switch (dh_type(var.nil.data_header)) {
case NIL_T:
break;
case INT_T:
break;
case CONST_STR_T:
break;
case STR_T:
free(var.str.value);
break;
case LIST_T:
if (var.list.value != NULL) {
free_var_ptr(to_var(var.list.value));
}
if (var.list.next != NULL) {
free_var_ptr(to_var(var.list.next));
}
break;
case ARRAY_T:
// dh param is size
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);
break;
case SEXP_T:
// tag is const string, no need to free
if (var.sexp.next != NULL) {
free(var.sexp.next);
}
break;
case FUN_T:
break;
}
}
// TODO: use gc
inline void free_var_ptr(union VarT *var) {
free_var(*var);
free((void *)var);
}
//
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;
// }
// ------ put on stack ---
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();
var->data_header = NIL_T; // no param
s_put_var(s, var);
}
inline void s_putn_nil(struct State *s, size_t n) {
for (size_t i = 0; i < n; ++i) {
s_put_nil(s);
}
}
inline void s_put_i(struct State *s, int val) {
struct IntT *var = alloc();
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();
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();
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_list(struct State *s,
struct NilT *first_elem) { // memory controlled by var
struct ListT *var;
var->data_header = LIST_T; // no param
var->value = first_elem;
var->next = NULL;
s_put_var(s, (NilT *)var);
*first_elem = clear_var();
}
// ------ take from stack ------
inline union VarT *s_take_var(struct State *s) {
if (s->vp == s->fp->end) {
failure("take: no var");
}
--s->vp;
union VarT *ret = *s->vp;
*s->vp = NULL; // clear top var
return ret;
}
inline int s_take_i(struct State *s) {
union VarT *v = s_take_var(s);
if (dh_type(v->nil.data_header) != INT_T) {
failure("take int: not int");
}
return v->int_t.value;
}
inline void s_drop_var(struct State *s) {
if (s->vp == s->fp->end) {
failure("drop: no var");
}
--s->vp;
free_var_ptr(*s->vp);
*s->vp = NULL;
}
inline void s_dropn_var(struct State *s, size_t n) {
for (size_t i = 0; i < n; ++i) {
s_drop_var(s);
}
}
// ------ 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
inline void s_enter_f(struct State *s, char *func_ip, size_t params_sz,
size_t locals_sz) {
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, // ??
};
// TODO:
s_put_nil(s); // ret
s_putn_nil(s, locals_sz); // locals
s->ip = func_ip;
(*s->fp) = frame;
++s->fp;
}

120
byterun/include/types.h Normal file
View file

@ -0,0 +1,120 @@
#pragma once
#include "parser.h"
#include <stdint.h>
// ------ Var ------
// TODO: clojures
enum Type {
NIL_T = 0x00000000,
INT_T = 0x00000001,
CONST_STR_T = 0x00000002,
STR_T = 0x00000003,
LIST_T = 0x00000004,
ARRAY_T = 0x00000005,
SEXP_T = 0x00000006,
FUN_T = 0x00000007
};
struct NilT { // AnyVarT too
int32_t data_header;
};
struct IntT {
int32_t data_header;
int32_t value; // int value => size = 1;
};
struct ConstStrT {
int32_t data_header;
const char *value;
};
struct StrT {
int32_t data_header;
char *value;
};
struct ListT {
int32_t data_header;
struct NilT *value;
struct NilT *next;
};
struct ArrayT {
int32_t data_header;
struct NilT **values;
};
struct SExpT {
int32_t data_header;
const char *tag;
struct NilT *next;
};
struct FunT {
int32_t data_header;
char *fun_ip;
};
union VarT {
struct NilT nil;
struct IntT int_t;
struct ConstStrT const_str;
struct StrT str;
struct ListT list;
struct ArrayT array;
struct SExpT sexp;
struct FunT fun;
};
// same to TAG in runtime
inline enum Type dh_type(int data_header) {
return (Type)(data_header & 0x00000007);
}
// same to LEN in runtime
inline int dh_param(int data_header) { return (data_header & 0xFFFFFFF8) >> 3; }
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
};
inline uint64_t frame_locals_sz(struct Frame *frame) {
return frame->locals - frame->params;
}
inline uint64_t frame_params_sz(struct Frame *frame) {
return frame->end - frame->locals;
}
// ------ State ------
union StackValue {
union VarT *var;
union VarT **var_ptr;
// struct Frame frame; // TODO
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
char *ip; // instruction pointer
char *prev_ip; // prev instruction pointer
};
struct State init_state(bytefile *bf);
void destruct_state(struct State *state);

View file

@ -1,338 +1,21 @@
#include "../include/interpreter.h"
#include "../include/types.h"
#include "../../runtime/runtime.h"
int read_int(char** ip) {
int ip_read_int(char** ip) {
*ip += sizeof(int);
return *(int*)((*ip) - sizeof(int));
}
char read_byte(char** ip) {
char ip_read_byte(char** ip) {
return *(*ip)++;
}
char* read_string(char** ip, bytefile* bf) {
return get_string(bf, read_int(ip));
char* ip_read_string(char** ip, bytefile* bf) {
return get_string(bf, ip_read_int(ip));
}
//
struct Var;
struct ListElem {
struct Var* elem;
struct Var* next;
};
struct Var {
enum Type { INT_T, CONST_STR_T, STR_T, LIST_T, ARRAY_T, FUN_T, NIL_T/*?*/ } type;
size_t size;
union {
int int_v; // int value => size = 1;
const char* const_str_v; // point to str in str table => size = string size (?)
char* str_v; // poitert ot string => size = string size (?)
struct ListElem list_v; // elem => size = list size
struct Var* array_v; // pointer to array of elements => size = array size
char* fun_v; // pointer to function definition => size = 1
} value;
};
struct Frame {
struct Var* params; // store arguments
struct Var* ret; // store returned value
struct Var* locals; // store locals
size_t locals_sz;
size_t params_sz;
char* rp; // ret instruction pointer
};
// TODO: store globals in some way ?? // maybe some first vars ??
struct State {
struct Var* vars;
struct Frame* funcs;
struct Var* vp; // var pointer
struct Frame* fp; // function pointer
char* ip; // instruction pointer
char* prev_ip; // prev instruction pointer
};
struct State init_state(bytefile *bf) {
struct State state = {
.vars = calloc(1000/* TODO */, sizeof(struct Var)),
.funcs = calloc(1000/* TODO */, sizeof(struct Frame)),
.ip = bf->code_ptr,
.prev_ip = NULL,
};
// TODO: init vars vith NIL_T ??
state.vp = state.vars;
state.fp = state.funcs;
return state;
}
void free_state(struct State* state) {
free(state->vars);
free(state->funcs);
state->vars = NULL;
state->funcs = NULL;
state->vp = NULL;
state->fp = NULL;
state->ip = NULL;
state->prev_ip = NULL;
}
//
void free_var_ptr(struct Var* var);
void free_var(struct Var var) {
switch (var.type) {
case INT_T:
break;
case CONST_STR_T:
break;
case STR_T:
free(var.value.str_v);
break;
case LIST_T:
if (var.value.list_v.elem != NULL) {
free_var_ptr(var.value.list_v.elem);
}
if (var.value.list_v.next != NULL) {
free_var_ptr(var.value.list_v.next);
}
break;
case ARRAY_T:
for (size_t i = 0; i < var.size; ++i) {
free_var(*(var.value.array_v + i));
}
free(var.value.array_v);
break;
case FUN_T:
break;
case NIL_T:
break;
}
}
void free_var_ptr(struct Var* var) {
free_var(*var);
free(var);
}
//
struct Var clear_var() {
struct Var var = {
.type = NIL_T,
.size = 0,
};
return var;
}
struct Frame clear_frame() {
struct Frame frame = {
.params = NULL,
.ret = NULL,
.locals = NULL,
.params_sz = 0,
.locals_sz = 0,
};
return frame;
}
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;
}
//
void s_put_var(struct State* s, struct Var val) {
*s->vp = val;
++s->vp;
}
void s_put_nil(struct State* s) {
struct Var var = {.type = NIL_T, .size = 0};
s_put_var(s, var);
}
void s_putn_nil(struct State* s, size_t n) {
for (size_t i = 0; i < n; ++i) {
s_put_nil(s);
}
}
void s_put_i(struct State* s, int val) {
struct Var var = {.type = INT_T, .value.int_v = val, .size = 1};
s_put_var(s, var);
}
void s_put_const_str(struct State* s, const char* val) { // memory controlled externally
struct Var var = {.type = CONST_STR_T, .value.const_str_v = val, .size = strlen(val)};
s_put_var(s, var);
}
void s_put_str(struct State* s, char* val) { // memory controlled by var
struct Var var = {.type = STR_T, .value.str_v = val, .size = strlen(val)};
s_put_var(s, var);
}
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 ?
}
void s_put_list(struct State* s, struct Var* first_elem) { // memory controlled by var
struct Var var = {
.type = LIST_T,
.value.list_v = {.elem = first_elem, .next = NULL},
.size = 0,
}; // TODO: size ?
s_put_var(s, var);
*first_elem = clear_var();
}
//
struct Var s_take_var(struct State* s) {
if (s->vp == s->vars) {
failure("take: no var");
}
--s->vp;
struct Var ret = *s->vp;
*s->vp = clear_var(); // clear top var
return ret;
}
int s_take_i(struct State* s) {
struct Var v = s_take_var(s);
if (v.type != INT_T) {
failure("take int: not int");
}
return v.value.int_v;
}
void s_drop_var(struct State* s) {
if (s->vp == s->vars) {
failure("drop: no var");
}
--s->vp;
free_var(*s->vp);
*s->vp = clear_var(); // clear top var
}
void s_dropn_var(struct State* s, size_t n) {
for (size_t i = 0; i < n; ++i) {
s_drop_var(s);
}
}
//
void s_exit_f(struct State* s) {
if (s->fp == s->funcs) {
failure("exit: no func");
}
--s->fp;
s_dropn_var(s, s->vp - s->fp->locals); // drop local var stack and locals // TODO: check +-1
struct Var 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
}
void s_enter_f(struct State* s, char* func_ip, size_t params_sz, size_t locals_sz) {
struct Frame frame = {
.params = s->vp - params_sz, // TODO: check +-1
.ret = s->vp,
.locals = s->vp + 1,
.params_sz = params_sz,
.locals_sz = locals_sz,
.rp = s->ip,
};
s_put_nil(s); // ret
s_putn_nil(s, locals_sz); // locals
s->ip = func_ip;
(*s->fp) = frame;
++s->fp;
}
//
// --- builtin calls
void f_read(struct State* s) {
int x = 0;
printf("> "); // TODO: ??
scanf("%i", &x);
s_put_i(s, x);
}
void f_write(struct State* s) {
int x = s_take_i(s);
printf("%i", x);
// put 0 ??
}
void f_length(struct State* s) {
// TODO
}
void f_string(struct State* s) {
// TODO
}
void f_array(struct State* s, int sz) {
// TODO
}
void f_cons(struct State* s) {
// TODO
}
//
void run(bytefile *bf) {
struct State s = init_state(bf);
@ -343,7 +26,7 @@ void run(bytefile *bf) {
do {
char* before_op_ip = s.ip; // save to set s.prev_ip
char x = read_byte(&s.ip),
char x = ip_read_byte(&s.ip),
h = (x & 0xF0) >> 4,
l = x & 0x0F;
@ -361,11 +44,11 @@ void run(bytefile *bf) {
case 1:
switch (l) {
case 0: // CONST %d
s_put_i(&s, read_int(&s.ip));
s_put_i(&s, ip_read_int(&s.ip));
break;
case 1: // STRING %s
s_put_const_str(&s, read_string(&s.ip, bf));
s_put_const_str(&s, ip_read_string(&s.ip, bf));
break;
case 2: // SEXP %s %d
@ -381,7 +64,7 @@ void run(bytefile *bf) {
break;
case 5: // JMP 0x%.8x
s.ip = (char*)(long)read_int(&s.ip); // TODO: check
s.ip = (char*)(long)ip_read_int(&s.ip); // TODO: check
break;
case 6: // END
@ -452,7 +135,7 @@ void run(bytefile *bf) {
switch (l) {
case 0: { // CJMPz 0x%.8x
// FIXME: TODO: jump by top stack condition ??
char* new_ip = (char*)(long)read_int(&s.ip); // TODO: check
char* new_ip = (char*)(long)ip_read_int(&s.ip); // TODO: check
if (s_take_i(&s) != 0) { // FIXME: bools ??, other vars ??
s.ip = new_ip;
}
@ -460,27 +143,27 @@ void run(bytefile *bf) {
}
case 1: { // CJMPnz 0x%.8x
// FIXME: TODO: jump by top stack condition ??i
char* new_ip = (char*)(long)read_int(&s.ip); // TODO: check
char* new_ip = (char*)(long)ip_read_int(&s.ip); // TODO: check
if (s_take_i(&s) == 0) { // FIXME: bools ??, other vars ??
s.ip = new_ip;
}
break;
}
case 2: // BEGIN %d %d
s_enter_f(&s, s.prev_ip/*ip from call*/, read_int(&s.ip), read_int(&s.ip));
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
s_enter_f(&s, s.prev_ip/*ip from call*/, read_int(&s.ip), read_int(&s.ip));
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
{
int n = read_int(&s.ip);
int n = ip_read_int(&s.ip);
for (int i = 0; i < n; i++) {
switch (read_byte(&s.ip)) {
switch (ip_read_byte(&s.ip)) {
// case 0: // G(%d)
// case 1: // L(%d)
// case 2: // A(%d)
@ -495,13 +178,13 @@ void run(bytefile *bf) {
case 5: // CALLC %d
// TODO: no arguments given ??
// TODO: jump only ??
s.ip = (char*)(long)read_int(&s.ip); // TODO: check
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 ??
s.ip = (char*)(long)read_int(&s.ip); // TODO: check
s.ip = (char*)(long)ip_read_int(&s.ip); // TODO: check
break;
case 7: // TAG %s
@ -509,12 +192,12 @@ void run(bytefile *bf) {
break;
case 8: // ARRAY %d
s_put_array(&s, read_int(&s.ip));
s_put_array(&s, ip_read_int(&s.ip));
break;
case 9: // FAIL %d %d
// TODO: ??
failure("FAIL: %d-%d\n", read_int(&s.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
@ -549,7 +232,7 @@ void run(bytefile *bf) {
break;
case 4: // CALL Barray %d
f_array(&s, read_int(&s.ip));
f_array(&s, ip_read_int(&s.ip));
break;
default:

27
byterun/src/types.c Normal file
View file

@ -0,0 +1,27 @@
#include "../include/types.h"
#include <stdlib.h>
// TODO: gc use
struct State init_state(bytefile *bf) {
struct State state = {
.stack = calloc(1000/* TODO */, sizeof(void*)),
.ip = bf->code_ptr,
.prev_ip = NULL,
};
state.vp = *state.stack;
state.fp = NULL;
return state;
}
void destruct_state(struct State* state) {
free(state->stack);
state->vp = NULL;
state->fp = NULL;
state->ip = NULL;
state->prev_ip = NULL;
}