Compare commits
5 Commits
b705e5d0ad
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| d2eaf2247f | |||
| 51869bf081 | |||
| 3b2f68111e | |||
| 4940b652eb | |||
| 8007825800 |
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.
|
||||||
24
justfile
24
justfile
@@ -15,5 +15,25 @@ count-file:
|
|||||||
# you need download `tokei` it can download by cargo
|
# you need download `tokei` it can download by cargo
|
||||||
tokei libs runtime src -e tests --files
|
tokei libs runtime src -e tests --files
|
||||||
|
|
||||||
build_lexer:
|
clean:
|
||||||
python ./tools/cbuild/cbuild.py --path libs/lexer build
|
cbuild clean
|
||||||
|
|
||||||
|
build:
|
||||||
|
cbuild build -cclang
|
||||||
|
|
||||||
|
build-install: build
|
||||||
|
cp ./build/dev/scc.exe ./scc.exe
|
||||||
|
|
||||||
|
test-scc:
|
||||||
|
# windows: (Get-Content a.txt -Raw) -replace '\x1b\[[0-9;]*[a-zA-Z]', '' | Set-Content clean.txt
|
||||||
|
# linux: sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' a.txt > clean.txt
|
||||||
|
just clean
|
||||||
|
just build-install
|
||||||
|
just clean
|
||||||
|
cbuild build -cscc
|
||||||
|
|
||||||
|
debug-scc:
|
||||||
|
just clean
|
||||||
|
just build-install
|
||||||
|
just clean
|
||||||
|
cbuild build -cscc --dev --record --dry-run
|
||||||
|
|||||||
@@ -145,8 +145,8 @@ typedef enum scc_cstd {
|
|||||||
// END
|
// END
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
|
|
||||||
// 定义TokenType枚举
|
|
||||||
typedef enum scc_tok_type {
|
typedef enum scc_tok_type {
|
||||||
|
/* clang-format off */
|
||||||
// must first becase the unknown token must be 0
|
// must first becase the unknown token must be 0
|
||||||
#define X(str, subtype, tok) tok,
|
#define X(str, subtype, tok) tok,
|
||||||
SCC_CTOK_TABLE
|
SCC_CTOK_TABLE
|
||||||
@@ -159,6 +159,7 @@ typedef enum scc_tok_type {
|
|||||||
#define X(name, subtype, tok, std) tok,
|
#define X(name, subtype, tok, std) tok,
|
||||||
SCC_CKEYWORD_TABLE
|
SCC_CKEYWORD_TABLE
|
||||||
#undef X
|
#undef X
|
||||||
|
/* clang-format on*/
|
||||||
} scc_tok_type_t;
|
} scc_tok_type_t;
|
||||||
|
|
||||||
typedef enum scc_tok_subtype {
|
typedef enum scc_tok_subtype {
|
||||||
|
|||||||
@@ -67,7 +67,14 @@ static inline void scc_pproc_add_include_path_cstr(scc_pproc_t *pp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void scc_pproc_handle_directive(scc_pproc_t *pp);
|
void scc_pproc_handle_directive(scc_pproc_t *pp);
|
||||||
void scc_pproc_parse_include(scc_pproc_t *pp, scc_lexer_tok_t *include_tok);
|
|
||||||
|
cbool scc_pproc_parse_if_defined(scc_pproc_t *pp, scc_tok_type_t type,
|
||||||
|
const scc_lexer_tok_t *tok);
|
||||||
|
cbool scc_pproc_parse_if_condition(scc_pproc_t *pp, scc_tok_type_t type,
|
||||||
|
scc_lexer_tok_ring_t *tok_ring);
|
||||||
|
void scc_pproc_parse_include(scc_pproc_t *pp, scc_lexer_tok_t *include_tok,
|
||||||
|
scc_lexer_tok_ring_t *tok_ring);
|
||||||
|
|
||||||
void scc_pproc_parse_macro_arguments(scc_lexer_tok_ring_t *ring,
|
void scc_pproc_parse_macro_arguments(scc_lexer_tok_ring_t *ring,
|
||||||
scc_lexer_tok_vec_t *args, int need_full);
|
scc_lexer_tok_vec_t *args, int need_full);
|
||||||
void scc_pproc_parse_function_macro(scc_pproc_t *pp,
|
void scc_pproc_parse_function_macro(scc_pproc_t *pp,
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
#include <pproc_expand.h>
|
||||||
|
#include <scc_core_ring.h>
|
||||||
#include <scc_lexer_utils.h>
|
#include <scc_lexer_utils.h>
|
||||||
#include <scc_pproc.h>
|
#include <scc_pproc.h>
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
scc_tok_type_t tok_type;
|
scc_tok_type_t tok_type;
|
||||||
@@ -148,6 +151,25 @@ void scc_pproc_parse_object_macro(scc_pproc_t *pp,
|
|||||||
scc_pproc_macro_table_set(&pp->macro_table, macro);
|
scc_pproc_macro_table_set(&pp->macro_table, macro);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void scc_pproc_parse_line_and_expand(scc_pproc_t *pp,
|
||||||
|
scc_lexer_tok_ring_t *out_ring) {
|
||||||
|
int ok;
|
||||||
|
scc_lexer_tok_t tok;
|
||||||
|
scc_lexer_tok_ring_t *stream = pp->cur_ring;
|
||||||
|
scc_lexer_tok_vec_t org_toks;
|
||||||
|
scc_vec_init(org_toks);
|
||||||
|
while (1) {
|
||||||
|
scc_ring_peek(*stream, tok, ok);
|
||||||
|
if (ok == false)
|
||||||
|
break;
|
||||||
|
scc_ring_next_consume(*stream, tok, ok);
|
||||||
|
scc_vec_push(org_toks, tok);
|
||||||
|
if (tok.type == SCC_TOK_ENDLINE)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
scc_pproc_expand_by_vec(&pp->macro_table, &org_toks, out_ring);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
```txt
|
```txt
|
||||||
6.10 Preprocessing directives
|
6.10 Preprocessing directives
|
||||||
@@ -297,121 +319,43 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
|
|||||||
scc_lexer_tok_drop(&tok);
|
scc_lexer_tok_drop(&tok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case SCC_PP_TOK_INCLUDE:
|
case SCC_PP_TOK_INCLUDE: {
|
||||||
scc_pproc_parse_include(pp, &tok);
|
scc_lexer_tok_ring_t out_ring;
|
||||||
|
scc_pproc_parse_line_and_expand(pp, &out_ring);
|
||||||
|
scc_pproc_parse_include(pp, &tok, &out_ring);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
case SCC_PP_TOK_IFDEF:
|
case SCC_PP_TOK_IFDEF:
|
||||||
case SCC_PP_TOK_IFNDEF: {
|
case SCC_PP_TOK_IFNDEF:
|
||||||
// 解析标识符
|
|
||||||
if (!scc_lexer_next_non_blank(pp->cur_ring, &tok) ||
|
|
||||||
tok.type != SCC_TOK_IDENT) {
|
|
||||||
scc_lexer_tok_drop(&tok);
|
|
||||||
LOG_ERROR("expected identifier");
|
|
||||||
goto ERROR;
|
|
||||||
}
|
|
||||||
cbool defined =
|
|
||||||
(scc_pproc_macro_table_get(&pp->macro_table, &tok.lexeme) != null);
|
|
||||||
cbool condition = (type == SCC_PP_TOK_IFDEF) ? defined : !defined;
|
|
||||||
scc_lexer_tok_drop(&tok);
|
|
||||||
|
|
||||||
scc_pproc_if_t new_if;
|
|
||||||
new_if.found_true = condition;
|
|
||||||
new_if.seen_else = 0;
|
|
||||||
new_if.active = pp->enable ? condition : 0;
|
|
||||||
scc_vec_push(pp->if_stack, new_if);
|
|
||||||
|
|
||||||
pp->enable = new_if.active;
|
|
||||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case SCC_PP_TOK_ELIFDEF:
|
case SCC_PP_TOK_ELIFDEF:
|
||||||
case SCC_PP_TOK_ELIFNDEF: {
|
case SCC_PP_TOK_ELIFNDEF: {
|
||||||
if (scc_vec_size(pp->if_stack) == 0) {
|
scc_lexer_tok_drop(&tok);
|
||||||
LOG_ERROR("#elif without #if");
|
|
||||||
goto ERROR;
|
|
||||||
}
|
|
||||||
scc_pproc_if_t *top =
|
|
||||||
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
|
|
||||||
if (top->seen_else) {
|
|
||||||
LOG_ERROR("#elif after #else");
|
|
||||||
goto ERROR;
|
|
||||||
}
|
|
||||||
// 解析标识符
|
|
||||||
if (!scc_lexer_next_non_blank(pp->cur_ring, &tok) ||
|
if (!scc_lexer_next_non_blank(pp->cur_ring, &tok) ||
|
||||||
tok.type != SCC_TOK_IDENT) {
|
tok.type != SCC_TOK_IDENT) {
|
||||||
scc_lexer_tok_drop(&tok);
|
scc_lexer_tok_drop(&tok);
|
||||||
LOG_ERROR("expected identifier");
|
LOG_ERROR("expected identifier");
|
||||||
goto ERROR;
|
} else {
|
||||||
}
|
scc_pproc_parse_if_defined(pp, type, &tok);
|
||||||
int defined =
|
|
||||||
(scc_pproc_macro_table_get(&pp->macro_table, &tok.lexeme) != NULL);
|
|
||||||
int condition = (type == SCC_PP_TOK_ELIFDEF) ? defined : !defined;
|
|
||||||
scc_lexer_tok_drop(&tok);
|
scc_lexer_tok_drop(&tok);
|
||||||
|
|
||||||
if (top->found_true) {
|
|
||||||
// 前面已有真分支,本 elif 不激活
|
|
||||||
top->active = 0;
|
|
||||||
} else {
|
|
||||||
if (condition) {
|
|
||||||
top->active = 1;
|
|
||||||
top->found_true = 1;
|
|
||||||
} else {
|
|
||||||
top->active = 0;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// seen_else 仍为 0
|
|
||||||
pp->enable = top->active;
|
|
||||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case SCC_PP_TOK_ELSE: {
|
|
||||||
if (scc_vec_size(pp->if_stack) == 0) {
|
|
||||||
LOG_ERROR("#else without #if");
|
|
||||||
goto ERROR;
|
|
||||||
}
|
|
||||||
scc_pproc_if_t *top =
|
|
||||||
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
|
|
||||||
if (top->seen_else) {
|
|
||||||
LOG_ERROR("multiple #else");
|
|
||||||
goto ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (top->found_true) {
|
|
||||||
top->active = 0;
|
|
||||||
} else {
|
|
||||||
top->active = 1;
|
|
||||||
top->found_true = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
top->seen_else = 1;
|
|
||||||
|
|
||||||
pp->enable = top->active;
|
|
||||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
scc_lexer_skip_until_newline(pp->cur_ring);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case SCC_PP_TOK_ELSE:
|
||||||
case SCC_PP_TOK_ENDIF: {
|
case SCC_PP_TOK_ENDIF: {
|
||||||
if (scc_vec_size(pp->if_stack) == 0) {
|
scc_lexer_tok_drop(&tok);
|
||||||
LOG_ERROR("#endif without #if");
|
scc_pproc_parse_if_defined(pp, type, null);
|
||||||
} else {
|
|
||||||
scc_vec_pop(pp->if_stack);
|
|
||||||
}
|
|
||||||
if (scc_vec_size(pp->if_stack) == 0) {
|
|
||||||
pp->enable = 1;
|
|
||||||
} else {
|
|
||||||
pp->enable =
|
|
||||||
scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1).active;
|
|
||||||
}
|
|
||||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
scc_lexer_skip_until_newline(pp->cur_ring);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case SCC_PP_TOK_IF:
|
case SCC_PP_TOK_IF:
|
||||||
case SCC_PP_TOK_ELIF:
|
case SCC_PP_TOK_ELIF: {
|
||||||
// parse constant expression
|
scc_lexer_tok_drop(&tok);
|
||||||
// 暂不实现表达式求值
|
scc_lexer_tok_ring_t out_ring;
|
||||||
LOG_ERROR("Expression in #if/#elif not implemented");
|
scc_pproc_parse_line_and_expand(pp, &out_ring);
|
||||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
scc_pproc_parse_if_condition(pp, type, &out_ring);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
case SCC_PP_TOK_LINE:
|
case SCC_PP_TOK_LINE:
|
||||||
case SCC_PP_TOK_EMBED:
|
case SCC_PP_TOK_EMBED:
|
||||||
goto ERROR;
|
goto ERROR;
|
||||||
@@ -443,7 +387,8 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
|
|||||||
return;
|
return;
|
||||||
case SCC_PP_TOK_PRAGMA:
|
case SCC_PP_TOK_PRAGMA:
|
||||||
LOG_WARN("Pragma ignored");
|
LOG_WARN("Pragma ignored");
|
||||||
break;
|
scc_lexer_skip_until_newline(pp->cur_ring);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,32 +123,52 @@ void scc_pproc_expand_by_vec(scc_pproc_macro_table_t *macro_table,
|
|||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
split_arguments(scc_pproc_macro_extened_params_t *splited_params,
|
split_arguments(scc_pproc_macro_extened_params_t *splited_params,
|
||||||
scc_lexer_tok_vec_t *raw_args) {
|
scc_lexer_tok_vec_t *raw_args, const scc_pproc_macro_t *macro) {
|
||||||
scc_lexer_tok_vec_t arg;
|
scc_lexer_tok_vec_t arg;
|
||||||
scc_vec_init(arg);
|
scc_vec_init(arg);
|
||||||
|
|
||||||
|
int named_count = (int)scc_vec_size(macro->params);
|
||||||
|
cbool is_variadic =
|
||||||
|
(named_count > 0 &&
|
||||||
|
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) {
|
||||||
scc_lexer_tok_drop(raw_arg);
|
depth++;
|
||||||
if (scc_vec_size(arg) &&
|
} else if (raw_arg->type == SCC_TOK_R_PAREN) {
|
||||||
scc_vec_at(arg, scc_vec_size(arg) - 1).type == SCC_TOK_BLANK) {
|
depth--;
|
||||||
scc_lexer_tok_drop(&scc_vec_pop(arg));
|
|
||||||
}
|
}
|
||||||
scc_vec_push(*splited_params, arg);
|
if (depth != 0 || raw_arg->type != SCC_TOK_COMMA ||
|
||||||
scc_vec_init(arg);
|
(is_variadic && scc_vec_size(*splited_params) == named_count - 1)) {
|
||||||
} else {
|
|
||||||
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);
|
||||||
} else {
|
} else {
|
||||||
scc_vec_push(arg, *raw_arg);
|
scc_vec_push(arg, *raw_arg);
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
scc_vec_push(*splited_params, arg);
|
scc_vec_push(*splited_params, arg);
|
||||||
|
if (is_variadic && scc_vec_size(*splited_params) == named_count - 1) {
|
||||||
|
scc_vec_init(arg);
|
||||||
|
scc_vec_push(*splited_params, arg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
@@ -219,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;
|
||||||
@@ -232,7 +271,7 @@ static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
|
|||||||
// collect, fill and expand arg
|
// collect, fill and expand arg
|
||||||
scc_pproc_macro_extened_params_t splited_params;
|
scc_pproc_macro_extened_params_t splited_params;
|
||||||
scc_vec_init(splited_params);
|
scc_vec_init(splited_params);
|
||||||
split_arguments(&splited_params, &raw_args);
|
split_arguments(&splited_params, &raw_args, macro);
|
||||||
|
|
||||||
scc_pproc_macro_extened_params_t expanded_params;
|
scc_pproc_macro_extened_params_t expanded_params;
|
||||||
scc_vec_init(expanded_params);
|
scc_vec_init(expanded_params);
|
||||||
@@ -252,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);
|
||||||
@@ -274,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");
|
||||||
}
|
}
|
||||||
@@ -296,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)
|
||||||
@@ -368,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));
|
||||||
|
|||||||
173
libs/pproc/src/pproc_if.c
Normal file
173
libs/pproc/src/pproc_if.c
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#include <scc_lexer_utils.h>
|
||||||
|
#include <scc_pproc.h>
|
||||||
|
|
||||||
|
cbool scc_pproc_parse_if_defined(scc_pproc_t *pp, scc_tok_type_t type,
|
||||||
|
const scc_lexer_tok_t *tok) {
|
||||||
|
int defined = 0;
|
||||||
|
if (tok) {
|
||||||
|
defined = (scc_pproc_macro_table_get(&pp->macro_table,
|
||||||
|
&(tok->lexeme)) != null);
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case SCC_PP_TOK_IFDEF:
|
||||||
|
case SCC_PP_TOK_IFNDEF: {
|
||||||
|
cbool condition = (type == SCC_PP_TOK_IFDEF) ? defined : !defined;
|
||||||
|
|
||||||
|
scc_pproc_if_t new_if;
|
||||||
|
new_if.found_true = condition;
|
||||||
|
new_if.seen_else = 0;
|
||||||
|
new_if.active = pp->enable ? condition : 0;
|
||||||
|
scc_vec_push(pp->if_stack, new_if);
|
||||||
|
|
||||||
|
pp->enable = new_if.active;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SCC_PP_TOK_ELIFDEF:
|
||||||
|
case SCC_PP_TOK_ELIFNDEF: {
|
||||||
|
if (scc_vec_size(pp->if_stack) == 0) {
|
||||||
|
LOG_ERROR("#elif without #if");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
scc_pproc_if_t *top =
|
||||||
|
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
|
||||||
|
if (top->seen_else) {
|
||||||
|
LOG_ERROR("#elif after #else");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int condition = (type == SCC_PP_TOK_ELIFDEF) ? defined : !defined;
|
||||||
|
|
||||||
|
if (top->found_true) {
|
||||||
|
// 前面已有真分支,本 elif 不激活
|
||||||
|
top->active = 0;
|
||||||
|
} else {
|
||||||
|
if (condition) {
|
||||||
|
top->active = 1;
|
||||||
|
top->found_true = 1;
|
||||||
|
} else {
|
||||||
|
top->active = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// seen_else 仍为 0
|
||||||
|
pp->enable = top->active;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SCC_PP_TOK_ELSE: {
|
||||||
|
if (scc_vec_size(pp->if_stack) == 0) {
|
||||||
|
LOG_ERROR("#else without #if");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
scc_pproc_if_t *top =
|
||||||
|
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
|
||||||
|
if (top->seen_else) {
|
||||||
|
LOG_ERROR("multiple #else");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (top->found_true) {
|
||||||
|
top->active = 0;
|
||||||
|
} else {
|
||||||
|
top->active = 1;
|
||||||
|
top->found_true = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
top->seen_else = 1;
|
||||||
|
|
||||||
|
pp->enable = top->active;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SCC_PP_TOK_ENDIF: {
|
||||||
|
if (scc_vec_size(pp->if_stack) == 0) {
|
||||||
|
LOG_ERROR("#endif without #if");
|
||||||
|
} else {
|
||||||
|
scc_vec_pop(pp->if_stack);
|
||||||
|
}
|
||||||
|
if (scc_vec_size(pp->if_stack) == 0) {
|
||||||
|
pp->enable = 1;
|
||||||
|
} else {
|
||||||
|
pp->enable =
|
||||||
|
scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1).active;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
LOG_FATAL("unexpected directive");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cbool parse_constant_condition() { return false; }
|
||||||
|
|
||||||
|
cbool scc_pproc_parse_if_condition(scc_pproc_t *pp, scc_tok_type_t type,
|
||||||
|
scc_lexer_tok_ring_t *tok_ring) {
|
||||||
|
// TODO
|
||||||
|
int ok;
|
||||||
|
scc_lexer_tok_t tok = {0};
|
||||||
|
ok = scc_lexer_next_non_blank(tok_ring, &tok);
|
||||||
|
if (ok == false) {
|
||||||
|
LOG_FATAL("unexpected EOF");
|
||||||
|
}
|
||||||
|
int condition = 0;
|
||||||
|
if (tok.type == SCC_TOK_INT_LITERAL) {
|
||||||
|
condition = scc_cstring_as_cstr(&tok.lexeme)[0] == '0' ? 0 : 1;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("expected integer constant but got %s",
|
||||||
|
scc_cstring_as_cstr(&tok.lexeme));
|
||||||
|
}
|
||||||
|
scc_lexer_tok_drop(&tok);
|
||||||
|
|
||||||
|
ok = scc_lexer_next_non_blank(tok_ring, &tok);
|
||||||
|
if (ok == false) {
|
||||||
|
LOG_FATAL("unexpected EOF");
|
||||||
|
}
|
||||||
|
if (tok.type != SCC_TOK_ENDLINE) {
|
||||||
|
LOG_ERROR("expected endline");
|
||||||
|
scc_lexer_skip_until_newline(tok_ring);
|
||||||
|
} else {
|
||||||
|
scc_lexer_tok_drop(&tok);
|
||||||
|
}
|
||||||
|
scc_ring_free(*tok_ring);
|
||||||
|
|
||||||
|
// 根据指令类型更新条件编译栈
|
||||||
|
switch (type) {
|
||||||
|
case SCC_PP_TOK_IF: {
|
||||||
|
scc_pproc_if_t new_if;
|
||||||
|
new_if.found_true = condition;
|
||||||
|
new_if.seen_else = 0;
|
||||||
|
new_if.active = pp->enable ? condition : 0;
|
||||||
|
scc_vec_push(pp->if_stack, new_if);
|
||||||
|
pp->enable = new_if.active;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SCC_PP_TOK_ELIF: {
|
||||||
|
if (scc_vec_size(pp->if_stack) == 0) {
|
||||||
|
LOG_ERROR("#elif without #if");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
scc_pproc_if_t *top =
|
||||||
|
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
|
||||||
|
if (top->seen_else) {
|
||||||
|
LOG_ERROR("#elif after #else");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (top->found_true) {
|
||||||
|
top->active = 0;
|
||||||
|
} else {
|
||||||
|
if (condition) {
|
||||||
|
top->active = 1;
|
||||||
|
top->found_true = 1;
|
||||||
|
} else {
|
||||||
|
top->active = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pp->enable = top->active;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
LOG_FATAL("unexpected directive in parse_if_condition");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <pproc_expand.h>
|
#include <pproc_expand.h>
|
||||||
|
#include <scc_core_ring.h>
|
||||||
#include <scc_pproc.h>
|
#include <scc_pproc.h>
|
||||||
|
|
||||||
static int switch_file_stack(scc_pproc_t *pp, scc_cstring_t *fname,
|
static int switch_file_stack(scc_pproc_t *pp, scc_cstring_t *fname,
|
||||||
@@ -56,31 +57,16 @@ FOPEN:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void scc_pproc_parse_include(scc_pproc_t *pp, scc_lexer_tok_t *include_tok) {
|
void scc_pproc_parse_include(scc_pproc_t *pp, scc_lexer_tok_t *include_tok,
|
||||||
|
scc_lexer_tok_ring_t *tok_ring) {
|
||||||
int ok;
|
int ok;
|
||||||
scc_lexer_tok_t tok;
|
scc_lexer_tok_t tok;
|
||||||
scc_pos_t pos = include_tok->loc;
|
scc_pos_t pos = include_tok->loc;
|
||||||
scc_lexer_tok_drop(include_tok);
|
scc_lexer_tok_drop(include_tok);
|
||||||
|
|
||||||
scc_lexer_tok_ring_t *stream = pp->cur_ring;
|
|
||||||
scc_lexer_tok_vec_t org_toks;
|
|
||||||
scc_vec_init(org_toks);
|
|
||||||
while (1) {
|
|
||||||
scc_ring_peek(*stream, tok, ok);
|
|
||||||
if (ok == false)
|
|
||||||
break;
|
|
||||||
scc_ring_next_consume(*stream, tok, ok);
|
|
||||||
scc_vec_push(org_toks, tok);
|
|
||||||
// FIXME endline needed?
|
|
||||||
if (tok.type == SCC_TOK_ENDLINE)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
scc_lexer_tok_ring_t out_ring;
|
|
||||||
scc_pproc_expand_by_vec(&pp->macro_table, &org_toks, &out_ring);
|
|
||||||
scc_cstring_t line = scc_cstring_create();
|
scc_cstring_t line = scc_cstring_create();
|
||||||
while (1) {
|
while (1) {
|
||||||
scc_ring_next_consume(out_ring, tok, ok);
|
scc_ring_next_consume(*tok_ring, tok, ok);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
break;
|
break;
|
||||||
if (scc_get_tok_subtype(tok.type) != SCC_TOK_SUBTYPE_EMPTYSPACE &&
|
if (scc_get_tok_subtype(tok.type) != SCC_TOK_SUBTYPE_EMPTYSPACE &&
|
||||||
@@ -89,7 +75,7 @@ void scc_pproc_parse_include(scc_pproc_t *pp, scc_lexer_tok_t *include_tok) {
|
|||||||
}
|
}
|
||||||
scc_lexer_tok_drop(&tok);
|
scc_lexer_tok_drop(&tok);
|
||||||
}
|
}
|
||||||
scc_ring_free(out_ring);
|
scc_ring_free(*tok_ring);
|
||||||
|
|
||||||
const char *includename = scc_cstring_as_cstr(&line);
|
const char *includename = scc_cstring_as_cstr(&line);
|
||||||
int len = scc_cstring_len(&line);
|
int len = scc_cstring_len(&line);
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -350,6 +365,169 @@ static void test_conditional_ifdef(void) {
|
|||||||
"x defined\nY not defined\nafter inner\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}
|
#define TEST_LIST_CASE(func_name) {#func_name, func_name}
|
||||||
TEST_LIST = {
|
TEST_LIST = {
|
||||||
TEST_LIST_CASE(test_define_simple_no_macro),
|
TEST_LIST_CASE(test_define_simple_no_macro),
|
||||||
@@ -363,5 +541,10 @@ TEST_LIST = {
|
|||||||
TEST_LIST_CASE(test_undef_macros),
|
TEST_LIST_CASE(test_undef_macros),
|
||||||
TEST_LIST_CASE(hard_test_define_func_macros),
|
TEST_LIST_CASE(hard_test_define_func_macros),
|
||||||
TEST_LIST_CASE(test_conditional_ifdef),
|
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},
|
{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);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""cbuild.py - 优化的轻量C构建系统"""
|
"""cbuild.py - 优化的轻量C构建系统""" # pylint: disable=too-many-lines
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
import tomllib
|
import tomllib
|
||||||
@@ -517,7 +517,7 @@ class BuildCache:
|
|||||||
with open(self.cache_file, "r", encoding="utf-8") as f:
|
with open(self.cache_file, "r", encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
self.cache = {k: CacheEntry(**v) for k, v in data.items()}
|
self.cache = {k: CacheEntry(**v) for k, v in data.items()}
|
||||||
except OSError, json.JSONDecodeError, TypeError:
|
except (OSError, json.JSONDecodeError, TypeError):
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
@@ -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:
|
||||||
@@ -679,6 +682,25 @@ class ClangCompiler(Compiler):
|
|||||||
self.run(cmd)
|
self.run(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
class SccCompiler(Compiler):
|
||||||
|
"""SCC编译器"""
|
||||||
|
|
||||||
|
def get_flags(self, mode: BuildMode) -> list[str]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def compile(
|
||||||
|
self, source: Path, output: Path, includes: list[Path], flags: list[str]
|
||||||
|
):
|
||||||
|
# cmd = ["clang"] + flags + ["-c", str(source), "-o", str(output)]
|
||||||
|
cmd = ["scc", "--emit-pp", "-o", str(output), str(source), "-I", "scc_libs"]
|
||||||
|
for inc in includes:
|
||||||
|
cmd += ["-I", f"{inc}"]
|
||||||
|
self.run(cmd)
|
||||||
|
|
||||||
|
def link(self, objects: list[Path], output: Path, flags: list[str]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DummyCompiler(Compiler):
|
class DummyCompiler(Compiler):
|
||||||
"""虚拟编译器(用于测试)"""
|
"""虚拟编译器(用于测试)"""
|
||||||
|
|
||||||
@@ -1018,12 +1040,18 @@ 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):
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
"--compiler", "-c", choices=["gcc", "clang"], default="gcc", help="编译器"
|
"--compiler",
|
||||||
|
"-c",
|
||||||
|
choices=["gcc", "clang", "scc"],
|
||||||
|
default="gcc",
|
||||||
|
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(
|
||||||
@@ -1102,6 +1130,7 @@ def create_parser():
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
|
# print("current cwd: " + os.getcwd())
|
||||||
parser = create_parser()
|
parser = create_parser()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -1117,9 +1146,12 @@ def main():
|
|||||||
compiler_map = {
|
compiler_map = {
|
||||||
"gcc": GccCompiler(),
|
"gcc": GccCompiler(),
|
||||||
"clang": ClangCompiler(),
|
"clang": ClangCompiler(),
|
||||||
|
"scc": SccCompiler(),
|
||||||
}
|
}
|
||||||
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()
|
||||||
|
|
||||||
|
|||||||
53
tools/wc.py
53
tools/wc.py
@@ -1,53 +0,0 @@
|
|||||||
"""统计目录下C/C++文件的行数(write by AI)"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
def count_lines(file_path):
|
|
||||||
"""统计单个文件的代码行数"""
|
|
||||||
try:
|
|
||||||
with open(file_path, "rb") as f: # 二进制模式读取避免编码问题
|
|
||||||
return sum(1 for _ in f)
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
print(f"警告:无法解码文件 {file_path}(可能不是文本文件)")
|
|
||||||
return 0
|
|
||||||
except Exception as e:
|
|
||||||
print(f"读取 {file_path} 出错: {str(e)}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def scan_files(directory, exclude_dirs=None):
|
|
||||||
"""扫描目录获取所有C/C++文件"""
|
|
||||||
if exclude_dirs is None:
|
|
||||||
exclude_dirs = [".git", "venv", "__pycache__", ".old"] # 默认排除的目录
|
|
||||||
|
|
||||||
c_files = []
|
|
||||||
for root, dirs, files in os.walk(directory):
|
|
||||||
# 跳过排除目录
|
|
||||||
dirs[:] = [d for d in dirs if d not in exclude_dirs]
|
|
||||||
|
|
||||||
for file in files:
|
|
||||||
if file.endswith((".c", ".h")):
|
|
||||||
full_path = os.path.join(root, file)
|
|
||||||
c_files.append(full_path)
|
|
||||||
return c_files
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""main function"""
|
|
||||||
target_dir = input("请输入要扫描的目录路径(留空为当前目录): ") or "."
|
|
||||||
|
|
||||||
files = scan_files(target_dir)
|
|
||||||
total_lines = 0
|
|
||||||
|
|
||||||
print("\n统计结果:")
|
|
||||||
for idx, file in enumerate(files, 1):
|
|
||||||
lines = count_lines(file)
|
|
||||||
total_lines += lines
|
|
||||||
print(f"{idx:4d}. {file} ({lines} 行)")
|
|
||||||
|
|
||||||
print(f"\n总计: {len(files)} 个C/C++文件,共 {total_lines} 行代码")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Reference in New Issue
Block a user