shithub: femtolisp

ref: cf3f244661f70421b8c266b3670a8481835d604e
dir: /flisp.h/

View raw version
#ifndef FLISP_H
#define FLISP_H

enum {
	TAG_NUM,
	TAG_CPRIM,
	TAG_FUNCTION,
	TAG_VECTOR,
	TAG_NUM1,
	TAG_CVALUE,
	TAG_SYM,
	TAG_CONS,
};

enum {
	FLAG_CONST = 1<<0,
	FLAG_KEYWORD = 1<<1,
};

typedef enum {
	T_INT8, T_UINT8,
	T_INT16, T_UINT16,
	T_INT32, T_UINT32,
	T_INT64, T_UINT64,
	T_MPINT,
	T_FLOAT,
	T_DOUBLE,
}numerictype_t;

typedef uintptr_t value_t;
typedef lltint_t fixnum_t;

typedef struct {
	value_t car;
	value_t cdr;
}cons_t;

typedef struct _symbol_t {
	value_t binding;   // global value binding
	uint32_t hash;
	uint8_t numtype;
	uint8_t size;
	uint8_t align;
	uint8_t flags;
	struct _fltype_t *type;
	void *dlcache;	 // dlsym address
	// below fields are private
	struct _symbol_t *left;
	struct _symbol_t *right;
	union {
		char name[1];
		void *_pad;	// ensure field aligned to pointer size
	};
}symbol_t;

typedef struct {
	value_t isconst;
	value_t binding;   // global value binding
	struct _fltype_t *type;
	uint32_t id;
}gensym_t;

typedef struct Builtin Builtin;

struct Builtin {
        char *name;
        int  nargs;
};

typedef value_t (*builtin_t)(value_t*, int);

#ifdef BITS64
#define T_FIXNUM T_INT64
#define fits_fixnum(x) (((x)>>61) == 0 || (~((x)>>61)) == 0)
#define mk_xlong mk_int64
#else
#define T_FIXNUM T_INT32
#define fits_fixnum(x) (((x)>>29) == 0 || (~((x)>>29)) == 0)
#define mk_xlong mk_int32
#endif

#define ANYARGS -10000
#define NONNUMERIC (0xff)
#define valid_numtype(v) ((v) <= T_DOUBLE)
#define UNBOUND ((value_t)0x1) // an invalid value
#define TAG_FWD UNBOUND
#define tag(x) ((x) & 0x7)
#define ptr(x) ((void*)((x) & (~(value_t)0x7)))
#define tagptr(p, t) (((value_t)(p)) | (t))
#define fixnum(x) ((value_t)((fixnum_t)(x))<<2)
#define numval(x)  (((fixnum_t)(x))>>2)
#define fits_bits(x, b) (((x)>>(b-1)) == 0 || (~((x)>>(b-1))) == 0) /* mag: UNUSED? */
#define uintval(x) (((unsigned int)(x))>>3)
#define builtin(n) tagptr((((int)n)<<3), TAG_FUNCTION)
#define iscons(x) (tag(x) == TAG_CONS)
#define issymbol(x) (tag(x) == TAG_SYM)
#define isfixnum(x) (((x)&3) == TAG_NUM)
#define bothfixnums(x, y) ((((x)|(y)) & 3) == TAG_NUM)
#define isvector(x) (tag(x) == TAG_VECTOR)
#define iscvalue(x) (tag(x) == TAG_CVALUE)
#define iscprim(x)  (tag(x) == TAG_CPRIM)
#define selfevaluating(x) (tag(x) < 6) /* mag: UNUSED? */
// doesn't lead to other values
#define leafp(a) (((a)&3) != 3)

// allocate n consecutive conses
#define cons_reserve(n) tagptr(alloc_words((n)*2), TAG_CONS)
#define cons_index(c)  (((cons_t*)ptr(c))-((cons_t*)fromspace))
#define ismarked(c)     bitvector_get(consflags, cons_index(c))
#define mark_cons(c)   bitvector_set(consflags, cons_index(c), 1)
#define unmark_cons(c) bitvector_set(consflags, cons_index(c), 0)

#define isforwarded(v) (((value_t*)ptr(v))[0] == TAG_FWD)
#define forwardloc(v) (((value_t*)ptr(v))[1])
#define forward(v, to) \
	do{ \
		(((value_t*)ptr(v))[0] = TAG_FWD); \
		(((value_t*)ptr(v))[1] = to); \
	}while (0)

#define vector_size(v) (((size_t*)ptr(v))[0]>>2)
#define vector_setsize(v, n) (((size_t*)ptr(v))[0] = ((n)<<2))
#define vector_elt(v, i) (((value_t*)ptr(v))[1+(i)])
#define vector_grow_amt(x) ((x)<8 ? 5 : 6*((x)>>3))
// functions ending in _ are unsafe, faster versions
#define car_(v) (((cons_t*)ptr(v))->car)
#define cdr_(v) (((cons_t*)ptr(v))->cdr)
#define car(v) (tocons((v))->car)
#define cdr(v) (tocons((v))->cdr)
#define fn_bcode(f) (((value_t*)ptr(f))[0])
#define fn_vals(f) (((value_t*)ptr(f))[1])
#define fn_env(f) (((value_t*)ptr(f))[2])
#define fn_name(f) (((value_t*)ptr(f))[3])
#define set(s, v)  (((symbol_t*)ptr(s))->binding = (v))
#define setc(s, v) \
	do{ \
		((symbol_t*)ptr(s))->flags |= FLAG_CONST; \
		((symbol_t*)ptr(s))->binding = (v); \
	}while (0)
#define isconstant(s) ((s)->flags & FLAG_CONST)
#define iskeyword(s) ((s)->flags & FLAG_KEYWORD)
#define symbol_value(s) (((symbol_t*)ptr(s))->binding)
#define sym_to_numtype(s) (((symbol_t*)ptr(s))->numtype)
#define ismanaged(v) ((((uint8_t*)ptr(v)) >= fromspace) && (((uint8_t*)ptr(v)) < fromspace+heapsize))
#define isgensym(x)  (issymbol(x) && ismanaged(x))
#define isfunction(x) (tag(x) == TAG_FUNCTION && (x) > (N_BUILTINS<<3))
#define isclosure(x) isfunction(x)
#define iscbuiltin(x) (iscvalue(x) && cv_class(ptr(x)) == builtintype)
// utility for iterating over all arguments in a builtin
// i=index, i0=start index, arg = var for each arg, args = arg array
// assumes "nargs" is the argument count
#define FOR_ARGS(i, i0, arg, args) for(i=i0; i<nargs && ((arg=args[i]) || 1); i++)
#define N_BUILTINS ((int)N_OPCODES)
#define FL_UNSPECIFIED FL_T

#define PUSH(v) \
        do{ \
                Stack[SP++] = (v); \
        }while(0)
#define POP()   (Stack[--SP])
#define POPN(n) \
        do{ \
                SP -= (n); \
        }while(0)

extern value_t *Stack;
extern uint32_t SP;
extern uint32_t N_STACK;
extern uint32_t curr_frame;
extern char *curr_fname;

extern value_t FL_NIL, FL_T, FL_F, FL_EOF, QUOTE;
extern value_t NIL, LAMBDA, IF, TRYCATCH;
extern value_t BACKQUOTE, COMMA, COMMAAT, COMMADOT, FUNCTION;

extern value_t printprettysym, printreadablysym, printwidthsym, printlengthsym;
extern value_t printlevelsym, builtins_table_sym;
extern value_t pairsym, symbolsym, fixnumsym, vectorsym, builtinsym, vu8sym;
extern value_t definesym, defmacrosym, forsym, setqsym;
extern value_t tsym, Tsym, fsym, Fsym, booleansym, nullsym, evalsym, fnsym;
extern value_t nulsym, alarmsym, backspacesym, tabsym, linefeedsym, newlinesym;
extern value_t vtabsym, pagesym, returnsym, escsym, spacesym, deletesym;

extern value_t IOError, ParseError, TypeError, ArgError, MemoryError;
extern value_t DivideError, BoundsError, Error, KeyError, EnumerationError;
extern value_t ArgError, IOError, KeyError, MemoryError, EnumerationError;

extern uint8_t *fromspace;
extern uint32_t heapsize;//bytes
extern uint8_t *tospace;
extern uint8_t *curheap;
extern uint8_t *lim;
extern uint32_t *consflags;

int isbuiltin(value_t x);
void fl_init(size_t initial_heapsize);
int fl_load_system_image(value_t ios);

/* collector */
value_t relocate(value_t v);
void gc(int mustgrow);
void fl_gc_handle(value_t *pv);
void fl_free_gc_handles(uint32_t n);

/* symbol table */
value_t gensym(void);
value_t symbol(char *str);
char *symbol_name(value_t v);

/* read, eval, print main entry points */
value_t fl_toplevel_eval(value_t expr);
value_t fl_apply(value_t f, value_t l);
value_t fl_applyn(uint32_t n, value_t f, ...);

/* object model manipulation */
value_t fl_cons(value_t a, value_t b);
value_t fl_list2(value_t a, value_t b);
value_t fl_listn(size_t n, ...);
int fl_isnumber(value_t v);
int fl_is_keyword_name(char *str, size_t len);
value_t alloc_vector(size_t n, int init);

/* safe casts */
cons_t *tocons(value_t v);
symbol_t *tosymbol(value_t v);
fixnum_t tofixnum(value_t v);
char *tostring(value_t v);

/* conses */
extern value_t the_empty_vector;
value_t mk_cons(void);
void *alloc_words(int n);

/* error handling */
typedef struct _fl_readstate_t {
	htable_t backrefs;
	htable_t gensyms;
	value_t source;
	struct _fl_readstate_t *prev;
}fl_readstate_t;

typedef struct _ectx_t {
	jmp_buf buf;
	uint32_t sp;
	uint32_t frame;
	uint32_t ngchnd;
	fl_readstate_t *rdst;
	struct _ectx_t *prev;
}fl_exception_context_t;

extern fl_readstate_t *readstate;
extern fl_exception_context_t *fl_ctx;
extern uint32_t fl_throwing_frame;
extern value_t fl_lasterror;

void free_readstate(fl_readstate_t *rs);

#define FL_TRY_EXTERN \
	fl_exception_context_t _ctx; int l__tr, l__ca; \
	fl_savestate(&_ctx); fl_ctx = &_ctx; \
	if(!setjmp(_ctx.buf)) \
		for(l__tr=1; l__tr; l__tr=0, (void)(fl_ctx = fl_ctx->prev))

#define FL_CATCH_EXTERN_NO_RESTORE \
	else \
		for(l__ca=1; l__ca;)

#define FL_CATCH_EXTERN \
	else \
		for(l__ca=1; l__ca; l__ca=0, fl_restorestate(&_ctx))

_Noreturn void lerrorf(value_t e, char *format, ...);
void fl_savestate(fl_exception_context_t *_ctx);
void fl_restorestate(fl_exception_context_t *_ctx);
_Noreturn void fl_raise(value_t e);
_Noreturn void type_error(char *expected, value_t got);
_Noreturn void bounds_error(value_t arr, value_t ind);
_Noreturn void unbound_error(value_t sym);

#define argcount(nargs, c) \
	do{ \
		if(__unlikely(nargs != c)) \
			lerrorf(ArgError, "arity mismatch: wanted %d, got %d", c, nargs); \
	}while(0)

typedef struct {
	void (*print)(value_t self, ios_t *f);
	void (*relocate)(value_t oldv, value_t newv);
	void (*finalize)(value_t self);
	void (*print_traverse)(value_t self);
} cvtable_t;

value_t relocate_lispvalue(value_t v);

typedef int (*cvinitfunc_t)(struct _fltype_t*, value_t, void*);

typedef struct _fltype_t {
	value_t type;
	cvtable_t *vtable;
	struct _fltype_t *eltype;  // for arrays
	struct _fltype_t *artype;  // (array this)
	cvinitfunc_t init;
	size_t size;
	size_t elsz;
	int marked;
	numerictype_t numtype;
}fltype_t;

typedef struct {
	fltype_t *type;
	void *data;
	size_t len;			// length of *data in bytes
	union {
		value_t parent;	// optional
		char _space[1];	// variable size
	};
}cvalue_t;

typedef struct {
	fltype_t *type;
	char _space[1];
}cprim_t;

typedef struct {
	value_t bcode;
	value_t vals;
	value_t env;
	value_t name;
}function_t;

#define CPRIM_NWORDS 2
#define cv_class(cv) ((fltype_t*)(((uintptr_t)((cvalue_t*)cv)->type)&~3))
#define cv_len(cv) (((cvalue_t*)(cv))->len)
#define cv_type(cv) (cv_class(cv)->type)
#define cv_data(cv) (((cvalue_t*)(cv))->data)
#define cv_isstr(cv) (cv_class(cv)->eltype == bytetype)
#define cv_isPOD(cv) (cv_class(cv)->init != nil)
#define cvalue_data(v) cv_data((cvalue_t*)ptr(v))
#define cvalue_len(v) cv_len((cvalue_t*)ptr(v))
#define value2c(type, v) ((type)cv_data((cvalue_t*)ptr(v)))
#define cp_class(cp) (((cprim_t*)(cp))->type)
#define cp_type(cp)	(cp_class(cp)->type)
#define cp_numtype(cp) (cp_class(cp)->numtype)
#define cp_data(cp)	(&((cprim_t*)(cp))->_space[0])
// WARNING: multiple evaluation!
#define cptr(v) (iscprim(v) ? cp_data(ptr(v)) : cv_data(ptr(v)))

#define BUILTIN(lname, cname) \
	value_t fn_builtin_##cname(value_t *args, int nargs)

#define BUILTIN_FN(l, c) extern BUILTIN(l, c);
#include "builtin_fns.h"
#undef BUILTIN_FN

#endif