Compare commits

...

2 Commits

Author SHA1 Message Date
zzy
51869bf081 feat(pproc): 改进宏处理器以支持括号嵌套和GNU扩展
- 实现了括号深度跟踪来正确分割带括号的宏参数
- 添加了对 GNU 扩展中 `##` 操作符逗号删除的支持
- 新增辅助函数 `got_left_non_blank` 和 `got_right_non_blank`
  来优化查找非空白 token 的逻辑
- 改进了错误消息以显示预期但得到的实际值类型

fix(pproc): 修复条件编译和包含文件路径的错误消息

- 在 `scc_pproc_parse_if_condition` 中改进错误消息格式
- 修复 `switch_file_stack` 函数中的日志字符串格式问题

test(pproc): 添加宏处理相关的单元测试

- 增加了连接操作符、嵌套宏、括号处理等测试用例
- 添加了 C99 标准示例和 GNU 变参宏删除逗号的测试
- 包含了复杂的宏展开场景测试

chore(justfile): 更新构建脚本添加调试目标

- 为 `test-scc` 目标添加了 `debug-scc` 调试版本
- 更新构建命令以支持开发模式

feat(cbuild): 添加 dry-run 模式和改进编译器参数

- 为编译器类添加 dry-run 功能,只打印命令不执行
- 改进 scc 编译器的包含路径处理逻辑
- 为命令行解析器添加 dry-run 参数选项

refactor(log): 重命名 static_assert 为 StaticAssert 避免冲突

- 为了避免与标准库冲突,将自定义 static_assert 重命名为 StaticAssert

style(scc_core): 移除未使用的预定义宏定义

- 删除了不再需要的基础类型前缀宏定义

fix(scc_core): 初始化 ring 测试中的未初始化变量

- 为测试函数中的字符变量添加初始化值避免未定义行为
2026-02-21 23:53:44 +08:00
zzy
3b2f68111e chore(license): 添加MIT许可证文件
向项目中添加MIT许可证,明确软件使用的条款和条件,
包括版权声明、许可声明以及免责声明等内容。
2026-02-21 16:46:38 +08:00
10 changed files with 280 additions and 112 deletions

8
LISENCE Normal file
View File

@@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright © 2026 <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -30,4 +30,10 @@ test-scc:
just clean just clean
just build-install just build-install
just clean just clean
cbuild run -cscc -- -h cbuild build -cscc
debug-scc:
just clean
just build-install
just clean
cbuild build -cscc --dev --record --dry-run

View File

@@ -132,9 +132,16 @@ split_arguments(scc_pproc_macro_extened_params_t *splited_params,
(named_count > 0 && (named_count > 0 &&
scc_vec_at(macro->params, named_count - 1).type == SCC_TOK_ELLIPSIS); scc_vec_at(macro->params, named_count - 1).type == SCC_TOK_ELLIPSIS);
int depth = 0;
scc_vec_foreach(*raw_args, i) { scc_vec_foreach(*raw_args, i) {
scc_lexer_tok_t *raw_arg = &scc_vec_at(*raw_args, i); scc_lexer_tok_t *raw_arg = &scc_vec_at(*raw_args, i);
if (raw_arg->type != SCC_TOK_COMMA || if (raw_arg->type == SCC_TOK_L_PAREN) {
depth++;
} else if (raw_arg->type == SCC_TOK_R_PAREN) {
depth--;
}
if (depth != 0 || raw_arg->type != SCC_TOK_COMMA ||
(is_variadic && scc_vec_size(*splited_params) == named_count - 1)) { (is_variadic && scc_vec_size(*splited_params) == named_count - 1)) {
if (scc_vec_size(arg) == 0 && raw_arg->type == SCC_TOK_BLANK) { if (scc_vec_size(arg) == 0 && raw_arg->type == SCC_TOK_BLANK) {
scc_lexer_tok_drop(raw_arg); scc_lexer_tok_drop(raw_arg);
@@ -142,14 +149,15 @@ split_arguments(scc_pproc_macro_extened_params_t *splited_params,
scc_vec_push(arg, *raw_arg); scc_vec_push(arg, *raw_arg);
} }
continue; continue;
} else {
scc_lexer_tok_drop(raw_arg);
if (scc_vec_size(arg) &&
scc_vec_at(arg, scc_vec_size(arg) - 1).type == SCC_TOK_BLANK) {
scc_lexer_tok_drop(&scc_vec_pop(arg));
}
scc_vec_push(*splited_params, arg);
scc_vec_init(arg);
} }
scc_lexer_tok_drop(raw_arg);
if (scc_vec_size(arg) &&
scc_vec_at(arg, scc_vec_size(arg) - 1).type == SCC_TOK_BLANK) {
scc_lexer_tok_drop(&scc_vec_pop(arg));
}
scc_vec_push(*splited_params, arg);
scc_vec_init(arg);
} }
if (scc_vec_size(arg) && if (scc_vec_size(arg) &&
scc_vec_at(arg, scc_vec_size(arg) - 1).type == SCC_TOK_BLANK) { scc_vec_at(arg, scc_vec_size(arg) - 1).type == SCC_TOK_BLANK) {
@@ -231,6 +239,25 @@ static int find_params(const scc_lexer_tok_t *tok,
return -1; return -1;
} }
static inline int got_left_non_blank(int i,
const scc_lexer_tok_vec_t *replaces) {
int left_idx = i - 1;
while (left_idx >= 0 &&
scc_vec_at(*replaces, left_idx).type == SCC_TOK_BLANK) {
left_idx--;
}
return left_idx;
}
static inline int got_right_non_blank(int i,
const scc_lexer_tok_vec_t *replaces) {
int right_idx = i + 1;
while (right_idx < (int)scc_vec_size(*replaces) &&
scc_vec_at(*replaces, right_idx).type == SCC_TOK_BLANK) {
right_idx++;
}
return right_idx;
}
static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx, static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
const scc_pproc_macro_t *macro) { const scc_pproc_macro_t *macro) {
scc_lexer_tok_vec_t tok_buffer; scc_lexer_tok_vec_t tok_buffer;
@@ -264,12 +291,7 @@ static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
if (tok.type == SCC_TOK_SHARP) { if (tok.type == SCC_TOK_SHARP) {
// # stringify // # stringify
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
int right_idx = i + 1; int right_idx = got_right_non_blank(i, &macro->replaces);
while (right_idx < (int)macro->replaces.size &&
scc_vec_at(macro->replaces, right_idx).type ==
SCC_TOK_BLANK) {
right_idx++;
}
if (right_idx >= (int)macro->replaces.size) { if (right_idx >= (int)macro->replaces.size) {
LOG_WARN("generate empty stringify"); LOG_WARN("generate empty stringify");
scc_cstring_free(&tok.lexeme); scc_cstring_free(&tok.lexeme);
@@ -286,21 +308,10 @@ static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
continue; continue;
} else if (tok.type == SCC_TOK_SHARP_SHARP) { } else if (tok.type == SCC_TOK_SHARP_SHARP) {
// ## contact // ## contact
// 向左扫描找到上一个非空白 token
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
int left_idx = i - 1; int left_idx = got_left_non_blank(i, &macro->replaces);
while (left_idx >= 0 && int right_idx = got_right_non_blank(i, &macro->replaces);
scc_vec_at(macro->replaces, left_idx).type ==
SCC_TOK_BLANK) {
left_idx--;
}
// 向右扫描找到下一个非空白 token
int right_idx = i + 1;
while (right_idx < (int)macro->replaces.size &&
scc_vec_at(macro->replaces, right_idx).type ==
SCC_TOK_BLANK) {
right_idx++;
}
if (left_idx < 0 || right_idx >= (int)macro->replaces.size) { if (left_idx < 0 || right_idx >= (int)macro->replaces.size) {
LOG_FATAL("Invalid ## operator"); LOG_FATAL("Invalid ## operator");
} }
@@ -308,13 +319,53 @@ static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
scc_lexer_tok_drop(&scc_vec_pop(tok_buffer)); scc_lexer_tok_drop(&scc_vec_pop(tok_buffer));
} }
int j; scc_lexer_tok_t *left_tok = &scc_vec_at(macro->replaces, left_idx);
j = find_params(&scc_vec_at(macro->replaces, left_idx), macro); scc_lexer_tok_t *right_tok =
Assert(j != -1 && j < (int)scc_vec_size(splited_params)); &scc_vec_at(macro->replaces, right_idx);
scc_lexer_tok_vec_t left_vec = scc_vec_at(splited_params, j);
j = find_params(&scc_vec_at(macro->replaces, right_idx), macro); if (left_tok->type == SCC_TOK_COMMA &&
Assert(j != -1 && j < (int)scc_vec_size(splited_params)); scc_strcmp(scc_cstring_as_cstr(&(right_tok->lexeme)),
scc_lexer_tok_vec_t right_vec = scc_vec_at(splited_params, j); "__VA_ARGS__") == 0) {
// GNU 扩展:处理逗号删除
int right_param_idx = find_params(right_tok, macro);
Assert(right_param_idx != -1);
scc_lexer_tok_vec_t right_vec =
scc_vec_at(expanded_params, right_param_idx);
if (scc_vec_size(right_vec) != 0) {
// 可变参数非空:输出逗号副本,然后输出右侧参数的展开
scc_lexer_tok_t comma_tok = scc_lexer_tok_copy(left_tok);
scc_vec_push(tok_buffer, comma_tok);
}
scc_vec_foreach(right_vec, k) {
scc_lexer_tok_t tok =
scc_lexer_tok_copy(&scc_vec_at(right_vec, k));
scc_vec_push(tok_buffer, tok);
}
i = right_idx;
continue;
}
int idx;
idx = find_params(left_tok, macro);
scc_lexer_tok_vec_t left_vec;
if (idx != -1) {
Assert(idx < (int)scc_vec_size(splited_params));
left_vec = scc_vec_at(splited_params, idx);
} else {
scc_vec_init(left_vec);
scc_vec_push(left_vec, scc_lexer_tok_copy(left_tok));
}
idx = find_params(right_tok, macro);
scc_lexer_tok_vec_t right_vec;
if (idx != -1) {
Assert(idx < (int)scc_vec_size(splited_params));
right_vec = scc_vec_at(splited_params, idx);
} else {
scc_vec_init(right_vec);
scc_vec_push(right_vec, scc_lexer_tok_copy(right_tok));
}
scc_lexer_tok_t *left = scc_lexer_tok_t *left =
scc_vec_size(left_vec) scc_vec_size(left_vec)
? &scc_vec_at(left_vec, scc_vec_size(left_vec) - 1) ? &scc_vec_at(left_vec, scc_vec_size(left_vec) - 1)
@@ -380,25 +431,16 @@ static inline void expand_object_macro(scc_pproc_expand_t *expand_ctx,
tok.lexeme = scc_cstring_from_cstr(" "); tok.lexeme = scc_cstring_from_cstr(" ");
} else if (tok.type == SCC_TOK_SHARP_SHARP) { } else if (tok.type == SCC_TOK_SHARP_SHARP) {
// ## contact // ## contact
// 向左扫描找到上一个非空白 token int left_idx = got_left_non_blank(i, &macro->replaces);
int left_idx = i - 1; int right_idx = got_right_non_blank(i, &macro->replaces);
while (left_idx >= 0 &&
scc_vec_at(macro->replaces, left_idx).type == if (left_idx < 0 ||
SCC_TOK_BLANK) { right_idx >= (int)scc_vec_size(macro->replaces)) {
left_idx--;
}
// 向右扫描找到下一个非空白 token
int right_idx = i + 1;
while (right_idx < (int)macro->replaces.size &&
scc_vec_at(macro->replaces, right_idx).type ==
SCC_TOK_BLANK) {
right_idx++;
}
if (left_idx < 0 || right_idx >= (int)macro->replaces.size) {
LOG_FATAL("Invalid ## operator"); LOG_FATAL("Invalid ## operator");
} }
scc_lexer_tok_t *left = &scc_vec_at(macro->replaces, left_idx); scc_lexer_tok_t *left = &scc_vec_at(macro->replaces, left_idx);
scc_lexer_tok_t *right = &scc_vec_at(macro->replaces, right_idx); scc_lexer_tok_t *right = &scc_vec_at(macro->replaces, right_idx);
scc_lexer_tok_t concate_tok = concatenate_tokens(left, right); scc_lexer_tok_t concate_tok = concatenate_tokens(left, right);
while (i++ < right_idx) { while (i++ < right_idx) {
scc_lexer_tok_drop(&scc_vec_pop(tok_buffer)); scc_lexer_tok_drop(&scc_vec_pop(tok_buffer));

View File

@@ -113,7 +113,8 @@ cbool scc_pproc_parse_if_condition(scc_pproc_t *pp, scc_tok_type_t type,
if (tok.type == SCC_TOK_INT_LITERAL) { if (tok.type == SCC_TOK_INT_LITERAL) {
condition = scc_cstring_as_cstr(&tok.lexeme)[0] == '0' ? 0 : 1; condition = scc_cstring_as_cstr(&tok.lexeme)[0] == '0' ? 0 : 1;
} else { } else {
LOG_ERROR("expected integer constant"); LOG_ERROR("expected integer constant but got %s",
scc_cstring_as_cstr(&tok.lexeme));
} }
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);

View File

@@ -33,7 +33,7 @@ static int switch_file_stack(scc_pproc_t *pp, scc_cstring_t *fname,
goto FOPEN; goto FOPEN;
} }
} }
LOG_ERROR("In %s:%d:%d include %c%s%c , the file is not found", org_fname, LOG_ERROR("In %s:%d:%d include %c%s%c, the file is not found", org_fname,
pos->line, pos->col, is_system ? '<' : '\"', pos->line, pos->col, is_system ? '<' : '\"',
scc_cstring_as_cstr(fname), is_system ? '>' : '\"'); scc_cstring_as_cstr(fname), is_system ? '>' : '\"');
return -1; return -1;

View File

@@ -132,6 +132,35 @@ static void test_define_nested_macros(void) {
CHECK_PP_OUTPUT_EXACT(" # define A 1\nA", "1"); CHECK_PP_OUTPUT_EXACT(" # define A 1\nA", "1");
// CHECK_PP_OUTPUT_EXACT(" # define A 1 \nA", "1"); // TODO // 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) { static void test_undef_macros(void) {
@@ -181,33 +210,19 @@ static void hard_test_define_func_macros(void) {
"M3(M3(M2)(0))\n", "M3(M3(M2)(0))\n",
"M1(0 + 1)\n"); "M1(0 + 1)\n");
// TEST_CASE("TODO"); /*FALSE*/ TEST_CASE("mulit braces");
// CHECK_PP_OUTPUT_EXACT("#define str(x) # x\n" CHECK_PP_OUTPUT_EXACT("#define MACRO(a, b, c) a, b, c\n"
// "str()\n", "MACRO(1, (2,3), 4)\n",
// "\"\"\n"); "1, (2,3), 4\n");
TEST_CASE("TODO"); TEST_CASE("max_macro hard");
CHECK_PP_OUTPUT_EXACT("#define x 1\n" CHECK_PP_OUTPUT_EXACT("#define max(a, b) ((a) > (b) ? (a) : (b))\n"
"#define f(a) f(x * (a))\n" "max(1, 2)\n",
"f(0)\n" "((1) > (2) ? (1) : (2))\n");
"f(x)", CHECK_PP_OUTPUT_EXACT("#define max(a, b) ((a) > (b) ? (a) : (b))\n"
"f(1 * (0))\n" "max(max(x, y), z)\n",
"f(1 * (1))"); "((((x) > (y) ? (x) : (y))) > (z) ? (((x) > (y) ? "
CHECK_PP_OUTPUT_EXACT("#define x x(0)\n" "(x) : (y))) : (z))\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) { static void test_error_cases(void) {
@@ -406,6 +421,111 @@ static void test_variadic_macros(void) {
CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) x __VA_ARGS__\n" CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) x __VA_ARGS__\n"
"FOO(1)\n", "FOO(1)\n",
"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} #define TEST_LIST_CASE(func_name) {#func_name, func_name}
@@ -423,5 +543,8 @@ TEST_LIST = {
TEST_LIST_CASE(test_conditional_ifdef), TEST_LIST_CASE(test_conditional_ifdef),
TEST_LIST_CASE(test_simple_number_conditional_if), TEST_LIST_CASE(test_simple_number_conditional_if),
TEST_LIST_CASE(test_variadic_macros), TEST_LIST_CASE(test_variadic_macros),
TEST_LIST_CASE(test_gnu_comma_variadic_deletion),
TEST_LIST_CASE(test_c99_docs),
{NULL, NULL}, {NULL, NULL},
}; };

View File

@@ -187,13 +187,10 @@ void log_set_handler(logger_t *logger, log_handler handler);
* 利用数组大小不能为负的特性 * 利用数组大小不能为负的特性
* 或使用 _Static_assert (C11) * 或使用 _Static_assert (C11)
*/ */
#ifdef static_assert
#undef static_assert
#endif
#if __STDC_VERSION__ >= 201112L #if __STDC_VERSION__ >= 201112L
#define static_assert _Static_assert #define StaticAssert static_assert
#else #else
#define static_assert(cond, msg) extern char __static_assertion[(cond) ? 1 : -1] #define StaticAssert(cond, msg) extern char __static_assertion[(cond) ? 1 : -1]
#endif #endif
#ifdef __SCC_LOG_IMPL_IMPORT_SRC__ #ifdef __SCC_LOG_IMPL_IMPORT_SRC__

View File

@@ -33,23 +33,6 @@ typedef bool cbool;
static_assert(sizeof(cbool) == 1, "cbool size must 1"); static_assert(sizeof(cbool) == 1, "cbool size must 1");
#else #else
#define __scc_i8
#define __scc_i16
#define __scc_i32
#define __scc_i64
#define __scc_u8
#define __scc_u16
#define __scc_u32
#define __scc_u64
#define __scc_f32
#define __scc_f64
#define __scc_bool
#define __scc_char
#define __scc_void
#define __scc_null
#define __scc_isize
#define __scc_usize
/* clang-format off */ /* clang-format off */
typedef __scc_i8 i8; typedef __scc_i8 i8;
typedef __scc_i16 i16; typedef __scc_i16 i16;

View File

@@ -60,7 +60,7 @@ void test_char_ring_basic(void) {
reset_char_fill(); reset_char_fill();
char_ring_t ring; char_ring_t ring;
scc_ring_init(ring, 4, char_fill, 0); scc_ring_init(ring, 4, char_fill, 0);
char c; char c = 0;
cbool ok; cbool ok;
scc_ring_next(ring, c, ok); scc_ring_next(ring, c, ok);
@@ -109,7 +109,7 @@ void test_char_ring_full(void) {
reset_char_fill(); reset_char_fill();
char_ring_t ring; char_ring_t ring;
scc_ring_init(ring, 3, char_fill, 0); scc_ring_init(ring, 3, char_fill, 0);
char c; char c = 0;
cbool ok; cbool ok;
scc_ring_next(ring, c, ok); scc_ring_next(ring, c, ok);
@@ -140,7 +140,7 @@ void test_char_ring_eof(void) {
reset_char_fill(); reset_char_fill();
char_ring_t ring; char_ring_t ring;
scc_ring_init(ring, 32, char_fill, 0); scc_ring_init(ring, 32, char_fill, 0);
char c; char c = 0;
cbool ok; cbool ok;
for (int i = 0; i < 26; i++) { for (int i = 0; i < 26; i++) {
@@ -160,7 +160,7 @@ void test_char_ring_back_boundary(void) {
reset_char_fill(); reset_char_fill();
char_ring_t ring; char_ring_t ring;
scc_ring_init(ring, 4, char_fill, 0); scc_ring_init(ring, 4, char_fill, 0);
char c; char c = 0;
cbool ok; cbool ok;
scc_ring_next(ring, c, ok); scc_ring_next(ring, c, ok);
@@ -186,7 +186,7 @@ void test_char_ring_consume_reset(void) {
reset_char_fill(); reset_char_fill();
char_ring_t ring; char_ring_t ring;
scc_ring_init(ring, 5, char_fill, 0); scc_ring_init(ring, 5, char_fill, 0);
char c; char c = 0;
cbool ok; cbool ok;
scc_ring_next(ring, c, ok); scc_ring_next(ring, c, ok);
@@ -219,7 +219,7 @@ void test_char_ring_wrap(void) {
reset_char_fill(); reset_char_fill();
char_ring_t ring; char_ring_t ring;
scc_ring_init(ring, 3, char_fill, 0); scc_ring_init(ring, 3, char_fill, 0);
char c; char c = 0;
cbool ok; cbool ok;
for (int i = 0; i < 26; i++) { for (int i = 0; i < 26; i++) {
@@ -239,7 +239,7 @@ void test_token_ring_basic(void) {
reset_token_fill(); reset_token_fill();
token_ring_t ring; token_ring_t ring;
scc_ring_init(ring, 3, token_fill, 0); scc_ring_init(ring, 3, token_fill, 0);
test_token_t tok; test_token_t tok = {0};
cbool ok; cbool ok;
scc_ring_next(ring, tok, ok); scc_ring_next(ring, tok, ok);
@@ -284,7 +284,7 @@ void test_token_ring_full(void) {
reset_token_fill(); reset_token_fill();
token_ring_t ring; token_ring_t ring;
scc_ring_init(ring, 2, token_fill, 0); scc_ring_init(ring, 2, token_fill, 0);
test_token_t tok; test_token_t tok = {0};
cbool ok; cbool ok;
scc_ring_next(ring, tok, ok); scc_ring_next(ring, tok, ok);

View File

@@ -564,6 +564,7 @@ class Compiler(ABC):
def __init__(self): def __init__(self):
self.recorded = [] self.recorded = []
self.recording = False self.recording = False
self.dry_run = False
def enable_recording(self, enable=True): def enable_recording(self, enable=True):
"""启用命令记录""" """启用命令记录"""
@@ -580,6 +581,8 @@ class Compiler(ABC):
"""运行命令""" """运行命令"""
self.record(cmd) self.record(cmd)
logger.debug("执行命令: %s", cmd) logger.debug("执行命令: %s", cmd)
if self.dry_run:
return # 只打印,不执行
try: try:
subprocess.run(cmd, check=True) subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
@@ -689,8 +692,9 @@ class SccCompiler(Compiler):
self, source: Path, output: Path, includes: list[Path], flags: list[str] self, source: Path, output: Path, includes: list[Path], flags: list[str]
): ):
# cmd = ["clang"] + flags + ["-c", str(source), "-o", str(output)] # cmd = ["clang"] + flags + ["-c", str(source), "-o", str(output)]
cmd = ["scc", "--emit-pp", "-o", str(output), str(source)] cmd = ["scc", "--emit-pp", "-o", str(output), str(source), "-I", "scc_libs"]
cmd += [f"-I{inc}" for inc in includes] for inc in includes:
cmd += ["-I", f"{inc}"]
self.run(cmd) self.run(cmd)
def link(self, objects: list[Path], output: Path, flags: list[str]): def link(self, objects: list[Path], output: Path, flags: list[str]):
@@ -1036,7 +1040,6 @@ def create_parser():
parser = argparse.ArgumentParser(description="轻量C构建系统", prog="cbuild") parser = argparse.ArgumentParser(description="轻量C构建系统", prog="cbuild")
parser.add_argument("--verbose", "-v", action="store_true", help="详细输出") parser.add_argument("--verbose", "-v", action="store_true", help="详细输出")
parser.add_argument("--path", "-p", default=".", help="项目路径") parser.add_argument("--path", "-p", default=".", help="项目路径")
subparsers = parser.add_subparsers(dest="command", required=True, metavar="COMMAND") subparsers = parser.add_subparsers(dest="command", required=True, metavar="COMMAND")
def add_common_args(subparser): def add_common_args(subparser):
@@ -1047,6 +1050,9 @@ def create_parser():
default="gcc", default="gcc",
help="编译器", help="编译器",
) )
subparser.add_argument(
"--dry-run", "-d", action="store_true", help="仅打印命令,不实际执行"
)
subparser.add_argument("--record", "-r", action="store_true", help="记录命令") subparser.add_argument("--record", "-r", action="store_true", help="记录命令")
subparser.add_argument( subparser.add_argument(
"--jobs", "-j", type=int, default=0, help="并行编译任务数 (0=自动检测)" "--jobs", "-j", type=int, default=0, help="并行编译任务数 (0=自动检测)"
@@ -1144,6 +1150,8 @@ def main():
} }
compiler = compiler_map.get(args.compiler, GccCompiler()) compiler = compiler_map.get(args.compiler, GccCompiler())
if hasattr(args, "dry_run") and args.dry_run:
compiler.dry_run = True
if hasattr(args, "record") and args.record: if hasattr(args, "record") and args.record:
compiler.enable_recording() compiler.enable_recording()