aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOskari Timperi <oskari.timperi@iki.fi>2014-05-17 12:37:09 +0300
committerOskari Timperi <oskari.timperi@iki.fi>2014-05-17 12:37:09 +0300
commit2836fa60045bcdd8c1c8c5ed775d711cc7f385a9 (patch)
tree7892216d6ae7f0c51bd6c147dadc0bb88f06070c
parent6814f6b99562620e70538787b6f8d66c80f7b990 (diff)
downloadlispish-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--Makefile2
-rw-r--r--atom.c71
-rw-r--r--atom.h35
-rw-r--r--env.c105
-rw-r--r--env.h19
-rw-r--r--eval.c353
-rw-r--r--eval.h10
-rw-r--r--list.c99
-rw-r--r--list.h19
-rw-r--r--parse.c232
-rw-r--r--parse.h6
-rw-r--r--repl.c9
12 files changed, 439 insertions, 521 deletions
diff --git a/Makefile b/Makefile
index b520b1b..5771671 100644
--- a/Makefile
+++ b/Makefile
@@ -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))
diff --git a/atom.c b/atom.c
index cbcdbe8..2167442 100644
--- a/atom.c
+++ b/atom.c
@@ -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"
diff --git a/atom.h b/atom.h
index 62f567a..251eb3a 100644
--- a/atom.h
+++ b/atom.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;
diff --git a/env.c b/env.c
index 6d873df..4f113a4 100644
--- a/env.c
+++ b/env.c
@@ -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)));
}
diff --git a/env.h b/env.h
index 6fcf8cc..744424d 100644
--- a/env.h
+++ b/env.h
@@ -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
diff --git a/eval.c b/eval.c
index d71ebc6..f48937f 100644
--- a/eval.c
+++ b/eval.c
@@ -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 */
diff --git a/eval.h b/eval.h
index eab225d..25a2f33 100644
--- a/eval.h
+++ b/eval.h
@@ -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
diff --git a/list.c b/list.c
deleted file mode 100644
index 58510ac..0000000
--- a/list.c
+++ /dev/null
@@ -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
diff --git a/list.h b/list.h
deleted file mode 100644
index ea82440..0000000
--- a/list.h
+++ /dev/null
@@ -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
diff --git a/parse.c b/parse.c
index b5101e1..a34063c 100644
--- a/parse.c
+++ b/parse.c
@@ -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, &quoted);
+ 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
diff --git a/parse.h b/parse.h
index 94d6fa8..b05fd93 100644
--- a/parse.h
+++ b/parse.h
@@ -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
diff --git a/repl.c b/repl.c
index 615609f..c9d6861 100644
--- a/repl.c
+++ b/repl.c
@@ -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);