diff options
| -rw-r--r-- | test.c | 460 |
1 files changed, 460 insertions, 0 deletions
@@ -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); |
