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 }