Files
scc/libs/parser/tests/test_parse_expr.c
zzy 5f915ba8d3 refactor(ast): 移除内置类型头文件并优化类型初始化
移除了独立的ast_builtin.h头文件,将内置类型定义整合到现有结构中,
同时修复了类型初始化函数命名不一致的问题。

BREAKING CHANGE: 原有的ast_builtin.h头文件已被移除,
相关内置类型声明已重构为新的接口形式。

fix(parser): 修复表达式解析中的位置信息处理

确保条件表达式和逗号表达式的解析正确获取并传递位置信息,
避免在语法树构建过程中丢失源码位置。

refactor(ir): 修复IR转储中的类型转换问题

添加必要的类型转换以防止整数溢出,并优化代码格式以提高可读性。

feat(parser): 添加表达式解析测试套件

引入全面的表达式解析测试框架,覆盖从基本表达式到复杂嵌套表达式
的各种场景,确保解析器功能的正确性和稳定性。
2026-03-18 12:18:56 +08:00

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(&lt, SCC_AST_OP_LESS, &a, &b, LOC);
SCC_CHECK_AST(&lt.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},
};