#include #include #include "apfl.h" #include "resizable.h" #include "test.h" struct parser_test { testctx t; void *source_reader_ctx; apfl_tokenizer_ptr tokenizer; apfl_parser_ptr parser; }; static struct parser_test * new_parser_test(testctx t, const char *source) { struct parser_test *pt = must_alloc(t, sizeof(struct parser_test)); pt->t = t; if ((pt->source_reader_ctx = apfl_string_source_reader_new(apfl_string_view_from(source))) == NULL) { test_fatalf(t, "Failed initializing source reader"); } if ((pt->tokenizer = apfl_tokenizer_new(apfl_string_source_reader, pt->source_reader_ctx)) == NULL) { test_fatalf(t, "Failed initializing the tokenizer"); } if ((pt->parser = apfl_parser_new(apfl_tokenizer_as_token_source(pt->tokenizer))) == NULL) { test_fatalf(t, "Failed initializing the parser"); } return pt; } static void destroy_parser_test(struct parser_test *pt) { apfl_parser_destroy(pt->parser); apfl_tokenizer_destroy(pt->tokenizer); apfl_string_source_reader_destroy(pt->source_reader_ctx); free(pt); } static void expect_eof(struct parser_test *pt) { switch (apfl_parser_next(pt->parser)) { case APFL_PARSE_OK: test_fatalf(pt->t, "Expected EOF but got an expression"); break; case APFL_PARSE_EOF: break; case APFL_PARSE_ERROR: test_failf(pt->t, "Got an error instead of an EOF"); apfl_error_print(apfl_parser_get_error(pt->parser), stderr); test_fatal(pt->t); break; } } static void expect_expr(struct parser_test *pt, struct apfl_expr expected) { struct apfl_expr expr; switch (apfl_parser_next(pt->parser)) { case APFL_PARSE_OK: expr = apfl_parser_get_expr(pt->parser); if (!apfl_expr_eq(expr, expected)) { test_failf(pt->t, "Expected expression differs from actual expression"); test_failf(pt->t, "Expected:"); apfl_expr_print(expected, stderr); test_failf(pt->t, "Have:"); apfl_expr_print(expr, stderr); } apfl_expr_deinit(&expr); apfl_expr_deinit(&expected); break; case APFL_PARSE_EOF: test_fatalf(pt->t, "Extected an expression but got EOF"); break; case APFL_PARSE_ERROR: test_failf(pt->t, "Got an error instead of an expression"); apfl_error_print(apfl_parser_get_error(pt->parser), stderr); test_fatal(pt->t); break; } } static struct apfl_string new_string(struct parser_test *pt, const char *in) { struct apfl_string out = { .bytes = NULL, .len = 0 }; if (!apfl_string_copy(&out, apfl_string_view_from(in))) { test_fatalf(pt->t, "Failed copying string in new_string"); } return out; } static void * new_helper(struct parser_test *pt, size_t size, void *data) { void *out = must_alloc(pt->t, size); memcpy(out, data, size); return out; } static struct apfl_expr_list_item list_item(bool expand, struct apfl_expr *expr) { return (struct apfl_expr_list_item) { .expand = expand, .expr = expr, }; } // Fugly macros to make it a bit easier to create a heap allocated struct value #define BEGIN_NEW(pt, T) ((T *)new_helper(pt, sizeof(T), &((T) #define END_NEW ))) enum listbuilder_cmd { LISTBUILDER_ADD, LISTBUILDER_STOP, }; #define LISTBUILDER_NAME(n) listbuilder_##n #define MKLISTBUILDER(name, listtype, itemtype, items_memb, len_memb) \ listtype \ LISTBUILDER_NAME(name)(struct parser_test *pt, ...) \ { \ listtype body; \ size_t cap = 0; \ \ apfl_resizable_init((void **)&body.items_memb, &body.len_memb, &cap); \ \ va_list ap; \ va_start(ap, pt); \ \ for (;;) { \ enum listbuilder_cmd cmd = va_arg(ap, enum listbuilder_cmd); \ if (cmd == LISTBUILDER_STOP) { \ break; \ } \ assert(cmd == LISTBUILDER_ADD); \ itemtype item = va_arg(ap, itemtype); \ \ if (!apfl_resizable_append( \ sizeof(itemtype), \ (void **)&body.items_memb, \ &body.len, \ &cap, \ &item, \ 1 \ )) { \ va_end(ap); \ test_fatalf(pt->t, "Failed appending"); \ assert(false); \ } \ } \ \ va_end(ap); \ \ return body; \ } \ #define LIST_BEGIN(pt, builder) (LISTBUILDER_NAME(builder)(pt, #define LIST_END LISTBUILDER_STOP)) #define LIST_ADD LISTBUILDER_ADD, MKLISTBUILDER(list, struct apfl_expr_list, struct apfl_expr_list_item, items, len) MKLISTBUILDER(body, struct apfl_expr_body, struct apfl_expr, items, len) MKLISTBUILDER(dict, struct apfl_expr_dict, struct apfl_expr_dict_pair, items, len) MKLISTBUILDER(complex_func, struct apfl_expr_complex_func, struct apfl_expr_subfunc, subfuncs, len) MKLISTBUILDER(params, struct apfl_expr_params, struct apfl_expr_param, params, len) #define POS(l, c) (struct apfl_position) { .line = l, .col = c } static struct apfl_expr var(struct parser_test *pt, int line, int col, const char *v) { return (struct apfl_expr) { .type = APFL_EXPR_VAR, .position = POS(line, col), .var = new_string(pt, v), }; } struct apfl_expr * new_var(struct parser_test *pt, int line, int col, const char *v) { struct apfl_expr expr = var(pt, line, col, v); return new_helper(pt, sizeof(struct apfl_expr), &expr); } static struct apfl_expr dot(struct parser_test *pt, int line, int col, const char *rhs, struct apfl_expr *lhs) { return (struct apfl_expr) { .type = APFL_EXPR_DOT, .position = POS(line, col), .dot.lhs = lhs, .dot.rhs = new_string(pt, rhs), }; } static struct apfl_expr * new_dot(struct parser_test *pt, int line, int col, const char *rhs, struct apfl_expr *lhs) { struct apfl_expr expr = dot(pt, line, col, rhs, lhs); return new_helper(pt, sizeof(struct apfl_expr), &expr); } TEST(empty, t) { struct parser_test *pt = new_parser_test(t, ""); expect_eof(pt); destroy_parser_test(pt); } TEST(hello_world, t) { struct parser_test *pt = new_parser_test(t, "print \"Hello World!\""); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 1), .call = (struct apfl_expr_call) { .callee = new_var(pt, 1, 1, "print"), .arguments = LIST_BEGIN(pt, list) LIST_ADD (struct apfl_expr_list_item) { .expand = false, .expr = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CONSTANT, .position = POS(1, 7), .constant.type = APFL_EXPR_CONST_STRING, .constant.string = new_string(pt, "Hello World!"), } END_NEW, }, LIST_END, }, }); expect_eof(pt); destroy_parser_test(pt); } TEST(empty_function, t) { struct parser_test *pt = new_parser_test(t, "{}"); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_SIMPLE_FUNC, .position = POS(1, 1), .simple_func = LIST_BEGIN(pt, body) LIST_END, }); expect_eof(pt); destroy_parser_test(pt); } TEST(empty_list, t) { struct parser_test *pt = new_parser_test(t, "[]"); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_LIST, .position = POS(1, 1), .list = LIST_BEGIN(pt, list) LIST_END, }); expect_eof(pt); destroy_parser_test(pt); } TEST(empty_dict, t) { struct parser_test *pt = new_parser_test(t, "[->]"); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_DICT, .position = POS(1, 1), .dict = LIST_BEGIN(pt, dict) LIST_END, }); expect_eof(pt); destroy_parser_test(pt); } TEST(simple_dot_access, t) { struct parser_test *pt = new_parser_test(t, "foo.bar"); expect_expr(pt, dot(pt, 1, 4, "bar", new_var(pt, 1, 1, "foo"))); expect_eof(pt); destroy_parser_test(pt); } TEST(simple_at_access, t) { struct parser_test *pt = new_parser_test(t, "foo@bar"); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_AT, .position = POS(1, 4), .at.lhs = new_var(pt, 1, 1, "foo"), .at.rhs = new_var(pt, 1, 5, "bar"), }); expect_eof(pt); destroy_parser_test(pt); } TEST(parens, t) { // 1 2 3 4 // 12345678901234567890123456789012345678901234567 struct parser_test *pt = new_parser_test(t, "foo (bar baz) (aaa ~bbb (ccc) ~(ddd (eee fff)))"); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 1), .call = (struct apfl_expr_call) { .callee = new_var(pt, 1, 1, "foo"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 5), .call = (struct apfl_expr_call) { .callee = new_var(pt, 1, 6, "bar"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 1, 10, "baz")), LIST_END }, } END_NEW), LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 15), .call = (struct apfl_expr_call) { .callee = new_var(pt, 1, 16, "aaa"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(true, new_var(pt, 1, 21, "bbb")), LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 25), .call = (struct apfl_expr_call) { .callee = new_var(pt, 1, 26, "ccc"), .arguments = LIST_BEGIN(pt, list) LIST_END, }, } END_NEW), LIST_ADD list_item(true, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 32), .call = (struct apfl_expr_call) { .callee = new_var(pt, 1, 33, "ddd"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 37), .call = (struct apfl_expr_call) { .callee = new_var(pt, 1, 38, "eee"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 1, 42, "fff")), LIST_END, }, } END_NEW), LIST_END, }, } END_NEW), LIST_END, }, } END_NEW), LIST_END, }, }); expect_eof(pt); destroy_parser_test(pt); } TEST(complex_dot_at_access, t) { // 1 2 // 123456789 01 23456789012345 struct parser_test *pt = new_parser_test(t, "a@b.c@1@\"d\"@(e@(f.g h).i)"); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_AT, .position = POS(1, 12), .at = (struct apfl_expr_at) { .lhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_AT, .position = POS(1, 8), .at = (struct apfl_expr_at) { .lhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_AT, .position = POS(1, 6), .at = (struct apfl_expr_at) { .lhs = new_dot(pt, 1, 4, "c", BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_AT, .position = POS(1, 2), .at = (struct apfl_expr_at) { .lhs = new_var(pt, 1, 1, "a"), .rhs = new_var(pt, 1, 3, "b"), } } END_NEW), .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CONSTANT, .position = POS(1, 7), .constant.type = APFL_EXPR_CONST_NUMBER, .constant.number = 1, } END_NEW, }, } END_NEW, .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CONSTANT, .position = POS(1, 9), .constant.type = APFL_EXPR_CONST_STRING, .constant.string = new_string(pt, "d"), } END_NEW, }, } END_NEW, .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 13), .call = (struct apfl_expr_call) { .callee = new_dot(pt, 1, 23, "i", BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_AT, .position = POS(1, 15), .at = (struct apfl_expr_at) { .lhs = new_var(pt, 1, 14, "e"), .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 16), .call = (struct apfl_expr_call) { .callee = new_dot(pt, 1, 18, "g", new_var(pt, 1, 17, "f") ), .arguments = LIST_BEGIN(pt, list) LIST_ADD (struct apfl_expr_list_item) { .expr = new_var(pt, 1, 21, "h"), .expand = false, }, LIST_END, }, } END_NEW, }, } END_NEW), .arguments = LIST_BEGIN(pt, list) LIST_END, }, } END_NEW, }, }); expect_eof(pt); destroy_parser_test(pt); } TEST(factorial, t) { struct parser_test *pt = new_parser_test(t, // 1 2 3 // 12345678901234567890123456789012 "factorial := {\n" " 0 -> 1\n" " n -> * n (factorial (- n 1))\n" "}\n" "factorial 10" ); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_ASSIGNMENT, .position = POS(1, 11), .assignment = (struct apfl_expr_assignment) { .lhs = (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_VAR, .var = new_string(pt, "factorial"), }, .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_COMPLEX_FUNC, .position = POS(1, 14), .complex_func = LIST_BEGIN(pt, complex_func) LIST_ADD (struct apfl_expr_subfunc) { .params = LIST_BEGIN(pt, params) LIST_ADD (struct apfl_expr_param) { .type = APFL_EXPR_PARAM_CONSTANT, .constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_NUMBER, .number = 0, }, }, LIST_END, .body = LIST_BEGIN(pt, body) LIST_ADD (struct apfl_expr) { .type = APFL_EXPR_CONSTANT, .position = POS(2, 10), .constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_NUMBER, .number = 1, }, }, LIST_END }, LIST_ADD (struct apfl_expr_subfunc) { .params = LIST_BEGIN(pt, params) LIST_ADD (struct apfl_expr_param) { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "n"), }, LIST_END, .body = LIST_BEGIN(pt, body) LIST_ADD (struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(3, 10), .call = (struct apfl_expr_call) { .callee = new_var(pt, 3, 10, "*"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 3, 12, "n")), LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(3, 14), .call = (struct apfl_expr_call) { .callee = new_var(pt, 3, 15, "factorial"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(3, 25), .call = (struct apfl_expr_call) { .callee = new_var(pt, 3, 26, "-"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 3, 28, "n")), LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CONSTANT, .position = POS(3, 30), .constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_NUMBER, .number = 1, }, } END_NEW), LIST_END, }, } END_NEW), LIST_END, }, } END_NEW), LIST_END, }, }, LIST_END }, LIST_END } END_NEW }, }); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(5, 1), .call = (struct apfl_expr_call) { .callee = new_var(pt, 5, 1, "factorial"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CONSTANT, .position = POS(5, 11), .constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_NUMBER, .number = 10, }, } END_NEW), LIST_END, }, }); expect_eof(pt); destroy_parser_test(pt); } TEST(map, t) { struct parser_test *pt = new_parser_test(t, // 123456789012345678901234 "map := {\n" "_ [] -> []\n" "f [x ~xs] ->\n" " [(f x) ~(map f xs)]\n" "}\n" ); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_ASSIGNMENT, .position = POS(1, 5), .assignment = (struct apfl_expr_assignment) { .lhs = (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_VAR, .var = new_string(pt, "map"), }, .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_COMPLEX_FUNC, .position = POS(1, 8), .complex_func = LIST_BEGIN(pt, complex_func) LIST_ADD (struct apfl_expr_subfunc) { .params = LIST_BEGIN(pt, params) LIST_ADD (struct apfl_expr_param) { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "_"), }, LIST_ADD (struct apfl_expr_param) { .type = APFL_EXPR_PARAM_LIST, .list = LIST_BEGIN(pt, params) LIST_END, }, LIST_END, .body = LIST_BEGIN(pt, body) LIST_ADD (struct apfl_expr) { .type = APFL_EXPR_LIST, .position = POS(2, 9), .list = LIST_BEGIN(pt, list) LIST_END, }, LIST_END, }, LIST_ADD (struct apfl_expr_subfunc) { .params = LIST_BEGIN(pt, params) LIST_ADD (struct apfl_expr_param) { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "f"), }, LIST_ADD (struct apfl_expr_param) { .type = APFL_EXPR_PARAM_LIST, .list = LIST_BEGIN(pt, params) LIST_ADD (struct apfl_expr_param) { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "x"), }, LIST_ADD (struct apfl_expr_param) { .type = APFL_EXPR_PARAM_EXPAND, .expand = BEGIN_NEW(pt, struct apfl_expr_param) { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "xs"), } END_NEW }, LIST_END, }, LIST_END, .body = LIST_BEGIN(pt, body) LIST_ADD (struct apfl_expr) { .type = APFL_EXPR_LIST, .position = POS(4, 5), .list = LIST_BEGIN(pt, list) LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(4, 6), .call = (struct apfl_expr_call) { .callee = new_var(pt, 4, 7, "f"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 4, 9, "x")), LIST_END, }, } END_NEW), LIST_ADD list_item(true, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(4, 13), .call = (struct apfl_expr_call) { .callee = new_var(pt, 4, 14, "map"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 4, 18, "f")), LIST_ADD list_item(false, new_var(pt, 4, 20, "xs")), LIST_END }, } END_NEW), LIST_END, }, LIST_END }, LIST_END, } END_NEW, }, }); expect_eof(pt); destroy_parser_test(pt); } TESTS_BEGIN ADDTEST(empty), ADDTEST(hello_world), ADDTEST(empty_function), ADDTEST(empty_list), ADDTEST(empty_dict), ADDTEST(simple_dot_access), ADDTEST(simple_at_access), ADDTEST(parens), ADDTEST(complex_dot_at_access), ADDTEST(factorial), ADDTEST(map), TESTS_END