diff options
author | Yaroslav de la Peña Smirnov <yps@yaroslavps.com> | 2022-01-20 02:34:32 +0300 |
---|---|---|
committer | Yaroslav de la Peña Smirnov <yps@yaroslavps.com> | 2022-01-20 02:34:32 +0300 |
commit | c0cd4e5f199e8567ec3b5e216fbee27837d21bea (patch) | |
tree | c78eee02932fc6e85413e367d27ec5b5627e1534 /src/tests/eval.c | |
download | cmonkey-c0cd4e5f199e8567ec3b5e216fbee27837d21bea.tar.gz cmonkey-c0cd4e5f199e8567ec3b5e216fbee27837d21bea.zip |
init
Diffstat (limited to 'src/tests/eval.c')
-rw-r--r-- | src/tests/eval.c | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/src/tests/eval.c b/src/tests/eval.c new file mode 100644 index 0000000..b341181 --- /dev/null +++ b/src/tests/eval.c @@ -0,0 +1,332 @@ +#include "eval.h" +#include "tests/tests.h" + +#include "parser.h" +#include "object.h" + +#include <string.h> + +struct parser *parser; + +static struct object * +test_eval(const char *input) +{ + parser_reset(parser, input); + struct program *prog = parser_parse_program(parser); + struct environment *env = environment_new(); + struct object *obj = eval(env, prog); + environment_destroy(env); + node_destroy(prog); + + return obj; +} + +static void +test_eval_integer_expressions(void) +{ + struct { + char *input; + int64_t expect; + } tests[] = { + { "5", 5 }, + { "69", 69 }, + { "-5", -5 }, + { "-69", -69 }, + { "5 + 5 + 5 + 5 - 10", 10 }, + { "2 * 2 * 2 * 2 * 2", 32 }, + { "-50 + 100 + -50", 0 }, + { "5 * 2 + 10", 20 }, + { "5 + 2 * 10", 25 }, + { "20 + 2 * -10", 0 }, + { "50 / 2 * 2 + 10", 60 }, + { "2 * (5 + 10)", 30 }, + { "3 * 3 * 3 + 10", 37 }, + { "3 * (3 * 3) + 10", 37 }, + { "(5 + 10 * 2 + 15 / 3) * 2 + -10", 50 }, + { 0 }, + }; + + for (size_t i = 0; tests[i].input != NULL; i++) { + struct object *res = test_eval(tests[i].input); + assertneq(res, NULL); + asserteq(res->type, OBJECT_INT); + asserteq(tests[i].expect, res->integer); + object_unref(res); + } +} + +static void +test_eval_boolean_expressions(void) +{ + struct { + char *input; + bool expect; + } tests[] = { + { "true", true }, + { "false", false }, + { "1 < 2", true }, + { "1 > 2", false }, + { "1 < 1", false }, + { "1 > 1", false }, + { "1 == 1", true }, + { "1 != 1", false }, + { "1 == 2", false }, + { "1 != 2", true }, + { "true == true", true }, + { "false == false", true }, + { "true == false", false }, + { "true != false", true }, + { "false != true", true }, + { "(1 < 2) == true", true }, + { "(1 < 2) == false", false }, + { "(1 > 2) == true", false }, + { "(1 > 2) == false", true }, + { 0 }, + }; + + for (size_t i = 0; tests[i].input != NULL; i++) { + struct object *res = test_eval(tests[i].input); + assertneq(res, NULL); + asserteq(res->type, OBJECT_BOOL); + asserteq(tests[i].expect, res->boolean); + } +} + +static void +test_eval_bang_operators(void) +{ + struct { + char *input; + bool expect; + } tests[] = { + { "!true", false }, + { "!false", true }, + { "!5", false }, + { "!!true", true }, + { "!!false", false }, + { "!!5", true }, + { 0 }, + }; + + for (size_t i = 0; tests[i].input != NULL; i++) { + struct object *res = test_eval(tests[i].input); + assertneq(res, NULL); + asserteq(res->type, OBJECT_BOOL); + asserteq(tests[i].expect, res->boolean); + object_unref(res); + } +} + +#define OBJ_INT(v) (struct object) { OBJECT_INT, .integer = v } +#define OBJ_BOOL(v) (struct object) { OBJECT_BOOL, .boolean = v } +#define OBJ_NULL() (struct object) { OBJECT_NULL, 0, } + +#define OBJ(v) _Generic((v), \ + int64_t: OBJ_INT(v), \ + int: OBJ_INT(v), \ + bool: OBJ_BOOL(v) \ + ) + +static void +test_eval_ifelse_expressions(void) +{ + struct { + char *input; + struct object expect; + } tests[] = { + { "if (true) { 10 }", OBJ(10) }, + { "if (false) { 10 }", OBJ_NULL() }, + { "if (1) { 10 }", OBJ(10) }, + { "if (1 < 2) { 10 }", OBJ(10) }, + { "if (1 > 2) { 10 }", OBJ_NULL() }, + { "if (1 > 2) { 10 } else { 20 }", OBJ(20) }, + { "if (1 < 2) { 10 } else { 20 }", OBJ(10) }, + { 0 }, + }; + + for (size_t i = 0; tests[i].input != NULL; i++) { + struct object *res = test_eval(tests[i].input); + asserteq(tests[i].expect.type, res->type); + asserteq(tests[i].expect.integer, res->integer); + object_unref(res); + } +} + +static void +test_eval_return_statements(void) +{ + struct { + char *input; + int64_t expect; + } tests[] = { + { "return 10;", 10 }, + { "return 10; 9;", 10 }, + { "return 2 * 5; 9;", 10 }, + { "9; return 2 * 5; 9;", 10 }, + { + "if (10 > 1) {\n" + "\tif(10 > 1) {\n" + "\t\treturn 10;\n" + "\t\n}" + "return 1;\n" + "}", + 10, + }, + { 0 }, + }; + + for (size_t i = 0; tests[i].input != NULL; i++) { + struct object *res = test_eval(tests[i].input); + assertneq(res, NULL); + asserteq(res->type, OBJECT_INT); + asserteq(tests[i].expect, res->integer); + object_unref(res); + } +} + +static void +test_errors(void) +{ + struct { + char *input; + char *expect; + } tests[] = { + { + "5 + true;", + "type mismatch: int + bool", + }, + { + "5 + true; 5;", + "type mismatch: int + bool", + }, + { + "-true", + "unknown operator: -bool", + }, + { + "true + false;", + "unknown operator: bool + bool", + }, + { + "5; true + false; 5;", + "unknown operator: bool + bool", + }, + { + "if (10 > 1) { true + false; }", + "unknown operator: bool + bool", + }, + { + "if (10 > 1) {\n" + "\tif(10 > 1) {\n" + "\t\treturn true + false;\n" + "\t\n}" + "return 1;\n" + "}", + "unknown operator: bool + bool", + }, + { + "foobar;", + "not declared: foobar", + }, + { 0 }, + }; + + for (size_t i = 0; tests[i].input != NULL; i++) { + struct object *res = test_eval(tests[i].input); + assertneq(res, NULL); + asserteq(res->type, OBJECT_ERROR); + asserteq(strcmp(tests[i].expect, res->error.msg), 0); + object_unref(res); + } +} + +static void +test_eval_let_statements(void) +{ + struct { + char *input; + int64_t expect; + } tests[] = { + { "let a = 5; a;", 5 }, + { "let a = 5 * 5; a;", 25 }, + { "let a = 5; let b = a; b;", 5 }, + { "let a = 5; let b = a; let c = a + b + 5; c;", 15 }, + { 0 }, + }; + + for (size_t i = 0; tests[i].input != NULL; i++) { + struct object *res = test_eval(tests[i].input); + assertneq(res, NULL); + asserteq(res->type, OBJECT_INT); + asserteq(tests[i].expect, res->integer); + object_unref(res); + } +} + +static void +test_eval_funcs(void) +{ + char *input = "fn(x) { x + 2; };"; + char *expect = "fn(x) {\n(x + 2)\n}"; + struct object *res = test_eval(input); + assertneq(res, NULL); + asserteq(res->type, OBJECT_FUNC); + char fbuf[128]; + object_sprint(res, fbuf); + asserteq(strcmp(fbuf, expect), 0); + object_unref(res); +} + +static void +test_call_funcs(void) +{ + struct { + char *input; + int64_t expect; + } tests[] = { + { "let identity = fn(x) { x; }; identity(5);", 5 }, + { "let identity = fn(x) { return x; }; identity(5);", 5 }, + { "let double = fn(x) { x * 2; }; double(5);", 10 }, + { "let add = fn(x, y) { x + y; }; add(5, 5);", 10 }, + { "let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20 }, + { "fn(x) { x; }(5)", 5 }, + { 0 }, + }; + + for (size_t i = 0; tests[i].input != NULL; i++) { + struct object *res = test_eval(tests[i].input); + assertneq(res, NULL); + asserteq(res->type, OBJECT_INT); + asserteq(tests[i].expect, res->integer); + object_unref(res); + } +} + +static void +init(void) +{ + parser = parser_new(); +} + +static void +cleanup(void) +{ + parser_destroy(parser); +} + +int +main(void) +{ + init(); + INIT_TESTS(); + RUN_TEST(test_eval_integer_expressions); + RUN_TEST(test_eval_boolean_expressions); + RUN_TEST(test_eval_bang_operators); + RUN_TEST(test_eval_ifelse_expressions); + RUN_TEST(test_eval_return_statements); + RUN_TEST(test_errors); + RUN_TEST(test_eval_let_statements); + RUN_TEST(test_eval_funcs); + RUN_TEST(test_call_funcs); + cleanup(); +} |