tisp

tiny lisp
git clone git://edryd.org/tisp
Log | Files | Refs | README | LICENSE

tisp.c (27265B)


      1 /* zlib License
      2  *
      3  * Copyright (c) 2017-2022 Ed van Bruggen
      4  *
      5  * This software is provided 'as-is', without any express or implied
      6  * warranty.  In no event will the authors be held liable for any damages
      7  * arising from the use of this software.
      8  *
      9  * Permission is granted to anyone to use this software for any purpose,
     10  * including commercial applications, and to alter it and redistribute it
     11  * freely, subject to the following restrictions:
     12  *
     13  * 1. The origin of this software must not be misrepresented; you must not
     14  *    claim that you wrote the original software. If you use this software
     15  *    in a product, an acknowledgment in the product documentation would be
     16  *    appreciated but is not required.
     17  * 2. Altered source versions must be plainly marked as such, and must not be
     18  *    misrepresented as being the original software.
     19  * 3. This notice may not be removed or altered from any source distribution.
     20  */
     21 #include <ctype.h>
     22 #include <dlfcn.h>
     23 #include <fcntl.h>
     24 #include <limits.h>
     25 #include <stdarg.h>
     26 #include <stdint.h>
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <unistd.h>
     31 
     32 #include "tisp.h"
     33 
     34 #define BETWEEN(X, A, B)  ((A) <= (X) && (X) <= (B))
     35 #define LEN(X)            (sizeof(X) / sizeof((X)[0]))
     36 
     37 /* functions */
     38 static void hash_add(Hash ht, char *key, Val val);
     39 
     40 /* utility functions */
     41 
     42 /* return named string for each type */
     43 /* TODO loop through each type bit to print */
     44 char *
     45 type_str(TspType t)
     46 {
     47 	switch (t) {
     48 	case TSP_NONE:  return "Void";
     49 	case TSP_NIL:   return "Nil";
     50 	case TSP_INT:   return "Int";
     51 	case TSP_DEC:   return "Dec";
     52 	case TSP_RATIO: return "Ratio";
     53 	case TSP_STR:   return "Str";
     54 	case TSP_SYM:   return "Sym";
     55 	case TSP_PRIM:  return "Prim";
     56 	case TSP_FORM:  return "Form";
     57 	case TSP_FUNC:  return "Func";
     58 	case TSP_MACRO: return "Macro";
     59 	case TSP_PAIR:  return "Pair";
     60 	default:
     61 		if (t == TSP_EXPR)
     62 			return "Expr";
     63 		if (t == TSP_RATIONAL)
     64 			return "Rational";
     65 		if (t & TSP_NUM)
     66 			return "Num";
     67 		return "Invalid";
     68 	}
     69 }
     70 
     71 /* check if character can be a part of a symbol */
     72 static int
     73 issym(char c)
     74 {
     75 	return BETWEEN(c, 'a', 'z') || BETWEEN(c, 'A', 'Z') ||
     76 	       BETWEEN(c, '0', '9') || strchr("_+-*/=<>!?$&^#@:~", c);
     77 }
     78 
     79 /* check if character is start of a number */
     80 /* TODO only if -/+ is not followed by a delim */
     81 static int
     82 isnum(char *str)
     83 {
     84 	return isdigit(*str) || (*str == '.' && isdigit(str[1])) ||
     85 	       ((*str == '-' || *str == '+') && (isdigit(str[1]) || str[1] == '.'));
     86 }
     87 
     88 /* check if character is a symbol delimiter */
     89 static char
     90 isdelim(int c)
     91 {
     92 	/* add .{}[]`', */
     93 	return isspace(c) || c == '(' || c == ')' || c == '"' || c == ';';
     94 }
     95 
     96 /* skip over comments and white space */
     97 /* TODO support mac/windows line endings */
     98 static void
     99 skip_ws(Tsp st, int skipnl)
    100 {
    101 	const char *s = skipnl ? " \t\n" : " \t";
    102 	while (tsp_fget(st) && (strchr(s, tsp_fget(st)) || tsp_fget(st) == ';')) {
    103 		st->filec += strspn(st->file+st->filec, s); /* skip white space */
    104 		for (; tsp_fget(st) == ';'; tsp_finc(st)) /* skip comments until newline */
    105 			st->filec += strcspn(st->file+st->filec, "\n") - !skipnl;
    106 	}
    107 }
    108 
    109 /* count number of parenthesis */
    110 static int
    111 count_parens(char *s, int len)
    112 {
    113 	int count = 0;
    114 	for (int i = 0; i < len && s[i]; i++)
    115 		if (s[i] == '(')
    116 			count++;
    117 		else if (s[i] == ')')
    118 			count--;
    119 	return count;
    120 }
    121 
    122 /* get length of list, if improper list return -1 */
    123 int
    124 list_len(Val v)
    125 {
    126 	int len = 0;
    127 	for (; v->t == TSP_PAIR; v = cdr(v))
    128 		len++;
    129 	return nilp(v) ? len : -1;
    130 }
    131 
    132 /* check if two values are equal */
    133 static int
    134 vals_eq(Val a, Val b)
    135 {
    136 	if (a->t & TSP_NUM && b->t & TSP_NUM) { /* NUMBERs */
    137 		if (num(a) != num(b) || den(a) != den(b))
    138 			return 0;
    139 		return 1;
    140 	}
    141 	if (a->t != b->t)
    142 		return 0;
    143 	if (a->t == TSP_PAIR) /* PAIR */
    144 		return vals_eq(car(a), car(b)) && vals_eq(cdr(a), cdr(b));
    145 	/* TODO function var names should not matter in comparison */
    146 	if (a->t & (TSP_FUNC | TSP_MACRO)) /* FUNCTION, MACRO */
    147 		return vals_eq(a->v.f.args, b->v.f.args) &&
    148 		       vals_eq(a->v.f.body, b->v.f.body);
    149 	if (a != b) /* PRIMITIVE, STRING, SYMBOL, NIL, VOID */
    150 		return 0;
    151 	return 1;
    152 }
    153 
    154 /* reduce fraction by modifying supplied numerator and denominator */
    155 static void
    156 frac_reduce(int *num, int *den)
    157 {
    158 	int a = abs(*num), b = abs(*den), c = a % b;
    159 	while (c > 0) {
    160 		a = b;
    161 		b = c;
    162 		c = a % b;
    163 	}
    164 	*num = *num / b;
    165 	*den = *den / b;
    166 }
    167 
    168 /* hash map */
    169 
    170 /* return hashed number based on key */
    171 static uint32_t
    172 hash(char *key)
    173 {
    174 	uint32_t h = 0;
    175 	char c;
    176 	while (h < UINT32_MAX && (c = *key++))
    177 		h = h * 33 + c;
    178 	return h;
    179 }
    180 
    181 /* create new empty hash table with given capacity */
    182 static Hash
    183 hash_new(size_t cap, Hash next)
    184 {
    185 	if (cap < 1) return NULL;
    186 	Hash ht = malloc(sizeof(struct Hash));
    187 	if (!ht) perror("; malloc"), exit(1);
    188 	ht->size = 0;
    189 	ht->cap = cap;
    190 	ht->items = calloc(cap, sizeof(struct Entry));
    191 	if (!ht->items) perror("; calloc"), exit(1);
    192 	ht->next = next;
    193 	return ht;
    194 }
    195 
    196 /* get entry in one hash table for the key */
    197 static Entry
    198 entry_get(Hash ht, char *key)
    199 {
    200 	int i = hash(key) % ht->cap;
    201 	char *s;
    202 	while ((s = ht->items[i].key)) {
    203 		if (!strcmp(s, key))
    204 			break;
    205 		if (++i == ht->cap)
    206 			i = 0;
    207 	}
    208 	return &ht->items[i]; /* returns entry if found or empty one to be filled */
    209 }
    210 
    211 /* get value of given key in each hash table */
    212 static Val
    213 hash_get(Hash ht, char *key)
    214 {
    215 	Entry e;
    216 	for (; ht; ht = ht->next) {
    217 		e = entry_get(ht, key);
    218 		if (e->key)
    219 			return e->val;
    220 	}
    221 	return NULL;
    222 }
    223 
    224 /* enlarge the hash table to ensure algorithm's efficiency */
    225 static void
    226 hash_grow(Hash ht)
    227 {
    228 	int i, ocap = ht->cap;
    229 	Entry oitems = ht->items;
    230 	ht->cap *= 2;
    231 	ht->items = calloc(ht->cap, sizeof(struct Entry));
    232 	if (!ht->items) perror("; calloc"), exit(1);
    233 	for (i = 0; i < ocap; i++) /* repopulate new hash table with old values */
    234 		if (oitems[i].key)
    235 			hash_add(ht, oitems[i].key, oitems[i].val);
    236 	free(oitems);
    237 }
    238 
    239 /* create new key and value pair to the hash table */
    240 static void
    241 hash_add(Hash ht, char *key, Val val)
    242 {
    243 	Entry e = entry_get(ht, key);
    244 	e->val = val;
    245 	if (!e->key) {
    246 		e->key = key;
    247 		ht->size++;
    248 		if (ht->size > ht->cap / 2) /* grow table if it is more than half full */
    249 			hash_grow(ht);
    250 	}
    251 }
    252 
    253 /* add each binding args[i] -> vals[i] */
    254 /* args and vals are both lists */
    255 /* TODO don't return anything in hash_extend since it's modifying ht */
    256 static Hash
    257 hash_extend(Hash ht, Val args, Val vals)
    258 {
    259 	Val arg, val;
    260 	for (; !nilp(args); args = cdr(args), vals = cdr(vals)) {
    261 		if (args->t == TSP_PAIR) {
    262 			arg = car(args);
    263 			val = car(vals);
    264 		} else {
    265 			arg = args;
    266 			val = vals;
    267 		}
    268 		if (arg->t != TSP_SYM)
    269 			tsp_warnf("expected symbol for argument of function"
    270 				  " definition, recieved %s", type_str(arg->t));
    271 		hash_add(ht, arg->v.s, val);
    272 		if (args->t != TSP_PAIR)
    273 			break;
    274 	}
    275 	return ht;
    276 }
    277 
    278 /* make types */
    279 
    280 Val
    281 mk_val(TspType t)
    282 {
    283 	Val ret = malloc(sizeof(struct Val));
    284 	if (!ret) perror("; malloc"), exit(1);
    285 	ret->t = t;
    286 	return ret;
    287 }
    288 
    289 Val
    290 mk_int(int i)
    291 {
    292 	Val ret = mk_val(TSP_INT);
    293 	num(ret) = i;
    294 	den(ret) = 1;
    295 	return ret;
    296 }
    297 
    298 Val
    299 mk_dec(double d)
    300 {
    301 	Val ret = mk_val(TSP_DEC);
    302 	num(ret) = d;
    303 	den(ret) = 1;
    304 	return ret;
    305 }
    306 
    307 Val
    308 mk_rat(int num, int den)
    309 {
    310 	Val ret;
    311 	if (den == 0)
    312 		tsp_warn("division by zero");
    313 	frac_reduce(&num, &den);
    314 	if (den < 0) { /* simplify so only numerator is negative */
    315 		den = abs(den);
    316 		num = -num;
    317 	}
    318 	if (den == 1) /* simplify into integer if denominator is 1 */
    319 		return mk_int(num);
    320 	ret = mk_val(TSP_RATIO);
    321 	ret->v.n.num = num;
    322 	ret->v.n.den = den;
    323 	return ret;
    324 }
    325 
    326 /* TODO combine mk_str and mk_sym */
    327 Val
    328 mk_str(Tsp st, char *s)
    329 {
    330 	Val ret;
    331 	if ((ret = hash_get(st->strs, s)))
    332 		return ret;
    333 	ret = mk_val(TSP_STR);
    334 	ret->v.s = strndup(s, strlen(s));
    335 	if (!ret->v.s) perror("; strndup"), exit(1);
    336 	hash_add(st->strs, s, ret);
    337 	return ret;
    338 }
    339 
    340 Val
    341 mk_sym(Tsp st, char *s)
    342 {
    343 	Val ret;
    344 	if ((ret = hash_get(st->syms, s)))
    345 		return ret;
    346 	ret = mk_val(TSP_SYM);
    347 	ret->v.s = strndup(s, strlen(s));
    348 	if (!ret->v.s) perror("; strndup"), exit(1);
    349 	hash_add(st->syms, s, ret);
    350 	return ret;
    351 }
    352 
    353 Val
    354 mk_prim(TspType t, Prim pr, char *name)
    355 {
    356 	Val ret = mk_val(t);
    357 	ret->v.pr.name = name;
    358 	ret->v.pr.pr = pr;
    359 	return ret;
    360 }
    361 
    362 Val
    363 mk_func(TspType t, char *name, Val args, Val body, Hash env)
    364 {
    365 	Val ret = mk_val(t);
    366 	ret->v.f.name = name;
    367 	ret->v.f.args = args;
    368 	ret->v.f.body = body;
    369 	ret->v.f.env  = env;
    370 	return ret;
    371 }
    372 
    373 Val
    374 mk_pair(Val a, Val b)
    375 {
    376 	Val ret = mk_val(TSP_PAIR);
    377 	car(ret) = a;
    378 	cdr(ret) = b;
    379 	return ret;
    380 }
    381 
    382 Val
    383 mk_list(Tsp st, int n, ...)
    384 {
    385 	Val lst, cur;
    386 	va_list argp;
    387 	va_start(argp, n);
    388 	lst = mk_pair(va_arg(argp, Val), st->nil);
    389 	for (cur = lst; n > 1; n--, cur = cdr(cur))
    390 		cdr(cur) = mk_pair(va_arg(argp, Val), st->nil);
    391 	va_end(argp);
    392 	return lst;
    393 }
    394 
    395 /* read */
    396 
    397 /* read first character of number to determine sign */
    398 static int
    399 read_sign(Tsp st)
    400 {
    401 	switch (tsp_fget(st)) {
    402 	case '-': tsp_finc(st); return -1;
    403 	case '+': tsp_finc(st); return 1;
    404 	default: return 1;
    405 	}
    406 }
    407 
    408 /* return read integer */
    409 static int
    410 read_int(Tsp st)
    411 {
    412 	int ret = 0;
    413 	for (; tsp_fget(st) && isdigit(tsp_fget(st)); tsp_finc(st))
    414 		ret = ret * 10 + tsp_fget(st) - '0';
    415 	return ret;
    416 }
    417 
    418 /* return read scientific notation */
    419 static Val
    420 read_sci(Tsp st, double val, int isint)
    421 {
    422 	if (tolower(tsp_fget(st)) != 'e')
    423 		goto finish;
    424 
    425 	tsp_finc(st);
    426 	double sign = read_sign(st) == 1 ? 10.0 : 0.1;
    427 	for (int expo = read_int(st); expo--; val *= sign) ;
    428 
    429 finish:
    430 	if (isint)
    431 		return mk_int(val);
    432 	return mk_dec(val);
    433 }
    434 
    435 /* return read number */
    436 static Val
    437 read_num(Tsp st)
    438 {
    439 	int sign = read_sign(st);
    440 	int num = read_int(st);
    441 	size_t oldc;
    442 	switch (tsp_fget(st)) {
    443 	case '/':
    444 		if (!isnum(st->file + ++st->filec))
    445 			tsp_warn("incorrect ratio format, no denominator found");
    446 		return mk_rat(sign * num, read_sign(st) * read_int(st));
    447 	case '.':
    448 		tsp_finc(st);
    449 		oldc = st->filec;
    450 		double d = (double) read_int(st);
    451 		int size = st->filec - oldc;
    452 		while (size--)
    453 			d /= 10.0;
    454 		return read_sci(st, sign * (num+d), 0);
    455 	default:
    456 		return read_sci(st, sign * num, 1);
    457 	}
    458 }
    459 
    460 /* return character for escape */
    461 static char
    462 esc_char(char c)
    463 {
    464 	switch (c) {
    465 	case 'n': return '\n';
    466 	case 't': return '\t';
    467 	case '\n': return ' ';
    468 	case '\\':
    469 	case '"':
    470 	default:  return c;
    471 	}
    472 }
    473 
    474 /* replace all encoded escape characters in string with their actual character */
    475 static char *
    476 esc_str(char *s, int len)
    477 {
    478 	char *pos, *ret = malloc((len+1) * sizeof(char));
    479 	if (!ret) perror("; malloc"), exit(1);
    480 	for (pos = ret; pos-ret < len; pos++, s++)
    481 		*pos = (*s == '\\') ? esc_char(*(++s)) : *s;
    482 	*pos = '\0';
    483 	return ret;
    484 }
    485 
    486 /* return read string */
    487 static Val
    488 read_str(Tsp st)
    489 {
    490 	int len = 0;
    491 	char *s = st->file + ++st->filec; /* skip starting open quote */
    492 	for (; tsp_fget(st) != '"'; tsp_finc(st), len++)
    493 		if (!tsp_fget(st))
    494 			tsp_warn("reached end before closing double quote");
    495 		else if (tsp_fget(st) == '\\' && tsp_fgetat(st, -1) != '\\')
    496 			tsp_finc(st); /* skip over break condition */
    497 	tsp_finc(st); /* skip last closing quote */
    498 	return mk_str(st, esc_str(s, len));
    499 }
    500 
    501 /* return read symbol */
    502 static Val
    503 read_sym(Tsp st)
    504 {
    505 	int n = 1, i = 0;
    506 	char *sym = malloc(n);
    507 	for (; tsp_fget(st) && issym(tsp_fget(st)); tsp_finc(st)) {
    508 		if (!sym) perror("; alloc"), exit(1);
    509 		sym[i++] = tsp_fget(st);
    510 		if (i == n) {
    511 			n *= 2;
    512 			sym = realloc(sym, n);
    513 		}
    514 	}
    515 	sym[i] = '\0';
    516 	return mk_sym(st, sym);
    517 }
    518 
    519 /* return read string containing a list */
    520 /* TODO read pair after as well, allow lambda((x) (* x 2))(4) */
    521 static Val
    522 read_pair(Tsp st, char endchar)
    523 {
    524 	Val a, b;
    525 	skip_ws(st, 1);
    526 	if (tsp_fget(st) == endchar)
    527 		return tsp_finc(st), st->nil;
    528 	/* TODO simplify read_pair by supporting (. x) => x */
    529 	if (!(a = tisp_read(st)))
    530 		return NULL;
    531 	skip_ws(st, 1);
    532 	if (!tsp_fget(st))
    533 		tsp_warnf("reached end before closing '%c'", endchar);
    534 	if (tsp_fget(st) == '.' && isdelim(tsp_fgetat(st,1))) {
    535 		tsp_finc(st);
    536 		if (!(b = tisp_read(st)))
    537 			return NULL;
    538 		skip_ws(st, 1);
    539 		if (tsp_fget(st) != endchar)
    540 			tsp_warnf("did not find closing '%c'", endchar);
    541 		tsp_finc(st);
    542 		skip_ws(st, 1);
    543 	} else {
    544 		if (!(b = read_pair(st, endchar)))
    545 			return NULL;
    546 	}
    547 	return mk_pair(a, b);
    548 }
    549 
    550 /* reads given string returning its tisp value */
    551 Val
    552 tisp_read(Tsp st)
    553 {
    554 	char *prefix[] = {
    555 		"'",  "quote",
    556 		"`",  "quasiquote",
    557 		",@", "unquote-splice", /* always check before , */
    558 		",",  "unquote",
    559 		"@",  "Func",
    560 	};
    561 	skip_ws(st, 1);
    562 	if (strlen(st->file+st->filec) == 0) /* empty list */
    563 		return st->none;
    564 	if (isnum(st->file+st->filec)) /* number */
    565 		return read_num(st);
    566 	/* TODO support | for symbols */
    567 	if (tsp_fget(st) == '"') /* strings */
    568 		return read_str(st);
    569 	for (int i = 0; i < LEN(prefix); i += 2) { /* character prefix */
    570 		if (!strncmp(st->file+st->filec, prefix[i], strlen(prefix[i]))) {
    571 			Val v;
    572 			tsp_fincn(st, strlen(prefix[i]));
    573 			if (!(v = tisp_read(st)))
    574 				return NULL;
    575 			return mk_list(st, 2, mk_sym(st, prefix[i+1]), v);
    576 		}
    577 	}
    578 	if (issym(tsp_fget(st))) /* symbols */
    579 		return read_sym(st);
    580 	if (tsp_fget(st) == '(') /* list */
    581 		return tsp_finc(st), read_pair(st, ')');
    582 	tsp_warnf("could not read given input '%c'", st->file[st->filec]);
    583 }
    584 
    585 /* return string containing contents of file name */
    586 char *
    587 tisp_read_file(char *fname)
    588 {
    589 	char buf[BUFSIZ], *file = NULL;
    590 	int len = 0, n, fd, parens = 0;
    591 	if (!fname) /* read from stdin if no file given */
    592 		fd = 0;
    593 	else if ((fd = open(fname, O_RDONLY)) < 0)
    594 		tsp_warnf("could not find file '%s'", fname);
    595 	while ((n = read(fd, buf, sizeof(buf))) > 0) {
    596 		file = realloc(file, len + n + 1);
    597 		if (!file) perror("; realloc"), exit(1);
    598 		memcpy(file + len, buf, n);
    599 		len += n;
    600 		file[len] = '\0';
    601 		if (fd == 0 && !(parens += count_parens(buf, n)))
    602 			break;
    603 	}
    604 	if (fd) /* close file if not stdin */
    605 		close(fd);
    606 	if (n < 0)
    607 		tsp_warnf("could not read file '%s'", fname);
    608 	return file;
    609 }
    610 
    611 /* read given file name returning its tisp value */
    612 Val
    613 tisp_parse_file(Tsp st, char *fname)
    614 {
    615 	Val ret = mk_pair(st->none, st->nil);
    616 	Val v, last = ret;
    617 	char *file = st->file;
    618 	size_t filec = st->filec;
    619 	if (!(st->file = tisp_read_file(fname)))
    620 		return ret;
    621 	for (st->filec = 0; tsp_fget(st) && (v = tisp_read(st)); last = cdr(last))
    622 		cdr(last) = mk_pair(v, st->nil);
    623 	free(st->file);
    624 	st->file = file;
    625 	st->filec = filec;
    626 	return cdr(ret);
    627 }
    628 
    629 /* eval */
    630 
    631 /* evaluate each element of list */
    632 Val
    633 tisp_eval_list(Tsp st, Hash env, Val v)
    634 {
    635 	Val cur = mk_pair(NULL, st->none);
    636 	Val ret = cur, ev;
    637 	for (; !nilp(v); v = cdr(v), cur = cdr(cur)) {
    638 		if (v->t != TSP_PAIR) {
    639 			if (!(ev = tisp_eval(st, env, v)))
    640 				return NULL;
    641 			cdr(cur) = ev;
    642 			return cdr(ret);
    643 		}
    644 		if (!(ev = tisp_eval(st, env, car(v))))
    645 			return NULL;
    646 		cdr(cur) = mk_pair(ev, st->none);
    647 	}
    648 	cdr(cur) = st->nil;
    649 	return cdr(ret);
    650 }
    651 
    652 /* evaluate all elements of list returning last */
    653 Val
    654 tisp_eval_seq(Tsp st, Hash env, Val v)
    655 {
    656 	Val ret = st->none;
    657 	for (; v->t == TSP_PAIR; v = cdr(v))
    658 		if (!(ret = tisp_eval(st, env, car(v))))
    659 			return NULL;
    660 	return nilp(v) ? ret : tisp_eval(st, env, v);
    661 }
    662 
    663 static void
    664 prepend_bt(Tsp st, Hash env, Val f)
    665 {
    666 	if (!f->v.f.name) /* no need to record anonymous functions */
    667 		return;
    668 	for (; env->next; env = env->next) ; /* bt var located at base env */
    669 	Entry e = entry_get(env, "bt");
    670 	if (e->val->t == TSP_PAIR && car(e->val)->t == TSP_SYM &&
    671 	    !strncmp(f->v.f.name, car(e->val)->v.s, strlen(car(e->val)->v.s)))
    672 		return; /* don't record same function on recursion */
    673 	e->val = mk_pair(mk_sym(st, f->v.f.name), e->val);
    674 }
    675 
    676 /* evaluate procedure f with arguments */
    677 static Val
    678 eval_proc(Tsp st, Hash env, Val f, Val args)
    679 {
    680 	Val ret;
    681 	Hash e;
    682 	/* evaluate function and primitive arguments before being passed */
    683 	if (f->t & (TSP_FUNC|TSP_PRIM))
    684 		if (!(args = tisp_eval_list(st, env, args)))
    685 			return NULL;
    686 	switch (f->t) {
    687 	case TSP_FORM:
    688 	case TSP_PRIM:
    689 		return (*f->v.pr.pr)(st, env, args);
    690 	case TSP_FUNC:
    691 	case TSP_MACRO:
    692 		tsp_arg_num(args, f->v.f.name ? f->v.f.name : "anon",
    693 		            list_len(f->v.f.args));
    694 		e = hash_new(8, f->v.f.env);
    695 		/* TODO call hash_extend in hash_new to know new hash size */
    696 		if (!(hash_extend(e, f->v.f.args, args)))
    697 			return NULL;
    698 		if (!(ret = tisp_eval_seq(st, e, f->v.f.body)))
    699 			return prepend_bt(st, env, f), NULL;
    700 		if (f->t == TSP_MACRO)
    701 			ret = tisp_eval(st, env, ret);
    702 		return ret;
    703 	default:
    704 		tsp_warnf("attempt to evaluate non procedural type %s", type_str(f->t));
    705 	}
    706 }
    707 
    708 /* evaluate given value */
    709 Val
    710 tisp_eval(Tsp st, Hash env, Val v)
    711 {
    712 	Val f;
    713 	switch (v->t) {
    714 	case TSP_SYM:
    715 		if (!(f = hash_get(env, v->v.s)))
    716 			tsp_warnf("could not find symbol %s", v->v.s);
    717 		return f;
    718 	case TSP_PAIR:
    719 		if (!(f = tisp_eval(st, env, car(v))))
    720 			return NULL;
    721 		return eval_proc(st, env, f, cdr(v));
    722 	case TSP_STR: /* TODO string interpolation */
    723 	default:
    724 		return v;
    725 	}
    726 }
    727 
    728 /* print */
    729 
    730 /* main print function */
    731 void
    732 tisp_print(FILE *f, Val v)
    733 {
    734 	switch (v->t) {
    735 	case TSP_NONE:
    736 		fputs("Void", f);
    737 		break;
    738 	case TSP_NIL:
    739 		fputs("Nil", f);
    740 		break;
    741 	case TSP_INT:
    742 		fprintf(f, "%d", (int)num(v));
    743 		break;
    744 	case TSP_DEC:
    745 		fprintf(f, "%.15g", num(v));
    746 		if (num(v) == (int)num(v))
    747 			fprintf(f, ".0");
    748 		break;
    749 	case TSP_RATIO:
    750 		fprintf(f, "%d/%d", (int)num(v), (int)den(v));
    751 		break;
    752 	case TSP_STR:
    753 	case TSP_SYM:
    754 		fputs(v->v.s, f);
    755 		break;
    756 	case TSP_FUNC:
    757 	case TSP_MACRO:
    758 		fprintf(f, "#<%s%s%s>", /* if proc name is not null print it */
    759 		            v->t == TSP_FUNC ? "function" : "macro",
    760 		            v->v.pr.name ? ":" : "",
    761 		            v->v.pr.name ? v->v.pr.name : "");
    762 		break;
    763 	case TSP_PRIM:
    764 		fprintf(f, "#<primitive:%s>", v->v.pr.name);
    765 		break;
    766 	case TSP_FORM:
    767 		fprintf(f, "#<form:%s>", v->v.pr.name);
    768 		break;
    769 	case TSP_PAIR:
    770 		putc('(', f);
    771 		tisp_print(f, car(v));
    772 		for (v = cdr(v); !nilp(v); v = cdr(v)) {
    773 			if (v->t == TSP_PAIR) {
    774 				putc(' ', f);
    775 				tisp_print(f, car(v));
    776 			} else {
    777 				fputs(" . ", f);
    778 				tisp_print(f, v);
    779 				break;
    780 			}
    781 		}
    782 		putc(')', f);
    783 		break;
    784 	default:
    785 		fprintf(stderr, "; tisp: could not print value type %s\n", type_str(v->t));
    786 	}
    787 }
    788 
    789 /* primitives */
    790 
    791 /* return first element of list */
    792 static Val
    793 prim_car(Tsp st, Hash env, Val args)
    794 {
    795 	tsp_arg_num(args, "car", 1);
    796 	tsp_arg_type(car(args), "car", TSP_PAIR);
    797 	return caar(args);
    798 }
    799 
    800 /* return elements of a list after the first */
    801 static Val
    802 prim_cdr(Tsp st, Hash env, Val args)
    803 {
    804 	tsp_arg_num(args, "cdr", 1);
    805 	tsp_arg_type(car(args), "cdr", TSP_PAIR);
    806 	return cdar(args);
    807 }
    808 
    809 /* return new pair */
    810 static Val
    811 prim_cons(Tsp st, Hash env, Val args)
    812 {
    813 	tsp_arg_num(args, "cons", 2);
    814 	return mk_pair(car(args), cadr(args));
    815 }
    816 
    817 /* do not evaluate argument */
    818 static Val
    819 form_quote(Tsp st, Hash env, Val args)
    820 {
    821 	tsp_arg_num(args, "quote", 1);
    822 	return car(args);
    823 }
    824 
    825 /* evaluate argument given */
    826 static Val
    827 prim_eval(Tsp st, Hash env, Val args)
    828 {
    829 	Val v;
    830 	tsp_arg_num(args, "eval", 1);
    831 	return (v = tisp_eval(st, st->global, car(args))) ? v : st->none;
    832 }
    833 
    834 /* test equality of all values given */
    835 static Val
    836 prim_eq(Tsp st, Hash env, Val args)
    837 {
    838 	if (nilp(args))
    839 		return st->t;
    840 	for (; !nilp(cdr(args)); args = cdr(args))
    841 		if (!vals_eq(car(args), cadr(args)))
    842 			return st->nil;
    843 	return st->t;
    844 }
    845 
    846 /* evaluates all expressions if their conditions are met */
    847 static Val
    848 form_cond(Tsp st, Hash env, Val args)
    849 {
    850 	Val v, cond;
    851 	for (v = args; !nilp(v); v = cdr(v))
    852 		if (!(cond = tisp_eval(st, env, caar(v))))
    853 			return NULL;
    854 		else if (!nilp(cond)) /* TODO incorporate else directly into cond */
    855 			return tisp_eval_seq(st, env, cdar(v));
    856 	return st->none;
    857 }
    858 
    859 /* return type of tisp value */
    860 static Val
    861 prim_typeof(Tsp st, Hash env, Val args)
    862 {
    863 	tsp_arg_num(args, "typeof", 1);
    864 	return mk_str(st, type_str(car(args)->t));
    865 }
    866 
    867 /* TODO rename get to getattr like python ? */
    868 /* get a property of given value */
    869 static Val
    870 prim_get(Tsp st, Hash env, Val args)
    871 {
    872 	Val v, prop;
    873 	tsp_arg_num(args, "get", 2);
    874 	v = car(args), prop = cadr(args);
    875 	tsp_arg_type(prop, "get", TSP_SYM);
    876 	switch (v->t) {
    877 	case TSP_FORM:
    878 	case TSP_PRIM:
    879 		if (!strncmp(prop->v.s, "name", 4))
    880 			return mk_sym(st, v->v.pr.name);
    881 		break;
    882 	case TSP_FUNC:
    883 	case TSP_MACRO:
    884 		if (!strncmp(prop->v.s, "name", 4))
    885 			return mk_sym(st, v->v.f.name ? v->v.f.name : "anon");
    886 		if (!strncmp(prop->v.s, "body", 4))
    887 			return v->v.f.body;
    888 		if (!strncmp(prop->v.s, "args", 4))
    889 			return v->v.f.args;
    890 		break;
    891 	case TSP_INT:
    892 	case TSP_RATIO:
    893 		if (!strncmp(prop->v.s, "numerator", 9))
    894 			return mk_int(v->v.n.num);
    895 		if (!strncmp(prop->v.s, "denominator", 9))
    896 			return mk_int(v->v.n.den);
    897 		break;
    898 	case TSP_PAIR: /* TODO get nth element if number */
    899 		if (!strncmp(prop->v.s, "car", 3))
    900 			return v->v.p.car;
    901 		if (!strncmp(prop->v.s, "cdr", 3))
    902 			return v->v.p.cdr;
    903 		break;
    904 	case TSP_STR:
    905 	case TSP_SYM:
    906 		if (!strncmp(prop->v.s, "len", 3))
    907 			return mk_int(strlen(v->v.s));
    908 	default: break;
    909 	}
    910 	tsp_warnf("get: can not access %s from type %s",
    911 		   prop->v.s, type_str(v->t));
    912 }
    913 
    914 /* creates new tisp function */
    915 static Val
    916 form_Func(Tsp st, Hash env, Val args)
    917 {
    918 	Val params, body;
    919 	tsp_arg_min(args, "Func", 1);
    920 	if (nilp(cdr(args))) { /* if only 1 argument is given, auto fill func parameters */
    921 		params = mk_pair(mk_sym(st, "it"), st->nil);
    922 		body = args;
    923 	} else {
    924 		params = car(args);
    925 		body = cdr(args);
    926 	}
    927 	return mk_func(TSP_FUNC, NULL, params, body, env);
    928 }
    929 
    930 /* creates new tisp defined macro */
    931 static Val
    932 form_Macro(Tsp st, Hash env, Val args)
    933 {
    934 	tsp_arg_min(args, "Macro", 1);
    935 	Val ret = form_Func(st, env, args);
    936 	ret->t = TSP_MACRO;
    937 	return ret;
    938 }
    939 
    940 /* creates new variable of given name and value
    941  * if pair is given as name of variable, creates function with the car as the
    942  * function name and the cdr the function arguments */
    943 /* TODO if var not func error if more than 2 args */
    944 static Val
    945 form_def(Tsp st, Hash env, Val args)
    946 {
    947 	Val sym, val;
    948 	tsp_arg_min(args, "def", 1);
    949 	if (car(args)->t == TSP_PAIR) { /* create function if given argument list */
    950 		sym = caar(args); /* first element of argument list is function name */
    951 		if (sym->t != TSP_SYM)
    952 			tsp_warnf("def: incorrect format,"
    953 			          " expected symbol for function name, received %s",
    954 			          type_str(sym->t));
    955 		val = mk_func(TSP_FUNC, sym->v.s, cdar(args), cdr(args), env);
    956 	} else if (car(args)->t == TSP_SYM) { /* create variable */
    957 		sym = car(args); /* if only symbol given, make it self evaluating */
    958 		val = nilp(cdr(args)) ? sym : tisp_eval(st, env, cadr(args));
    959 	} else tsp_warn("def: incorrect format, no variable name found");
    960 	if (!val)
    961 		return NULL;
    962 	/* set procedure name if it was previously anonymous */
    963 	if (val->t & (TSP_FUNC|TSP_MACRO) && !val->v.f.name)
    964 		val->v.f.name = sym->v.s;
    965 	hash_add(env, sym->v.s, val);
    966 	return st->none;
    967 }
    968 
    969 /* set symbol to new value */
    970 static Val
    971 form_set(Tsp st, Hash env, Val args)
    972 {
    973 	Val val;
    974 	Hash h;
    975 	Entry e = NULL;
    976 	tsp_arg_num(args, "set!", 2);
    977 	tsp_arg_type(car(args), "set!", TSP_SYM);
    978 	if (!(val = tisp_eval(st, env, cadr(args))))
    979 		return NULL;
    980 	/* find first occurrence of symbol */
    981 	for (h = env; h; h = h->next) {
    982 		e = entry_get(h, car(args)->v.s);
    983 		if (e->key)
    984 			break;
    985 	}
    986 	if (!e || !e->key)
    987 		tsp_warnf("set!: variable %s is not defined", car(args)->v.s);
    988 	if (e->val->t == TSP_PRIM) /* TODO hard code other values */
    989 		tsp_warnf("set!: can not modify %s, is primitive procedure", e->val->v.pr.name);
    990 	e->val = val;
    991 	return val;
    992 }
    993 
    994 /* TODO fix crashing if try to undefine builtin */
    995 static Val
    996 form_undefine(Tsp st, Hash env, Val args)
    997 {
    998 	tsp_arg_min(args, "undefine!", 1);
    999 	tsp_arg_type(car(args), "undefine!", TSP_SYM);
   1000 	for (Hash h = env; h; h = h->next) {
   1001 		Entry e = entry_get(h, car(args)->v.s);
   1002 		if (e->key) {
   1003 			e->key = NULL;
   1004 			/* TODO tsp_free(e->val); */
   1005 			return st->none;
   1006 		}
   1007 	}
   1008 	tsp_warnf("undefine!: could not find symbol %s to undefine", car(args)->v.s);
   1009 }
   1010 
   1011 static Val
   1012 form_definedp(Tsp st, Hash env, Val args)
   1013 {
   1014 	Entry e = NULL;
   1015 	tsp_arg_min(args, "defined?", 1);
   1016 	tsp_arg_type(car(args), "defined?", TSP_SYM);
   1017 	for (Hash h = env; h; h = h->next) {
   1018 		e = entry_get(h, car(args)->v.s);
   1019 		if (e->key)
   1020 			break;
   1021 	}
   1022 	return (!e || !e->key) ? st->nil : st->t;
   1023 }
   1024 
   1025 /* loads tisp file or C dynamic library */
   1026 /* TODO lua like error listing places load looked */
   1027 /* TODO only use dlopen if -ldl is given with TIB_DYNAMIC */
   1028 /* TODO define load in lisp which calls load-dl */
   1029 static Val
   1030 prim_load(Tsp st, Hash env, Val args)
   1031 {
   1032 	Val tib;
   1033 	void (*tibenv)(Tsp);
   1034 	char name[PATH_MAX];
   1035 	const char *paths[] = {
   1036 		"/usr/local/share/tisp/", "/usr/share/tisp/", "./", NULL
   1037 	};
   1038 
   1039 	tsp_arg_num(args, "load", 1);
   1040 	tib = car(args);
   1041 	tsp_arg_type(tib, "load", TSP_STR);
   1042 
   1043 	for (int i = 0; paths[i]; i++) {
   1044 		strcpy(name, paths[i]);
   1045 		strcat(name, tib->v.s);
   1046 		strcat(name, ".tsp");
   1047 		if (access(name, R_OK) != -1) {
   1048 			tisp_eval_seq(st, env, tisp_parse_file(st, name));
   1049 			return st->none;
   1050 		}
   1051 	}
   1052 
   1053 	/* If not tisp file, try loading shared object library */
   1054 	st->libh = realloc(st->libh, (st->libhc+1)*sizeof(void*));
   1055 	if (!st->libh) perror("; realloc"), exit(1);
   1056 
   1057 	memset(name, 0, sizeof(name));
   1058 	strcpy(name, "libtib");
   1059 	strcat(name, tib->v.s);
   1060 	strcat(name, ".so");
   1061 	if (!(st->libh[st->libhc] = dlopen(name, RTLD_LAZY)))
   1062 		tsp_warnf("load: could not load '%s':\n%s", tib->v.s, dlerror());
   1063 	dlerror();
   1064 
   1065 	memset(name, 0, sizeof(name));
   1066 	strcpy(name, "tib_env_");
   1067 	strcat(name, tib->v.s);
   1068 	tibenv = dlsym(st->libh[st->libhc], name);
   1069 	if (dlerror())
   1070 		tsp_warnf("load: could not run '%s':\n%s", tib->v.s, dlerror());
   1071 	(*tibenv)(st);
   1072 
   1073 	st->libhc++;
   1074 	return st->none;
   1075 }
   1076 
   1077 /* display message and return error */
   1078 static Val
   1079 prim_error(Tsp st, Hash env, Val args)
   1080 {
   1081 	/* TODO have error auto print function name that was pre-defined */
   1082 	tsp_arg_min(args, "error", 2);
   1083 	tsp_arg_type(car(args), "error", TSP_SYM);
   1084 	/* TODO specify error raised by error func */
   1085 	fprintf(stderr, "; tisp: error: %s: ", car(args)->v.s);
   1086 	for (args = cdr(args); !nilp(args); args = cdr(args))
   1087 		tisp_print(stderr, car(args));
   1088 	fputc('\n', stderr);
   1089 	return NULL;
   1090 }
   1091 
   1092 
   1093 /* environment */
   1094 
   1095 /* add new variable of name key and value v to the given environment */
   1096 void
   1097 tisp_env_add(Tsp st, char *key, Val v)
   1098 {
   1099 	hash_add(st->global, key, v);
   1100 }
   1101 
   1102 /* initialise tisp's state and global environment */
   1103 Tsp
   1104 tisp_env_init(size_t cap)
   1105 {
   1106 	Tsp st = malloc(sizeof(struct Tsp));
   1107 	if (!st) perror("; malloc"), exit(1);
   1108 
   1109 	st->file = NULL;
   1110 	st->filec = 0;
   1111 
   1112 	st->strs = hash_new(cap, NULL);
   1113 	st->syms = hash_new(cap, NULL);
   1114 
   1115 	st->nil = mk_val(TSP_NIL);
   1116 	st->none = mk_val(TSP_NONE);
   1117 	st->t = mk_val(TSP_SYM);
   1118 	st->t->v.s = "True";
   1119 
   1120 	st->global = hash_new(cap, NULL);
   1121 	tisp_env_add(st, "True", st->t);
   1122 	tisp_env_add(st, "Nil", st->nil);
   1123 	tisp_env_add(st, "Void", st->none);
   1124 	tisp_env_add(st, "bt", st->nil);
   1125 	tisp_env_add(st, "version", mk_str(st, "0.0.0"));
   1126 	tsp_env_prim(car);
   1127 	tsp_env_prim(cdr);
   1128 	tsp_env_prim(cons);
   1129 	tsp_env_form(quote);
   1130 	tsp_env_prim(eval);
   1131 	tsp_env_name_prim(=, eq);
   1132 	tsp_env_form(cond);
   1133 	tsp_env_prim(typeof);
   1134 	tsp_env_prim(get);
   1135 	tsp_env_form(Func);
   1136 	tsp_env_form(Macro);
   1137 	tsp_env_form(def);
   1138 	tsp_env_name_form(set!, set);
   1139 	tsp_env_prim(load);
   1140 	tsp_env_name_form(undefine!, undefine);
   1141 	tsp_env_name_form(defined?, definedp);
   1142 	tsp_env_prim(error);
   1143 
   1144 	st->libh = NULL;
   1145 	st->libhc = 0;
   1146 
   1147 	return st;
   1148 }
   1149 
   1150 void
   1151 tisp_env_lib(Tsp st, char* lib)
   1152 {
   1153 	Val v;
   1154 	char *file = st->file;
   1155 	size_t filec = st->filec;
   1156 	st->file = lib;
   1157 	st->filec = 0;
   1158 	if ((v = tisp_read(st)))
   1159 		tisp_eval_seq(st, st->global, v);
   1160 	st->file = file;
   1161 	st->filec = filec;
   1162 }