移除了独立的ast_builtin.h头文件,将内置类型定义整合到现有结构中, 同时修复了类型初始化函数命名不一致的问题。 BREAKING CHANGE: 原有的ast_builtin.h头文件已被移除, 相关内置类型声明已重构为新的接口形式。 fix(parser): 修复表达式解析中的位置信息处理 确保条件表达式和逗号表达式的解析正确获取并传递位置信息, 避免在语法树构建过程中丢失源码位置。 refactor(ir): 修复IR转储中的类型转换问题 添加必要的类型转换以防止整数溢出,并优化代码格式以提高可读性。 feat(parser): 添加表达式解析测试套件 引入全面的表达式解析测试框架,覆盖从基本表达式到复杂嵌套表达式 的各种场景,确保解析器功能的正确性和稳定性。
396 lines
13 KiB
C
396 lines
13 KiB
C
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},
|
|
};
|