Files
scc/libs/pproc/tests/test_unit.c
zzy 8007825800 feat(pproc): 实现预处理器条件编译和可变参数宏支持
- 添加了完整的条件编译功能,包括 #if、#elif、#else、#endif 指令
- 实现了数值常量表达式的解析和求值
- 支持嵌套条件编译和与其他指令混合使用
- 实现了可变参数宏定义和 __VA_ARGS__ 替换功能
- 改进了宏展开机制以正确处理可变参数宏
- 重构了预处理器指令处理逻辑,提高了代码可维护性
- 添加了相应的单元测试用例验证新功能
2026-02-21 15:59:31 +08:00

428 lines
16 KiB
C

#include <assert.h>
#include <scc_pproc.h>
#include <string.h>
#include <utest/acutest.h>
static cbool process_input(const char *input, scc_cstring_t *output) {
int ret = 0;
scc_sstream_t mem_stream;
ret = scc_sstream_init_by_buffer(&mem_stream, input, strlen(input), false,
16);
Assert(ret == 0);
scc_lexer_t lexer;
scc_lexer_init(&lexer, scc_sstream_to_ring(&mem_stream));
scc_pproc_t pp;
scc_pproc_init(&pp, scc_lexer_to_ring(&lexer, 8, true));
scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pp, 8);
*output = scc_cstring_from_cstr("");
scc_lexer_tok_t tok;
while (1) {
scc_ring_next_consume(*tok_ring, tok, ret);
if (!ret) {
break;
}
scc_cstring_append(output, &tok.lexeme);
scc_lexer_tok_drop(&tok);
}
scc_pproc_drop(&pp);
scc_lexer_drop(&lexer);
scc_sstream_drop(&mem_stream);
return true;
}
#define CHECK_PP_OUTPUT_EXACT(input, expect) \
do { \
scc_cstring_t output; \
process_input(input, &output); \
assert(output.data != NULL); \
TEST_CHECK(strcmp(output.data, expect) == 0); \
TEST_MSG("Expected: %s", expect); \
TEST_MSG("Produced: %s", output.data); \
} while (0)
#define CHECK_PP_OUTPUT_CONTAIN(input, expect) \
do { \
scc_cstring_t output; \
process_input(input, &output); \
assert(output.data != NULL); \
TEST_CHECK(strstr(output.data, expect) != NULL); \
TEST_MSG("Expected: %s", expect); \
TEST_MSG("Produced: %s", output.data); \
} while (0)
static void test_define_simple_no_macro(void) {
TEST_CASE("simple no macro");
CHECK_PP_OUTPUT_EXACT("a", "a");
CHECK_PP_OUTPUT_EXACT("a()", "a()");
CHECK_PP_OUTPUT_EXACT("a(b)", "a(b)");
CHECK_PP_OUTPUT_EXACT("a(b, c)", "a(b, c)");
CHECK_PP_OUTPUT_EXACT("a(b, c, d)", "a(b, c, d)");
}
static void test_define_simple_object_macro(void) {
TEST_CASE("simple object-like macro");
CHECK_PP_OUTPUT_EXACT("#define MAX 100\nMAX\n", "100\n");
CHECK_PP_OUTPUT_EXACT("#define NAME test\r\nNAME\n", "test\n");
}
static void test_define_complex_object_macro(void) {
TEST_CASE("complex object-like macro");
CHECK_PP_OUTPUT_EXACT("#define VALUE (100 + 50)\nVALUE\n", "(100 + 50)\n");
CHECK_PP_OUTPUT_EXACT("#define PI 3.14159\nPI\n", "3.14159\n");
}
static void test_define_object_macro_backspace(void) {
TEST_CASE("object-like macro check backspace");
CHECK_PP_OUTPUT_EXACT("#define MAX 100\nMAX\n", "100\n");
CHECK_PP_OUTPUT_EXACT("#define NAME \ttest\r\nNAME\n", "test\n");
CHECK_PP_OUTPUT_EXACT("#define \tVALUE (100 \t+ 50)\nVALUE\n",
"(100 + 50)\n");
CHECK_PP_OUTPUT_EXACT("#define \tPI \t 3.14159\nPI\n", "3.14159\n");
}
static void test_define_function_macro(void) {
TEST_CASE("function-like macro");
CHECK_PP_OUTPUT_EXACT("#define ADD(a,b) a + b\nADD(1, 2)\n", "1 + 2\n");
CHECK_PP_OUTPUT_EXACT("#define ADD( a , b ) a + b\nADD(1, 2)\n", "1 + 2\n");
CHECK_PP_OUTPUT_EXACT(
"#define MAX(a,b) ((a) > (b) ? (a) : (b))\nMAX(10, 20)\n",
"((10) > (20) ? (10) : (20))\n");
}
static void test_define_stringify_operator(void) {
TEST_CASE("stringify operator (#)");
CHECK_PP_OUTPUT_EXACT("#define STRINGIFY(x) #x\nSTRINGIFY(hello)\n",
"\"hello\"\n");
CHECK_PP_OUTPUT_EXACT("#define STR(x) #x\nSTR(test value)\n",
"\"test value\"\n");
CHECK_PP_OUTPUT_EXACT("#define STR(x) #x\nSTR(A B \"ab\")\n",
"\"A B \"ab\"\"\n");
CHECK_PP_OUTPUT_EXACT("#define STR(x) # x\nSTR(A B \"ab\")\n",
"\"A B \"ab\"\"\n");
}
static void test_define_concat_operator(void) {
TEST_CASE("concatenation operator (##)");
CHECK_PP_OUTPUT_EXACT("#define CONCAT a##b\nCONCAT\n", "ab\n");
CHECK_PP_OUTPUT_EXACT("#define CONCAT a ## b\nCONCAT\n", "ab\n");
CHECK_PP_OUTPUT_EXACT("#define CONCAT(a,b) a##b\nCONCAT(hello,world)\n",
"helloworld\n");
CHECK_PP_OUTPUT_EXACT(
"#define CONCAT( a , b ) a ## b\nCONCAT( hello , world )\n",
"helloworld\n");
CHECK_PP_OUTPUT_EXACT("#define JOIN(pre,suf) pre ## suf\nJOIN(var, 123)\n",
"var123\n");
}
static void test_define_nested_macros(void) {
TEST_CASE("nested macros");
CHECK_PP_OUTPUT_EXACT(
"#define MAX 100\n#define TWICE_MAX (MAX * 2)\nTWICE_MAX\n",
"(100 * 2)\n");
CHECK_PP_OUTPUT_EXACT(
"#define A 1\n#define B (A + 1)\n#define C (B + 1)\nC\n",
"((1 + 1) + 1)\n");
CHECK_PP_OUTPUT_EXACT("#define A\n", "");
CHECK_PP_OUTPUT_EXACT("#undef A\n", "");
CHECK_PP_OUTPUT_EXACT(" # define A 1\nA", "1");
// CHECK_PP_OUTPUT_EXACT(" # define A 1 \nA", "1"); // TODO
}
static void test_undef_macros(void) {
TEST_CASE("test_undef_macros");
CHECK_PP_OUTPUT_EXACT("#define x 1\n"
"x\n"
"#undef x\n"
"x\n"
"#define x 2\n"
"x\n",
"1\nx\n2\n");
}
static void hard_test_define_func_macros(void) {
TEST_CASE("func_macros_hard with pp_01");
CHECK_PP_OUTPUT_EXACT("#define hash_hash # ## #\n"
"#define mkstr(a) # a\n"
"#define in_between(a) mkstr(a)\n"
"#define join(c, d) in_between(c hash_hash d)\n"
"char p[] = join(x, y);\n",
"char p[] = \"x ## y\";\n");
TEST_CASE("func_macros_hard with recursive define");
CHECK_PP_OUTPUT_EXACT("#define M1(x) M2(x + 1)\n"
"#define M2(x) M1(x * 2)\n"
"M1(5)\n",
"M1(5 + 1 * 2)\n");
CHECK_PP_OUTPUT_EXACT("#define A B\n"
"#define B C\n"
"#define C 1\n"
"A\n",
"1\n");
TEST_CASE("func_macros_hard with self recursive call");
CHECK_PP_OUTPUT_EXACT("#define M(x) x\n"
"M(M(10))\n",
"10\n");
CHECK_PP_OUTPUT_EXACT("#define M(x) M(x)\n"
"#define N(x) x\n"
"N(M(1))\n",
"M(1)\n");
TEST_CASE("func_macros_hard with define by macro");
CHECK_PP_OUTPUT_EXACT("#define M1(x) M1(x + 1)\n"
"#define M2 M1\n"
"#define M3(x) x\n"
"M3(M3(M2)(0))\n",
"M1(0 + 1)\n");
// TEST_CASE("TODO"); /*FALSE*/
// CHECK_PP_OUTPUT_EXACT("#define str(x) # x\n"
// "str()\n",
// "\"\"\n");
TEST_CASE("TODO");
CHECK_PP_OUTPUT_EXACT("#define x 1\n"
"#define f(a) f(x * (a))\n"
"f(0)\n"
"f(x)",
"f(1 * (0))\n"
"f(1 * (1))");
CHECK_PP_OUTPUT_EXACT("#define x x(0)\n"
"#define f(a) f(x * (a))\n"
"f(f(0))\n"
"f(f(x))\n"
"f(f(a))\n",
"f(x(0) * (f(x(0) * (0))))\n"
"f(x(0) * (f(x(0) * (x(0)))))\n"
"f(x(0) * (f(x(0) * (a))))\n");
}
static void test_conditional_compilation(void) {
TEST_CASE("conditional compilation");
CHECK_PP_OUTPUT_EXACT("#if 1\ntrue\n#endif\n", "true\n");
CHECK_PP_OUTPUT_EXACT("#if 0\nfalse\n#endif\n", "");
CHECK_PP_OUTPUT_EXACT("#define FLAG 1\n#if FLAG\ntrue\n#endif\n", "true\n");
}
static void test_error_cases(void) {
TEST_CASE("macro redefinition");
// 应检测到警告或错误
// CHECK_PP_OUTPUT_CONTAIN("#define A 1\n#define A 2\n", "warning");
TEST_CASE("undefined macro");
CHECK_PP_OUTPUT_EXACT("UNDEFINED_MACRO\n", "UNDEFINED_MACRO\n");
}
static void test_edge_cases(void) {
TEST_CASE("empty macro");
CHECK_PP_OUTPUT_EXACT("#define EMPTY()\nEMPTY()\n", "\n");
TEST_CASE("macro with only spaces");
CHECK_PP_OUTPUT_EXACT("#define SPACE \nSPACE\n", "\n");
TEST_CASE("deep nesting");
CHECK_PP_OUTPUT_EXACT("#define A B\n#define B C\n#define C 1\nA\n", "1\n");
}
static void test_conditional_ifdef(void) {
TEST_CASE("ifdef and ifndef");
// 基本 ifdef / ifndef
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#ifdef FOO\n"
"foo\n"
"#endif\n",
"foo\n");
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#ifndef FOO\n"
"foo\n"
"#endif\n",
"");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#ifdef FOO\n"
"foo\n"
"#endif\n",
"");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#ifndef FOO\n"
"foo\n"
"#endif\n",
"foo\n");
// ifdef + else
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#ifdef FOO\n"
"foo\n"
"#else\n"
"bar\n"
"#endif\n",
"foo\n");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#ifdef FOO\n"
"foo\n"
"#else\n"
"bar\n"
"#endif\n",
"bar\n");
// ifdef + elifdef (C23)
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#ifdef FOO\n"
"foo\n"
"#elifdef FOO\n"
"foo2\n"
"#endif\n",
"foo\n");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#define BAR\n"
"#ifdef FOO\n"
"foo\n"
"#elifdef BAR\n"
"bar\n"
"#else\n"
"none\n"
"#endif\n",
"bar\n");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#undef BAR\n"
"#ifdef FOO\n"
"foo\n"
"#elifdef BAR\n"
"bar\n"
"#else\n"
"none\n"
"#endif\n",
"none\n");
// 嵌套
CHECK_PP_OUTPUT_EXACT("#define A\n"
"#ifdef A\n"
" #ifdef B\n"
" inner\n"
" #endif\n"
" outer\n"
"#endif\n",
" outer\n");
CHECK_PP_OUTPUT_EXACT("#define B\n"
"#ifndef A\n"
" #ifdef B\n"
" inner\n"
" #endif\n"
" outer\n"
"#endif\n",
" inner\n outer\n");
// 外层假,内层真
CHECK_PP_OUTPUT_EXACT("#ifdef __NONE\n"
"#define OUTER\n"
"#endif\n"
"#ifdef OUTER\n"
"should not appear\n"
"#endif\n",
""); // 期望为空
// 更复杂的嵌套条件
CHECK_PP_OUTPUT_EXACT("#define X\n"
"#ifdef X\n"
"x defined\n"
"#ifdef Y\n"
"Y defined\n"
"#else\n"
"Y not defined\n"
"#endif\n"
"after inner\n"
"#else\n"
"X not defined\n"
"#endif\n",
"x defined\nY not defined\nafter inner\n");
}
static void test_simple_number_conditional_if(void) {
TEST_CASE("if and elif with one integer constants");
// 基本 if
CHECK_PP_OUTPUT_EXACT("#if 1\ntrue\n#endif\n", "true\n");
CHECK_PP_OUTPUT_EXACT("#if 0\nfalse\n#endif\n", "");
CHECK_PP_OUTPUT_EXACT("#if 4\ntrue\n#endif\n", "true\n");
CHECK_PP_OUTPUT_EXACT("#if 0\nfalse\n#else\nother\n#endif\n", "other\n");
// if + elif + else
CHECK_PP_OUTPUT_EXACT("#if 0\nzero\n#elif 1\none\n#else\nother\n#endif\n",
"one\n");
CHECK_PP_OUTPUT_EXACT("#if 0\nzero\n#elif 0\none\n#else\nother\n#endif\n",
"other\n");
CHECK_PP_OUTPUT_EXACT("#if 1\nfirst\n#elif 1\nsecond\n#endif\n", "first\n");
// 嵌套
CHECK_PP_OUTPUT_EXACT(
"#if 1\n #if 0\n inner\n #endif\n outer\n#endif\n", " outer\n");
CHECK_PP_OUTPUT_EXACT("#if 0\n #if 1\n inner\n #endif\n "
"outer\n#else\n alternative\n#endif\n",
" alternative\n");
// 与 #ifdef 混合
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#if 1\n"
" #ifdef FOO\n"
" foo\n"
" #endif\n"
" bar\n"
"#endif\n",
" foo\n bar\n");
}
static void test_variadic_macros(void) {
TEST_CASE("variadic macros with __VA_ARGS__");
// 基本可变参数宏
CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) x __VA_ARGS__\n"
"FOO(1, 2, 3)\n",
"1 2, 3\n");
// 多参数
CHECK_PP_OUTPUT_EXACT("#define SUM(...) (__VA_ARGS__)\n"
"SUM(1, 2, 3)\n",
"(1, 2, 3)\n");
// 与 printf 结合
CHECK_PP_OUTPUT_EXACT("#define DEBUG(fmt, ...) printf(fmt, __VA_ARGS__)\n"
"DEBUG(\"hello\", 1, 2)\n",
"printf(\"hello\", 1, 2)\n");
// 空可变参数
CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) x __VA_ARGS__\n"
"FOO(1)\n",
"1 \n");
}
#define TEST_LIST_CASE(func_name) {#func_name, func_name}
TEST_LIST = {
TEST_LIST_CASE(test_define_simple_no_macro),
TEST_LIST_CASE(test_define_simple_object_macro),
TEST_LIST_CASE(test_define_complex_object_macro),
TEST_LIST_CASE(test_define_object_macro_backspace),
TEST_LIST_CASE(test_define_function_macro),
TEST_LIST_CASE(test_define_stringify_operator),
TEST_LIST_CASE(test_define_concat_operator),
TEST_LIST_CASE(test_define_nested_macros),
TEST_LIST_CASE(test_undef_macros),
TEST_LIST_CASE(hard_test_define_func_macros),
TEST_LIST_CASE(test_conditional_ifdef),
TEST_LIST_CASE(test_simple_number_conditional_if),
TEST_LIST_CASE(test_variadic_macros),
{NULL, NULL},
};