summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Perktold <Felix.Perktold@student.uibk.ac.at>2025-12-21 14:53:26 +0100
committerFelix Perktold <Felix.Perktold@student.uibk.ac.at>2025-12-21 14:53:26 +0100
commit20b1cea7a74a4feb759155eedbb6299bd891a325 (patch)
tree2cf15c07d2284fff2d0d3cbd2fff55ce92345e20
parent4f025b96cfe6bc121ea9b37eb26d8814dd838647 (diff)
add tests
-rw-r--r--.gitignore3
-rw-r--r--Makefile24
-rw-r--r--lisp.c18
-rw-r--r--main.c26
-rw-r--r--test.c109
5 files changed, 156 insertions, 24 deletions
diff --git a/.gitignore b/.gitignore
index 69b1f11..364238c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+*.o
lex.yy.*
lisp.tab.*
-lisp
+main
diff --git a/Makefile b/Makefile
index 4ff9837..095a7ae 100644
--- a/Makefile
+++ b/Makefile
@@ -3,15 +3,19 @@ YACC = bison
CC = gcc -g
CFLAGS = -DYYDEBUG=1
LIBS = -lreadline -lfl -lm
+OBJS = lisp.o lisp.tab.o lex.yy.o
.SUFFIXES:
-lisp: lisp.c lisp.tab.o lex.yy.o
- $(CC) $(CFLAGS) -o $@ lisp.c lisp.tab.o lex.yy.o $(LIBS)
+main: main.c $(OBJS)
+ $(CC) $(CFLAGS) -o $@ main.c $(OBJS) $(LIBS)
+
+lisp.o: lisp.c lisp.h lex.yy.o
+ $(CC) $(CFLAGS) -c lisp.c
lisp.tab.c lisp.tab.h: lisp.y
$(YACC) -d lisp.y
-lex.yy.c: lisp.l lisp.tab.h
+lex.yy.c lex.yy.h: lisp.l lisp.tab.h
$(LEX) --header-file=lex.yy.h lisp.l
lisp.tab.o: lisp.tab.c
@@ -21,6 +25,16 @@ lex.yy.o: lex.yy.c
$(CC) $(CFLAGS) -c lex.yy.c
clean:
- rm -f *.o lisp lisp.tab.c lisp.tab.h lex.yy.c lex.yy.h
+ rm -f *.o main test lisp.tab.c lisp.tab.h lex.yy.c lex.yy.h
+
+test: test.o $(OBJS)
+ $(CC) $(CFLAGS) -o $@ test.o $(OBJS) $(LIBS)
+ ./test
+
+test.o: test.c lisp.h lex.yy.o
+ $(CC) $(CFLAGS) -c test.c
+
+run: main
+ ./main
-.PHONY: clean
+.PHONY: clean test run
diff --git a/lisp.c b/lisp.c
index b195352..8b29423 100644
--- a/lisp.c
+++ b/lisp.c
@@ -175,21 +175,3 @@ value *eval_pair(env *e, value *v) {
}
return v;
}
-
-env *global_env = NULL;
-
-int main(void) {
- // init global env
- global_env = env_create(NULL);
- // START REPL
- char* line;
- while ((line = readline("λ > ")) != NULL) {
- if (*line) {
- add_history(line);
- }
- yy_scan_string(line);
- yyparse();
- free(line);
- }
- return 0;
-}
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..c9f9acd
--- /dev/null
+++ b/main.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include "lisp.h"
+#include "lex.yy.h"
+#include "lisp.tab.h"
+
+
+env *global_env = NULL;
+
+int main(void) {
+ // init global env
+ global_env = env_create(NULL);
+ // START REPL
+ char* line;
+ while ((line = readline("λ > ")) != NULL) {
+ if (*line) {
+ add_history(line);
+ }
+ yy_scan_string(line);
+ yyparse();
+ free(line);
+ }
+ return 0;
+}
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..0e462b9
--- /dev/null
+++ b/test.c
@@ -0,0 +1,109 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lisp.h"
+#include "lex.yy.h"
+#include "lisp.tab.h"
+
+static int tests_run = 0;
+static int tests_failed = 0;
+
+env *global_env = NULL;
+
+#define ASSERT(cond, msg) do { \
+ tests_run++; \
+ if (!(cond)) { \
+ tests_failed++; \
+ printf("FAIL: %s\n", msg); \
+ } else { \
+ printf("PASS: %s\n", msg); \
+ } \
+} while (0)
+
+void test_make_int() {
+ value *v = make_int(42);
+ ASSERT(v->type == VALTYPE_INT, "make_int sets type");
+ ASSERT(v->as.i == 42, "make_int sets value");
+}
+
+void test_cons_car_cdr() {
+ value *a = make_int(1);
+ value *b = make_int(2);
+ value *p = cons(a, b);
+
+ ASSERT(p->type == VALTYPE_PAIR, "cons creates pair");
+ ASSERT(car(p)->as.i == 1, "car works");
+ ASSERT(cdr(p)->as.i == 2, "cdr works");
+}
+
+
+void test_env_define_lookup() {
+ env *e = env_create(NULL);
+ value *v = make_int(10);
+
+ env_define(e, "x", v);
+ value *found = env_lookup(e, "x");
+
+ ASSERT(found != NULL, "env_lookup finds symbol");
+ ASSERT(found->as.i == 10, "env_lookup returns correct value");
+}
+
+void test_env_parent_lookup() {
+ env *parent = env_create(NULL);
+ env_define(parent, "x", make_int(5));
+
+ env *child = env_create(parent);
+ value *found = env_lookup(child, "x");
+
+ ASSERT(found != NULL, "lookup finds parent symbol");
+ ASSERT(found->as.i == 5, "parent symbol value correct");
+}
+
+void test_eval_literal() {
+ env *e = env_create(NULL);
+
+ value *v = make_int(7);
+ value *r = eval(e, v);
+
+ ASSERT(r->as.i == 7, "eval of int returns itself");
+}
+
+void test_eval_symbol() {
+ env *e = env_create(NULL);
+ env_define(e, "x", make_int(99));
+
+ value *sym = make_symbol("x");
+ value *r = eval(e, sym);
+
+ ASSERT(r->as.i == 99, "eval resolves symbol");
+}
+
+
+void test_define() {
+ env *e = env_create(NULL);
+
+ value *expr =cons(make_symbol("define"),
+ cons(make_symbol("x"),
+ cons(make_int(3), make_nil())));
+
+ eval(e, expr);
+ value *r = env_lookup(e, "x");
+
+ ASSERT(r != NULL, "define creates binding");
+ ASSERT(r->as.i == 3, "define sets correct value");
+}
+
+int main(void) {
+ test_make_int();
+ test_cons_car_cdr();
+ test_env_define_lookup();
+ test_env_parent_lookup();
+ test_eval_literal();
+ test_eval_symbol();
+ test_define();
+
+ printf("\nTests run: %d\n", tests_run);
+ printf("Tests failed: %d\n", tests_failed);
+
+ return tests_failed ? 1 : 0;
+}