summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test.c460
1 files changed, 460 insertions, 0 deletions
diff --git a/test.c b/test.c
index 8622fe3..48b844c 100644
--- a/test.c
+++ b/test.c
@@ -93,6 +93,447 @@ void test_define() {
ASSERT(r->as.i == 3, "define sets correct value");
}
+
+void test_lambda_identity() {
+ env *e = env_create(NULL);
+
+ // ((lambda (x) x) 5)
+ value *expr =
+ cons(
+ cons(make_symbol("lambda"),
+ cons(
+ cons(make_symbol("x"), make_nil()),
+ cons(make_symbol("x"), make_nil())
+ )
+ ),
+ cons(make_int(5), make_nil())
+ );
+
+ value *r = eval(e, expr);
+
+ ASSERT(r->type == VT_INT, "lambda identity returns int");
+ ASSERT(r->as.i == 5, "lambda identity returns argument");
+}
+
+void test_lambda_add() {
+ env *e = env_create(NULL);
+
+ // install +
+ env_define(e, "+", make_builtin(builtin_add));
+
+ value *expr =
+ cons(
+ cons(make_symbol("lambda"),
+ cons(
+ cons(make_symbol("x"), make_nil()),
+ cons(
+ cons(make_symbol("+"),
+ cons(make_symbol("x"),
+ cons(make_int(1), make_nil())
+ )
+ ),
+ make_nil()
+ )
+ )
+ ),
+ cons(make_int(4), make_nil())
+ );
+
+ value *r = eval(e, expr);
+
+ ASSERT(r->as.i == 5, "lambda body with + works");
+}
+
+
+void test_lambda_multiple_args() {
+ env *e = env_create(NULL);
+ env_define(e, "+", make_builtin(builtin_add));
+
+ value *expr =
+ cons(
+ cons(make_symbol("lambda"),
+ cons(
+ cons(make_symbol("x"),
+ cons(make_symbol("y"), make_nil())
+ ),
+ cons(
+ cons(make_symbol("+"),
+ cons(make_symbol("x"),
+ cons(make_symbol("y"), make_nil())
+ )
+ ),
+ make_nil()
+ )
+ )
+ ),
+ cons(make_int(2),
+ cons(make_int(3), make_nil())
+ )
+ );
+
+ value *r = eval(e, expr);
+
+ ASSERT(r->as.i == 5, "lambda with multiple parameters");
+}
+
+
+void test_lambda_closure() {
+
+ //(define x 10)
+ //((lambda (y) (+ x y)) 5)
+
+ env *e = env_create(NULL);
+ env_define(e, "+", make_builtin(builtin_add));
+ env_define(e, "x", make_int(10));
+
+ value *expr =
+ cons(
+ cons(make_symbol("lambda"),
+ cons(
+ cons(make_symbol("y"), make_nil()),
+ cons(
+ cons(make_symbol("+"),
+ cons(make_symbol("x"),
+ cons(make_symbol("y"), make_nil())
+ )
+ ),
+ make_nil()
+ )
+ )
+ ),
+ cons(make_int(5), make_nil())
+ );
+
+ value *r = eval(e, expr);
+
+ ASSERT(r->as.i == 15, "lambda closes over outer environment");
+}
+
+void test_nested_lambdas() {
+ // (((lambda (x) (lambda (y) (+ x y))) 3) 4)
+ env *e = env_create(NULL);
+ env_define(e, "+", make_builtin(builtin_add));
+
+ value *expr =
+ cons(
+ cons(
+ cons(make_symbol("lambda"),
+ cons(
+ cons(make_symbol("x"), make_nil()),
+ cons(
+ cons(make_symbol("lambda"),
+ cons(
+ cons(make_symbol("y"), make_nil()),
+ cons(
+ cons(make_symbol("+"),
+ cons(make_symbol("x"),
+ cons(make_symbol("y"), make_nil())
+ )
+ ),
+ make_nil()
+ )
+ )
+ ),
+ make_nil()
+ )
+ )
+ ),
+ cons(make_int(3), make_nil())
+ ),
+ cons(make_int(4), make_nil())
+ );
+
+ value *r = eval(e, expr);
+
+ ASSERT(r->as.i == 7, "nested lambdas capture variables");
+}
+
+void test_lambda_does_not_modify_outer_env() {
+ env *e = env_create(NULL);
+ env_define(e, "x", make_int(1));
+
+ value *expr =
+ cons(
+ cons(make_symbol("lambda"),
+ cons(
+ cons(make_symbol("x"), make_nil()),
+ cons(make_symbol("x"), make_nil())
+ )
+ ),
+ cons(make_int(99), make_nil())
+ );
+
+ value *r = eval(e, expr);
+ value *outer = env_lookup(e, "x");
+
+ ASSERT(r->as.i == 99, "lambda returns inner x");
+ ASSERT(outer->as.i == 1, "outer x unchanged");
+}
+
+void test_recursive_factorial() {
+ env *e = env_create(NULL);
+
+ env_define(e, "+", make_builtin(builtin_add));
+ env_define(e, "-", make_builtin(builtin_sub));
+ env_define(e, "*", make_builtin(builtin_mul));
+ env_define(e, "<", make_builtin(builtin_lt));
+
+ /*
+ (define fact
+ (lambda (n)
+ (if (< n 2)
+ 1
+ (* n (fact (- n 1))))))
+ */
+ value *fact_def =
+ cons(make_symbol("define"),
+ cons(make_symbol("fact"),
+ cons(
+ cons(make_symbol("lambda"),
+ cons(
+ cons(make_symbol("n"), make_nil()),
+ cons(
+ cons(make_symbol("if"),
+ cons(
+ cons(make_symbol("<"),
+ cons(make_symbol("n"),
+ cons(make_int(2), make_nil())
+ )
+ ),
+ cons(
+ make_int(1),
+ cons(
+ cons(make_symbol("*"),
+ cons(make_symbol("n"),
+ cons(
+ cons(make_symbol("fact"),
+ cons(
+ cons(make_symbol("-"),
+ cons(make_symbol("n"),
+ cons(make_int(1), make_nil())
+ )
+ ),
+ make_nil()
+ )
+ ),
+ make_nil()
+ )
+ )
+ ),
+ make_nil()
+ )
+ )
+ )
+ ),
+ make_nil()
+ )
+ )
+ ),
+ make_nil()
+ )
+ )
+ );
+
+ eval(e, fact_def);
+
+ // (fact 5)
+ value *call =
+ cons(make_symbol("fact"),
+ cons(make_int(5), make_nil())
+ );
+
+ value *r = eval(e, call);
+ ASSERT(r->as.i == 120, "recursive factorial works");
+}
+
+void test_eq_ints() {
+ env *e = env_create(NULL);
+ env_define(e, "eq", make_builtin(builtin_eq));
+
+ value *expr =
+ cons(make_symbol("eq"),
+ cons(make_int(3),
+ cons(make_int(3), make_nil())
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->type == VT_INT, "eq returns true as int");
+ ASSERT(r->as.i == 1, "eq works on equal ints");
+}
+
+
+void test_eq_ints_false() {
+ env *e = env_create(NULL);
+ env_define(e, "eq", make_builtin(builtin_eq));
+
+ value *expr =
+ cons(make_symbol("eq"),
+ cons(make_int(3),
+ cons(make_int(4), make_nil())
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->type == VT_NIL, "eq returns nil on unequal ints");
+}
+
+void test_eq_symbols() {
+ env *e = env_create(NULL);
+ env_define(e, "eq", make_builtin(builtin_eq));
+
+ value *expr =
+ cons(make_symbol("eq"),
+ cons(make_symbol("foo"),
+ cons(make_symbol("foo"), make_nil())
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->as.i == 1, "eq works on symbols");
+}
+
+void test_eq_lists() {
+ env *e = env_create(NULL);
+ env_define(e, "eq", make_builtin(builtin_eq));
+
+ value *list1 =
+ cons(make_int(1),
+ cons(make_int(2), make_nil())
+ );
+
+ value *list2 =
+ cons(make_int(1),
+ cons(make_int(2), make_nil())
+ );
+
+ value *expr =
+ cons(make_symbol("eq"),
+ cons(list1,
+ cons(list2, make_nil())
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->as.i == 1, "eq works on structurally equal lists");
+}
+
+void test_null_true() {
+ env *e = env_create(NULL);
+ env_define(e, "null?", make_builtin(builtin_isnull));
+
+ value *expr =
+ cons(make_symbol("null?"),
+ cons(make_nil(), make_nil())
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->as.i == 1, "null? true on ()");
+}
+
+void test_null_false() {
+ env *e = env_create(NULL);
+ env_define(e, "null?", make_builtin(builtin_isnull));
+
+ value *expr =
+ cons(make_symbol("null?"),
+ cons(
+ cons(make_int(1), make_nil()),
+ make_nil()
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->type == VT_NIL, "null? false on non-empty list");
+}
+
+void test_if_true() {
+ env *e = env_create(NULL);
+ env_define(e, "<", make_builtin(builtin_lt));
+
+ value *expr =
+ cons(make_symbol("if"),
+ cons(
+ make_int(1), // true (non-nil)
+ cons(make_int(42),
+ cons(make_int(0), make_nil())
+ )
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->as.i == 42, "if true branch evaluated");
+}
+
+void test_if_false() {
+ env *e = env_create(NULL);
+
+ value *expr =
+ cons(make_symbol("if"),
+ cons(
+ make_nil(), // false
+ cons(make_int(1),
+ cons(make_int(2), make_nil())
+ )
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->as.i == 2, "if false branch evaluated");
+}
+
+void test_if_short_circuit() {
+ env *e = env_create(NULL);
+
+ // (if 1 42 undefined-symbol)
+ value *expr =
+ cons(make_symbol("if"),
+ cons(
+ make_int(1), // true
+ cons(make_int(42),
+ cons(make_symbol("nope"), make_nil())
+ )
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->as.i == 42, "if short-circuits unused branch");
+}
+
+void test_add_multiple_args() {
+ env *e = env_create(NULL);
+ env_define(e, "+", make_builtin(builtin_add));
+
+ value *expr =
+ cons(make_symbol("+"),
+ cons(make_int(1),
+ cons(make_int(2),
+ cons(make_int(3), make_nil())
+ )
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->as.i == 6, "+ works with multiple args");
+}
+
+void test_lt_chain() {
+ env *e = env_create(NULL);
+ env_define(e, "<", make_builtin(builtin_lt));
+
+ value *expr =
+ cons(make_symbol("<"),
+ cons(make_int(1),
+ cons(make_int(2),
+ cons(make_int(3), make_nil())
+ )
+ )
+ );
+
+ value *r = eval(e, expr);
+ ASSERT(r->as.i == 1, "< works as chain comparison");
+}
+
int main(void) {
test_make_int();
test_cons_car_cdr();
@@ -101,6 +542,25 @@ int main(void) {
test_eval_literal();
test_eval_symbol();
test_define();
+ test_lambda_identity();
+ test_lambda_add();
+ test_lambda_multiple_args();
+ test_lambda_closure();
+ test_nested_lambdas();
+ test_lambda_does_not_modify_outer_env();
+ test_recursive_factorial();
+ test_eq_ints();
+ test_eq_ints_false();
+ test_eq_symbols();
+ test_eq_lists();
+ test_null_true();
+ test_null_false();
+ test_if_true();
+ test_if_false();
+ test_if_short_circuit();
+ test_add_multiple_args();
+ test_lt_chain();
+
printf("\nTests run: %d\n", tests_run);
printf("Tests failed: %d\n", tests_failed);