shithub: sl

ref: a70379d7e4b822f532fb0a8ccdd1624a90b64a68
dir: /src/builtins.c/

View raw version
#include "sl.h"
#include "operators.h"
#include "cvalues.h"
#include "timefuncs.h"
#include "table.h"
#include "nan.h"

#define DBL_MAXINT (1LL<<DBL_MANT_DIG)
#define FLT_MAXINT (1<<FLT_MANT_DIG)

usize
llength(sl_v v)
{
	usize n = 0;
	while(iscons(v)){
		n++;
		v = cdr_(v);
	}
	return n;
}

BUILTIN("nconc", nconc)
{
	if(nargs == 0)
		return sl_nil;

	sl_v lst, first = sl_nil;
	sl_v *pcdr = &first;
	sl_cons *c;
	int i = 0;

	while(1){
		lst = args[i++];
		if(i >= nargs)
			break;
		if(iscons(lst)){
			*pcdr = lst;
			c = ptr(lst);
			while(iscons(c->cdr))
				c = ptr(c->cdr);
			pcdr = &c->cdr;
		}else if(lst != sl_nil)
			type_error("cons", lst);
	}
	*pcdr = lst;
	return first;
}

sl_purefn
BUILTIN("assq", assq)
{
	argcount(nargs, 2);

	sl_v item = args[0];
	sl_v v = args[1];
	sl_v bind;

	while(iscons(v)){
		bind = car_(v);
		if(iscons(bind) && car_(bind) == item)
			return bind;
		v = cdr_(v);
	}
	return sl_nil;
}

sl_purefn
BUILTIN("memq", memq)
{
	argcount(nargs, 2);

	sl_v v;
	sl_cons *c;
	for(v = args[1]; iscons(v); v = c->cdr){
		if((c = ptr(v))->car == args[0])
			return v;
	}
	return sl_nil;
}

BUILTIN("length", length)
{
	argcount(nargs, 1);

	sl_v a = args[0];
	sl_cv *cv;

	if(iscons(a)){
		usize n = 0;
		sl_v v = a, v2 = a;
		do{
			n++;
			v = cdr_(v);
			v2 = cdr_(v2);
			if(iscons(v2))
				v2 = cdr_(v2);
		}while(iscons(v) && iscons(v2) && v != v2);
		if(iscons(v2))
			return mk_double(D_PINF);
		n += llength(v);
		return size_wrap(n);
	}
	if(iscprim(a)){
		cv = ptr(a);
		if(cp_class(cv) == sl_bytetype)
			return fixnum(1);
		if(cp_class(cv) == sl_runetype)
			return fixnum(runelen(*(Rune*)cp_data(cv)));
	}
	if(iscvalue(a) && cv_class(ptr(a))->eltype != nil)
		return size_wrap(cvalue_arrlen(a));
	if(isvec(a))
		return size_wrap(vec_size(a));
	if(ishashtable(a)){
		sl_htable *h = totable(a);
		void **t = h->table;
		usize sz = h->size;
		usize n = 0;
		for(usize i = 0; i < sz; i += 2){
			if(t[i+1] != HT_NOTFOUND)
				n++;
		}
		return size_wrap(n);
	}
	if(a == sl_nil)
		return fixnum(0);
	type_error("sequence", a);
}

_Noreturn
BUILTIN("raise", raise)
{
	argcount(nargs, 1);
	sl_raise(args[0]);
}

_Noreturn
BUILTIN("exit", exit)
{
	if(nargs > 1)
		argcount(nargs, 1);
	sl_exit((nargs > 0 && args[0] != sl_nil) ? tostr(args[0]) : nil);
}

BUILTIN("sym", sym)
{
	if(nargs < 1)
		argcount(nargs, 1);
	sl_v name;
	if(nargs == 1 && sl_isstr(args[0]))
		name = args[0];
	else
		name = fn_builtin_str(args, nargs);
	return mk_sym(cvalue_data(name), true);
}

sl_purefn
BUILTIN("keyword?", keywordp)
{
	argcount(nargs, 1);
	return (issym(args[0]) && iskeyword((sl_sym*)ptr(args[0]))) ? sl_t : sl_nil;
}

sl_purefn
BUILTIN("top-level-value", top_level_value)
{
	argcount(nargs, 1);
	sl_sym *sym = tosym(args[0]);
	if(sym->binding == UNBOUND)
		unbound_error(args[0]);
	return sym->binding;
}

BUILTIN("set-top-level-value!", set_top_level_value)
{
	argcount(nargs, 2);
	sl_sym *sym = tosym(args[0]);
	if(sl_unlikely(isconst(sym)))
		const_error(sym);
	sym->binding = args[1];
	return args[1];
}

BUILTIN("makunbound", makunbound)
{
	argcount(nargs, 1);
	sl_sym *sym = tosym(args[0]);
	if(sl_unlikely(isconst(sym)))
		const_error(sym);
	sym->binding = UNBOUND;
	return sl_void;
}

BUILTIN("environment", environment)
{
	USED(args);
	argcount(nargs, 0);
	sl_v lst = sl_nil;
	sl_gc_handle(&lst);
	const char *k = nil;
	sl_sym *v;
	while(Tnext(slg.symbols, &k, (void**)&v)){
		if(v->binding != UNBOUND && (v->flags & FLAG_KEYWORD) == 0)
			lst = mk_cons(tagptr(v, TAG_SYM), lst);
	}
	sl_free_gc_handles(1);
	return lst;
}

sl_purefn
BUILTIN("const?", constp)
{
	argcount(nargs, 1);
	if(issym(args[0]))
		return isconst((sl_sym*)ptr(args[0])) ? sl_t : sl_nil;
	if(iscons(args[0])){
		if(car_(args[0]) == sl_quote)
			return sl_t;
		return sl_nil;
	}
	return sl_t;
}

sl_purefn
BUILTIN("int-valued?", int_valuedp)
{
	argcount(nargs, 1);
	sl_v v = args[0];
	if(isfixnum(v) || ismp(v))
		return sl_t;
	if(iscprim(v)){
		sl_numtype nt = cp_numtype(ptr(v));
		if(nt < T_FLOAT)
			return sl_t;
		void *data = cp_data(ptr(v));
		if(nt == T_FLOAT){
			float f = *(float*)data;
			if(f < 0)
				f = -f;
			if(f <= FLT_MAXINT && (float)(s32int)f == f)
				return sl_t;
		}else{
			assert(nt == T_DOUBLE);
			double d = *(double*)data;
			if(d < 0)
				d = -d;
			if(d <= DBL_MAXINT && (double)(s64int)d == d)
				return sl_t;
		}
	}
	return sl_nil;
}

sl_purefn
BUILTIN("int?", intp)
{
	argcount(nargs, 1);
	sl_v v = args[0];
	return (isfixnum(v) || ismp(v) ||
			(iscprim(v) && cp_numtype(ptr(v)) < T_FLOAT)) ?
		sl_t : sl_nil;
}

sl_purefn
BUILTIN("bignum?", bignump)
{
	argcount(nargs, 1);
	return ismp(args[0]) ? sl_t : sl_nil;
}

BUILTIN("fixnum", fixnum)
{
	argcount(nargs, 1);
	sl_v v = args[0];
	if(isfixnum(v))
		return v;
	if(iscprim(v)){
		void *p = ptr(v);
		return fixnum(conv_to_s64(cp_data(p), cp_numtype(p)));
	}
	if(ismp(v))
#ifdef BITS64
		return fixnum(mptov(tomp(v)));
#else
		return fixnum(mptoi(tomp(v)));
#endif
	type_error("num", v);
}

BUILTIN("truncate", truncate)
{
	argcount(nargs, 1);
	sl_v v = args[0];
	if(isfixnum(v) || ismp(v))
		return v;
	if(iscprim(v)){
		sl_cprim *cp = ptr(v);
		void *data = cp_data(cp);
		sl_numtype nt = cp_numtype(cp);
		double d;
		if(nt == T_FLOAT)
			d = (double)*(float*)data;
		else if(nt == T_DOUBLE)
			d = *(double*)data;
		else
			return v;

		if(d > 0){
			if(d > (double)INT64_MAX)
				return v;
			return return_from_u64((u64int)d);
		}
		if(d > (double)INT64_MAX || d < (double)INT64_MIN)
			return args[0];
		return return_from_s64((s64int)d);
	}
	type_error("num", v);
}

BUILTIN("vec-alloc", vec_alloc)
{
	if(nargs < 1)
		argcount(nargs, 1);
	usize i = tosize(args[0]);
	sl_v v = alloc_vec(i, 0);
	int a = 1;
	for(usize k = 0; k < i; k++){
		sl_v f = a < nargs ? args[a] : sl_void;
		vec_elt(v, k) = f;
		if((a = (a + 1) % nargs) < 1)
			a = 1;
	}
	return v;
}

BUILTIN("time-now", time_now)
{
	argcount(nargs, 0);
	USED(args);
	return mk_double(sec_realtime());
}

BUILTIN("nanoseconds-monotonic", nanoseconds_monotonic)
{
	argcount(nargs, 0);
	USED(args);
	return mk_u64(nanosec_monotonic());
}

double
todouble(sl_v a)
{
	if(isfixnum(a))
		return (double)numval(a);
	if(iscprim(a)){
		sl_cprim *cp = ptr(a);
		sl_numtype nt = cp_numtype(cp);
		return conv_to_double(cp_data(cp), nt);
	}
	if(ismp(a))
		return conv_to_double(cv_data(ptr(a)), T_MP);
	type_error("num", a);
}

BUILTIN("time->str", time_str)
{
	argcount(nargs, 1);
	double t = todouble(args[0]);
	char buf[64];
	timestr(t, buf, sizeof(buf));
	return str_from_cstr(buf);
}

BUILTIN("str->time", str_time)
{
	argcount(nargs, 1);
	char *ptr = tostr(args[0]);
	double t = parsetime(ptr);
	s64int it = (s64int)t;
	if((double)it == t && fits_fixnum(it))
		return fixnum(it);
	return mk_double(t);
}

BUILTIN("path-cwd", path_cwd)
{
	if(nargs > 1)
		argcount(nargs, 1);
	if(nargs == 0){
		char buf[4096];
		if(getcwd(buf, sizeof(buf)) == nil)
			lerrorf(sl_errio, "could not get current dir");
		return str_from_cstr(buf);
	}
	char *ptr = tostr(args[0]);
	if(chdir(ptr) != 0)
		lerrorf(sl_errio, "could not cd to %s", ptr);
	return sl_void;
}

BUILTIN("path-exists?", path_existsp)
{
	argcount(nargs, 1);
	const char *path = tostr(args[0]);
	return access(path, F_OK) == 0 ? sl_t : sl_nil;
}

BUILTIN("delete-file", delete_file)
{
	argcount(nargs, 1);
	const char *path = tostr(args[0]);
	if(remove(path) != 0)
		lerrorf(sl_errio, "could not remove %s", path);
	return sl_void;
}

BUILTIN("os-getenv", os_getenv)
{
	argcount(nargs, 1);
	char *name = tostr(args[0]);
	char *val = getenv(name);
	if(val == nil)
		return sl_nil;
	return cvalue_static_cstr(val);
}

BUILTIN("os-setenv", os_setenv)
{
	argcount(nargs, 2);
	char *name = tostr(args[0]);
	int result;
	if(args[1] == sl_nil)
		result = unsetenv(name);
	else{
		char *val = tostr(args[1]);
		result = setenv(name, val, 1);
	}
	if(result != 0)
		lerrorf(sl_errarg, "invalid environment variable");
	return sl_t;
}