#include "sl.h"#include "equal.h"#include "equalhash.h"#include "cvalues.h"#include "types.h"#include "print.h"#include "table.h" #define inline_space sizeof(((sl_htable*)nil)->_space) static sl_type *sl_tabletype; static voidprint_htable(sl_v v, ios *f){ sl_htable *h = cvalue_data(v); bool first = true; sl_print_str(f, "#table(", true); for(int i = 0, k = 0; k < h->num && i < h->size; i += 2){ if(h->table[i+1] == HT_NOTFOUND) continue; k++; if(!first) sl_print_str(f, " ", true); sl_print_child(f, (sl_v)h->table[i]); sl_print_chr(f, ' '); sl_print_child(f, (sl_v)h->table[i+1]); first = false; } sl_print_chr(f, ')');} static voidprint_traverse_htable(sl_v self){ sl_htable *h = cvalue_data(self); for(int i = 0; i < h->size; i += 2){ if(h->table[i+1] != HT_NOTFOUND){ sl_print_traverse((sl_v)h->table[i]); sl_print_traverse((sl_v)h->table[i+1]); } }} static voidfree_htable(sl_v self){ sl_htable *h = cvalue_data(self); htable_free(h);} static voidrelocate_htable(sl_v oldv, sl_v newv){ sl_htable *oldh = cvalue_data(oldv); sl_htable *h = cvalue_data(newv); if(oldh->table == &oldh->_space[0]) h->table = &h->_space[0]; h->i = oldh->i; h->num = oldh->num; for(int i = 0, k = 0; k < h->num && i < h->size; i += 2){ if(oldh->table[i+1] == HT_NOTFOUND) continue; k++; h->table[i] = (void*)sl_relocate((sl_v)oldh->table[i]); h->table[i+1] = (void*)sl_relocate((sl_v)oldh->table[i+1]); }} static sl_vbounded_compare_htable(sl_v a, sl_v b, int bound, bool eq){ sl_htable *ha = totable(a), *hb = totable(b); int na = ha->size; void **ta = ha->table; bound--; for(int i = 0, k = 0; k < ha->num && i < na; i += 2){ if(ta[i+1] == HT_NOTFOUND) continue; k++; sl_v v = (sl_v)equalhash_get(hb, ta[i]); if(v == (sl_v)HT_NOTFOUND) return fixnum(1); v = bounded_compare((sl_v)ta[i+1], v, bound, eq); if(v != fixnum(0)) return v; } if(ha->num != hb->num) return ha->num > hb->num ? fixnum(1) : fixnum(-1); return fixnum(0);} static sl_cvtable table_vtable = { print_htable, relocate_htable, free_htable, print_traverse_htable, bounded_compare_htable}; boolishashtable(sl_v v){ return iscvalue(v) && cv_class(ptr(v)) == sl_tabletype;} sl_purefnBUILTIN("table?", tablep){ argcount(nargs, 1); return ishashtable(args[0]) ? sl_t : sl_nil;} sl_htable *totable(sl_v v){ if(!ishashtable(v)) cthrow(type_error(nil, "table", v), v); return cvalue_data(v);} BUILTIN("table", table){ int cnt = nargs; if(cnt & 1) bthrow(lerrorf(sl_errarg, "arguments must come in pairs")); sl_v nt; // prevent small tables from being added to finalizer list if(cnt <= HT_N_INLINE) nt = cvalue_nofinalizer(sl_tabletype, sizeof(sl_htable)); else nt = cvalue(sl_tabletype, sizeof(sl_htable)-inline_space); sl_htable *h = cvalue_data(nt); htable_new(h, cnt/2); sl_v k = sl_nil, arg; int i; FOR_ARGS(i, 0, arg, args){ if(i & 1) equalhash_put(h, (void*)k, (void*)arg); else k = arg; } assert(h->table == &h->_space[0] || cnt > HT_N_INLINE); return nt;} BUILTIN("put!", put){ argcount(nargs, 3); sl_htable *h = totable(args[0]); void **table0 = h->table; equalhash_put(h, (void*)args[1], (void*)args[2]); // register finalizer if we outgrew inline space if(table0 == &h->_space[0] && h->table != &h->_space[0]){ sl_cv *cv = ptr(args[0]); add_finalizer(cv); cv->len = sizeof(sl_htable) - inline_space; } return args[0];} _Noreturnstatic voidkey_error(sl_v key){ sl_raise(mk_list2(sl_errkey, key));} sl_purefnBUILTIN("get", get){ if(nargs != 3) argcount(nargs, 2); sl_htable *h = totable(args[0]); sl_v v = (sl_v)equalhash_get(h, (void*)args[1]); if(v == (sl_v)HT_NOTFOUND){ if(nargs == 3) return args[2]; bthrow(key_error(args[1])); } return v;} sl_purefnBUILTIN("has?", has){ argcount(nargs, 2); sl_htable *h = totable(args[0]); return equalhash_has(h, (void*)args[1]) ? sl_t : sl_nil;} BUILTIN("del!", del){ argcount(nargs, 2); sl_htable *h = totable(args[0]); equalhash_remove(h, (void*)args[1]); return args[0];} BUILTIN("table-fold", table_fold){ argcount(nargs, 3); sl_v f = args[0], zero = args[1], t = args[2]; sl_htable *h = totable(t); int n = h->size; void **table = h->table; sl_gc_handle(&f); sl_gc_handle(&zero); sl_gc_handle(&t); for(int i = 0, k = 0; k < h->num && i < n; i += 2){ if(table[i+1] == HT_NOTFOUND) continue; k++; zero = sl_applyn(3, f, (sl_v)table[i], (sl_v)table[i+1], zero); // reload pointer h = cvalue_data(t); n = h->size; table = h->table; } sl_free_gc_handles(3); return zero;} voidtable_init(void){ sl_tabletype = define_opaque_type(mk_sym("table", false), sizeof(sl_htable), &table_vtable, nil);}
#include "sl.h"
#include "equal.h"
#include "equalhash.h"
#include "cvalues.h"
#include "types.h"
#include "print.h"
#include "table.h"
#define inline_space sizeof(((sl_htable*)nil)->_space)
static sl_type *sl_tabletype;
static void
print_htable(sl_v v, ios *f)
{
sl_htable *h = cvalue_data(v);
bool first = true;
sl_print_str(f, "#table(", true);
for(int i = 0, k = 0; k < h->num && i < h->size; i += 2){
if(h->table[i+1] == HT_NOTFOUND)
continue;
k++;
if(!first)
sl_print_str(f, " ", true);
sl_print_child(f, (sl_v)h->table[i]);
sl_print_chr(f, ' ');
sl_print_child(f, (sl_v)h->table[i+1]);
first = false;
}
sl_print_chr(f, ')');
}
static void
print_traverse_htable(sl_v self)
{
sl_htable *h = cvalue_data(self);
for(int i = 0; i < h->size; i += 2){
if(h->table[i+1] != HT_NOTFOUND){
sl_print_traverse((sl_v)h->table[i]);
sl_print_traverse((sl_v)h->table[i+1]);
}
}
}
static void
free_htable(sl_v self)
{
sl_htable *h = cvalue_data(self);
htable_free(h);
}
static void
relocate_htable(sl_v oldv, sl_v newv)
{
sl_htable *oldh = cvalue_data(oldv);
sl_htable *h = cvalue_data(newv);
if(oldh->table == &oldh->_space[0])
h->table = &h->_space[0];
h->i = oldh->i;
h->num = oldh->num;
for(int i = 0, k = 0; k < h->num && i < h->size; i += 2){
if(oldh->table[i+1] == HT_NOTFOUND)
continue;
k++;
h->table[i] = (void*)sl_relocate((sl_v)oldh->table[i]);
h->table[i+1] = (void*)sl_relocate((sl_v)oldh->table[i+1]);
}
}
static sl_v
bounded_compare_htable(sl_v a, sl_v b, int bound, bool eq)
{
sl_htable *ha = totable(a), *hb = totable(b);
int na = ha->size;
void **ta = ha->table;
bound--;
for(int i = 0, k = 0; k < ha->num && i < na; i += 2){
if(ta[i+1] == HT_NOTFOUND)
continue;
k++;
sl_v v = (sl_v)equalhash_get(hb, ta[i]);
if(v == (sl_v)HT_NOTFOUND)
return fixnum(1);
v = bounded_compare((sl_v)ta[i+1], v, bound, eq);
if(v != fixnum(0))
return v;
}
if(ha->num != hb->num)
return ha->num > hb->num ? fixnum(1) : fixnum(-1);
return fixnum(0);
}
static sl_cvtable table_vtable = {
print_htable,
relocate_htable,
free_htable,
print_traverse_htable,
bounded_compare_htable
};
bool
ishashtable(sl_v v)
{
return iscvalue(v) && cv_class(ptr(v)) == sl_tabletype;
}
sl_purefn
BUILTIN("table?", tablep)
{
argcount(nargs, 1);
return ishashtable(args[0]) ? sl_t : sl_nil;
}
sl_htable *
totable(sl_v v)
{
if(!ishashtable(v))
cthrow(type_error(nil, "table", v), v);
return cvalue_data(v);
}
BUILTIN("table", table)
{
int cnt = nargs;
if(cnt & 1)
bthrow(lerrorf(sl_errarg, "arguments must come in pairs"));
sl_v nt;
// prevent small tables from being added to finalizer list
if(cnt <= HT_N_INLINE)
nt = cvalue_nofinalizer(sl_tabletype, sizeof(sl_htable));
else
nt = cvalue(sl_tabletype, sizeof(sl_htable)-inline_space);
sl_htable *h = cvalue_data(nt);
htable_new(h, cnt/2);
sl_v k = sl_nil, arg;
int i;
FOR_ARGS(i, 0, arg, args){
if(i & 1)
equalhash_put(h, (void*)k, (void*)arg);
else
k = arg;
}
assert(h->table == &h->_space[0] || cnt > HT_N_INLINE);
return nt;
}
BUILTIN("put!", put)
{
argcount(nargs, 3);
sl_htable *h = totable(args[0]);
void **table0 = h->table;
equalhash_put(h, (void*)args[1], (void*)args[2]);
// register finalizer if we outgrew inline space
if(table0 == &h->_space[0] && h->table != &h->_space[0]){
sl_cv *cv = ptr(args[0]);
add_finalizer(cv);
cv->len = sizeof(sl_htable) - inline_space;
}
return args[0];
}
_Noreturn
static void
key_error(sl_v key)
{
sl_raise(mk_list2(sl_errkey, key));
}
sl_purefn
BUILTIN("get", get)
{
if(nargs != 3)
argcount(nargs, 2);
sl_htable *h = totable(args[0]);
sl_v v = (sl_v)equalhash_get(h, (void*)args[1]);
if(v == (sl_v)HT_NOTFOUND){
if(nargs == 3)
return args[2];
bthrow(key_error(args[1]));
}
return v;
}
sl_purefn
BUILTIN("has?", has)
{
argcount(nargs, 2);
sl_htable *h = totable(args[0]);
return equalhash_has(h, (void*)args[1]) ? sl_t : sl_nil;
}
BUILTIN("del!", del)
{
argcount(nargs, 2);
sl_htable *h = totable(args[0]);
equalhash_remove(h, (void*)args[1]);
return args[0];
}
BUILTIN("table-fold", table_fold)
{
argcount(nargs, 3);
sl_v f = args[0], zero = args[1], t = args[2];
sl_htable *h = totable(t);
int n = h->size;
void **table = h->table;
sl_gc_handle(&f);
sl_gc_handle(&zero);
sl_gc_handle(&t);
for(int i = 0, k = 0; k < h->num && i < n; i += 2){
if(table[i+1] == HT_NOTFOUND)
continue;
k++;
zero = sl_applyn(3, f, (sl_v)table[i], (sl_v)table[i+1], zero);
// reload pointer
h = cvalue_data(t);
n = h->size;
table = h->table;
}
sl_free_gc_handles(3);
return zero;
}
void
table_init(void)
{
sl_tabletype = define_opaque_type(mk_sym("table", false), sizeof(sl_htable), &table_vtable, nil);
}