From 20b1cea7a74a4feb759155eedbb6299bd891a325 Mon Sep 17 00:00:00 2001 From: Felix Perktold Date: Sun, 21 Dec 2025 14:53:26 +0100 Subject: add tests --- .gitignore | 3 +- Makefile | 24 +++++++++++--- lisp.c | 18 ---------- main.c | 26 +++++++++++++++ test.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 24 deletions(-) create mode 100644 main.c create mode 100644 test.c 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 +#include +#include +#include +#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 +#include +#include +#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; +} -- cgit v1.2.3