apfl/src/parser_test.c

677 lines
28 KiB
C
Raw Normal View History

2021-12-17 20:08:03 +00:00
#include <assert.h>
#include <stdarg.h>
2021-12-16 22:42:37 +00:00
#include "apfl.h"
2021-12-17 20:08:03 +00:00
#include "resizable.h"
2021-12-16 22:42:37 +00:00
#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");
2021-12-16 22:42:37 +00:00
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;
}
2021-12-17 20:08:03 +00:00
static struct apfl_expr_list_item
list_item(bool expand, struct apfl_expr *expr)
{
return (struct apfl_expr_list_item) {
.expand = expand,
.expr = expr,
};
}
2021-12-16 22:42:37 +00:00
// 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 )))
2021-12-17 20:08:03 +00:00
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);
}
2021-12-16 22:42:37 +00:00
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,
2021-12-17 20:08:03 +00:00
.position = POS(1, 1),
2021-12-16 22:42:37 +00:00
.call = (struct apfl_expr_call) {
2021-12-17 20:08:03 +00:00
.callee = new_var(pt, 1, 1, "print"),
.arguments = LIST_BEGIN(pt, list)
LIST_ADD (struct apfl_expr_list_item) {
2021-12-16 22:42:37 +00:00
.expand = false,
.expr = BEGIN_NEW(pt, struct apfl_expr) {
.type = APFL_EXPR_CONSTANT,
2021-12-17 20:08:03 +00:00
.position = POS(1, 7),
2021-12-16 22:42:37 +00:00
.constant.type = APFL_EXPR_CONST_STRING,
.constant.string = new_string(pt, "Hello World!"),
} END_NEW,
2021-12-17 20:08:03 +00:00
},
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"),
2021-12-16 22:42:37 +00:00
},
2021-12-17 20:08:03 +00:00
.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,
2021-12-16 22:42:37 +00:00
},
});
expect_eof(pt);
destroy_parser_test(pt);
}
TESTS_BEGIN
ADDTEST(empty),
ADDTEST(hello_world),
2021-12-17 20:08:03 +00:00
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),
2021-12-16 22:42:37 +00:00
TESTS_END