#include "parser.h" #include "tests/tests.h" #include "ast.h" #include "slice.h" #include enum value_type { VALUE_IDENT, VALUE_INT, VALUE_BOOL, }; struct value { enum value_type type; union { struct slice ident; int64_t integer; bool boolean; }; }; struct parser *parser; static void check_parser_errors(struct parser *parser, const char *file, int line, const char *func) { if (parser->errors->len > 0) { printf("\n"); size_t i; char *val; vector_foreach (parser->errors, i, val) { printf("parser error %lu: %s\n", i, val); } printf(TBLD TRED "FAIL!\n" TRST); printf("%s:%d: %s: ", file, line, func); printf("parser encountered errors\n"); abort(); } } #define check_parser_errors(p) \ check_parser_errors(p, __FILE__, __LINE__, __func__) static void test_return_statement(struct statement *st) { struct slice retrn_lit = slice_fullstr("return"); struct slice st_lit = node_token_literal(st); asserteq(st->type, STATEMENT_RETURN); asserteq(slice_cmp(&retrn_lit, &st_lit), 0); } static void test_integer_literal(struct expression *expr, int64_t val) { char buf[128]; struct slice sval; asserteq(expr->type, EXPRESSION_INT); asserteq(expr->integer.value, val); sprintf(buf, "%ld", val); sval = slice_fullstr(buf); asserteq(slice_cmp(&expr->token.literal, &sval), 0); } static void test_identifier(struct expression *expr, struct slice val) { asserteq(expr->type, EXPRESSION_IDENT); } static void test_boolean_literal(struct expression *expr, bool val) { char *str = val ? "true" : "false"; struct slice sval = slice_fullstr(str); asserteq(expr->type, EXPRESSION_BOOL); asserteq(expr->boolean.value, val); asserteq(slice_cmp(&expr->token.literal, &sval), 0); } #define test_literal_expression(expr, expect) _Generic((expect), \ int64_t: test_integer_literal, \ struct slice: test_identifier, \ bool: test_boolean_literal \ )(expr, expect) static inline void test_expected(struct expression *expr, struct value v) { switch(v.type) { case VALUE_IDENT: test_literal_expression(expr, v.ident); break; case VALUE_INT: test_literal_expression(expr, v.integer); break; case VALUE_BOOL: test_literal_expression(expr, v.boolean); break; } } #define VIDENT(v) \ (struct value){ .type = VALUE_IDENT, .ident = slice_fullstr(v) } #define VINT(v) \ (struct value){ .type = VALUE_INT, .integer = v } #define VBOOL(v) \ (struct value){ .type = VALUE_BOOL, .boolean = v } static inline void test_infix(struct infix_expression *expr, struct value lval, struct slice *op, struct value rval) { test_expected(expr->left, lval); asserteq(slice_cmp(&expr->operator, op), 0); test_expected(expr->right, rval); } static void test_let_statement(struct statement *st, struct slice *name) { struct slice let_lit = slice_fullstr("let"); struct slice st_lit = node_token_literal(st); asserteq(slice_cmp(&let_lit, &st_lit), 0); asserteq(st->type, STATEMENT_LET); asserteq(slice_cmp(&st->let.name->value, name), 0); asserteq(slice_cmp(&st->let.name->token.literal, name), 0); } static void test_let_statements(void) { struct { char *input; struct slice ident; struct value val; } tests[] = { { "let x = 5;", slice_fullstr("x"), VINT(5) }, { "let y = true;", slice_fullstr("y"), VBOOL(true) }, { "let foo = bar;", slice_fullstr("foo"), VIDENT("bar") }, { 0 }, }; for (size_t i = 0; tests[i].input != NULL; i++) { parser_reset(parser, tests[i].input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; test_let_statement(st, &tests[i].ident); test_expected(st->let.value, tests[i].val); node_destroy(prog); } } static void test_return_statements(void) { struct { char *input; struct slice ident; struct value val; } tests[] = { { "return 5;", slice_fullstr("x"), VINT(5) }, { "return true;", slice_fullstr("y"), VBOOL(true) }, { "return bar;", slice_fullstr("foo"), VIDENT("bar") }, { 0 }, }; for (size_t i = 0; tests[i].input != NULL; i++) { parser_reset(parser, tests[i].input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; test_return_statement(st); test_expected(st->retrn.value, tests[i].val); node_destroy(prog); } } static void test_identifier_expression_statements(void) { char *input = "foobar;\n"; struct slice val = slice_fullstr("foobar"); parser_reset(parser, input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); test_literal_expression(st->expr.expr, val); node_destroy(prog); } static void test_integer_expression_statements(void) { char *input = "469;\n"; int64_t val = 469; parser_reset(parser, input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); test_literal_expression(st->expr.expr, val); node_destroy(prog); } static void test_prefix_expression_statements(void) { struct { char *input; struct slice operator; struct value val; } tests[] = { { "!5;", slice_fullstr("!"), VINT(5) }, { "-15;", slice_fullstr("-"), VINT(15) }, { "!foo;", slice_fullstr("!"), VIDENT("foo") }, { "-bar;", slice_fullstr("-"), VIDENT("bar") }, { "!true;", slice_fullstr("!"), VBOOL(true) }, { "!false;", slice_fullstr("!"), VBOOL(false) }, { 0 }, }; for (size_t i = 0; tests[i].input != NULL; i++) { parser_reset(parser, tests[i].input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); asserteq(st->expr.expr->type, EXPRESSION_PREFIX); test_expected(st->expr.expr->prefix.right, tests[i].val); node_destroy(prog); } } static void test_infix_expression_statements(void) { struct { char *input; struct value lval; struct slice operator; struct value rval; } tests[] = { { "5 + 5;", VINT(5), slice_fullstr("+"), VINT(5) }, { "5 - 5;", VINT(5), slice_fullstr("-"), VINT(5) }, { "5 * 5;", VINT(5), slice_fullstr("*"), VINT(5) }, { "5 / 5;", VINT(5), slice_fullstr("/"), VINT(5) }, { "5 > 5;", VINT(5), slice_fullstr(">"), VINT(5) }, { "5 < 5;", VINT(5), slice_fullstr("<"), VINT(5) }, { "foo + bar;", VIDENT("foo"), slice_fullstr("+"), VIDENT("bar") }, { "foo - bar;", VIDENT("foo"), slice_fullstr("-"), VIDENT("bar") }, { "foo * bar;", VIDENT("foo"), slice_fullstr("*"), VIDENT("bar") }, { "foo / bar;", VIDENT("foo"), slice_fullstr("/"), VIDENT("bar") }, { "foo > bar;", VIDENT("foo"), slice_fullstr(">"), VIDENT("bar") }, { "foo < bar;", VIDENT("foo"), slice_fullstr("<"), VIDENT("bar") }, { "true == true;", VBOOL(true), slice_fullstr("=="), VBOOL(true) }, { "true != false;", VBOOL(true), slice_fullstr("!="), VBOOL(false) }, { "false != false;", VBOOL(false), slice_fullstr("!="), VBOOL(false) }, { 0 }, }; for (size_t i = 0; tests[i].input != NULL; i++) { parser_reset(parser, tests[i].input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); asserteq(st->expr.expr->type, EXPRESSION_INFIX); test_infix(&st->expr.expr->infix, tests[i].lval, &tests[i].operator, tests[i].rval); node_destroy(prog); } } static void test_operator_precedence(void) { struct { char *input; char *expect; } tests[] = { { "-a * b", "((-a) * b)", }, { "!-a", "(!(-a))", }, { "a + b + c", "((a + b) + c)", }, { "a + b - c", "((a + b) - c)", }, { "a * b * c", "((a * b) * c)", }, { "a * b / c", "((a * b) / c)", }, { "a + b / c", "(a + (b / c))", }, { "a + b * c + d / e - f", "(((a + (b * c)) + (d / e)) - f)", }, { "3 + 4; -5 * 5", "(3 + 4)((-5) * 5)", }, { "5 > 4 == 3 < 4", "((5 > 4) == (3 < 4))", }, { "5 < 4 != 3 > 4", "((5 < 4) != (3 > 4))", }, { "3 + 4 * 5 == 3 * 1 + 4 * 5", "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))", }, { "true", "true", }, { "false", "false", }, { "3 > 5 == false", "((3 > 5) == false)", }, { "3 < 5 == true", "((3 < 5) == true)", }, { "1 + (2 + 3) + 4", "((1 + (2 + 3)) + 4)", }, { "(5 + 5) * 2", "((5 + 5) * 2)", }, { "2 / (5 + 5)", "(2 / (5 + 5))", }, { "(5 + 5) * 2 * (5 + 5)", "(((5 + 5) * 2) * (5 + 5))", }, { "-(5 + 5)", "(-(5 + 5))", }, { "!(true == true)", "(!(true == true))", }, { "a + add(b * c) + d", "((a + add((b * c))) + d)", }, { "add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))", "add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))", }, { "add(a + b + c * d / f + g)", "add((((a + b) + ((c * d) / f)) + g))", }, { 0 }, }; for (size_t i = 0; tests[i].input != NULL; i++) { char buf[128]; parser_reset(parser, tests[i].input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); node_sprint(prog, buf); asserteq(strcmp(tests[i].expect, buf), 0); node_destroy(prog); } } static void test_boolean_expression(void) { struct { char *input; bool value; } tests[] = { { "true;", true }, { "false;", false }, { 0 }, }; for (size_t i = 0; tests[i].input != NULL; i++) { parser_reset(parser, tests[i].input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); test_literal_expression(st->expr.expr, tests[i].value); node_destroy(prog); } } static void test_if_expression(void) { char *input = "if (x < y) { x }"; struct value x = VIDENT("x"); struct slice op = slice_fullstr("<"); struct value y = VIDENT("y"); parser_reset(parser, input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); asserteq(st->expr.expr->type, EXPRESSION_IF); struct if_expression *ifexpr = &st->expr.expr->cond; asserteq(ifexpr->condition->type, EXPRESSION_INFIX); test_infix(&ifexpr->condition->infix, x, &op, y); asserteq(ifexpr->consequence->type, STATEMENT_BLOCK); asserteq(ifexpr->consequence->block.statements->len, 1); struct statement *ifst = ifexpr->consequence->block.statements->values[0]; asserteq(ifst->type, STATEMENT_EXPRESSION); test_identifier(ifst->expr.expr, x.ident); asserteq(ifexpr->alternative, NULL); node_destroy(prog); } static void test_if_else_expression(void) { char *input = "if (x < y) { x } else { y }"; struct value x = VIDENT("x"); struct slice op = slice_fullstr("<"); struct value y = VIDENT("y"); parser_reset(parser, input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); asserteq(st->expr.expr->type, EXPRESSION_IF); struct if_expression *ifexpr = &st->expr.expr->cond; asserteq(ifexpr->condition->type, EXPRESSION_INFIX); test_infix(&ifexpr->condition->infix, x, &op, y); asserteq(ifexpr->consequence->type, STATEMENT_BLOCK); asserteq(ifexpr->consequence->block.statements->len, 1); struct statement *ifst = ifexpr->consequence->block.statements->values[0]; asserteq(ifst->type, STATEMENT_EXPRESSION); test_identifier(ifst->expr.expr, x.ident); assertneq(ifexpr->alternative, NULL); asserteq(ifexpr->alternative->block.statements->len, 1); asserteq(ifexpr->alternative->type, STATEMENT_BLOCK); struct statement *elsest = ifexpr->alternative->block.statements->values[0]; asserteq(elsest->type, STATEMENT_EXPRESSION); test_identifier(elsest->expr.expr, y.ident); node_destroy(prog); } static void test_func_literal(void) { char *input = "fn(x, y) { x + y; }"; struct value x = VIDENT("x"); struct slice op = slice_fullstr("+"); struct value y = VIDENT("y"); parser_reset(parser, input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); asserteq(st->expr.expr->type, EXPRESSION_FUNC); struct func_literal *func = &st->expr.expr->func; asserteq(func->parameters->len, 2); test_identifier(func->parameters->values[0], x.ident); test_identifier(func->parameters->values[1], y.ident); asserteq(func->body->type, STATEMENT_BLOCK); asserteq(func->body->block.statements->len, 1); struct statement *fst = func->body->block.statements->values[0]; test_infix(&fst->expr.expr->infix, x, &op, y); node_destroy(prog); } static void test_func_parameters(void) { struct { char *input; struct { struct slice vals[4]; size_t len; } params; } tests[] = { { "fn() {};", { 0 }, }, { "fn(x) {};", { { slice_fullstr("x") }, 1 }, }, { "fn(x, y) {};", { { slice_fullstr("x"), slice_fullstr("y") }, 2, } }, { "fn(x, y, z) {};", { { slice_fullstr("x"), slice_fullstr("y"), slice_fullstr("z") }, 3, } }, { 0 }, }; for (size_t i = 0; tests[i].input != NULL; i++) { parser_reset(parser, tests[i].input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); asserteq(st->expr.expr->type, EXPRESSION_FUNC); struct func_literal *func = &st->expr.expr->func; asserteq(func->parameters->len, tests[i].params.len); for (size_t j = 0; j < tests[i].params.len; j++) { struct expression *param = func->parameters->values[j]; asserteq(slice_cmp(¶m->ident.token.literal, &tests[i].params.vals[j]), 0); } node_destroy(prog); } } static void test_call_expression(void) { char *input = "add(1, x * y, add(x, y, z));"; int64_t par1 = 1; struct value x = VIDENT("x"); struct value y = VIDENT("y"); struct slice op1 = slice_fullstr("*"); parser_reset(parser, input); struct program *prog = parser_parse_program(parser); check_parser_errors(parser); assertneq(prog, NULL); asserteq(prog->statements->len, 1); struct statement *st = prog->statements->values[0]; asserteq(st->type, STATEMENT_EXPRESSION); asserteq(st->expr.expr->type, EXPRESSION_CALL); struct call_expression *call = &st->expr.expr->call; test_identifier(call->func, slice_fullstr("add")); asserteq(call->arguments->len, 3); struct expression *expr1 = call->arguments->values[0], *expr2 = call->arguments->values[1], *expr3 = call->arguments->values[2]; test_literal_expression(expr1, par1); asserteq(expr2->type, EXPRESSION_INFIX); test_infix(&expr2->infix, x, &op1, y); asserteq(expr3->type, EXPRESSION_CALL); asserteq(expr3->call.arguments->len, 3); } static void init(void) { parser = parser_new(); } static void cleanup(void) { parser_destroy(parser); } int main(void) { INIT_TESTS(); init(); RUN_TEST(test_let_statements); RUN_TEST(test_return_statements); RUN_TEST(test_identifier_expression_statements); RUN_TEST(test_integer_expression_statements); RUN_TEST(test_prefix_expression_statements); RUN_TEST(test_infix_expression_statements); RUN_TEST(test_operator_precedence); RUN_TEST(test_boolean_expression); RUN_TEST(test_if_expression); RUN_TEST(test_if_else_expression); RUN_TEST(test_func_literal); RUN_TEST(test_func_parameters); RUN_TEST(test_call_expression); cleanup(); }