diff --git a/src/parser_test.c b/src/parser_test.c index b0a1b89..95d9f98 100644 --- a/src/parser_test.c +++ b/src/parser_test.c @@ -1,4 +1,8 @@ +#include +#include + #include "apfl.h" +#include "resizable.h" #include "test.h" @@ -102,10 +106,111 @@ new_helper(struct parser_test *pt, size_t size, void *data) 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); @@ -116,28 +221,440 @@ 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.line = 1, - .position.col = 1, + .position = POS(1, 1), .call = (struct apfl_expr_call) { - .callee = BEGIN_NEW(pt, struct apfl_expr) { - .type = APFL_EXPR_VAR, - .position.line = 1, - .position.col = 1, - .var = new_string(pt, "print"), - } END_NEW, - .arguments = (struct apfl_expr_list) { - .len = 1, - .items = BEGIN_NEW(pt, struct apfl_expr_list_item) { + .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.line = 1, - .position.col = 7, + .position = POS(1, 7), .constant.type = APFL_EXPR_CONST_STRING, .constant.string = new_string(pt, "Hello World!"), } END_NEW, - } 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); @@ -147,4 +664,13 @@ TEST(hello_world, t) { 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