ref: fb908fbd038ae14d54738ab146495bb962a5ec47
dir: /src/cvalues.c/
#include "sl.h"
#include "operators.h"
#include "cvalues.h"
#include "types.h"
#include "iostream.h"
#include "equal.h"
enum {
MAX_INL_SIZE = 384,
CVALUE_NWORDS = sizeof(csl_v)/sizeof(sl_v),
CV_OWNED = 1<<0,
};
#define owned(cv) ((uintptr)(cv)->type & CV_OWNED)
#define isinlined(cv) ((cv)->data == (cv)->_space)
static void cvalue_init(sl_type *type, sl_v v, void *dest);
void
add_finalizer(csl_v *cv)
{
if(slg.nfinalizers == slg.maxfinalizers){
slg.maxfinalizers *= 2;
slg.finalizers = MEM_REALLOC(slg.finalizers, slg.maxfinalizers*sizeof(slg.finalizers));
assert(slg.finalizers != nil);
}
slg.finalizers[slg.nfinalizers++] = cv;
}
// remove dead objects from finalization list in-place
void
sweep_finalizers(void)
{
csl_v **lst = slg.finalizers;
usize n = 0, ndel = 0, l = slg.nfinalizers;
csl_v *tmp;
#define SWAP_sf(a, b) (tmp = a, a = b, b = tmp, 1)
if(l == 0)
return;
bool exiting = slg.exiting;
do{
tmp = lst[n];
if(isforwarded((sl_v)tmp)){
// object is alive
lst[n] = ptr(forwardloc((sl_v)tmp));
n++;
}else{
sl_type *t = cv_class(tmp);
if(t->vtable != nil && t->vtable->finalize != nil)
t->vtable->finalize(tagptr(tmp, TAG_CVALUE));
if(!isinlined(tmp) && owned(tmp) && !exiting)
MEM_FREE(cv_data(tmp));
ndel++;
}
}while((n < l-ndel) && SWAP_sf(lst[n], lst[n+ndel]));
slg.nfinalizers -= ndel;
slg.malloc_pressure = 0;
}
// compute the size of the metadata object for a cvalue
static usize
cv_nwords(csl_v *cv)
{
if(!isinlined(cv))
return CVALUE_NWORDS;
usize n = cv_len(cv);
if(cv_isstr(cv))
n++;
return CVALUE_NWORDS + NWORDS(n);
}
void
cv_autorelease(csl_v *cv)
{
cv->type = (sl_type*)(((uintptr)cv->type) | CV_OWNED);
add_finalizer(cv);
}
static sl_v
cprim(sl_type *type, usize sz)
{
assert(!ismanaged((uintptr)type));
assert(sz == type->size);
sl_cprim *pcp = alloc_words(CPRIM_NWORDS+NWORDS(sz));
pcp->type = type;
return tagptr(pcp, TAG_CPRIM);
}
sl_v
cvalue_(sl_type *type, usize sz, bool nofinalize)
{
assert(type != nil);
if(valid_numtype(type->numtype) && type->numtype != T_MP)
return cprim(type, sz);
bool str = false;
if(type->eltype == sl_bytetype){
if(sz == 0)
return sl_emptystr;
sz++;
str = true;
}
csl_v *pcv;
if(sz <= MAX_INL_SIZE){
usize nw = CVALUE_NWORDS + NWORDS(sz);
pcv = alloc_words(nw);
pcv->type = type;
pcv->data = pcv->_space;
if(!nofinalize && type->vtable != nil && type->vtable->finalize != nil)
add_finalizer(pcv);
}else{
if(slg.malloc_pressure > ALLOC_LIMIT_TRIGGER)
sl_gc(false);
pcv = alloc_words(CVALUE_NWORDS);
pcv->type = type;
pcv->data = MEM_ALLOC(sz);
cv_autorelease(pcv);
slg.malloc_pressure += sz;
}
if(str)
((char*)pcv->data)[--sz] = '\0';
pcv->len = sz;
return tagptr(pcv, TAG_CVALUE);
}
// this effectively dereferences a pointer
// just like *p in C, it only removes a level of indirection from the type,
// it doesn't copy any data.
// this method of creating a cvalue only allocates metadata.
// ptr is user-managed; we don't autorelease it unless the
// user explicitly calls (autorelease ) on the result of this function.
// 'parent' is an optional cvalue that this pointer is known to point
// into; NIL if none.
sl_v
cvalue_from_ref(sl_type *type, void *ptr, usize sz)
{
csl_v *pcv;
assert(type != nil);
assert(ptr != nil);
pcv = alloc_words(CVALUE_NWORDS);
pcv->data = ptr;
pcv->len = sz;
pcv->type = type;
return tagptr(pcv, TAG_CVALUE);
}
sl_v
cvalue_string(usize sz)
{
if(sz == 0)
return sl_emptystr;
return cvalue(sl_stringtype, sz);
}
sl_v
cvalue_static_cstring(const char *str)
{
if(*str == 0)
return sl_emptystr;
return cvalue_from_ref(sl_stringtype, (char*)str, strlen(str));
}
sl_v
string_from_cstrn(char *str, usize n)
{
sl_v v = cvalue_string(n);
memcpy(cvalue_data(v), str, n);
return v;
}
sl_v
string_from_cstr(char *str)
{
return string_from_cstrn(str, strlen(str));
}
bool
sl_isstring(sl_v v)
{
return iscvalue(v) && cv_isstr(ptr(v));
}
// convert to malloc representation (fixed address)
void
cv_pin(csl_v *cv)
{
if(!isinlined(cv))
return;
usize sz = cv_len(cv);
if(cv_isstr(cv))
sz++;
void *data = MEM_ALLOC(sz);
memcpy(data, cv_data(cv), sz);
cv->data = data;
cv_autorelease(cv);
}
#define num_init(ctype, cnvt, tag) \
static void \
cvalue_##ctype##_init(sl_type *type, sl_v arg, void *dest) \
{ \
ctype n; \
USED(type); \
if(isfixnum(arg)) \
n = (ctype)numval(arg); \
else if(iscprim(arg)){ \
sl_cprim *cp = ptr(arg); \
void *p = cp_data(cp); \
n = (ctype)conv_to_##cnvt(p, cp_numtype(cp)); \
}else if(ismp(arg)){ \
void *p = cv_data(ptr(arg)); \
n = (ctype)conv_to_##cnvt(p, T_MP); \
}else \
type_error("number", arg); \
*((ctype*)dest) = n; \
}
num_init(s8int, s32, T_S8)
num_init(u8int, u32, T_U8)
num_init(s16int, s32, T_S16)
num_init(u16int, u32, T_U16)
num_init(s32int, s32, T_S32)
num_init(u32int, u32, T_U32)
num_init(s64int, s64, T_S64)
num_init(u64int, u64, T_U64)
num_init(float, double, T_FLOAT)
num_init(double, double, T_DOUBLE)
#define num_ctor_init(typenam, ctype, tag) \
static \
BUILTIN(#typenam, typenam) \
{ \
if(nargs == 0){ \
PUSH(fixnum(0)); \
args = sl.sp-1; \
} \
sl_v cp = cprim(sl_##typenam##type, sizeof(ctype)); \
cvalue_##ctype##_init(sl_##typenam##type, args[0], cp_data(ptr(cp))); \
return cp; \
}
#define num_ctor_ctor(typenam, ctype, tag) \
sl_v mk_##typenam(ctype n) \
{ \
sl_v cp = cprim(sl_##typenam##type, sizeof(ctype)); \
*(ctype*)cp_data(ptr(cp)) = n; \
return cp; \
}
#define num_ctor(typenam, ctype, tag) \
num_ctor_init(typenam, ctype, tag) \
num_ctor_ctor(typenam, ctype, tag)
num_ctor_init(s8, s8int, T_S8)
num_ctor_init(u8, u8int, T_U8)
num_ctor_init(s16, s16int, T_S16)
num_ctor_init(u16, u16int, T_U16)
num_ctor(s32, s32int, T_S32)
num_ctor(u32, u32int, T_U32)
num_ctor(s64, s64int, T_S64)
num_ctor(u64, u64int, T_U64)
num_ctor_init(byte, u8int, T_U8)
num_ctor(float, float, T_FLOAT)
num_ctor(double, double, T_DOUBLE)
num_ctor(rune, u32int, T_U32)
static void
cvalue_mp_init(sl_type *type, sl_v arg, void *dest)
{
mpint *n;
USED(type);
if(isfixnum(arg)){
n = vtomp(numval(arg), nil);
}else if(iscvalue(arg)){
csl_v *cv = ptr(arg);
void *p = cv_data(cv);
n = conv_to_mp(p, cp_numtype(cv));
}else if(iscprim(arg)){
sl_cprim *cp = ptr(arg);
void *p = cp_data(cp);
n = conv_to_mp(p, cp_numtype(cp));
}else
type_error("number", arg);
*((mpint**)dest) = n;
}
BUILTIN("bignum", bignum)
{
if(nargs == 0){
PUSH(fixnum(0));
args = sl.sp-1;
}
sl_v cv = cvalue(sl_mptype, sizeof(mpint*));
cvalue_mp_init(sl_mptype, args[0], cvalue_data(cv));
return cv;
}
sl_v
mk_mp(mpint *n)
{
sl_v cv = cvalue(sl_mptype, sizeof(mpint*));
*(mpint**)cvalue_data(cv) = n;
return cv;
}
static void
free_mp(sl_v self)
{
mpint **s = value2c(mpint**, self);
if(*s != mpzero && *s != mpone && *s != mptwo)
mpfree(*s);
}
static sl_cvtable mp_vtable = { nil, nil, free_mp, nil };
sl_v
size_wrap(usize sz)
{
if(sizeof(usize) == 8)
return fits_fixnum(sz) ? fixnum(sz): mk_u64(sz);
else
return fits_fixnum(sz) ? fixnum(sz): mk_u32(sz);
}
usize
tosize(sl_v n)
{
if(isfixnum(n))
return (usize)numval(n);
if(iscprim(n)){
sl_cprim *cp = ptr(n);
if(sizeof(usize) == 8)
return conv_to_u64(cp_data(cp), cp_numtype(cp));
return conv_to_u32(cp_data(cp), cp_numtype(cp));
}
type_error("number", n);
}
soffset
tooffset(sl_v n)
{
if(isfixnum(n))
return numval(n);
if(iscprim(n)){
sl_cprim *cp = ptr(n);
return conv_to_s64(cp_data(cp), cp_numtype(cp));
}
type_error("number", n);
}
bool
isarray(sl_v v)
{
return iscvalue(v) && cv_class(ptr(v))->eltype != nil;
}
static usize
predict_arraylen(sl_v arg)
{
if(isvector(arg))
return vector_size(arg);
if(iscons(arg))
return llength(arg);
if(arg == sl_nil)
return 0;
if(isarray(arg))
return cvalue_arraylen(arg);
return 1;
}
void
cvalue_array_init(sl_type *ft, sl_v arg, void *dest)
{
sl_v type = ft->type;
usize elsize, i, cnt, sz;
sl_type *eltype = ft->eltype;
elsize = ft->elsz;
cnt = predict_arraylen(arg);
if(iscons(cdr_(cdr_(type)))){
usize tc = tosize(car_(cdr_(cdr_(type))));
if(tc != cnt)
lerrorf(sl_errarg, "size mismatch");
}
sz = elsize * cnt;
if(isvector(arg)){
assert(cnt <= vector_size(arg));
for(i = 0; i < cnt; i++){
cvalue_init(eltype, vector_elt(arg, i), dest);
dest = (char*)dest + elsize;
}
return;
}
if(iscons(arg) || arg == sl_nil){
i = 0;
while(iscons(arg)){
if(i == cnt){
i++;
break;
} // trigger error
cvalue_init(eltype, car_(arg), dest);
i++;
dest = (char*)dest + elsize;
arg = cdr_(arg);
}
if(i != cnt)
lerrorf(sl_errarg, "size mismatch");
return;
}
if(iscvalue(arg)){
csl_v *cv = ptr(arg);
if(isarray(arg)){
sl_type *aet = cv_class(cv)->eltype;
if(aet == eltype){
if(cv_len(cv) == sz)
memcpy(dest, cv_data(cv), sz);
else
lerrorf(sl_errarg, "size mismatch");
return;
}else{
// TODO: initialize array from different type elements
lerrorf(sl_errarg, "element type mismatch");
}
}
}
if(cnt == 1)
cvalue_init(eltype, arg, dest);
type_error("sequence", arg);
}
BUILTIN("array", array)
{
usize elsize, cnt, sz;
sl_v arg;
if(nargs < 1)
argcount(nargs, 1);
cnt = nargs - 1;
sl_type *type = get_array_type(args[0]);
elsize = type->elsz;
sz = elsize * cnt;
sl_v cv = cvalue(type, sz);
char *dest = cvalue_data(cv);
int i;
FOR_ARGS(i, 1, arg, args){
if(!sl_isnumber(arg))
type_error("number", arg);
cvalue_init(type->eltype, arg, dest);
dest += elsize;
}
return cv;
}
BUILTIN("array-alloc", array_alloc)
{
usize elsize, sz;
long i, cnt, a;
if(nargs < 3)
argcount(nargs, 3);
cnt = tosize(args[1]);
if(cnt < 0)
lerrorf(sl_errarg, "invalid size: %"PRIu64, (u64int)cnt);
sl_type *type = get_array_type(args[0]);
elsize = type->elsz;
sz = elsize * cnt;
sl_v cv = cvalue(type, sz);
char *dest = cvalue_data(cv);
a = 2;
for(i = 0; i < cnt; i++){
sl_v arg = args[a];
if(!sl_isnumber(arg))
type_error("number", arg);
cvalue_init(type->eltype, arg, dest);
dest += elsize;
if((a = (a + 1) % nargs) < 2)
a = 2;
}
return cv;
}
// NOTE: v must be an array
usize
cvalue_arraylen(sl_v v)
{
csl_v *cv = ptr(v);
return cv_len(cv)/cv_class(cv)->elsz;
}
usize
ctype_sizeof(sl_v type)
{
sl_sym *s;
if(issymbol(type) && (s = ptr(type)) != nil && valid_numtype(s->numtype))
return s->size;
if(iscons(type)){
sl_v hed = car_(type);
if(hed == sl_arraysym){
sl_v t = car(cdr_(type));
if(!iscons(cdr_(cdr_(type))))
lerrorf(sl_errarg, "incomplete type");
sl_v n = car_(cdr_(cdr_(type)));
usize sz = tosize(n);
return sz * ctype_sizeof(t);
}
}
lerrorf(sl_errarg, "invalid c type");
}
// get pointer and size for any plain-old-data value
void
to_sized_ptr(sl_v v, u8int **pdata, usize *psz)
{
if(iscvalue(v)){
csl_v *pcv = ptr(v);
sl_ios *x;
if(isiostream(v) && (x = value2c(sl_ios*, v))->bm == bm_mem){
*pdata = x->buf;
*psz = x->size;
return;
}
if(cv_isPOD(pcv)){
*pdata = cv_data(pcv);
*psz = cv_len(pcv);
return;
}
}
if(iscprim(v)){
sl_cprim *pcp = ptr(v);
*pdata = cp_data(pcp);
*psz = cp_class(pcp)->size;
return;
}
type_error("plain-old-data", v);
}
BUILTIN("sizeof", sizeof)
{
argcount(nargs, 1);
if(issymbol(args[0]) || iscons(args[0]))
return size_wrap(ctype_sizeof(args[0]));
usize n;
u8int *data;
to_sized_ptr(args[0], &data, &n);
return size_wrap(n);
}
sl_purefn
BUILTIN("typeof", typeof)
{
argcount(nargs, 1);
switch(tag(args[0])){
case TAG_CONS: return sl_conssym;
case TAG_NUM1: case TAG_NUM: return sl_fixnumsym;
case TAG_SYM: return sl_symbolsym;
case TAG_VECTOR: return sl_vectorsym;
case TAG_FUNCTION:
if(args[0] == sl_t)
return sl_booleansym;
if(args[0] == sl_nil)
return sl_nullsym;
if(args[0] == sl_eof)
return sl_eof;
if(args[0] == sl_void)
return sl_void;
if(isbuiltin(args[0]))
return sl_builtinsym;
return sl_function;
}
return cv_type(ptr(args[0]));
}
sl_v
cvalue_relocate(sl_v v)
{
usize nw;
csl_v *cv = ptr(v);
csl_v *nv;
sl_v ncv;
nw = cv_nwords(cv);
nv = alloc_words(nw);
memcpy(nv, cv, nw*sizeof(sl_v));
if(isinlined(cv))
nv->data = nv->_space;
ncv = tagptr(nv, TAG_CVALUE);
sl_type *t = cv_class(cv);
if(t->vtable != nil && t->vtable->relocate != nil)
t->vtable->relocate(v, ncv);
forward(v, ncv);
if(slg.exiting)
cv_autorelease(ptr(ncv));
return ncv;
}
sl_v
cvalue_copy(sl_v v)
{
assert(iscvalue(v));
PUSH(v);
csl_v *cv = ptr(v);
usize nw = cv_nwords(cv);
csl_v *ncv = alloc_words(nw);
v = POP();
cv = ptr(v);
memcpy(ncv, cv, nw * sizeof(sl_v));
if(!isinlined(cv)){
usize len = cv_len(cv);
if(cv_isstr(cv))
len++;
ncv->data = MEM_ALLOC(len);
memcpy(ncv->data, cv_data(cv), len);
cv_autorelease(ncv);
}else{
ncv->data = ncv->_space;
}
return tagptr(ncv, TAG_CVALUE);
}
BUILTIN("copy", copy)
{
argcount(nargs, 1);
if(iscons(args[0]) || isvector(args[0]))
lerrorf(sl_errarg, "argument must be a leaf atom");
if(!iscvalue(args[0]))
return args[0];
if(!cv_isPOD(ptr(args[0])))
lerrorf(sl_errarg, "argument must be a plain-old-data type");
return cvalue_copy(args[0]);
}
sl_purefn
BUILTIN("plain-old-data?", plain_old_datap)
{
argcount(nargs, 1);
return (iscprim(args[0]) ||
(iscvalue(args[0]) && cv_isPOD(ptr(args[0])))) ?
sl_t : sl_nil;
}
static void
cvalue_init(sl_type *type, sl_v v, void *dest)
{
cvinitfunc_t f = type->init;
if(f == nil)
lerrorf(sl_errarg, "invalid c type");
f(type, v, dest);
}
// (new type . args)
// this provides (1) a way to allocate values with a shared type for
// efficiency, (2) a uniform interface for allocating cvalues of any
// type, including user-defined.
BUILTIN("c-value", c_value)
{
if(nargs < 1 || nargs > 2)
argcount(nargs, 2);
sl_v type = args[0];
sl_type *ft = get_type(type);
sl_v cv;
if(ft->eltype != nil){
// special case to handle incomplete array types bla[]
usize elsz = ft->elsz;
usize cnt;
if(iscons(cdr_(cdr_(type))))
cnt = tosize(car_(cdr_(cdr_(type))));
else if(nargs == 2)
cnt = predict_arraylen(args[1]);
else
cnt = 0;
cv = cvalue(ft, elsz * cnt);
if(nargs == 2)
cvalue_array_init(ft, args[1], cvalue_data(cv));
}else{
cv = cvalue(ft, ft->size);
if(nargs == 2)
cvalue_init(ft, args[1], cptr(cv));
}
return cv;
}
// NOTE: this only compares lexicographically; it ignores numeric formats
sl_v
cvalue_compare(sl_v a, sl_v b)
{
csl_v *ca = ptr(a);
csl_v *cb = ptr(b);
char *adata = cv_data(ca);
char *bdata = cv_data(cb);
usize asz = cv_len(ca);
usize bsz = cv_len(cb);
usize minsz = asz < bsz ? asz : bsz;
int diff = memcmp(adata, bdata, minsz);
if(diff == 0){
if(asz > bsz)
return fixnum(1);
if(asz < bsz)
return fixnum(-1);
}
return fixnum(diff);
}
static void
check_addr_args(sl_v arr, sl_v ind, u8int **data, int *index)
{
int numel;
csl_v *cv = ptr(arr);
*data = cv_data(cv);
numel = cv_len(cv)/cv_class(cv)->elsz;
*index = tosize(ind);
if(*index < 0 || *index >= numel)
bounds_error(arr, ind);
}
sl_v
cvalue_array_aref(sl_v *args)
{
u8int *data;
int index;
sl_type *eltype = cv_class(ptr(args[0]))->eltype;
sl_v el = 0;
sl_numtype nt = eltype->numtype;
if(nt >= T_S32)
el = cvalue(eltype, eltype->size);
check_addr_args(args[0], args[1], &data, &index);
if(nt < T_S32){
if(nt == T_S8)
return fixnum((s8int)data[index]);
if(nt == T_U8)
return fixnum((u8int)data[index]);
if(nt == T_S16)
return fixnum(((s16int*)data)[index]);
return fixnum(((u16int*)data)[index]);
}
u8int *dest = cptr(el);
usize sz = eltype->size;
if(sz == 1)
*dest = data[index];
else if(sz == 2)
*(s16int*)dest = ((s16int*)data)[index];
else if(sz == 4)
*(s32int*)dest = ((s32int*)data)[index];
else if(sz == 8)
*(s64int*)dest = ((s64int*)data)[index];
else
memcpy(dest, data + index*sz, sz);
return el;
}
sl_v
cvalue_array_aset(sl_v *args)
{
u8int *data; int index;
sl_type *eltype = cv_class(ptr(args[0]))->eltype;
check_addr_args(args[0], args[1], &data, &index);
u8int *dest = data + index*eltype->size;
cvalue_init(eltype, args[2], dest);
return args[2];
}
sl_purefn
BUILTIN("builtin", builtin)
{
argcount(nargs, 1);
sl_sym *s = tosymbol(args[0]);
if(!iscbuiltin(s->binding))
lerrorf(sl_errarg, "function \"%s\" not found", s->name);
return s->binding;
}
sl_v
cbuiltin(const char *name, builtin_t f)
{
csl_v *cv;
cv = MEM_CALLOC(CVALUE_NWORDS-1, sizeof(*cv));
assert(cv != nil);
cv->type = sl_builtintype;
cv->cbuiltin = f;
sl_v sym = symbol(name, false);
sl_sym *s = ptr(sym);
s->binding = tagptr(cv, TAG_CVALUE);
ptrhash_put(&slg.reverse_dlsym_lookup, cv, (void*)sym);
return s->binding;
}
#define cv_intern(tok) \
do{ \
sl_##tok##sym = csymbol(#tok); \
}while(0)
#define ctor_cv_intern(tok, nt, ctype) \
do{ \
sl_sym *s; \
cv_intern(tok); \
set(sl_##tok##sym, cbuiltin(#tok, fn_builtin_##tok)); \
if(valid_numtype(nt)){ \
s = ptr(sl_##tok##sym); \
s->numtype = nt; \
s->size = sizeof(ctype); \
} \
}while(0)
#define mk_primtype(name, ctype) \
do{ \
sl_##name##type = get_type(sl_##name##sym); \
sl_##name##type->init = cvalue_##ctype##_init; \
}while(0)
#define RETURN_NUM_AS(var, type) return(mk_##type(var))
sl_v
return_from_u64(u64int Uaccum)
{
if(fits_fixnum(Uaccum))
return fixnum((sl_fx)Uaccum);
if(Uaccum > (u64int)INT64_MAX)
RETURN_NUM_AS(Uaccum, u64);
if(Uaccum > (u64int)UINT32_MAX)
RETURN_NUM_AS(Uaccum, s64);
if(Uaccum > (u64int)INT32_MAX)
RETURN_NUM_AS(Uaccum, u32);
RETURN_NUM_AS(Uaccum, s32);
}
sl_v
return_from_s64(s64int Saccum)
{
if(fits_fixnum(Saccum))
return fixnum((sl_fx)Saccum);
RETURN_NUM_AS(vtomp(Saccum, nil), mp);
}
#define ACCUM_DEFAULT 0
#define ARITH_OP(a, b) (a)+(b)
#define MP_OP mpadd
#define ARITH_OVERFLOW sadd_overflow_64
sl_v
sl_add_any(sl_v *args, u32int nargs)
{
#include "sl_arith_any.h"
}
#define ACCUM_DEFAULT 1
#define ARITH_OP(a, b) (a)*(b)
#define MP_OP mpmul
#define ARITH_OVERFLOW smul_overflow_64
sl_v
sl_mul_any(sl_v *args, u32int nargs)
{
#include "sl_arith_any.h"
}
sl_v
sl_neg(sl_v n)
{
s64int i64;
u64int ui64;
mpint *mp;
sl_numtype pt;
sl_fx pi;
void *a;
if(num_to_ptr(n, &pi, &pt, &a)){
switch(pt){
case T_DOUBLE: return mk_double(-*(double*)a);
case T_FLOAT: return mk_float(-*(float*)a);
case T_S8: return fixnum(-(sl_fx)*(s8int*)a);
case T_U8: return fixnum(-(sl_fx)*(u8int*)a);
case T_S16: return fixnum(-(sl_fx)*(s16int*)a);
case T_U16: return fixnum(-(sl_fx)*(u16int*)a);
case T_U32:
i64 = -(s64int)*(u32int*)a;
goto i64neg;
case T_S32:
i64 = -(s64int)*(s32int*)a;
goto i64neg;
case T_S64:
i64 = *(s64int*)a;
if(i64 == INT64_MIN)
return mk_mp(uvtomp((u64int)INT64_MAX+1, nil));
i64 = -i64;
i64neg:
return fits_fixnum(i64) ? fixnum(i64) : mk_mp(vtomp(i64, nil));
case T_U64:
ui64 = *(u64int*)a;
if(ui64 >= (u64int)INT64_MAX+1){
mp = uvtomp(ui64, nil);
mp->sign = -1;
return mk_mp(mp);
}
i64 = -(s64int)ui64;
goto i64neg;
case T_MP:
mp = mpcopy(*(mpint**)a);
mp->sign = -mp->sign;
return mk_mp(mp);
}
}
type_error("number", n);
}
bool
num_to_ptr(sl_v a, sl_fx *pi, sl_numtype *pt, void **pp)
{
sl_cprim *cp;
csl_v *cv;
if(isfixnum(a)){
*pi = numval(a);
*pp = pi;
*pt = T_FIXNUM;
return true;
}else if(iscprim(a)){
cp = ptr(a);
*pp = cp_data(cp);
*pt = cp_numtype(cp);
return true;
}else if(iscvalue(a)){
cv = ptr(a);
*pp = cv_data(cv);
*pt = cv_class(cv)->numtype;
return valid_numtype(*pt);
}
return false;
}
/*
returns -1, 0, or 1 based on ordering of a and b
eq: consider equality only, returning 0 or nonzero
eqnans: NaNs considered equal to each other
-0.0 not considered equal to 0.0
inexact not considered equal to exact
typeerr: if not 0, throws type errors, else returns 2 for type errors
*/
int
numeric_compare(sl_v a, sl_v b, bool eq, bool eqnans, bool typeerr)
{
sl_fx ai, bi;
sl_numtype ta, tb;
void *aptr, *bptr;
if(bothfixnums(a, b)){
if(!eq && numval(a) < numval(b))
return -1;
if(a == b)
return 0;
return 1;
}
if(!num_to_ptr(a, &ai, &ta, &aptr)){
if(typeerr)
type_error("number", a);
return 2;
}
if(!num_to_ptr(b, &bi, &tb, &bptr)){
if(typeerr)
type_error("number", b);
return 2;
}
if(eq && eqnans && ((ta >= T_FLOAT) != (tb >= T_FLOAT)))
return 1;
if(cmp_eq(aptr, ta, bptr, tb, eqnans))
return 0;
if(eq)
return 1;
if(cmp_lt(aptr, ta, bptr, tb))
return -1;
return 1;
}
_Noreturn void
divide_by_0_error(void)
{
lerrorf(sl_errdiv0, "/: division by zero");
}
sl_v
sl_div2(sl_v a, sl_v b)
{
double da, db;
sl_fx ai, bi;
sl_numtype ta, tb;
void *aptr, *bptr;
if(!num_to_ptr(a, &ai, &ta, &aptr))
type_error("number", a);
if(!num_to_ptr(b, &bi, &tb, &bptr))
type_error("number", b);
da = conv_to_double(aptr, ta);
db = conv_to_double(bptr, tb);
if(db == 0 && tb < T_FLOAT) // exact 0
divide_by_0_error();
da = da/db;
if(ta < T_FLOAT && tb < T_FLOAT && (double)(s64int)da == da)
return return_from_s64((s64int)da);
return mk_double(da);
}
sl_v
sl_idiv2(sl_v a, sl_v b)
{
sl_fx ai, bi;
sl_numtype ta, tb;
void *aptr, *bptr;
s64int a64, b64;
mpint *x;
if(!num_to_ptr(a, &ai, &ta, &aptr))
type_error("number", a);
if(!num_to_ptr(b, &bi, &tb, &bptr))
type_error("number", b);
if(ta == T_MP){
if(tb == T_MP){
if(mpsignif(*(mpint**)bptr) == 0)
goto div_error;
x = mpnew(0);
mpdiv(*(mpint**)aptr, *(mpint**)bptr, x, nil);
return mk_mp(x);
}else{
b64 = conv_to_s64(bptr, tb);
if(b64 == 0)
goto div_error;
x = tb == T_U64 ? uvtomp(b64, nil) : vtomp(b64, nil);
mpdiv(*(mpint**)aptr, x, x, nil);
return mk_mp(x);
}
}
if(ta == T_U64){
if(tb == T_U64){
if(*(u64int*)bptr == 0)
goto div_error;
return return_from_u64(*(u64int*)aptr / *(u64int*)bptr);
}
b64 = conv_to_s64(bptr, tb);
if(b64 < 0)
return return_from_s64(-(s64int)(*(u64int*)aptr / (u64int)(-b64)));
if(b64 == 0)
goto div_error;
return return_from_u64(*(u64int*)aptr / (u64int)b64);
}
if(tb == T_U64){
if(*(u64int*)bptr == 0)
goto div_error;
a64 = conv_to_s64(aptr, ta);
if(a64 < 0)
return return_from_s64(-((s64int)((u64int)(-a64) / *(u64int*)bptr)));
return return_from_u64((u64int)a64 / *(u64int*)bptr);
}
b64 = conv_to_s64(bptr, tb);
if(b64 == 0)
goto div_error;
return return_from_s64(conv_to_s64(aptr, ta) / b64);
div_error:
divide_by_0_error();
}
static sl_v
sl_bitwise_op(sl_v a, sl_v b, int opcode)
{
sl_fx ai, bi;
sl_numtype ta, tb, itmp;
void *aptr = nil, *bptr = nil, *ptmp;
mpint *bmp = nil, *resmp = nil;
s64int b64;
if(!num_to_ptr(a, &ai, &ta, &aptr) || ta >= T_FLOAT)
type_error("integer", a);
if(!num_to_ptr(b, &bi, &tb, &bptr) || tb >= T_FLOAT)
type_error("integer", b);
if(ta < tb){
itmp = ta; ta = tb; tb = itmp;
ptmp = aptr; aptr = bptr; bptr = ptmp;
}
// now a's type is larger than or same as b's
if(ta == T_MP){
if(tb == T_MP){
bmp = *(mpint**)bptr;
resmp = mpnew(0);
}else{
bmp = conv_to_mp(bptr, tb);
resmp = bmp;
}
b64 = 0;
}else
b64 = conv_to_s64(bptr, tb);
switch(opcode){
case 0:
switch(ta){
case T_S8: return fixnum( *(s8int *)aptr & (s8int )b64);
case T_U8: return fixnum( *(u8int *)aptr & (u8int )b64);
case T_S16: return fixnum( *(s16int*)aptr & (s16int )b64);
case T_U16: return fixnum( *(u16int*)aptr & (u16int)b64);
case T_S32: return mk_s32( *(s32int*)aptr & (s32int )b64);
case T_U32: return mk_u32(*(u32int*)aptr & (u32int)b64);
case T_S64: return mk_s64( *(s64int*)aptr & (s64int )b64);
case T_U64: return mk_u64(*(u64int*)aptr & (u64int)b64);
case T_MP: mpand(*(mpint**)aptr, bmp, resmp); return mk_mp(resmp);
case T_FLOAT:
case T_DOUBLE: assert(0);
}
break;
case 1:
switch(ta){
case T_S8: return fixnum( *(s8int *)aptr | (s8int )b64);
case T_U8: return fixnum( *(u8int *)aptr | (u8int )b64);
case T_S16: return fixnum( *(s16int*)aptr | (s16int )b64);
case T_U16: return fixnum( *(u16int*)aptr | (u16int)b64);
case T_S32: return mk_s32( *(s32int*)aptr | (s32int )b64);
case T_U32: return mk_u32(*(u32int*)aptr | (u32int)b64);
case T_S64: return mk_s64( *(s64int*)aptr | (s64int )b64);
case T_U64: return mk_u64(*(u64int*)aptr | (u64int)b64);
case T_MP: mpor(*(mpint**)aptr, bmp, resmp); return mk_mp(resmp);
case T_FLOAT:
case T_DOUBLE: assert(0);
}
break;
case 2:
switch(ta){
case T_S8: return fixnum( *(s8int *)aptr ^ (s8int )b64);
case T_U8: return fixnum( *(u8int *)aptr ^ (u8int )b64);
case T_S16: return fixnum( *(s16int*)aptr ^ (s16int )b64);
case T_U16: return fixnum( *(u16int*)aptr ^ (u16int)b64);
case T_S32: return mk_s32( *(s32int*)aptr ^ (s32int )b64);
case T_U32: return mk_u32(*(u32int*)aptr ^ (u32int)b64);
case T_S64: return mk_s64( *(s64int*)aptr ^ (s64int )b64);
case T_U64: return mk_u64(*(u64int*)aptr ^ (u64int)b64);
case T_MP: mpxor(*(mpint**)aptr, bmp, resmp); return mk_mp(resmp);
case T_FLOAT:
case T_DOUBLE: assert(0);
}
}
assert(0);
return sl_nil;
}
BUILTIN("logand", logand)
{
sl_v v, e;
if(nargs == 0)
return fixnum(-1);
v = args[0];
int i;
FOR_ARGS(i, 1, e, args){
if(bothfixnums(v, e))
v = v & e;
else
v = sl_bitwise_op(v, e, 0);
}
return v;
}
BUILTIN("logior", logior)
{
sl_v v, e;
if(nargs == 0)
return fixnum(0);
v = args[0];
int i;
FOR_ARGS(i, 1, e, args){
if(bothfixnums(v, e))
v = v | e;
else
v = sl_bitwise_op(v, e, 1);
}
return v;
}
BUILTIN("logxor", logxor)
{
sl_v v, e;
if(nargs == 0)
return fixnum(0);
v = args[0];
int i;
FOR_ARGS(i, 1, e, args){
if(bothfixnums(v, e))
v = fixnum(numval(v) ^ numval(e));
else
v = sl_bitwise_op(v, e, 2);
}
return v;
}
BUILTIN("lognot", lognot)
{
argcount(nargs, 1);
sl_v a = args[0];
sl_cprim *cp;
int ta;
void *aptr;
if(isfixnum(a))
return fixnum(~numval(a));
if(iscprim(a)){
cp = ptr(a);
ta = cp_numtype(cp);
aptr = cp_data(cp);
switch(ta){
case T_S8: return fixnum(~*(s8int *)aptr);
case T_U8: return fixnum(~*(u8int *)aptr & 0xff);
case T_S16: return fixnum(~*(s16int *)aptr);
case T_U16: return fixnum(~*(u16int*)aptr & 0xffff);
case T_S32: return mk_s32(~*(s32int *)aptr);
case T_U32: return mk_u32(~*(u32int*)aptr);
case T_S64: return mk_s64(~*(s64int *)aptr);
case T_U64: return mk_u64(~*(u64int*)aptr);
}
}
if(iscvalue(a)){
csl_v *cv = ptr(a);
ta = cp_numtype(cv);
aptr = cv_data(cv);
if(ta == T_MP){
mpint *m = mpnew(0);
mpnot(*(mpint**)aptr, m);
return mk_mp(m);
}
}
type_error("integer", a);
}
BUILTIN("ash", ash)
{
sl_fx n;
s64int accum;
sl_cprim *cp;
int ta;
mpint *mp;
void *aptr;
argcount(nargs, 2);
sl_v a = args[0];
n = tofixnum(args[1]);
if(isfixnum(a)){
if(n <= 0)
return fixnum(numval(a)>>(-n));
accum = ((s64int)numval(a))<<n;
return fits_fixnum(accum) ? fixnum(accum) : return_from_s64(accum);
}
if(iscprim(a) || iscvalue(a)){
if(n == 0)
return a;
cp = ptr(a);
ta = cp_numtype(cp);
aptr = cp_data(cp);
if(n < 0){
n = -n;
switch(ta){
case T_S8: return fixnum((*(s8int *)aptr) >> n);
case T_U8: return fixnum((*(u8int *)aptr) >> n);
case T_S16: return fixnum((*(s16int *)aptr) >> n);
case T_U16: return fixnum((*(u16int*)aptr) >> n);
case T_S32: return mk_s32((*(s32int *)aptr) >> n);
case T_U32: return mk_u32((*(u32int*)aptr) >> n);
case T_S64: return mk_s64((*(s64int *)aptr) >> n);
case T_U64: return mk_u64((*(u64int*)aptr) >> n);
case T_MP:
aptr = cv_data(cp);
mp = mpnew(0);
mpright(*(mpint**)aptr, n, mp);
return mk_mp(mp);
}
}
if(ta == T_MP){
aptr = cv_data(cp);
mp = mpnew(0);
mpleft(*(mpint**)aptr, n, mp);
return mk_mp(mp);
}
if(ta == T_U64)
return return_from_u64((*(u64int*)aptr)<<n);
if(ta < T_FLOAT)
return return_from_s64(conv_to_s64(aptr, ta)<<n);
}
type_error("integer", a);
}
void
cvalues_init(void)
{
htable_new(&slg.types, 256);
htable_new(&slg.reverse_dlsym_lookup, 256);
sl_builtintype = define_opaque_type(sl_builtinsym, sizeof(builtin_t), nil, nil);
ctor_cv_intern(s8, T_S8, s8int);
ctor_cv_intern(u8, T_U8, u8int);
ctor_cv_intern(s16, T_S16, s16int);
ctor_cv_intern(u16, T_U16, u16int);
ctor_cv_intern(s32, T_S32, s32int);
ctor_cv_intern(u32, T_U32, u32int);
ctor_cv_intern(s64, T_S64, s64int);
ctor_cv_intern(u64, T_U64, u64int);
ctor_cv_intern(byte, T_U8, u8int);
ctor_cv_intern(rune, T_U32, u32int);
ctor_cv_intern(float, T_FLOAT, float);
ctor_cv_intern(double, T_DOUBLE, double);
ctor_cv_intern(array, NONNUMERIC, int);
sl_stringtypesym = csymbol("*string-type*");
setc(sl_stringtypesym, mk_list2(sl_arraysym, sl_bytesym));
sl_runestringtypesym = csymbol("*runestring-type*");
setc(sl_runestringtypesym, mk_list2(sl_arraysym, sl_runesym));
mk_primtype(s8, s8int);
mk_primtype(u8, u8int);
mk_primtype(s16, s16int);
mk_primtype(u16, u16int);
mk_primtype(s32, s32int);
mk_primtype(u32, u32int);
mk_primtype(s64, s64int);
mk_primtype(u64, u64int);
mk_primtype(byte, u8int);
mk_primtype(rune, u32int);
mk_primtype(float, float);
mk_primtype(double, double);
ctor_cv_intern(bignum, T_MP, mpint*);
sl_mptype = get_type(sl_bignumsym);
sl_mptype->init = cvalue_mp_init;
sl_mptype->vtable = &mp_vtable;
sl_stringtype = get_type(symbol_value(sl_stringtypesym));
sl_emptystr = cvalue_from_ref(sl_stringtype, (char*)"", 0);
sl_runestringtype = get_type(symbol_value(sl_runestringtypesym));
}