151 lines
4.3 KiB
C
151 lines
4.3 KiB
C
|
|
#include "apfl.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 EOF");
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 )))
|
||
|
|
|
||
|
|
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.line = 1,
|
||
|
|
.position.col = 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) {
|
||
|
|
.expand = false,
|
||
|
|
.expr = BEGIN_NEW(pt, struct apfl_expr) {
|
||
|
|
.type = APFL_EXPR_CONSTANT,
|
||
|
|
.position.line = 1,
|
||
|
|
.position.col = 7,
|
||
|
|
.constant.type = APFL_EXPR_CONST_STRING,
|
||
|
|
.constant.string = new_string(pt, "Hello World!"),
|
||
|
|
} END_NEW,
|
||
|
|
} END_NEW,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
expect_eof(pt);
|
||
|
|
destroy_parser_test(pt);
|
||
|
|
}
|
||
|
|
|
||
|
|
TESTS_BEGIN
|
||
|
|
ADDTEST(empty),
|
||
|
|
ADDTEST(hello_world),
|
||
|
|
TESTS_END
|