Compare commits
3 Commits
4940b652eb
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| d2eaf2247f | |||
| 51869bf081 | |||
| 3b2f68111e |
8
LICENSE
Normal file
8
LICENSE
Normal 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.
|
||||||
8
justfile
8
justfile
@@ -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
|
||||||
|
|||||||
@@ -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,7 +149,7 @@ 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);
|
scc_lexer_tok_drop(raw_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) {
|
||||||
@@ -151,6 +158,7 @@ split_arguments(scc_pproc_macro_extened_params_t *splited_params,
|
|||||||
scc_vec_push(*splited_params, arg);
|
scc_vec_push(*splited_params, arg);
|
||||||
scc_vec_init(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) {
|
||||||
scc_lexer_tok_drop(&scc_vec_pop(arg));
|
scc_lexer_tok_drop(&scc_vec_pop(arg));
|
||||||
@@ -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, ¯o->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, ¯o->replaces);
|
||||||
while (left_idx >= 0 &&
|
int right_idx = got_right_non_blank(i, ¯o->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, ¯o->replaces);
|
||||||
int left_idx = i - 1;
|
int right_idx = got_right_non_blank(i, ¯o->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));
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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__
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user