void init_func(void); #define TEST_INIT init_func() #include "parser_test.h" static scc_ast_type_t int_type; void init_func(void) { scc_ast_type_builtin_init(&int_type, SCC_AST_BUILTIN_TYPE_INT, scc_pos_create()); } #define LOC scc_pos_create() static void test_primary_expr(void) { TEST_CASE("identifier"); { scc_ast_expr_t ident; scc_ast_expr_identifier_init(&ident, "x", LOC); SCC_CHECK_AST(&ident.base, "x", scc_parse_expression); } TEST_CASE("integer literal"); { scc_ast_expr_t int_lit; scc_ast_expr_literal_int_init(&int_lit, "42", false, LOC); SCC_CHECK_AST(&int_lit.base, "42", scc_parse_expression); } TEST_CASE("string literal"); { scc_ast_expr_t str_lit; scc_ast_expr_literal_string_init(&str_lit, "\"hello\"", false, LOC); SCC_CHECK_AST(&str_lit.base, "\"hello\"", scc_parse_expression); } TEST_CASE("parenthesized expression"); { scc_ast_expr_t ident; scc_ast_expr_identifier_init(&ident, "y", LOC); // 解析器应直接返回内部表达式,因此期望结果与 "y" 相同 SCC_CHECK_AST(&ident.base, "(y)", scc_parse_expression); } } static void test_postfix_expr(void) { TEST_CASE("array subscript"); { scc_ast_expr_t a, index, subscript; scc_ast_expr_identifier_init(&a, "a", LOC); scc_ast_expr_literal_int_init(&index, "10", false, LOC); scc_ast_expr_array_subscript_init(&subscript, &a, &index, LOC); SCC_CHECK_AST(&subscript.base, "a[10]", scc_parse_expression); } TEST_CASE("function call (no arguments)"); { scc_ast_expr_t callee, call; scc_ast_expr_identifier_init(&callee, "f", LOC); scc_ast_expr_vec_t args; scc_vec_init(args); scc_ast_expr_call_init(&call, &callee, &args, LOC); SCC_CHECK_AST(&call.base, "f()", scc_parse_expression); } TEST_CASE("function call (with arguments)"); { scc_ast_expr_t callee, arg1, arg2, call; scc_ast_expr_identifier_init(&callee, "f", LOC); scc_ast_expr_literal_int_init(&arg1, "1", false, LOC); scc_ast_expr_identifier_init(&arg2, "x", LOC); scc_ast_expr_vec_t args; scc_vec_init(args); scc_vec_push(args, &arg1); scc_vec_push(args, &arg2); scc_ast_expr_call_init(&call, &callee, &args, LOC); SCC_CHECK_AST(&call.base, "f(1, x)", scc_parse_expression); } TEST_CASE("member access (.)"); { scc_ast_expr_t obj, member; scc_ast_expr_identifier_init(&obj, "s", LOC); scc_ast_expr_member_init(&member, &obj, "field", LOC); SCC_CHECK_AST(&member.base, "s.field", scc_parse_expression); } TEST_CASE("pointer member access (->)"); { scc_ast_expr_t ptr, member; scc_ast_expr_identifier_init(&ptr, "p", LOC); scc_ast_expr_ptr_member_init(&member, &ptr, "field", LOC); SCC_CHECK_AST(&member.base, "p->field", scc_parse_expression); } TEST_CASE("postfix increment/decrement"); { scc_ast_expr_t x, post_inc, post_dec; scc_ast_expr_identifier_init(&x, "x", LOC); scc_ast_expr_unary_init(&post_inc, SCC_AST_OP_POSTFIX_INCREMENT, &x, LOC); scc_ast_expr_unary_init(&post_dec, SCC_AST_OP_POSTFIX_DECREMENT, &x, LOC); SCC_CHECK_AST(&post_inc.base, "x++", scc_parse_expression); SCC_CHECK_AST(&post_dec.base, "x--", scc_parse_expression); } } static void test_unary_expr(void) { scc_ast_expr_t x; scc_ast_expr_identifier_init(&x, "x", LOC); TEST_CASE("prefix increment"); { scc_ast_expr_t pre_inc; scc_ast_expr_unary_init(&pre_inc, SCC_AST_OP_PREFIX_INCREMENT, &x, LOC); SCC_CHECK_AST(&pre_inc.base, "++x", scc_parse_expression); } TEST_CASE("prefix decrement"); { scc_ast_expr_t pre_dec; scc_ast_expr_unary_init(&pre_dec, SCC_AST_OP_PREFIX_DECREMENT, &x, LOC); SCC_CHECK_AST(&pre_dec.base, "--x", scc_parse_expression); } TEST_CASE("address-of"); { scc_ast_expr_t addr; scc_ast_expr_unary_init(&addr, SCC_AST_OP_ADDRESS_OF, &x, LOC); SCC_CHECK_AST(&addr.base, "&x", scc_parse_expression); } TEST_CASE("indirection (dereference)"); { scc_ast_expr_t deref; scc_ast_expr_unary_init(&deref, SCC_AST_OP_INDIRECTION, &x, LOC); SCC_CHECK_AST(&deref.base, "*x", scc_parse_expression); } TEST_CASE("unary plus"); { scc_ast_expr_t plus; scc_ast_expr_unary_init(&plus, SCC_AST_OP_UNARY_PLUS, &x, LOC); SCC_CHECK_AST(&plus.base, "+x", scc_parse_expression); } TEST_CASE("unary minus"); { scc_ast_expr_t minus; scc_ast_expr_unary_init(&minus, SCC_AST_OP_UNARY_MINUS, &x, LOC); SCC_CHECK_AST(&minus.base, "-x", scc_parse_expression); } TEST_CASE("bitwise NOT"); { scc_ast_expr_t bit_not; scc_ast_expr_unary_init(&bit_not, SCC_AST_OP_BITWISE_NOT, &x, LOC); SCC_CHECK_AST(&bit_not.base, "~x", scc_parse_expression); } TEST_CASE("logical NOT"); { scc_ast_expr_t log_not; scc_ast_expr_unary_init(&log_not, SCC_AST_OP_LOGICAL_NOT, &x, LOC); SCC_CHECK_AST(&log_not.base, "!x", scc_parse_expression); } TEST_CASE("sizeof expression"); { scc_ast_expr_t sizeof_expr; scc_ast_expr_sizeof_init(&sizeof_expr, NULL, &x, LOC); SCC_CHECK_AST(&sizeof_expr.base, "sizeof(x)", scc_parse_expression); } TEST_CASE("sizeof type"); { scc_ast_expr_t sizeof_type; scc_ast_expr_sizeof_init(&sizeof_type, &int_type, NULL, LOC); SCC_CHECK_AST(&sizeof_type.base, "sizeof(int)", scc_parse_expression); } } static void test_cast_expr(void) { TEST_CASE("cast"); { scc_ast_expr_t x, cast; scc_ast_expr_identifier_init(&x, "x", LOC); scc_ast_expr_cast_init(&cast, &int_type, &x, LOC); SCC_CHECK_AST(&cast.base, "(int)x", scc_parse_expression); } } static void test_binary_expr(void) { scc_ast_expr_t a, b, c, d; scc_ast_expr_identifier_init(&a, "a", LOC); scc_ast_expr_identifier_init(&b, "b", LOC); scc_ast_expr_identifier_init(&c, "c", LOC); scc_ast_expr_identifier_init(&d, "d", LOC); TEST_CASE("multiplication and addition (priority)"); { // a * b + c scc_ast_expr_t mul, add; scc_ast_expr_binary_init(&mul, SCC_AST_OP_MUL, &a, &b, LOC); scc_ast_expr_binary_init(&add, SCC_AST_OP_ADD, &mul, &c, LOC); SCC_CHECK_AST(&add.base, "a * b + c", scc_parse_expression); } TEST_CASE("subtraction (left associativity)"); { // a - b - c => (a - b) - c scc_ast_expr_t sub1, sub2; scc_ast_expr_binary_init(&sub1, SCC_AST_OP_SUB, &a, &b, LOC); scc_ast_expr_binary_init(&sub2, SCC_AST_OP_SUB, &sub1, &c, LOC); SCC_CHECK_AST(&sub2.base, "a - b - c", scc_parse_expression); } TEST_CASE("shift"); { // a << b scc_ast_expr_t shift; scc_ast_expr_binary_init(&shift, SCC_AST_OP_LEFT_SHIFT, &a, &b, LOC); SCC_CHECK_AST(&shift.base, "a << b", scc_parse_expression); } TEST_CASE("relational"); { // a < b scc_ast_expr_t lt; scc_ast_expr_binary_init(<, SCC_AST_OP_LESS, &a, &b, LOC); SCC_CHECK_AST(<.base, "a < b", scc_parse_expression); } TEST_CASE("equality"); { // a == b scc_ast_expr_t eq; scc_ast_expr_binary_init(&eq, SCC_AST_OP_EQUAL, &a, &b, LOC); SCC_CHECK_AST(&eq.base, "a == b", scc_parse_expression); } TEST_CASE("bitwise operators (priority)"); { // a & b ^ c | d => ((a & b) ^ c) | d scc_ast_expr_t bitand, bitxor, bitor; scc_ast_expr_binary_init(&bitand, SCC_AST_OP_BITWISE_AND, &a, &b, LOC); scc_ast_expr_binary_init(&bitxor, SCC_AST_OP_BITWISE_XOR, &bitand, &c, LOC); scc_ast_expr_binary_init(&bitor, SCC_AST_OP_BITWISE_OR, &bitxor, &d, LOC); SCC_CHECK_AST(&bitor.base, "a & b ^ c | d", scc_parse_expression); } TEST_CASE("logical operators (priority)"); { // a && b || c => (a && b) || c scc_ast_expr_t logand, logor; scc_ast_expr_binary_init(&logand, SCC_AST_OP_LOGICAL_AND, &a, &b, LOC); scc_ast_expr_binary_init(&logor, SCC_AST_OP_LOGICAL_OR, &logand, &c, LOC); SCC_CHECK_AST(&logor.base, "a && b || c", scc_parse_expression); } } static void test_conditional_expr(void) { scc_ast_expr_t a, b, c; scc_ast_expr_identifier_init(&a, "a", LOC); scc_ast_expr_identifier_init(&b, "b", LOC); scc_ast_expr_identifier_init(&c, "c", LOC); TEST_CASE("simple conditional"); { scc_ast_expr_t cond; scc_ast_expr_cond_init(&cond, &a, &b, &c, LOC); SCC_CHECK_AST(&cond.base, "a ? b : c", scc_parse_expression); } TEST_CASE("nested conditional (right associative)"); { // a ? b : c ? d : e => a ? b : (c ? d : e) scc_ast_expr_t d, e, inner, outer; scc_ast_expr_identifier_init(&d, "d", LOC); scc_ast_expr_identifier_init(&e, "e", LOC); scc_ast_expr_cond_init(&inner, &c, &d, &e, LOC); scc_ast_expr_cond_init(&outer, &a, &b, &inner, LOC); SCC_CHECK_AST(&outer.base, "a ? b : c ? d : e", scc_parse_expression); } } static void test_assignment_expr(void) { scc_ast_expr_t a, b, c; scc_ast_expr_identifier_init(&a, "a", LOC); scc_ast_expr_identifier_init(&b, "b", LOC); scc_ast_expr_identifier_init(&c, "c", LOC); scc_ast_expr_t lit42; scc_ast_expr_literal_int_init(&lit42, "42", false, LOC); TEST_CASE("simple assignment"); { // a = 42 scc_ast_expr_t assign; scc_ast_expr_binary_init(&assign, SCC_AST_OP_ASSIGN, &a, &lit42, LOC); SCC_CHECK_AST(&assign.base, "a = 42", scc_parse_expression); } TEST_CASE("chained assignment (right associative)"); { // a = b = c scc_ast_expr_t inner, outer; scc_ast_expr_binary_init(&inner, SCC_AST_OP_ASSIGN, &b, &c, LOC); scc_ast_expr_binary_init(&outer, SCC_AST_OP_ASSIGN, &a, &inner, LOC); SCC_CHECK_AST(&outer.base, "a = b = c", scc_parse_expression); } TEST_CASE("compound assignment"); { // a += b scc_ast_expr_t add_assign; scc_ast_expr_binary_init(&add_assign, SCC_AST_OP_ASSIGN_ADD, &a, &b, LOC); SCC_CHECK_AST(&add_assign.base, "a += b", scc_parse_expression); } TEST_CASE("assignment with expression"); { // a = a - b + 42 scc_ast_expr_t sub, add, assign; scc_ast_expr_binary_init(&sub, SCC_AST_OP_SUB, &a, &b, LOC); scc_ast_expr_binary_init(&add, SCC_AST_OP_ADD, &sub, &lit42, LOC); scc_ast_expr_binary_init(&assign, SCC_AST_OP_ASSIGN, &a, &add, LOC); SCC_CHECK_AST(&assign.base, "a = a - b + 42", scc_parse_expression); } } static void test_comma_expr(void) { TEST_CASE("comma operator"); { scc_ast_expr_t a, b, comma; scc_ast_expr_identifier_init(&a, "a", LOC); scc_ast_expr_identifier_init(&b, "b", LOC); scc_ast_expr_binary_init(&comma, SCC_AST_OP_COMMA, &a, &b, LOC); SCC_CHECK_AST(&comma.base, "a, b", scc_parse_expression); } } static void test_complex_expr(void) { TEST_CASE("mixed operators with precedence"); { scc_ast_expr_t a, b, c, d; scc_ast_expr_identifier_init(&a, "a", LOC); scc_ast_expr_identifier_init(&b, "b", LOC); scc_ast_expr_identifier_init(&c, "c", LOC); scc_ast_expr_identifier_init(&d, "d", LOC); // a + b * c - d scc_ast_expr_t mul, add, sub; scc_ast_expr_binary_init(&mul, SCC_AST_OP_MUL, &b, &c, LOC); scc_ast_expr_binary_init(&add, SCC_AST_OP_ADD, &a, &mul, LOC); scc_ast_expr_binary_init(&sub, SCC_AST_OP_SUB, &add, &d, LOC); SCC_CHECK_AST(&sub.base, "a + b * c - d", scc_parse_expression); } TEST_CASE("postfix and unary combination"); { scc_ast_expr_t p, post_inc, deref; scc_ast_expr_identifier_init(&p, "p", LOC); scc_ast_expr_unary_init(&post_inc, SCC_AST_OP_POSTFIX_INCREMENT, &p, LOC); scc_ast_expr_unary_init(&deref, SCC_AST_OP_INDIRECTION, &post_inc, LOC); SCC_CHECK_AST(&deref.base, "*p++", scc_parse_expression); } } static void test_detail_expr(void) { TEST_CASE("multi string literal connection"); scc_ast_expr_t str; scc_ast_expr_literal_string_init(&str, "\"ab\"", false, scc_pos_create()); SCC_CHECK_AST(&str.base, "\"a\" \"b\"", scc_parse_expression); } TEST_LIST = { {"test_primary_expr", test_primary_expr}, {"test_postfix_expr", test_postfix_expr}, {"test_unary_expr", test_unary_expr}, {"test_cast_expr", test_cast_expr}, {"test_binary_expr", test_binary_expr}, {"test_conditional_expr", test_conditional_expr}, {"test_assignment_expr", test_assignment_expr}, {"test_comma_expr", test_comma_expr}, {"test_complex_expr", test_complex_expr}, {"test_detail_expr", test_detail_expr}, {NULL, NULL}, };