#include #include #include #include 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 CHECK_PP_OUTPUT_EXACT("#define CONCAT(str) __scc_##str\nCONCAT(int)", "__scc_int"); CHECK_PP_OUTPUT_EXACT("#define CONCAT(str) str##_scc__\nCONCAT(int)", "int_scc__"); CHECK_PP_OUTPUT_EXACT("#define CONCAT(str) __scc_ ## str\nCONCAT(int)", "__scc_int"); // 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_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("mulit braces"); CHECK_PP_OUTPUT_EXACT("#define MACRO(a, b, c) a, b, c\n" "MACRO(1, (2,3), 4)\n", "1, (2,3), 4\n"); TEST_CASE("max_macro hard"); CHECK_PP_OUTPUT_EXACT("#define max(a, b) ((a) > (b) ? (a) : (b))\n" "max(1, 2)\n", "((1) > (2) ? (1) : (2))\n"); CHECK_PP_OUTPUT_EXACT("#define max(a, b) ((a) > (b) ? (a) : (b))\n" "max(max(x, y), z)\n", "((((x) > (y) ? (x) : (y))) > (z) ? (((x) > (y) ? " "(x) : (y))) : (z))\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"); CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) #__VA_ARGS__\nFOO(1);", "\"\";"); } static void test_gnu_comma_variadic_deletion(void) { TEST_CASE("GNU comma deletion with ## and __VA_ARGS__"); // 可变参数为空,逗号被删除 CHECK_PP_OUTPUT_EXACT("#define FOO(fmt, ...) printf(fmt, ## __VA_ARGS__)\n" "FOO(\"hello\")\n", "printf(\"hello\")\n"); // 可变参数非空,逗号保留 CHECK_PP_OUTPUT_EXACT("#define FOO(fmt, ...) printf(fmt, ## __VA_ARGS__)\n" "FOO(\"%d\", 42)\n", "printf(\"%d\",42)\n"); // 带空白变体 CHECK_PP_OUTPUT_EXACT("#define FOO(fmt,...) printf(fmt,##__VA_ARGS__)\n" "FOO(\"%d\", 42)\n", "printf(\"%d\",42)\n"); } static void test_c99_docs(void) { TEST_CASE("6.10.3.3 The ## operator EXAMPLE"); 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"); // 6.10.3.5 Scope of macrodefinitions TEST_CASE("EXAMPLE 3 To illustrate the rules for redefinition and " "reexamination, the sequence"); /* CHECK_PP_OUTPUT_EXACT( "#define x 3\n" "#define f(a) f(x * (a))\n" "#undef x\n" "#define x 2\n" "#define g f\n" "#define z z[0]\n" "#define h g(~\n" "#define m(a) a(w)\n" "#define w 0,1\n" "#define t(a) a\n" "#define p() int\n" "#define q(x) x\n" "#define r(x,y) x ## y\n" "#define str(x) # x\n" "f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);\n" "g(x+(3,4)-w) | h 5) & m\n" " (f)^m(m);\n" "p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) };\n" "char c[2][6] = { str(hello), str() };\n", "f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);\n" "f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);\n" "int i[] = { 1, 23, 4, 5, };\n" "char c[2][6] = { \"hello\", \"\" };\n"); */ TEST_CASE("EXAMPLE 4 To illustrate the rules for creating character string " "literals and concatenating tokens, the sequence"); TEST_CASE("EXAMPLE 5 To illustrate the rules for placemarker preprocessing " "tokens, the sequence"); /* CHECK_PP_OUTPUT_EXACT("#define t(x,y,z) x ## y ## z\n" "int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,),\n" "\t\t\tt(10,,), t(,11,), t(,,12), t(,,) };\n", "int j[] = { 123, 45, 67, 89,\n" "\t\t\t10, 11, 12, };\n"); */ TEST_CASE("EXAMPLE 6 To demonstrate the redefinition rules, the following " "sequence is valid."); CHECK_PP_OUTPUT_EXACT( "#define OBJ_LIKE (1-1)\n" "#define OBJ_LIKE /* white space */ (1-1) /* other */\n" "#define FUNC_LIKE(a) (a)\n" "#define FUNC_LIKE( a )( /* note the white space */ \\\n" " a /* other stuffonthis line\n" " */ )\n", ""); // INVALID // CHECK_PP_OUTPUT_EXACT( // "#define OBJ_LIKE (0) // different token sequence\n" // "#define OBJ_LIKE (1 - 1) // different white space\n" // "#define FUNC_LIKE(b) ( a ) // different parameter usage\n" // "#define FUNC_LIKE(b) ( b ) // different parameter spelling\n"); TEST_CASE("EXAMPLE 7 Finally,to show the variable argument list macro " "facilities:"); CHECK_PP_OUTPUT_EXACT( "#define debug(...) fprintf(stderr, __VA_ARGS__)\n" "#define showlist(...) puts(#__VA_ARGS__)\n" "#define report(test, ...) ((test)?puts(#test): \\\n" " printf(__VA_ARGS__))\n" "debug(\"Flag\");\n" "debug(\"X = %d\\n\", x);\n" "showlist(The first, second, and third items.);\n" "report(x>y, \"x is %d but y is %d\", x, y);\n", "fprintf(stderr, \"Flag\");\n" "fprintf(stderr, \"X = %d\\n\", x);\n" "puts(\"The first, second, and third items.\");\n" "((x>y)?puts(\"x>y\"): printf(\"x is %d but y is %d\", x, y));\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), TEST_LIST_CASE(test_gnu_comma_variadic_deletion), TEST_LIST_CASE(test_c99_docs), {NULL, NULL}, };