#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