diff options
| author | Oskari Timperi <oskari.timperi@iki.fi> | 2014-05-17 12:37:09 +0300 |
|---|---|---|
| committer | Oskari Timperi <oskari.timperi@iki.fi> | 2014-05-17 12:37:09 +0300 |
| commit | 2836fa60045bcdd8c1c8c5ed775d711cc7f385a9 (patch) | |
| tree | 7892216d6ae7f0c51bd6c147dadc0bb88f06070c | |
| parent | 6814f6b99562620e70538787b6f8d66c80f7b990 (diff) | |
| download | lispish-2836fa60045bcdd8c1c8c5ed775d711cc7f385a9.tar.gz lispish-2836fa60045bcdd8c1c8c5ed775d711cc7f385a9.zip | |
refactor code to use LIST from sys/queue.h
It's now easier and more natural to work with the code. :-P
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | atom.c | 71 | ||||
| -rw-r--r-- | atom.h | 35 | ||||
| -rw-r--r-- | env.c | 105 | ||||
| -rw-r--r-- | env.h | 19 | ||||
| -rw-r--r-- | eval.c | 353 | ||||
| -rw-r--r-- | eval.h | 10 | ||||
| -rw-r--r-- | list.c | 99 | ||||
| -rw-r--r-- | list.h | 19 | ||||
| -rw-r--r-- | parse.c | 232 | ||||
| -rw-r--r-- | parse.h | 6 | ||||
| -rw-r--r-- | repl.c | 9 |
12 files changed, 439 insertions, 521 deletions
@@ -1,4 +1,4 @@ -SOURCES = parse.c list.c atom.c eval.c tokens.c env.c +SOURCES = parse.c atom.c eval.c tokens.c env.c OBJECTS = $(SOURCES:.c=.o) TEST_OBJECTS = $(foreach obj,$(OBJECTS),test_$(obj)) @@ -1,7 +1,7 @@ #include "atom.h" -#include "list.h" #include <stdlib.h> +#include <stdio.h> #include <string.h> struct atom true_atom = { ATOM_TRUE }; @@ -44,6 +44,13 @@ struct atom *atom_new_list(struct list *list) return atom; } +struct atom *atom_new_list_empty() +{ + struct list *list = calloc(1, sizeof(*list)); + LIST_INIT(list); + return atom_new_list(list); +} + struct atom *atom_clone(struct atom *atom) { switch (atom->type) @@ -64,26 +71,68 @@ struct atom *atom_clone(struct atom *atom) case ATOM_LIST: { - struct list *list = atom->list; - struct list *clone = NULL, *last = NULL; + struct atom *elem, *last; - while (list) + struct list *list_clone = calloc(1, sizeof(*list_clone)); + LIST_INIT(list_clone); + + LIST_FOREACH(elem, atom->list, entries) { - struct atom *a = LIST_GET_ATOM(list); - struct atom *b = atom_clone(a); - last = list_append(last, b); - list = list->next; - if (!clone) - clone = last; + struct atom *a_clone = atom_clone(elem); + + if (LIST_EMPTY(list_clone)) + LIST_INSERT_HEAD(list_clone, a_clone, entries); + else + LIST_INSERT_AFTER(last, a_clone, entries); + + last = a_clone; } - return atom_new_list(clone); + return atom_new_list(list_clone); } } return NULL; } +void print_atom(struct atom *atom, int level) +{ + switch (ATOM_TYPE(atom)) + { + case ATOM_TRUE: printf("#t"); break; + case ATOM_FALSE: printf("#f"); break; + case ATOM_NIL: printf("nil"); break; + + case ATOM_SYMBOL: + printf("%.*s", atom->str.len, atom->str.str); + break; + + case ATOM_STR: + printf("\"%.*s\"", atom->str.len, atom->str.str); + break; + + case ATOM_INT: + printf("%ld", atom->l); + break; + + case ATOM_LIST: + { + printf("("); + struct atom *elem; + LIST_FOREACH(elem, atom->list, entries) + { + print_atom(elem, level+1); + if (LIST_NEXT(elem, entries)) + printf(" "); + } + printf(")"); + } + } + + if (level == 0) + printf("\n"); +} + #ifdef BUILD_TEST #include "test_util.h" @@ -1,24 +1,23 @@ #ifndef ATOM_H #define ATOM_H -#define LIST_GET_ATOM(LIST) ((struct atom *) (LIST)->data) +#include <sys/queue.h> -#define ATOM_TYPE(LIST) ((LIST_GET_ATOM(LIST))->type) +#define ATOM_TYPE(ATOM) ((ATOM)->type) -#define IS_INT(LIST) ((ATOM_TYPE(LIST)) == ATOM_INT) -#define IS_STR(LIST) ((ATOM_TYPE(LIST)) == ATOM_STR) -#define IS_SYM(LIST) ((ATOM_TYPE(LIST)) == ATOM_SYMBOL) -#define IS_LIST(LIST) ((ATOM_TYPE(LIST)) == ATOM_LIST) -#define IS_ATOM(LIST) (!(IS_LIST(LIST))) +#define IS_INT(ATOM) ((ATOM_TYPE(ATOM)) == ATOM_INT) +#define IS_STR(ATOM) ((ATOM_TYPE(ATOM)) == ATOM_STR) +#define IS_SYM(ATOM) ((ATOM_TYPE(ATOM)) == ATOM_SYMBOL) +#define IS_LIST(ATOM) ((ATOM_TYPE(ATOM)) == ATOM_LIST) +#define IS_ATOM(ATOM) (!(IS_LIST(ATOM))) -#define IS_TRUE(LIST) (LIST_GET_ATOM(LIST) == &true_atom) -#define IS_FALSE(LIST) (LIST_GET_ATOM(LIST) == &false_atom) +#define IS_TRUE(ATOM) (ATOM_TYPE(ATOM) == ATOM_TRUE) +#define IS_FALSE(ATOM) (ATOM_TYPE(ATOM) == ATOM_FALSE) -#define IS_NIL(LIST) (LIST_GET_ATOM(LIST) == &nil_atom) +#define IS_NIL(ATOM) (ATOM_TYPE(ATOM) == ATOM_NIL) -#define CAR(LIST) LIST - -#define CDR(LIST) ((LIST) != NULL ? (LIST)->next : NULL) +#define CAR(LIST) (LIST_FIRST(LIST)) +#define CDR(LIST) ((LIST) != NULL ? LIST_NEXT((LIST), entries) : NULL) #define CDDR(LIST) CDR(CDR(LIST)) enum @@ -32,11 +31,14 @@ enum ATOM_FALSE }; -struct list; +struct atom; + +LIST_HEAD(list, atom); struct atom { char type; + union { long l; @@ -47,6 +49,8 @@ struct atom } str; struct list *list; }; + + LIST_ENTRY(atom) entries; }; struct atom *atom_new(char type); @@ -54,8 +58,11 @@ struct atom *atom_new_int(long l); struct atom *atom_new_str(const char *str, int len); struct atom *atom_new_sym(const char *sym, int len); struct atom *atom_new_list(struct list *list); +struct atom *atom_new_list_empty(); struct atom *atom_clone(); +void print_atom(struct atom *atom, int level); + extern struct atom true_atom; extern struct atom false_atom; extern struct atom nil_atom; @@ -1,5 +1,4 @@ #include "env.h" -#include "list.h" #include "atom.h" #include <stdlib.h> @@ -10,75 +9,72 @@ struct kv { const char *symbol; struct atom *value; + LIST_ENTRY(kv) entries; }; -struct list *env_new() +struct env *env_new() { - return list_new(NULL); + struct env *env = calloc(1, sizeof(*env)); + LIST_INIT(env); + return env; } -struct atom *env_lookup(struct list *env, const char *symbol) +struct atom *env_lookup(struct env *env, const char *symbol) { - env = env->next; - while (env) + struct kv *elem; + LIST_FOREACH(elem, env, entries) { - struct kv *kv = (struct kv *) env->data; - - if (strcmp(kv->symbol, symbol) == 0) - { - return kv->value; - } - - env = env->next; + if (strcmp(elem->symbol, symbol) == 0) + return elem->value; } + return NULL; } -int env_set_(struct list *env, const char *symbol, +int env_set_(struct env *env, const char *symbol, struct atom *atom, int force) { - struct list *first = env; + struct kv *elem = NULL; struct kv *kv; - env = env->next; - while (env) + LIST_FOREACH(elem, env, entries) { - kv = (struct kv *) env->data; - - if (strcmp(symbol, kv->symbol) == 0) + if (strcmp(symbol, elem->symbol) == 0) { if (!force) return 0; - kv->value = atom; + elem->value = atom; return 1; } - if (!env->next) + if (!LIST_NEXT(elem, entries)) break; - - env = env->next; } - if (!env) - env = first; - kv = calloc(1, sizeof(*kv)); kv->symbol = strdup(symbol); kv->value = atom; - list_append(env, kv); + if (LIST_EMPTY(env)) + { + LIST_INSERT_HEAD(env, kv, entries); + } + else + { + LIST_INSERT_AFTER(elem, kv, entries); + } return 1; } -struct list *env_extend(struct list *env, int count, ...) +struct env *env_extend(struct env *env, int count, ...) { va_list ap; int i; - struct list *result = env_clone(env); + struct env *result = env_clone(env); va_start(ap, count); @@ -94,40 +90,35 @@ struct list *env_extend(struct list *env, int count, ...) return result; } -// struct list *env_extend_env(struct list *enva, struct list *envb) -// { - -// } - -int env_set(struct list *env, const char *symbol, +int env_set(struct env *env, const char *symbol, struct atom *value) { return env_set_(env, symbol, value, 0); } -void env_free(struct list *env) +void env_free(struct env *env) { } -struct list *env_clone(struct list *env) +struct env *env_clone(struct env *env) { - struct list *clone = env_new(); - struct list *last = clone; + struct env *clone = env_new(); + struct kv *elem, *last; - env = env->next; - while (env) + LIST_FOREACH(elem, env, entries) { - struct kv *kv = (struct kv *) env->data; - - struct kv *kv_clone = malloc(sizeof(*kv_clone)); + struct kv *kv_clone = calloc(1, sizeof(*kv_clone)); - kv_clone->symbol = strdup(kv->symbol); - kv_clone->value = atom_clone(kv->value); + kv_clone->symbol = strdup(elem->symbol); + kv_clone->value = atom_clone(elem->value); - last = list_append(last, kv_clone); + if (LIST_EMPTY(clone)) + LIST_INSERT_HEAD(clone, kv_clone, entries); + else + LIST_INSERT_AFTER(last, kv_clone, entries); - env = env->next; + last = kv_clone; } return clone; @@ -139,14 +130,14 @@ struct list *env_clone(struct list *env) TEST(simple_lookup_on_empty_env) { - struct list *env = env_new(); + struct env *env = env_new(); ASSERT_TRUE(env != NULL); ASSERT_EQ(NULL, env_lookup(env, "foobar")); } TEST(simple_set_and_lookup) { - struct list *env = env_new(); + struct env *env = env_new(); ASSERT_TRUE(env != NULL); struct atom atom; @@ -157,13 +148,13 @@ TEST(simple_set_and_lookup) TEST(lookup_from_inner_env) { - struct list *outer = env_new(); + struct env *outer = env_new(); struct atom *atom1 = atom_new_int(42); ASSERT_EQ(1, env_set(outer, "foo", atom1)); struct atom *atom2 = atom_new_int(6); - struct list *inner = env_extend(outer, 1, "bar", atom2); + struct env *inner = env_extend(outer, 1, "bar", atom2); ASSERT_TRUE(inner != NULL); ASSERT_TRUE(inner != outer); @@ -181,7 +172,7 @@ TEST(lookup_from_inner_env) TEST(lookup_deeply_nested) { - struct list *env = env_new(); + struct env *env = env_new(); env_set(env, "a", atom_new_int(1)); env = env_extend(env, 1, "b", atom_new_int(2)); env = env_extend(env, 1, "c", atom_new_int(3)); @@ -201,7 +192,7 @@ TEST(lookup_deeply_nested) TEST(extend) { - struct list *env = env_new(); + struct env *env = env_new(); env_set(env, "foo", atom_new_int(1)); @@ -215,7 +206,7 @@ TEST(extend) TEST(redefine_illegal) { - struct list *env = env_new(); + struct env *env = env_new(); ASSERT_EQ(1, env_set(env, "foo", atom_new_int(1))); ASSERT_EQ(0, env_set(env, "foo", atom_new_int(2))); } @@ -1,16 +1,19 @@ #ifndef ENV_H #define ENV_H -struct list; +#include <sys/queue.h> + +struct kv; +LIST_HEAD(env, kv); + struct atom; -struct list *env_new(); -struct atom *env_lookup(struct list *env, const char *symbol); -struct list *env_extend(struct list *env, int count, ...); //const char *symbol, struct atom *value -// struct list *env_extend_env(struct list *enva, struct list *envb); -int env_set(struct list *env, const char *symbol, +struct env *env_new(); +struct atom *env_lookup(struct env *env, const char *symbol); +struct env *env_extend(struct env *env, int count, ...); +int env_set(struct env *env, const char *symbol, struct atom *value); -void env_free(struct list *env); -struct list *env_clone(struct list *env); +void env_free(struct env *env); +struct env *env_clone(struct env *env); #endif @@ -1,5 +1,4 @@ #include "eval.h" -#include "list.h" #include "atom.h" #include "parse.h" #include "env.h" @@ -7,7 +6,7 @@ #include <stdio.h> #include <string.h> -static int atom_cmp(struct list *a, struct list *b) +static int atom_cmp(struct atom *a, struct atom *b) { if (ATOM_TYPE(a) != ATOM_TYPE(b)) return 0; @@ -35,20 +34,20 @@ static int atom_cmp(struct list *a, struct list *b) switch (ATOM_TYPE(a)) { case ATOM_INT: - if (LIST_GET_ATOM(a)->l != LIST_GET_ATOM(b)->l) + if (a->l != b->l) result = 0; break; case ATOM_STR: case ATOM_SYMBOL: - if (strcmp(LIST_GET_ATOM(a)->str.str, LIST_GET_ATOM(b)->str.str) != 0) + if (strcmp(a->str.str, b->str.str) != 0) result = 0; break; case ATOM_LIST: { - struct list *ai = LIST_GET_ATOM(a)->list; - struct list *bi = LIST_GET_ATOM(b)->list; + struct atom *ai = LIST_FIRST(a->list); + struct atom *bi = LIST_FIRST(b->list); while (ai && bi) { @@ -58,8 +57,8 @@ static int atom_cmp(struct list *a, struct list *b) break; } - ai = ai->next; - bi = bi->next; + ai = LIST_NEXT(ai, entries); + bi = LIST_NEXT(bi, entries); } if (ai != NULL || bi != NULL) @@ -72,225 +71,198 @@ static int atom_cmp(struct list *a, struct list *b) return result; } -struct list *eval(struct list *list) -{ - struct list env; - memset(&env, 0, sizeof(env)); - return eval_env(list, &env); -} - -struct list *eval_str(const char *str) -{ - struct list env; - memset(&env, 0, sizeof(env)); - return eval_str_env(str, &env); -} - -struct list *eval_env(struct list *expr, struct list *env) +struct atom *eval(struct atom *expr, struct env *env) { if (IS_SYM(expr)) { - struct atom *atom = env_lookup(env, LIST_GET_ATOM(expr)->str.str); + struct atom *atom = env_lookup(env, expr->str.str); if (atom) { - return list_append(NULL, atom); + return atom; } else { printf("error: undefined variable: %s\n", - LIST_GET_ATOM(expr)->str.str); - return list_append(NULL, &nil_atom); + expr->str.str); + return &nil_atom; } } if (!IS_LIST(expr)) return expr; - struct list *l = LIST_GET_ATOM(expr)->list; + struct list *list = expr->list; + + struct atom *op = LIST_FIRST(list); - if (IS_SYM(l)) + if (strcmp(op->str.str, "quote") == 0) { - const char *sym = LIST_GET_ATOM(l)->str.str; + return atom_clone(LIST_NEXT(op, entries)); + } + else if (strcmp(op->str.str, "atom") == 0) + { + struct atom *a = LIST_NEXT(op, entries); - if (strcmp(sym, "quote") == 0) - { - return l->next; - } - else if (strcmp(sym, "atom") == 0) + if (!a) + return &nil_atom; + + if (IS_LIST(a)) + return &false_atom; + else + return &true_atom; + } + else if (strcmp(op->str.str, "eq") == 0) + { + struct atom *a = CDR(op); + struct atom *b = CDR(a); + + if (!a || !b) { - if (IS_LIST(l->next)) - { - return list_append(NULL, &false_atom); - } - else - { - return list_append(NULL, &true_atom); - } + printf("error: eq takes 2 arguments\n"); + return &nil_atom; } - else if (strcmp(sym, "eq") == 0) - { - struct list *a = CDR(l); - struct list *b = CDR(a); - if (!a || !b) - { - printf("error: eq takes 2 arguments\n"); - return list_append(NULL, &nil_atom); - } + a = eval(a, env); + b = eval(b, env); - a = eval_env(a, env); - b = eval_env(b, env); + if (atom_cmp(a, b)) + return &true_atom; - if (atom_cmp(a, b)) - return list_append(NULL, &true_atom); + return &false_atom; + } + else if (strcmp(op->str.str, "+") == 0 || + strcmp(op->str.str, "-") == 0 || + strcmp(op->str.str, "/") == 0 || + strcmp(op->str.str, "*") == 0) + { + struct atom *a = CDR(op); + struct atom *b = CDR(a); - return list_append(NULL, &false_atom); - } - else if (strncmp(sym, "+", 1) == 0 || - strncmp(sym, "-", 1) == 0 || - strncmp(sym, "/", 1) == 0 || - strncmp(sym, "*", 1) == 0) + if (!a || !b) { - struct list *oper = CAR(l); - struct list *a = CDR(oper); - struct list *b = CDDR(oper); - - if (!a || !b) - return list_append(NULL, &nil_atom); - - a = eval_env(a, env); - b = eval_env(b, env); + printf("error: %s takes 2 arguments\n", op->str.str); + return &nil_atom; + } - if (!(ATOM_TYPE(a) == ATOM_TYPE(b) && ATOM_TYPE(a) == ATOM_INT)) - return list_append(NULL, &nil_atom); + a = eval(a, env); + b = eval(b, env); - long numa = LIST_GET_ATOM(a)->l; - long numb = LIST_GET_ATOM(b)->l; - long numr; + if (!(ATOM_TYPE(a) == ATOM_TYPE(b) && ATOM_TYPE(a) == ATOM_INT)) + return &nil_atom; - switch (*sym) - { - case '+': - numr = numa + numb; - break; - case '-': - numr = numa - numb; - break; - case '/': - numr = numa / numb; - break; - case '*': - numr = numa * numb; - break; - } + switch (*op->str.str) + { + case '+': return atom_new_int(a->l + b->l); + case '-': return atom_new_int(a->l - b->l); + case '/': return atom_new_int(a->l / b->l); + case '*': return atom_new_int(a->l * b->l); + } - struct list *result = list_append(NULL, atom_new_int(numr)); + return &nil_atom; + } + else if (strcmp(op->str.str, ">") == 0) + { + struct atom *a = CDR(op); + struct atom *b = CDR(a); - return result; - } - else if (strncmp(sym, ">", 1) == 0) + if (!a || !b) { - struct list *oper = CAR(l); - struct list *a = CDR(oper); - struct list *b = CDDR(oper); + printf("error: > takes 2 arguments\n"); + return &nil_atom; + } - if (!a || !b) - return list_append(NULL, &nil_atom); + a = eval(a, env); + b = eval(b, env); - a = eval_env(a, env); - b = eval_env(b, env); + if (!(ATOM_TYPE(a) == ATOM_TYPE(b) && ATOM_TYPE(a) == ATOM_INT)) + return &nil_atom; - if (!(ATOM_TYPE(a) == ATOM_TYPE(b) && ATOM_TYPE(a) == ATOM_INT)) - return list_append(NULL, &nil_atom); + if (a->l > b->l) + return &true_atom; - long numa = LIST_GET_ATOM(a)->l; - long numb = LIST_GET_ATOM(b)->l; + return &false_atom; + } + else if (strcmp(op->str.str, "if") == 0) + { + struct atom *predicate = CDR(op); + struct atom *true_case = CDR(predicate); + struct atom *false_case = CDR(true_case); - if (numa > numb) - return list_append(NULL, &true_atom); - else - return list_append(NULL, &false_atom); - } - else if (strcmp(sym, "if") == 0) + if (!predicate || !true_case || !false_case) { - struct list *predicate = CDR(l); - struct list *true_case = CDR(predicate); - struct list *false_case = CDR(true_case); - - if (!predicate || !true_case || !false_case) - return list_append(NULL, &nil_atom); - - predicate = eval_env(predicate, env); - - if (IS_TRUE(predicate)) - return eval_env(true_case, env); - else - return eval_env(false_case, env); + printf("error: if takes 3 arguments\n"); + return &nil_atom; } - else if (strcmp(sym, "mod") == 0) - { - struct list *a = CDR(l); - struct list *b = CDR(a); - if (!a || !b) - { - printf("error: mod takes two arguments\n"); - return list_append(NULL, &nil_atom); - } - - a = eval_env(a, env); - b = eval_env(b, env); + predicate = eval(predicate, env); - if (!IS_INT(a) || !IS_INT(b)) - { - printf("error: mod arguments must be integers\n"); - return list_append(NULL, &nil_atom); - } + if (IS_TRUE(predicate)) + return eval(true_case, env); - long result = LIST_GET_ATOM(a)->l % LIST_GET_ATOM(b)->l; + return eval(false_case, env); + } + else if (strcmp(op->str.str, "mod") == 0) + { + struct atom *a = CDR(op); + struct atom *b = CDR(a); - return list_append(NULL, atom_new_int(result)); + if (!a || !b) + { + printf("error: mod takes two arguments\n"); + return &nil_atom; } - else if (strcmp(sym, "define") == 0) + + a = eval(a, env); + b = eval(b, env); + + if (!IS_INT(a) || !IS_INT(b)) { - struct list *expr_name = CDR(l); - struct list *expr_value = CDR(expr_name); + printf("error: mod arguments must be integers\n"); + return &nil_atom; + } - if (!expr_name || !expr_value) - { - printf("error: define takes two arguments\n"); - return list_append(NULL, &nil_atom); - } + return atom_new_int(a->l % b->l); + } + else if (strcmp(op->str.str, "define") == 0) + { + struct atom *expr_name = CDR(op); + struct atom *expr_value = CDR(expr_name); - if (!IS_SYM(expr_name)) - { - printf("error: define: first arg must be symbol\n"); - return list_append(NULL, &nil_atom); - } + if (!expr_name || !expr_value) + { + printf("error: define takes two arguments\n"); + return &nil_atom; + } - expr_value = eval_env(expr_value, env); + if (!IS_SYM(expr_name)) + { + printf("error: define: first arg must be symbol\n"); + return &nil_atom; + } - env_set(env, LIST_GET_ATOM(expr_name)->str.str, - LIST_GET_ATOM(expr_value)); + expr_value = eval(expr_value, env); - return list_append(NULL, expr_value); + if (!env_set(env, expr_name->str.str, expr_value)) + { + printf("error: cannot redefine %s\n", expr_name->str.str); + return &nil_atom; } + + return expr_value; } - else if (IS_LIST(l)) - { - return eval(l); - } - return list_append(NULL, &nil_atom); + printf("error: unknown function %s\n", op->str.str); + + return &nil_atom; } -struct list *eval_str_env(const char *expr, struct list *env) +struct atom *eval_str(const char *expr, struct env *env) { - struct list *result; + struct atom *result; int pos = 0; - result = eval_env(parse(expr, &pos), env); + result = eval(parse(expr, &pos), env); return result; } @@ -301,46 +273,48 @@ struct list *eval_str_env(const char *expr, struct list *env) TEST(nested_expression) { - struct list *result = eval_str("(eq #f (> (- (+ 1 3) (* 2 (mod 7 4))) 4))"); + struct env *env = env_new(); + struct atom *result = eval_str("(eq #f (> (- (+ 1 3) (* 2 (mod 7 4))) 4))", env); ASSERT_TRUE(result != NULL); ASSERT_TRUE(IS_TRUE(result)); } TEST(basic_if) { - struct list *result = eval_str("(if #t 42 1000)"); + struct env *env = env_new(); + struct atom *result = eval_str("(if #t 42 1000)", env); + print_atom(result, 0); ASSERT_TRUE(result != NULL); ASSERT_EQ(ATOM_INT, ATOM_TYPE(result)); - ASSERT_EQ(42, LIST_GET_ATOM(result)->l); + ASSERT_EQ(42, result->l); } TEST(if_with_sub_expressions) { - struct list *result = eval_str("(if (> 1 2) (- 1000 1) (+ 40 (- 3 1)))"); + struct env *env = env_new(); + struct atom *result = eval_str("(if (> 1 2) (- 1000 1) (+ 40 (- 3 1)))", env); ASSERT_TRUE(result != NULL); ASSERT_EQ(ATOM_INT, ATOM_TYPE(result)); - ASSERT_EQ(42, LIST_GET_ATOM(result)->l); + ASSERT_EQ(42, result->l); } -/* EVALUTE WITH ENVIRONMENT TESTS */ - TEST(evaluate_symbol) { - struct list *env = env_new(); + struct env *env = env_new(); env_set(env, "foo", atom_new_int(42)); - struct list *result = eval_str_env("foo", env); + struct atom *result = eval_str("foo", env); ASSERT_TRUE(result != NULL); ASSERT_EQ(ATOM_INT, ATOM_TYPE(result)); - ASSERT_EQ(42, LIST_GET_ATOM(result)->l); + ASSERT_EQ(42, result->l); } TEST(evaluate_missing_symbol) { - struct list *env = env_new(); + struct env *env = env_new(); - struct list *result = eval_str_env("foo", env); + struct atom *result = eval_str("foo", env); ASSERT_TRUE(result != NULL); ASSERT_EQ(ATOM_NIL, ATOM_TYPE(result)); @@ -348,9 +322,9 @@ TEST(evaluate_missing_symbol) TEST(define) { - struct list *env = env_new(); + struct env *env = env_new(); - struct list *result = eval_str_env("(define x 100)", env); + struct atom *result = eval_str("(define x 100)", env); ASSERT_TRUE(result != NULL); @@ -363,9 +337,9 @@ TEST(define) TEST(define_missing_value) { - struct list *env = env_new(); + struct env *env = env_new(); - struct list *result = eval_str_env("(define x)", env); + struct atom *result = eval_str("(define x)", env); ASSERT_TRUE(result != NULL); ASSERT_EQ(ATOM_NIL, ATOM_TYPE(result)); @@ -375,18 +349,18 @@ TEST(define_missing_value) TEST(define_nonsymbol_as_name) { - struct list *env = env_new(); + struct env *env = env_new(); - struct list *result = eval_str_env("(define 1 100)", env); + struct atom *result = eval_str("(define 1 100)", env); ASSERT_TRUE(result != NULL); ASSERT_EQ(ATOM_NIL, ATOM_TYPE(result)); } TEST(define_with_val_as_expr) { - struct list *env = env_new(); + struct env *env = env_new(); - struct list *result = eval_str_env("(define x (* 3 3))", env); + struct atom *result = eval_str("(define x (* 3 3))", env); ASSERT_TRUE(result != NULL); @@ -397,5 +371,4 @@ TEST(define_with_val_as_expr) ASSERT_EQ(9, atom->l); } - #endif /* BUILD_TEST */ @@ -1,12 +1,10 @@ #ifndef EVAL_H #define EVAL_H -struct list; +struct atom; +struct env; -struct list *eval(struct list *expr); -struct list *eval_str(const char *expr); - -struct list *eval_env(struct list *expr, struct list *env); -struct list *eval_str_env(const char *expr, struct list *env); +struct atom *eval(struct atom *expr, struct env *env); +struct atom *eval_str(const char *expr, struct env *env); #endif @@ -1,99 +0,0 @@ -#include "list.h" - -#include <stdlib.h> -#include <string.h> - -void list_init(struct list *list) -{ - memset(list, 0, sizeof(*list)); -} - -struct list *list_new(void *data) -{ - struct list *list = calloc(1, sizeof(*list)); - list->data = data; - return list; -} - -struct list *list_append(struct list *list, void *data) -{ - if (list == NULL) - { - return list_new(data); - } - - struct list *last = list_get_last(list); - list = list_new(data); - last->next = list; - - return list; -} - -struct list *list_get_last(struct list *list) -{ - while (list && list->next) - list = list->next; - return list; -} - -struct list *list_pop_front(struct list **list) -{ - struct list *popped = *list; - - if (!popped) - return NULL; - - *list = popped->next; - popped->next = NULL; - - return popped; -} - -void list_free(struct list *list, list_free_cb free_cb, void *userdata) -{ - struct list *popped; - - while ((popped = list_pop_front(&list)) != NULL) - { - if (free_cb) - free_cb(popped->data, userdata); - free(popped); - } -} - -#ifdef BUILD_TEST - -#include "test_util.h" - -TEST(list_init) -{ - -} - -TEST(list_new) -{ - -} - -TEST(list_append) -{ - -} - -TEST(list_get_last) -{ - -} - -TEST(list_pop_front) -{ - -} - -TEST(list_free) -{ - -} - - -#endif @@ -1,19 +0,0 @@ -#ifndef LIST_H -#define LIST_H - -struct list -{ - void *data; - struct list *next; -}; - -typedef void (*list_free_cb)(void *data, void *userdata); - -void list_init(struct list *list); -struct list *list_new(void *data); -struct list *list_append(struct list *list, void *data); -struct list *list_get_last(struct list *list); -struct list *list_pop_front(struct list **list); -void list_free(struct list *list, list_free_cb free_cb, void *userdata); - -#endif @@ -1,6 +1,5 @@ #include "parse.h" #include "tokens.h" -#include "list.h" #include "atom.h" #include <stdio.h> @@ -8,134 +7,136 @@ #include <string.h> #include <ctype.h> -void print_list(struct list *list, int level) +struct atom *parse_token(struct token *token) { - while (list) + switch (token->type) { - struct atom *atom = LIST_GET_ATOM(list); + case TOKEN_INT: + return atom_new_int(strtol(token->s, NULL, 10)); - if (IS_TRUE(list)) - { - printf("#t"); - } - else if (IS_FALSE(list)) - { - printf("#f"); - } - else if (IS_NIL(list)) - { - printf("nil"); - } - else - { - switch (atom->type) - { - case ATOM_SYMBOL: - printf("%.*s", atom->str.len, atom->str.str); - break; - - case ATOM_LIST: - printf("("); - print_list(atom->list, level+1); - printf(")"); - break; - - case ATOM_STR: - printf("\"%.*s\"", atom->str.len, atom->str.str); - break; - - case ATOM_INT: - printf("%ld", atom->l); - } - } + case TOKEN_STR: + return atom_new_str(token->s, token->len); - if (list->next) - printf(" "); + case TOKEN_SYMBOL: + if (strncmp(token->s, "#t", 2) == 0) + return &true_atom; + else if (strncmp(token->s, "#f", 2) == 0) + return &false_atom; + else + return atom_new_sym(token->s, token->len); - list = list->next; + default: + return NULL; } - if (level == 0) - printf("\n"); + return NULL; +} + +void parse_quote(const char *src, int *pos, struct atom **result) +{ + struct atom *value = parse(src, pos); + struct atom *q = atom_new_sym("quote", 5); + struct atom *form = atom_new_list_empty(); + + LIST_INSERT_HEAD(form->list, q, entries); + LIST_INSERT_AFTER(q, value, entries); + + *result = form; } -int parse_next(const char *src, int *pos, struct atom **result) +int parse_list(const char *src, int *pos, struct atom **result) { struct token token; int rc; + struct list *list; + struct atom *last = NULL; - if ((rc = get_next_token(src, pos, &token)) > 0) + list = calloc(1, sizeof(*list)); + LIST_INIT(list); + + while ((rc = get_next_token(src, pos, &token))) { - switch (token.type) - { - case TOKEN_INT: - *result = atom_new_int(strtol(token.s, NULL, 10)); - break; + struct atom *atom; - case TOKEN_STR: - *result = atom_new_str(token.s, token.len); - break; + if (rc < 0) + break; - case TOKEN_SYMBOL: - if (strncmp(token.s, "#t", 2) == 0) - *result = &true_atom; - else if (strncmp(token.s, "#f", 2) == 0) - *result = &false_atom; - else - *result = atom_new_sym(token.s, token.len); - break; + switch (token.type) + { + case TOKEN_LPAREN: + if (parse_list(src, pos, &atom) < 0) + return -1; + break; - case TOKEN_LPAREN: - { - struct list *l = parse(src, pos); - *result = atom_new_list(l); - break; - } + case TOKEN_RPAREN: + goto out; + break; - case TOKEN_RPAREN: - return -2; + case TOKEN_QUOTE: + parse_quote(src, pos, &atom); + break; - case TOKEN_QUOTE: - { - struct atom *quoted = NULL; + default: + atom = parse_token(&token); + break; + } - parse_next(src, pos, "ed); + if (!last) + LIST_INSERT_HEAD(list, atom, entries); + else + LIST_INSERT_AFTER(last, atom, entries); - struct list *qlist = list_append(NULL, - atom_new_sym("quote", 5)); + last = atom; + } - list_append(qlist, quoted); +out: - *result = atom_new_list(qlist); + if (rc < 0) + return rc; - break; - } - } + if (LIST_EMPTY(list)) + { + free(list); + *result = &nil_atom; + return 1; } - return rc; + *result = atom_new_list(list); + return 1; } -struct list *parse(const char *src, int *pos) +struct atom *parse(const char *src, int *pos) { + struct token token; + struct atom *atom = NULL; int rc; - struct atom *atom; - struct list root, *last = &root; - list_init(last); + rc = get_next_token(src, pos, &token); - while ((rc = parse_next(src, pos, &atom))) - { - if (rc < 0) - break; + if (rc < 0) + return NULL; - last = list_append(last, atom); + switch (token.type) + { + case TOKEN_LPAREN: + if (parse_list(src, pos, &atom) < 0) + atom = NULL; + break; + + case TOKEN_RPAREN: + printf("syntax error: unexpected ')'\n"); + break; + + case TOKEN_QUOTE: + parse_quote(src, pos, &atom); + break; + + default: + atom = parse_token(&token); + break; } - if (rc < 0 && rc != -2) - return NULL; - - return root.next; + return atom; } #ifdef BUILD_TEST @@ -151,34 +152,51 @@ static const char *test_src_fact = " (* n (fact (- n 1))))))\n" "(fact 5) ;; this should evaluate to 120\n"; -#define ASSERT_SYM(LIST, SYM) \ - ASSERT_TRUE(IS_SYM(LIST)); \ - ASSERT_STREQ(SYM, LIST_GET_ATOM(LIST)->str.str) +#define ASSERT_SYM(ATOM, SYM) \ + ASSERT_TRUE(IS_SYM(ATOM)); \ + ASSERT_STREQ(SYM, (ATOM)->str.str) TEST(test_parse) { int pos = 0; - struct list *list; + struct atom *list; list = parse(test_src_fact, &pos); ASSERT_TRUE(list != NULL); - ASSERT_TRUE(IS_LIST(list)); - list = LIST_GET_ATOM(list)->list; + struct atom *a = CAR(list->list); + ASSERT_TRUE(a != NULL); + ASSERT_SYM(a, "define"); - ASSERT_TRUE(list != NULL); - ASSERT_SYM(list, "define"); + a = CDR(a); - list = list->next; + ASSERT_TRUE(a != NULL); + ASSERT_SYM(a, "fact"); - ASSERT_TRUE(list != NULL); - ASSERT_SYM(list, "fact"); + a = CDR(a); - list = list->next; + ASSERT_TRUE(a != NULL); +} - ASSERT_TRUE(list != NULL); +TEST(parse_quote) +{ + int pos = 0; + + struct atom *result = parse("'foobar", &pos); + ASSERT_TRUE(result != NULL); + ASSERT_EQ(ATOM_LIST, result->type); + + struct atom *op = CAR(result->list); + ASSERT_TRUE(op != NULL); + ASSERT_EQ(ATOM_SYMBOL, op->type); + ASSERT_STREQ("quote", op->str.str); + + struct atom *a = CDR(op); + ASSERT_TRUE(a != NULL); + ASSERT_EQ(ATOM_SYMBOL, a->type); + ASSERT_STREQ("foobar", a->str.str); } #endif @@ -2,11 +2,7 @@ #define PARSER_H struct atom; -struct list; -int parse_next(const char *src, int *pos, struct atom **result); -struct list *parse(const char *src, int *pos); - -void print_list(struct list *list, int level); +struct atom *parse(const char *src, int *pos); #endif @@ -4,14 +4,14 @@ #include "parse.h" #include "eval.h" -#include "list.h" #include "env.h" +#include "atom.h" #include "linenoise.h" int main(int argc, char **argv) { char *line; - struct list *env; + struct env *env; env = env_new(); @@ -23,12 +23,13 @@ int main(int argc, char **argv) if (strcmp(".clean", line) == 0) { + env_free(env); env = env_new(); } else { - struct list *result = eval_str_env(line, env); - print_list(result, 0); + struct atom *result = eval_str(line, env); + print_atom(result, 0); } free(line); |
