#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 void expect_error_of_type(struct parser_test *pt, enum apfl_error_type want) { struct apfl_error have; switch (apfl_parser_next(pt->parser)) { case APFL_PARSE_OK: test_failf(pt->t, "Expected error but got an OK"); break; case APFL_PARSE_EOF: test_fatalf(pt->t, "Expected error but got an EOF"); break; case APFL_PARSE_ERROR: have = apfl_parser_get_error(pt->parser); if (have.type != want) { test_failf(pt->t, "Expected error of type %s, got this error instead:", apfl_error_type_name(want)); apfl_error_print(have, stderr); } break; } } static struct apfl_string new_string(struct parser_test *pt, const char *in) { struct apfl_string out = apfl_string_blank(); 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 LISTBUILDER_ITEM_NAME(n) listbuilder_item_##n #define MKLISTBUILDER(name, listtype, itemtype, items_memb, len_memb) \ struct LISTBUILDER_ITEM_NAME(name) { \ bool has_item; \ itemtype item; \ }; \ listtype \ LISTBUILDER_NAME(name)( \ struct parser_test *pt, \ struct LISTBUILDER_ITEM_NAME(name) items[] \ ) { \ listtype body; \ size_t cap = 0; \ \ apfl_resizable_init((void **)&body.items_memb, &body.len_memb, &cap); \ \ for (items++; items->has_item; items++) { \ if (!apfl_resizable_append( \ sizeof(itemtype), \ (void **)&body.items_memb, \ &body.len, \ &cap, \ &items->item, \ 1 \ )) { \ test_fatalf(pt->t, "Failed appending"); \ assert(false); \ } \ } \ \ return body; \ } \ #define LIST_BEGIN(pt, builder) (LISTBUILDER_NAME(builder)(pt, (struct LISTBUILDER_ITEM_NAME(builder)[]) {{.has_item=false #define LIST_END }, {.has_item = false}})) #define LIST_ADD }, {.has_item = true, .item = 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) MKLISTBUILDER(assignable_list, struct apfl_expr_assignable_list, struct apfl_expr_assignable, children, 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); } static struct apfl_expr_const num_const(apfl_number n) { return (struct apfl_expr_const) { .type = APFL_EXPR_CONST_NUMBER, .number = n, }; } static struct apfl_expr_const string_const(struct parser_test *pt, const char *s) { return (struct apfl_expr_const) { .type = APFL_EXPR_CONST_STRING, .string = new_string(pt, s), }; } static struct apfl_expr_const bool_const(bool b) { return (struct apfl_expr_const) { .type = APFL_EXPR_CONST_BOOLEAN, .boolean = b, }; } static struct apfl_expr const_expr(int line, int col, struct apfl_expr_const constant) { return (struct apfl_expr) { .type = APFL_EXPR_CONSTANT, .position = POS(line, col), .constant = constant, }; } static struct apfl_expr * new_const_expr(struct parser_test *pt, int line, int col, struct apfl_expr_const constant) { struct apfl_expr expr = const_expr(line, col, constant); 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 = new_const_expr(pt, 1, 7, string_const(pt, "Hello World!")), }, 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 = new_const_expr(pt, 1, 7, num_const(1)), }, } END_NEW, .rhs = new_const_expr(pt, 1, 9, string_const(pt, "d")), }, } 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 = num_const(0), }, LIST_END, .body = LIST_BEGIN(pt, body) LIST_ADD const_expr(2, 10, num_const(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, new_const_expr(pt, 3, 30,num_const(1))), 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, new_const_expr(pt, 5, 11, num_const(10))), 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); } TEST(lists, t) { struct parser_test *pt = new_parser_test(t, // 123456789 "[1, 2 3,,# some comment\n" ",4 ~x ]" ); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_LIST, .position = POS(1, 1), .list = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_const_expr(pt, 1, 2, num_const(1))), LIST_ADD list_item(false, new_const_expr(pt, 1, 5, num_const(2))), LIST_ADD list_item(false, new_const_expr(pt, 1, 7, num_const(3))), LIST_ADD list_item(false, new_const_expr(pt, 2, 2, num_const(4))), LIST_ADD list_item(true, new_var(pt, 2, 5, "x")), LIST_END, }); // expect_eof(pt); destroy_parser_test(pt); } TEST(stringification, t) { struct parser_test *pt = new_parser_test(t, "'hello"); expect_expr(pt, const_expr(1, 1, string_const(pt, "hello"))); expect_eof(pt); destroy_parser_test(pt); } TEST(line_continuation, t) { struct parser_test *pt = new_parser_test(t, "foo \\ # A comment\n" "bar \\\n" "\\\n" "baz\n" "xyz" ); 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, new_var(pt, 2, 1, "bar")), LIST_ADD list_item(false, new_var(pt, 4, 1, "baz")), LIST_END, }, }); expect_expr(pt, var(pt, 5, 1, "xyz")); expect_eof(pt); destroy_parser_test(pt); } TEST(dict, t) { struct parser_test *pt = new_parser_test(t, // 1 2 3 // 12345678901234567890123456789012 "[ a -> b, 'c -> d\n" "e ->f g ->h\n" "(i j) -> [1 2 3],, 42 -> [x -> y]\n" "true -> false]\n" ); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_DICT, .position = POS(1, 1), .dict = LIST_BEGIN(pt, dict) LIST_ADD (struct apfl_expr_dict_pair) { .k = new_var(pt, 1, 3, "a"), .v = new_var(pt, 1, 8, "b"), }, LIST_ADD (struct apfl_expr_dict_pair) { .k = new_const_expr(pt, 1, 11, string_const(pt, "c")), .v = new_var(pt, 1, 17, "d"), }, LIST_ADD (struct apfl_expr_dict_pair) { .k = new_var(pt, 2, 1, "e"), .v = new_var(pt, 2, 5, "f"), }, LIST_ADD (struct apfl_expr_dict_pair) { .k = new_var(pt, 2, 7, "g"), .v = new_var(pt, 2, 11, "h"), }, LIST_ADD (struct apfl_expr_dict_pair) { .k = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(3, 1), .call = (struct apfl_expr_call) { .callee = new_var(pt, 3, 2, "i"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 3, 4, "j")), LIST_END, }, } END_NEW, .v = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_LIST, .position = POS(3, 10), .list = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_const_expr(pt, 3, 11, num_const(1))), LIST_ADD list_item(false, new_const_expr(pt, 3, 13, num_const(2))), LIST_ADD list_item(false, new_const_expr(pt, 3, 15, num_const(3))), LIST_END, } END_NEW, }, LIST_ADD (struct apfl_expr_dict_pair) { .k = new_const_expr(pt, 3, 20, num_const(42)), .v = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_DICT, .position = POS(3, 26), .dict = LIST_BEGIN(pt, dict) LIST_ADD (struct apfl_expr_dict_pair) { .k = new_var(pt, 3, 27, "x"), .v = new_var(pt, 3, 32, "y"), }, LIST_END, } END_NEW, }, LIST_ADD (struct apfl_expr_dict_pair) { .k = new_const_expr(pt, 4, 1, bool_const(true)), .v = new_const_expr(pt, 4, 9, bool_const(false)), }, LIST_END }); expect_eof(pt); destroy_parser_test(pt); } TEST(assignment, t) { struct parser_test *pt = new_parser_test(t, // 123456 "a = b\n" // 1 2 // 12345678901234567890123456 "foo.bar := bar?pred = baz\n" // 1 2 3 // 12345678901234567890123456789012345 "[[1 2] ~xs] = bla@(a b) := abc ~xyz\n" ); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_ASSIGNMENT, .position = POS(1, 3), .assignment = (struct apfl_expr_assignment) { .local = false, .lhs = (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_VAR, .var = new_string(pt, "a"), }, .rhs = new_var(pt, 1, 5, "b"), }, }); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_ASSIGNMENT, .position = POS(2, 9), .assignment = (struct apfl_expr_assignment) { .local = true, .lhs = (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_DOT, .dot = (struct apfl_expr_assignable_dot) { .lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_VAR, .var = new_string(pt, "foo"), } END_NEW, .rhs = new_string(pt, "bar"), }, }, .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_ASSIGNMENT, .position = POS(2, 21), .assignment = (struct apfl_expr_assignment) { .local = false, .lhs = (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_PREDICATE, .predicate = (struct apfl_expr_assignable_predicate) { .lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_VAR, .var = new_string(pt, "bar"), } END_NEW, .rhs = new_var(pt, 2, 16, "pred"), }, }, .rhs = new_var(pt, 2, 23, "baz"), }, } END_NEW, }, }); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_ASSIGNMENT, .position = POS(3, 13), .assignment = (struct apfl_expr_assignment) { .local = false, .lhs = (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_LIST, .list = LIST_BEGIN(pt, assignable_list) LIST_ADD (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_LIST, .list = LIST_BEGIN(pt, assignable_list) LIST_ADD (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_CONSTANT, .constant = num_const(1), }, LIST_ADD (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_CONSTANT, .constant = num_const(2), }, LIST_END, }, LIST_ADD (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_EXPAND, .expand = BEGIN_NEW(pt, struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_VAR, .var = new_string(pt, "xs"), } END_NEW, }, LIST_END, }, .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_ASSIGNMENT, .position = POS(3, 25), .assignment = (struct apfl_expr_assignment) { .local = true, .lhs = (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_AT, .at = (struct apfl_expr_assignable_at) { .lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_VAR, .var = new_string(pt, "bla"), } END_NEW, .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(3, 19), .call = (struct apfl_expr_call) { .callee = new_var(pt, 3, 20, "a"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 3, 22, "b")), LIST_END, }, } END_NEW, }, }, .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(3, 28), .call = (struct apfl_expr_call) { .callee = new_var(pt, 3, 28, "abc"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(true, new_var(pt, 3, 33, "xyz")), LIST_END, }, } END_NEW, }, } END_NEW, }, }); expect_eof(pt); destroy_parser_test(pt); } TEST(simple_function, t) { struct parser_test *pt = new_parser_test(t, // 12345678901 "{{\n" " }; foo \\\n" "bar { # comment...\n" " baz = 10\n" " a\n" " b\n" "}; c}\n" ); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_SIMPLE_FUNC, .position = POS(1, 1), .simple_func = LIST_BEGIN(pt, body) LIST_ADD (struct apfl_expr) { .type = APFL_EXPR_SIMPLE_FUNC, .position = POS(1, 2), .simple_func = LIST_BEGIN(pt, body) LIST_END, }, LIST_ADD (struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(2, 8), .call = (struct apfl_expr_call) { .callee = new_var(pt, 2, 8, "foo"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 3, 1, "bar")), LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_SIMPLE_FUNC, .position = POS(3, 5), .simple_func = LIST_BEGIN(pt, body) LIST_ADD (struct apfl_expr) { .type = APFL_EXPR_ASSIGNMENT, .position = POS(4, 9), .assignment = (struct apfl_expr_assignment) { .local = false, .lhs = (struct apfl_expr_assignable) { .type = APFL_EXPR_ASSIGNABLE_VAR, .var = new_string(pt, "baz"), }, .rhs = new_const_expr(pt, 4, 11, num_const(10)), }, }, LIST_ADD var(pt, 5, 5, "a"), LIST_ADD var(pt, 6, 5, "b"), LIST_END, } END_NEW), LIST_END, }, }, LIST_ADD var(pt, 7, 4, "c"), LIST_END, }); destroy_parser_test(pt); } TEST(complex_function, t) { struct parser_test *pt = new_parser_test(t, // 1 2 // 12345678901234567890123456789 "{ a ~b c?d?(e f) -> foo; bar\n" " baz\n" "1 [] ->; x [y?x ~ys] ->\n" " a\n" " b\n" " c { x -> y }\n" " \n" "}\n" ); expect_expr(pt, (struct apfl_expr) { .type = APFL_EXPR_COMPLEX_FUNC, .position = POS(1, 1), .complex_func = LIST_BEGIN(pt, complex_func) LIST_ADD { .params = LIST_BEGIN(pt, params) LIST_ADD { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "a"), }, LIST_ADD { .type = APFL_EXPR_PARAM_EXPAND, .expand = BEGIN_NEW(pt, struct apfl_expr_param) { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "b"), } END_NEW }, LIST_ADD { .type = APFL_EXPR_PARAM_PREDICATE, .predicate = { .lhs = BEGIN_NEW(pt, struct apfl_expr_param) { .type = APFL_EXPR_PARAM_PREDICATE, .predicate = { .lhs = BEGIN_NEW(pt, struct apfl_expr_param) { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "c") } END_NEW, .rhs = new_var(pt, 1, 10, "d"), }, } END_NEW, .rhs = BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_CALL, .position = POS(1, 12), .call = { .callee = new_var(pt, 1, 13, "e"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, new_var(pt, 1, 15, "f")), LIST_END, }, } END_NEW, }, }, LIST_END, .body = LIST_BEGIN(pt, body) LIST_ADD var(pt, 1, 21, "foo"), LIST_ADD var(pt, 1, 26, "bar"), LIST_ADD var(pt, 2, 5, "baz"), LIST_END, }, LIST_ADD { .params = LIST_BEGIN(pt, params) LIST_ADD { .type = APFL_EXPR_PARAM_CONSTANT, .constant = num_const(1), }, LIST_ADD { .type = APFL_EXPR_PARAM_LIST, .list = LIST_BEGIN(pt, params) LIST_END, }, LIST_END, .body = LIST_BEGIN(pt, body) LIST_END, }, LIST_ADD { .params = LIST_BEGIN(pt, params) LIST_ADD { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "x"), }, LIST_ADD { .type = APFL_EXPR_PARAM_LIST, .list = LIST_BEGIN(pt, params) LIST_ADD { .type = APFL_EXPR_PARAM_PREDICATE, .predicate = { .lhs = BEGIN_NEW(pt, struct apfl_expr_param) { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "y"), } END_NEW, .rhs = new_var(pt, 3, 15, "x"), }, }, LIST_ADD { .type = APFL_EXPR_PARAM_EXPAND, .expand = BEGIN_NEW(pt, struct apfl_expr_param) { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "ys"), } END_NEW, }, LIST_END }, LIST_END, .body = LIST_BEGIN(pt, body) LIST_ADD var(pt, 4, 5, "a"), LIST_ADD var(pt, 5, 5, "b"), LIST_ADD { .type = APFL_EXPR_CALL, .position = POS(6, 5), .call = { .callee = new_var(pt, 6, 5, "c"), .arguments = LIST_BEGIN(pt, list) LIST_ADD list_item(false, BEGIN_NEW(pt, struct apfl_expr) { .type = APFL_EXPR_COMPLEX_FUNC, .position = POS(6, 7), .complex_func = LIST_BEGIN(pt, complex_func) LIST_ADD { .params = LIST_BEGIN(pt, params) LIST_ADD { .type = APFL_EXPR_PARAM_VAR, .var = new_string(pt, "x"), }, LIST_END, .body = LIST_BEGIN(pt, body) LIST_ADD var(pt, 6, 14, "y"), LIST_END } LIST_END } END_NEW), LIST_END, }, } LIST_END }, LIST_END, }); expect_eof(pt); destroy_parser_test(pt); } TEST(err_empty_assignment, t) { struct parser_test *pt = new_parser_test(t, "= foo bar"); expect_error_of_type(pt, APFL_ERR_EMPTY_ASSIGNMENT); destroy_parser_test(pt); } TEST(err_mismatching_parens, t) { struct parser_test *pt = new_parser_test(t, "{[(}"); expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN); destroy_parser_test(pt); } TEST(err_unclosed_func, t) { struct parser_test *pt = new_parser_test(t, "{ foo -> bar; baz a; b ->"); expect_error_of_type(pt, APFL_ERR_UNEXPECTED_EOF); destroy_parser_test(pt); } TEST(err_unclosed_paren, t) { struct parser_test *pt = new_parser_test(t, "(a b.c@d"); expect_error_of_type(pt, APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN); destroy_parser_test(pt); } TEST(err_assignment_missing_rhs, t) { struct parser_test *pt = new_parser_test(t, "foo = bar = ;"); expect_error_of_type(pt, APFL_ERR_EMPTY_ASSIGNMENT); // TODO: Actually kinda weird that we return the same error as in err_empty_assignment. destroy_parser_test(pt); } TEST(err_assignment_invalid_lhs, t) { struct parser_test *pt = new_parser_test(t, "{foo} = bar"); expect_error_of_type(pt, APFL_ERR_INVALID_ASSIGNMENT_LHS); destroy_parser_test(pt); pt = new_parser_test(t, "(a b) = bar"); expect_error_of_type(pt, APFL_ERR_INVALID_ASSIGNMENT_LHS); destroy_parser_test(pt); } TEST(err_fragments_after_mapsto_in_empty_dict, t) { struct parser_test *pt = new_parser_test(t, "[-> foo]"); expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN); destroy_parser_test(pt); } TEST(err_fragments_after_mapsto_in_dict, t) { struct parser_test *pt = new_parser_test(t, "[a -> b, (c d) -> e foo->]"); expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN); destroy_parser_test(pt); } TEST(err_mapsto_in_list, t) { struct parser_test *pt = new_parser_test(t, "[a b c -> d]"); expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN); destroy_parser_test(pt); } TEST(err_expr_in_param, t) { struct parser_test *pt = new_parser_test(t, "{ foo (bar baz) -> }"); expect_error_of_type(pt, APFL_ERR_UNEXPECTED_EXPRESSION); destroy_parser_test(pt); } TEST(err_statements_before_params, t) { struct parser_test *pt = new_parser_test(t, "{ a = b = foo bar; 1 \n baz -> }"); expect_error_of_type(pt, APFL_ERR_STATEMENTS_BEFORE_PARAMETERS); 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), ADDTEST(lists), ADDTEST(stringification), ADDTEST(line_continuation), ADDTEST(dict), ADDTEST(assignment), ADDTEST(simple_function), ADDTEST(complex_function), ADDTEST(err_empty_assignment), ADDTEST(err_mismatching_parens), ADDTEST(err_unclosed_func), ADDTEST(err_unclosed_paren), ADDTEST(err_assignment_missing_rhs), ADDTEST(err_assignment_invalid_lhs), ADDTEST(err_fragments_after_mapsto_in_empty_dict), ADDTEST(err_fragments_after_mapsto_in_dict), ADDTEST(err_mapsto_in_list), ADDTEST(err_expr_in_param), ADDTEST(err_statements_before_params), TESTS_END