tisp

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

commit 50d3ecf044efdcf8830af3f4487b81da741bd055
parent 1f83d96d0829e4c60e697a2775cd8e32dd4516d8
Author: Ed van Bruggen <edvb@uw.edu>
Date:   Fri, 30 Mar 2018 12:07:09 -0700

Separate into tisp.c, tisp.h, main.c, and math.c

tisp.c and tisp.h are the library files for the language, main.c uses
them to create an example program which can be run as an standalone
interpreter. The tib/ directory contains tisp libraries, such as math.c
for math primitives.

Diffstat:
main.c | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tib/math.c | 27+++++++++++++++++++++++++++
tib/math.h | 3+++
tisp.c | 244++++++-------------------------------------------------------------------------
tisp.h | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 258 insertions(+), 226 deletions(-)

diff --git a/main.c b/main.c @@ -0,0 +1,103 @@ +/* See LICENSE file for copyright and license details. */ +#include <libgen.h> +#include <stdio.h> +#include <string.h> + +#include "extern/arg.h" +#include "extern/linenoise.h" + +#include "util.h" +#include "config.h" + +#include "tisp.h" +#include "tib/math.h" + +char *argv0; + +char * +hints(const char *buf, int *color, int *bold) +{ + struct { char *match, *hint; int color, bold; } hint[] = { + { "(lambda", " (args) body)", 35, 0 }, + { "(define", " var exp)", 35, 0 }, + { NULL, NULL, 0, 0 } + }; + for (int i = 0; hint[i].match; i++) { + if (!strcasecmp(buf, hint[i].match)){ + *color = hint[i].color; + *bold = hint[i].bold; + return hint[i].hint; + } + } + return NULL; +} + +static Val +read_val(Str cmd) +{ + struct Str str; + + if (cmd->d) + return tisp_read(cmd); + + if (SHOW_HINTS) + linenoiseSetHintsCallback(hints); + if (!(str.d = linenoise("> "))) + return NULL; + linenoiseHistoryAdd(str.d); + + return tisp_read(&str); +} + +static void +usage(const int eval) +{ + die(eval, "usage: %s [-hv] [FILENAME]", argv0); +} + +int +main(int argc, char *argv[]) +{ + ARGBEGIN { + case 'h': + usage(0); + case 'v': + printf("%s v%s (c) 2017-2018 Ed van Bruggen\n", argv0, VERSION); + return 0; + default: + usage(1); + } ARGEND; + + size_t nread; + char buf[BUFSIZ]; + struct Str str = { NULL }; + FILE *fp; + Val v; + Hash env = tisp_init_env(64); + env = tib_math_env(env); + + nil.t = NIL; + t.t = SYMBOL; + t.v.s = estrdup("t"); + + if (argc > 0) { + if (!(fp = fopen(*argv, "r"))) + die(1, "%s: %s:", argv[0], *argv); + while ((nread = fread(buf, 1, sizeof(buf), fp)) > 0) ; + str.d = estrdup(buf); + } + + while ((v = read_val(&str))) { + if (!(v = tisp_eval(env, v))) + continue; + + tisp_print(v); + putchar('\n'); + + if (!str.d) continue; + skip_spaces(&str); + if (!*str.d) break; + } + + return 0; +} diff --git a/tib/math.c b/tib/math.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> + +#include "../tisp.h" +#include "math.h" + +Val +prim_add(Hash env, Val args) +{ + Val v; + int i = 0; + if (!(v = eval_list(env, args))) + return NULL; + for (; !nilp(v); v = cdr(v)) + if (car(v)->t == INTEGER) + i += car(v)->v.i; + else + warnf("+: expected integer, recieved type [%d]", car(v)->t); + return mk_int(i); +} + +Hash +tib_math_env(Hash ht) +{ + hash_add(ht, "+", mk_prim(prim_add)); + return ht; +} diff --git a/tib/math.h b/tib/math.h @@ -0,0 +1,3 @@ +/* See LICENSE file for copyright and license details. */ + +Hash tib_math_env(Hash ht); diff --git a/tisp.c b/tisp.c @@ -1,130 +1,19 @@ /* See LICENSE file for copyright and license details. */ #include <ctype.h> -#include <libgen.h> #include <limits.h> -#include <regex.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> -#include "extern/arg.h" -#include "extern/linenoise.h" +#include "tisp.h" #include "util.h" -/* defines */ -#define BUF_SIZE 2048 - -#define car(P) ((P)->v.p.car) -#define cdr(P) ((P)->v.p.cdr) -#define nilp(P) ((P)->t == NIL) - -#define warnf(M, ...) do { \ - fprintf(stderr, "%s:%d: error: " M "\n", \ - argv0, __LINE__, ##__VA_ARGS__); \ - return NULL; \ -} while(0) - -#define warn(M) do { \ - fprintf(stderr, "%s:%d: error: " M "\n", \ - argv0, __LINE__); \ - return NULL; \ -} while(0) - -/* typedefs */ -struct Val; -typedef struct Val *Val; - -/* improved interface for a pointer to a string */ -typedef struct Str { - char *d; -} *Str; - -typedef enum { - ERROR_OK, - ERROR_SYNTAX -} Error; - -/* fraction */ -typedef struct { - int num, den; -} Ratio; - -typedef struct Entry *Entry; - -typedef struct Hash { - int size, cap; - struct Entry { - char *key; - Val val; - } *items; - struct Hash *next; -} *Hash; - -/* basic function written in C, not lisp */ -typedef Val (*Prim)(Hash, Val); - -/* function written directly in lisp instead of C */ -typedef struct { - Val args; - Val body; - Hash env; -} Func; - -typedef struct { - Val car, cdr; -} Pair; - -typedef enum { - NIL, - INTEGER, - RATIONAL, - STRING, - SYMBOL, - PRIMITIVE, - FUNCTION, - PAIR, -} Type; - -struct Val { - Type t; - union { - int i; - Ratio r; - char *s; - Prim pr; - Func f; - Pair p; - } v; -}; - /* functions */ -static Val hash_add(Hash ht, char *key, Val val); static Hash hash_extend(Hash ht, Val args, Val vals); static void hash_merge(Hash ht, Hash ht2); -Val read_val(Str str); -Val read_list(Str str); - -static void tisp_print(Val v); -static Val tisp_eval(Hash env, Val v); - -/* variables */ -char *argv0; - -struct Val nil; -struct Val t; - -#include "config.h" - -static int -issym(char c) -{ - return BETWEEN(c, 'a', 'z') || strchr("+-*/=?", c); -} - -static void +void skip_spaces(Str str) { str->d += strspn(str->d, " \t\n"); /* skip white space */ @@ -133,6 +22,12 @@ skip_spaces(Str str) } static int +issym(char c) +{ + return BETWEEN(c, 'a', 'z') || strchr("+-*/=?", c); +} + +static int list_len(Val v) { int len = 0; @@ -252,7 +147,7 @@ hash_grow(Hash ht) } /* create new key and value pair to the hash table */ -static Val +Val hash_add(Hash ht, char *key, Val val) { Entry e = entry_get(ht, key); @@ -399,7 +294,7 @@ read_list(Str str) skip_spaces(str); while (*str->d && *str->d != ')') { a = erealloc(a, (n+1) * sizeof(Val)); /* TODO realloc less */ - a[n++] = read_val(str); + a[n++] = tisp_read(str); skip_spaces(str); } b = mk_list(n, a); @@ -410,7 +305,7 @@ read_list(Str str) } Val -read_val(Str str) +tisp_read(Str str) { skip_spaces(str); if (isdigit(*str->d)) /* TODO negative numbers */ @@ -424,42 +319,7 @@ read_val(Str str) return NULL; } -char * -hints(const char *buf, int *color, int *bold) -{ - struct { char *match, *hint; int color, bold; } hint[] = { - { "(lambda", " (args) (body))", 35, 0 }, - { "(define", " var exp)", 35, 0 }, - { NULL, NULL, 0, 0 } - }; - for (int i = 0; hint[i].match; i++) { - if (!strcasecmp(buf, hint[i].match)){ - *color = hint[i].color; - *bold = hint[i].bold; - return hint[i].hint; - } - } - return NULL; -} - -static Val -tisp_read(Str cmd) -{ - struct Str str; - - if (cmd->d) - return read_val(cmd); - - if (SHOW_HINTS) - linenoiseSetHintsCallback(hints); - if (!(str.d = linenoise("> "))) - return NULL; - linenoiseHistoryAdd(str.d); - - return read_val(&str); -} - -static Val +Val eval_list(Hash env, Val v) { int cap = 1, size = 0; @@ -477,7 +337,7 @@ eval_list(Hash env, Val v) return ret; } -static Val +Val tisp_eval(Hash env, Val v) { Val f, args; @@ -513,7 +373,7 @@ tisp_eval(Hash env, Val v) } /* TODO return str for error msgs? */ -static void +void tisp_print(Val v) { switch (v->t) { @@ -556,7 +416,7 @@ tisp_print(Val v) putchar(')'); break; default: - printf("%s: could not print value type [%d]", argv0, v->t); + fprintf(stderr, "tisp: could not print value type [%d]", v->t); } } @@ -647,25 +507,10 @@ prim_define(Hash env, Val args) return NULL; } -static Val -prim_add(Hash env, Val args) -{ - Val v; - int i = 0; - if (!(v = eval_list(env, args))) - return NULL; - for (; !nilp(v); v = cdr(v)) - if (car(v)->t == INTEGER) - i += car(v)->v.i; - else - warnf("+: expected integer, recieved type [%d]", car(v)->t); - return mk_int(i); -} - -static Hash -init_env(void) +Hash +tisp_init_env(size_t cap) { - Hash h = hash_new(64); + Hash h = hash_new(cap); hash_add(h, "t", &t); hash_add(h, "car", mk_prim(prim_car)); hash_add(h, "cdr", mk_prim(prim_cdr)); @@ -675,7 +520,6 @@ init_env(void) hash_add(h, "cond", mk_prim(prim_cond)); hash_add(h, "lambda", mk_prim(prim_lambda)); hash_add(h, "define", mk_prim(prim_define)); - hash_add(h, "+", mk_prim(prim_add)); return h; } @@ -689,55 +533,3 @@ val_free(Val v) if (v->t != NIL) free(v); } - -static void -usage(const int eval) -{ - die(eval, "usage: %s [-hv] [FILENAME]", argv0); -} - -int -main(int argc, char *argv[]) -{ - ARGBEGIN { - case 'h': - usage(0); - case 'v': - printf("%s v%s (c) 2017-2018 Ed van Bruggen\n", argv0, VERSION); - return 0; - default: - usage(1); - } ARGEND; - - size_t nread; - char buf[BUF_SIZE]; - struct Str str = { NULL }; - FILE *fp; - Val v; - Hash env = init_env(); - - nil.t = NIL; - t.t = SYMBOL; - t.v.s = estrdup("t"); - - if (argc > 0) { - if (!(fp = fopen(*argv, "r"))) - die(1, "%s: %s:", argv[0], *argv); - while ((nread = fread(buf, 1, sizeof(buf), fp)) > 0) ; - str.d = estrdup(buf); - } - - while ((v = tisp_read(&str))) { - if (!(v = tisp_eval(env, v))) - continue; - - tisp_print(v); - putchar('\n'); - - if (!str.d) continue; - skip_spaces(&str); - if (!*str.d) break; - } - - return 0; -} diff --git a/tisp.h b/tisp.h @@ -0,0 +1,107 @@ +/* See LICENSE file for copyright and license details. */ + +#define warnf(M, ...) do { \ + fprintf(stderr, "tisp:%d: error: " M "\n", \ + __LINE__, ##__VA_ARGS__); \ + return NULL; \ +} while(0) + +#define warn(M) do { \ + fprintf(stderr, "tisp:%d: error: " M "\n", \ + __LINE__); \ + return NULL; \ +} while(0) + +#define car(P) ((P)->v.p.car) +#define cdr(P) ((P)->v.p.cdr) +#define nilp(P) ((P)->t == NIL) + +struct Val; +typedef struct Val *Val; + +/* improved interface for a pointer to a string */ +typedef struct Str { + char *d; +} *Str; + +typedef enum { + ERROR_OK, + ERROR_SYNTAX +} Error; + +/* fraction */ +typedef struct { + int num, den; +} Ratio; + +typedef struct Entry *Entry; + +typedef struct Hash { + int size, cap; + struct Entry { + char *key; + Val val; + } *items; + struct Hash *next; +} *Hash; + +/* basic function written in C, not lisp */ +typedef Val (*Prim)(Hash, Val); + +/* function written directly in lisp instead of C */ +typedef struct { + Val args; + Val body; + Hash env; +} Func; + +typedef struct { + Val car, cdr; +} Pair; + +typedef enum { + NIL, + INTEGER, + RATIONAL, + STRING, + SYMBOL, + PRIMITIVE, + FUNCTION, + PAIR, +} Type; + +struct Val { + Type t; + union { + int i; + Ratio r; + char *s; + Prim pr; + Func f; + Pair p; + } v; +}; + +struct Val nil; +struct Val t; + +void skip_spaces(Str str); + +Val hash_add(Hash ht, char *key, Val val); + +Val mk_int(int i); +Val mk_str(char *s); +Val mk_prim(Prim prim); +Val mk_rat(int num, int den); +Val mk_sym(char *s); +Val mk_func(Val args, Val body, Hash env); +Val mk_pair(Val a, Val b); +Val mk_list(int n, Val *a); + +Val eval_list(Hash env, Val v); + +Val tisp_read(Str str); +void tisp_print(Val v); +Val tisp_eval(Hash env, Val v); + +Hash tisp_init_env(size_t cap);