Nasal-Interpreter/nasal_gc.h

448 lines
11 KiB
C++

#ifndef __NASAL_GC_H__
#define __NASAL_GC_H__
enum nasal_type
{
vm_nil=0,
vm_num,
vm_str,
vm_func,
vm_vec,
vm_hash,
vm_obj,
vm_type_size
};
// change parameters here to make your own efficient gc
// better set bigger number on vm_num and vm_vec
const int increment[vm_type_size]=
{
0, // vm_nil,in fact it is not in use
65536,// vm_num
2048, // vm_str
512, // vm_func
8192, // vm_vec
512, // vm_hash
0 // vm_obj
};
// declaration of nasal_val
struct nasal_val;
// define nasal_ref => nasal_val*
typedef nasal_val* nasal_ref;
#ifdef __NASAL_REF__
struct nasal_ref
{
uint8_t type;
union
{
double num;
nasal_val* gcobj;
}value;
};
#endif
struct nasal_vec// 24 bytes
{
std::vector<nasal_ref> elems;
void print();
nasal_ref get_val(int);
nasal_ref* get_mem(int);
};
struct nasal_hash// 56 bytes
{
std::unordered_map<std::string,nasal_ref> elems;
void print();
nasal_ref get_val(std::string&);
nasal_ref* get_mem(std::string&);
};
struct nasal_func// 120 bytes
{
int32_t dynpara; // dynamic parameter name index in hash
uint32_t offset; // arguments will be loaded into local scope from this offset
uint32_t entry; // pc will set to entry-1 to call this function
std::vector<nasal_ref> default_para;// default value(nasal_ref)
std::unordered_map<std::string,int> key_table;// parameter name hash
nasal_ref closure; // closure will be loaded to gc.local.back() as the local scope
nasal_func();
void clear();
};
struct nasal_val// 16 bytes
{
#define GC_UNCOLLECTED 0
#define GC_COLLECTED 1
#define GC_FOUND 2
uint8_t mark;
uint8_t type;
union
{
double num;
std::string* str;
nasal_vec* vec;
nasal_hash* hash;
nasal_func* func;
void* obj;
}ptr;
nasal_val(int);
~nasal_val();
double to_number();
std::string to_string();
};
/*functions of nasal_vec*/
nasal_ref nasal_vec::get_val(int index)
{
int vec_size=elems.size();
if(index<-vec_size || index>=vec_size)
return nullptr;
return elems[index>=0?index:index+vec_size];
}
nasal_ref* nasal_vec::get_mem(int index)
{
int vec_size=elems.size();
if(index<-vec_size || index>=vec_size)
return nullptr;
return &elems[index>=0?index:index+vec_size];
}
void nasal_vec::print()
{
static int depth=0;
if(++depth>32)
{
std::cout<<"[..]";
--depth;
return;
}
if(!elems.size())
{
std::cout<<"[]";
return;
}
ssize_t iter=0;
std::cout<<'[';
for(auto i:elems)
{
switch(i->type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i->ptr.num; break;
case vm_str: std::cout<<*i->ptr.str; break;
case vm_vec: i->ptr.vec->print(); break;
case vm_hash: i->ptr.hash->print(); break;
case vm_func: std::cout<<"func(..){..}"; break;
}
std::cout<<",]"[(++iter)==elems.size()];
}
--depth;
return;
}
/*functions of nasal_hash*/
nasal_ref nasal_hash::get_val(std::string& key)
{
if(elems.count(key))
return elems[key];
else if(elems.count("parents"))
{
nasal_ref ret_addr=nullptr;
nasal_ref val_addr=elems["parents"];
if(val_addr->type==vm_vec)
for(auto i:val_addr->ptr.vec->elems)
{
if(i->type==vm_hash)
ret_addr=i->ptr.hash->get_val(key);
if(ret_addr)
return ret_addr;
}
}
return nullptr;
}
nasal_ref* nasal_hash::get_mem(std::string& key)
{
if(elems.count(key))
return &elems[key];
else if(elems.count("parents"))
{
nasal_ref* mem_addr=nullptr;
nasal_ref val_addr=elems["parents"];
if(val_addr->type==vm_vec)
for(auto i:val_addr->ptr.vec->elems)
{
if(i->type==vm_hash)
mem_addr=i->ptr.hash->get_mem(key);
if(mem_addr)
return mem_addr;
}
}
return nullptr;
}
void nasal_hash::print()
{
static int depth=0;
if(++depth>32)
{
std::cout<<"{..}";
--depth;
return;
}
if(!elems.size())
{
std::cout<<"{}";
return;
}
size_t iter=0;
std::cout<<'{';
for(auto& i:elems)
{
std::cout<<i.first<<':';
nasal_ref tmp=i.second;
switch(tmp->type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<tmp->ptr.num; break;
case vm_str: std::cout<<*tmp->ptr.str; break;
case vm_vec: tmp->ptr.vec->print(); break;
case vm_hash: tmp->ptr.hash->print(); break;
case vm_func: std::cout<<"func(..){..}"; break;
}
std::cout<<",}"[(++iter)==elems.size()];
}
--depth;
return;
}
/*functions of nasal_func*/
nasal_func::nasal_func()
{
dynpara=-1;
return;
}
void nasal_func::clear()
{
dynpara=-1;
default_para.clear();
key_table.clear();
return;
}
/*functions of nasal_val*/
nasal_val::nasal_val(int val_type)
{
mark=GC_COLLECTED;
type=val_type;
switch(type)
{
case vm_num: ptr.num=0; break;
case vm_str: ptr.str=new std::string; break;
case vm_vec: ptr.vec=new nasal_vec; break;
case vm_hash: ptr.hash=new nasal_hash; break;
case vm_func: ptr.func=new nasal_func; break;
}
return;
}
nasal_val::~nasal_val()
{
switch(type)
{
case vm_str: delete ptr.str; break;
case vm_vec: delete ptr.vec; break;
case vm_hash: delete ptr.hash; break;
case vm_func: delete ptr.func; break;
}
type=vm_nil;
return;
}
double nasal_val::to_number()
{
if(type==vm_str)
return str2num(ptr.str->c_str());
return ptr.num;
}
std::string nasal_val::to_string()
{
if(type==vm_str)
return *ptr.str;
else if(type==vm_num)
return std::to_string(ptr.num);
return "";
}
struct nasal_gc
{
#define STACK_MAX_DEPTH (4095)
nasal_ref zero_addr; // reserved address of nasal_val,type vm_num, 0
nasal_ref one_addr; // reserved address of nasal_val,type vm_num, 1
nasal_ref nil_addr; // reserved address of nasal_val,type vm_nil
nasal_ref val_stack[STACK_MAX_DEPTH+1];// 1 reserved to avoid stack overflow, stack grows 1 each time
nasal_ref* stack_top; // stack top
std::vector<nasal_ref> num_addrs; // reserved address for const vm_num
std::vector<nasal_ref> str_addrs; // reserved address for const vm_str
std::vector<nasal_ref> memory; // gc memory
std::queue <nasal_ref> free_list[vm_type_size]; // gc free list
std::vector<nasal_ref> local;
void mark();
void sweep();
void gc_init(const std::vector<double>&,const std::vector<std::string>&);
void gc_clear();
nasal_ref gc_alloc(int);
nasal_ref builtin_alloc(int);
};
/* gc functions */
void nasal_gc::mark()
{
std::queue<nasal_ref> bfs;
for(auto i:local)
bfs.push(i);
for(nasal_ref* i=val_stack;i<=stack_top;++i)
bfs.push(*i);
while(!bfs.empty())
{
nasal_ref tmp=bfs.front();
bfs.pop();
if(tmp->mark) continue;
tmp->mark=GC_FOUND;
switch(tmp->type)
{
case vm_vec:
for(auto i:tmp->ptr.vec->elems)
bfs.push(i);
break;
case vm_hash:
for(auto& i:tmp->ptr.hash->elems)
bfs.push(i.second);
break;
case vm_func:
bfs.push(tmp->ptr.func->closure);
for(auto i:tmp->ptr.func->default_para)
if(i)
bfs.push(i);
break;
}
}
return;
}
void nasal_gc::sweep()
{
for(auto i:memory)
{
if(i->mark==GC_UNCOLLECTED)
{
switch(i->type)
{
case vm_str: i->ptr.str->clear(); break;
case vm_vec: i->ptr.vec->elems.clear(); break;
case vm_hash:i->ptr.hash->elems.clear();break;
case vm_func:i->ptr.func->clear(); break;
}
free_list[i->type].push(i);
i->mark=GC_COLLECTED;
}
else if(i->mark==GC_FOUND)
i->mark=GC_UNCOLLECTED;
}
return;
}
void nasal_gc::gc_init(const std::vector<double>& nums,const std::vector<std::string>& strs)
{
for(int i=vm_num;i<vm_type_size;++i)
for(int j=0;j<increment[i];++j)
{
nasal_ref tmp=new nasal_val(i);
memory.push_back(tmp);
free_list[i].push(tmp);
}
stack_top=val_stack; // set stack_top to val_stack
zero_addr=new nasal_val(vm_num); // init constant 0
zero_addr->ptr.num=0;
one_addr=new nasal_val(vm_num); // init constant 1
one_addr->ptr.num=1;
nil_addr=new nasal_val(vm_nil); // init nil
// init constant numbers
num_addrs.resize(nums.size());
for(int i=0;i<nums.size();++i)
{
num_addrs[i]=new nasal_val(vm_num);
num_addrs[i]->ptr.num=nums[i];
}
// init constant strings
str_addrs.resize(strs.size());
for(int i=0;i<strs.size();++i)
{
str_addrs[i]=new nasal_val(vm_str);
*str_addrs[i]->ptr.str=strs[i];
}
return;
}
void nasal_gc::gc_clear()
{
for(auto i:memory)
delete i;
memory.clear();
for(int i=0;i<vm_type_size;++i)
while(!free_list[i].empty())
free_list[i].pop();
local.clear();
delete nil_addr;
delete one_addr;
delete zero_addr;
for(auto i:num_addrs)
delete i;
num_addrs.clear();
for(auto i:str_addrs)
delete i;
str_addrs.clear();
return;
}
nasal_ref nasal_gc::gc_alloc(int type)
{
if(free_list[type].empty())
{
mark();
sweep();
}
if(free_list[type].empty())
for(int i=0;i<increment[type];++i)
{
nasal_ref tmp=new nasal_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
}
nasal_ref ret=free_list[type].front();
ret->mark=GC_UNCOLLECTED;
free_list[type].pop();
return ret;
}
nasal_ref nasal_gc::builtin_alloc(int type)
{
// when running a builtin function,alloc will run more than one time
// this may cause mark-sweep in gc_alloc
// and the value got before will be collected,this is a fatal error
// so use builtin_alloc in builtin functions if this function uses alloc more then one time
if(free_list[type].empty())
for(int i=0;i<increment[type];++i)
{
nasal_ref tmp=new nasal_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
}
nasal_ref ret=free_list[type].front();
ret->mark=GC_UNCOLLECTED;
free_list[type].pop();
return ret;
}
#endif