Compare commits

..

8 Commits

Author SHA1 Message Date
zzy
d2eaf2247f build: 重命名许可证文件名
将文件名从 LISENCE 更正为 LICENSE,修正拼写错误。
2026-02-23 16:44:31 +08:00
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
zzy
4940b652eb feat(cbuild): 添加SCC编译器支持并更新构建脚本
添加了新的SccCompiler类以支持SCC编译器,包括编译和链接功能。
更新了justfile中的构建任务,添加了clean、build、build-install和test-scc等新任务。
同时修复了异常处理语法错误并将wc.py工具文件移除。
2026-02-21 16:02:53 +08:00
zzy
8007825800 feat(pproc): 实现预处理器条件编译和可变参数宏支持
- 添加了完整的条件编译功能,包括 #if、#elif、#else、#endif 指令
- 实现了数值常量表达式的解析和求值
- 支持嵌套条件编译和与其他指令混合使用
- 实现了可变参数宏定义和 __VA_ARGS__ 替换功能
- 改进了宏展开机制以正确处理可变参数宏
- 重构了预处理器指令处理逻辑,提高了代码可维护性
- 添加了相应的单元测试用例验证新功能
2026-02-21 15:59:31 +08:00
zzy
b705e5d0ad feat(argparse): 添加列表类型参数支持
新增 scc_argparse_list_t 类型用于处理多个字符串值的参数,
并添加相应的配置函数 scc_argparse_spec_setup_list。

fix(lexer): 调整关键字标记处理逻辑

将关键字子类型从 SCC_TOK_SUBTYPE_KEYWORD 改为
SCC_TOK_SUBTYPE_IDENTIFIER,并移除相关的枚举定义。

refactor(lexer): 优化跳过换行功能实现

修改 scc_lexer_skip_until_newline 函数实现,改进循环逻辑和错误处理。

feat(pproc): 完善预处理器条件编译支持

重构条件编译状态管理,添加对 #ifdef、#ifndef、#elifdef、
#else、#endif 等指令的支持,并实现嵌套条件处理。

refactor(pproc): 优化文件包含路径处理

添加最大包含深度限制,改进包含路径添加功能,
修复文件状态结构命名。

docs(log): 更新日志模块标准库依赖

调整 stdarg.h 包含逻辑,更新编译器内置宏定义名称。

feat(core): 扩展核心类型定义

添加基础数据类型别名定义,完善类型系统支持。

feat(main): 实现命令行包含路径参数

添加 -I/--include 参数支持,允许用户指定额外的头文件搜索路径。
2026-02-21 10:46:49 +08:00
zzy
9c2b4db22a feat(pproc): 修改include解析函数以支持位置信息传递
修改了scc_pproc_parse_include函数签名,添加了token参数用于传递位置信息,
使得在处理包含指令时能够提供更准确的错误定位。同时更新了文件切换逻辑,
将位置信息传递给错误日志,提高调试效率。
2026-02-20 14:28:11 +08:00
zzy
bc0b1d23e3 feat(pproc): 添加预处理器包含路径支持和改进头文件查找逻辑
添加了新的类型定义 scc_pproc_cstr_vec_t 用于存储包含路径,
并在 scc_pproc 结构中添加 include_paths 字段。实现改进的
switch_file_stack 函数,支持从当前目录、父目录和系统包含路径
中查找头文件,提供更完整的 #include 指令处理能力。

fix(core): 重命名环形缓冲区内联宏避免命名冲突

将 scc_ring_phys 宏重命名为 _scc_ring_phys,并添加其他相关
内部宏如 _scc_ring_cap、_scc_ring_head 等,以避免与外部接口
的命名冲突并提高代码清晰度。

refactor(main): 添加命令行包含路径选项并清理标准库引用

在命令行参数解析中添加 -I/--include 选项支持,允许用户指定
额外的头文件搜索路径。同时移除不必要的 stdio.h 引用并清理
一些调试相关的缓冲区设置。
2026-02-19 19:30:00 +08:00
20 changed files with 1043 additions and 285 deletions

8
LICENSE 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

@@ -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

View File

@@ -43,6 +43,7 @@ typedef enum scc_argparse_err {
SCC_ARGPARSE_ERR_UNKNOWN_VALUE, SCC_ARGPARSE_ERR_UNKNOWN_VALUE,
} scc_argparse_err_t; } scc_argparse_err_t;
typedef SCC_VEC(const char *) scc_argparse_list_t;
// 约束规范结构体 // 约束规范结构体
struct scc_argparse_spec { struct scc_argparse_spec {
scc_argparse_val_type_t value_type; // 值类型 scc_argparse_val_type_t value_type; // 值类型
@@ -56,6 +57,7 @@ struct scc_argparse_spec {
const char **str_store; // 字符串存储 const char **str_store; // 字符串存储
char **str_alloc_store; // 字符串存储使用alloc需要free char **str_alloc_store; // 字符串存储使用alloc需要free
void **ptr_store; // 通用指针存储 void **ptr_store; // 通用指针存储
scc_argparse_list_t *vec_store; // 新增:指向字符串向量的指针
} store; } store;
// 枚举值约束 // 枚举值约束
@@ -215,6 +217,15 @@ static inline void scc_argparse_spec_setup_choices(scc_argparse_spec_t *spec,
spec->choices.count = count; spec->choices.count = count;
} }
// 添加设置列表的辅助函数(内联)
static inline void scc_argparse_spec_setup_list(scc_argparse_spec_t *spec,
scc_argparse_list_t *vec) {
spec->value_type = SCC_ARGPARSE_VAL_TYPE_LIST;
spec->store.vec_store = vec;
// 自动设置 flag_takes_multiple 为 true因为列表需要多个值
spec->flag_takes_multiple = true;
}
#define SCC_ARGPARSE_MACRO_SETTER(attr) \ #define SCC_ARGPARSE_MACRO_SETTER(attr) \
static inline void scc_argparse_spec_set_##attr(scc_argparse_spec_t *spec, \ static inline void scc_argparse_spec_set_##attr(scc_argparse_spec_t *spec, \
cbool flag) { \ cbool flag) { \

View File

@@ -63,6 +63,10 @@ static inline void parse_cmd(scc_optparse_t *optparse,
min_args = 0; min_args = 0;
max_args = 0; max_args = 0;
break; break;
case SCC_ARGPARSE_VAL_TYPE_LIST: // 列表类型
min_args = 1;
max_args = 65535; // FIXME maybe INT_MAX ?
break;
default: default:
min_args = 0; min_args = 0;
max_args = 0; max_args = 0;
@@ -161,6 +165,10 @@ static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) {
return SCC_ARGPARSE_ERR_PNT_DEFAULT; return SCC_ARGPARSE_ERR_PNT_DEFAULT;
} }
if (parser->need_version && scc_strcmp(opt->long_name, "version") == 0) {
// TODO default version print
}
if (opt->spec.flag_store_as_count) { if (opt->spec.flag_store_as_count) {
(*opt->spec.store.int_store)++; (*opt->spec.store.int_store)++;
} }
@@ -169,8 +177,14 @@ static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) {
*opt->spec.store.bool_store = true; *opt->spec.store.bool_store = true;
} }
if (ctx->result.value) { if (!ctx->result.value) {
return SCC_ARGPARSE_ERR_NONE;
}
opt->spec.raw_value = ctx->result.value; opt->spec.raw_value = ctx->result.value;
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) {
scc_vec_push(*opt->spec.store.vec_store, ctx->result.value);
} else {
*opt->spec.store.str_store = ctx->result.value; *opt->spec.store.str_store = ctx->result.value;
} }

View File

@@ -42,44 +42,44 @@ typedef enum scc_cstd {
// WARNING: Using Binary Search To Fast Find Keyword // WARNING: Using Binary Search To Fast Find Keyword
// 你必须确保其中是按照字典序排列 // 你必须确保其中是按照字典序排列
#define SCC_CKEYWORD_TABLE \ #define SCC_CKEYWORD_TABLE \
X(asm , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ASM , SCC_CEXT_SCC) \ X(asm , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ASM , SCC_CEXT_SCC) \
X(atomic , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ATOMIC , SCC_CEXT_SCC) \ X(atomic , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ATOMIC , SCC_CEXT_SCC) \
X(auto , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_AUTO , SCC_CEXT_SCC) \ X(auto , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_AUTO , SCC_CEXT_SCC) \
X(bool , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_BOOL , SCC_CEXT_SCC) \ X(bool , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_BOOL , SCC_CEXT_SCC) \
X(break , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_BREAK , SCC_CSTD_C89) \ X(break , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_BREAK , SCC_CSTD_C89) \
X(case , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CASE , SCC_CSTD_C89) \ X(case , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CASE , SCC_CSTD_C89) \
X(char , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CHAR , SCC_CSTD_C89) \ X(char , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CHAR , SCC_CSTD_C89) \
X(complex , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_COMPLEX , SCC_CEXT_SCC) \ X(complex , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_COMPLEX , SCC_CEXT_SCC) \
X(const , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CONST , SCC_CSTD_C89) \ X(const , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CONST , SCC_CSTD_C89) \
X(continue , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CONTINUE , SCC_CSTD_C89) \ X(continue , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CONTINUE , SCC_CSTD_C89) \
X(default , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_DEFAULT , SCC_CSTD_C89) \ X(default , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_DEFAULT , SCC_CSTD_C89) \
X(do , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_DO , SCC_CSTD_C89) \ X(do , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_DO , SCC_CSTD_C89) \
X(double , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_DOUBLE , SCC_CSTD_C89) \ X(double , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_DOUBLE , SCC_CSTD_C89) \
X(else , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ELSE , SCC_CSTD_C89) \ X(else , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ELSE , SCC_CSTD_C89) \
X(enum , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ENUM , SCC_CSTD_C89) \ X(enum , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ENUM , SCC_CSTD_C89) \
X(extern , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_EXTERN , SCC_CSTD_C89) \ X(extern , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_EXTERN , SCC_CSTD_C89) \
X(float , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_FLOAT , SCC_CSTD_C89) \ X(float , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_FLOAT , SCC_CSTD_C89) \
X(for , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_FOR , SCC_CSTD_C89) \ X(for , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_FOR , SCC_CSTD_C89) \
X(goto , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_GOTO , SCC_CSTD_C89) \ X(goto , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_GOTO , SCC_CSTD_C89) \
X(if , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_IF , SCC_CSTD_C89) \ X(if , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_IF , SCC_CSTD_C89) \
X(inline , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_INLINE , SCC_CSTD_C99) \ X(inline , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_INLINE , SCC_CSTD_C99) \
X(int , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_INT , SCC_CSTD_C89) \ X(int , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_INT , SCC_CSTD_C89) \
X(long , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_LONG , SCC_CSTD_C89) \ X(long , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_LONG , SCC_CSTD_C89) \
X(register , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_REGISTER , SCC_CSTD_C89) \ X(register , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_REGISTER , SCC_CSTD_C89) \
X(restrict , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_RESTRICT , SCC_CSTD_C99) \ X(restrict , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_RESTRICT , SCC_CSTD_C99) \
X(return , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_RETURN , SCC_CSTD_C89) \ X(return , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_RETURN , SCC_CSTD_C89) \
X(short , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SHORT , SCC_CSTD_C89) \ X(short , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SHORT , SCC_CSTD_C89) \
X(signed , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SIGNED , SCC_CSTD_C89) \ X(signed , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SIGNED , SCC_CSTD_C89) \
X(sizeof , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SIZEOF , SCC_CSTD_C89) \ X(sizeof , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SIZEOF , SCC_CSTD_C89) \
X(static , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_STATIC , SCC_CSTD_C89) \ X(static , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_STATIC , SCC_CSTD_C89) \
X(struct , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_STRUCT , SCC_CSTD_C89) \ X(struct , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_STRUCT , SCC_CSTD_C89) \
X(switch , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SWITCH , SCC_CSTD_C89) \ X(switch , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SWITCH , SCC_CSTD_C89) \
X(typedef , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_TYPEDEF , SCC_CSTD_C89) \ X(typedef , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_TYPEDEF , SCC_CSTD_C89) \
X(union , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_UNION , SCC_CSTD_C89) \ X(union , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_UNION , SCC_CSTD_C89) \
X(unsigned , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_UNSIGNED , SCC_CSTD_C89) \ X(unsigned , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_UNSIGNED , SCC_CSTD_C89) \
X(void , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_VOID , SCC_CSTD_C89) \ X(void , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_VOID , SCC_CSTD_C89) \
X(volatile , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_VOLATILE , SCC_CSTD_C89) \ X(volatile , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_VOLATILE , SCC_CSTD_C89) \
X(while , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_WHILE , SCC_CSTD_C89) \ X(while , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_WHILE , SCC_CSTD_C89) \
// KEYWORD_TABLE // KEYWORD_TABLE
#define SCC_CTOK_TABLE \ #define SCC_CTOK_TABLE \
@@ -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,11 +159,11 @@ 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 {
SCC_TOK_SUBTYPE_INVALID, // 错误占位 SCC_TOK_SUBTYPE_INVALID, // 错误占位
SCC_TOK_SUBTYPE_KEYWORD, // 关键字
SCC_TOK_SUBTYPE_OPERATOR, // 操作符 SCC_TOK_SUBTYPE_OPERATOR, // 操作符
SCC_TOK_SUBTYPE_IDENTIFIER, // 标识符 SCC_TOK_SUBTYPE_IDENTIFIER, // 标识符
SCC_TOK_SUBTYPE_LITERAL, // 字面量 SCC_TOK_SUBTYPE_LITERAL, // 字面量

View File

@@ -28,11 +28,14 @@ static inline cbool scc_lexer_next_non_blank(scc_lexer_tok_ring_t *stream,
static inline void scc_lexer_skip_until_newline(scc_lexer_tok_ring_t *stream) { static inline void scc_lexer_skip_until_newline(scc_lexer_tok_ring_t *stream) {
scc_lexer_tok_t tok; scc_lexer_tok_t tok;
cbool ok; cbool ok;
while (scc_lexer_peek_non_blank(stream, &tok)) { while (1) {
if (tok.type == SCC_TOK_ENDLINE)
break;
scc_ring_next_consume(*stream, tok, ok); scc_ring_next_consume(*stream, tok, ok);
if (!ok)
break;
scc_tok_type_t type = tok.type;
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
if (type == SCC_TOK_ENDLINE)
break;
} }
} }

View File

@@ -15,19 +15,21 @@
// 条件编译状态栈 // 条件编译状态栈
typedef struct { typedef struct {
int active; // 当前层级是否有效(即应该输出 token cbool found_true;
int skip; // 当前层级是否跳过(即不输出 token cbool seen_else;
// 可根据需要增加状态,如 #if 的结果、#elif 已执行等 cbool active;
} scc_pproc_if_state_t; } scc_pproc_if_t;
typedef SCC_VEC(scc_pproc_if_state_t) scc_pproc_if_stack_t; typedef SCC_VEC(scc_pproc_if_t) scc_pproc_if_stack_t;
typedef struct { typedef struct {
scc_sstream_t sstream; scc_sstream_t sstream;
scc_lexer_t lexer; scc_lexer_t lexer;
scc_lexer_tok_ring_t *ring; scc_lexer_tok_ring_t *ring;
} scc_pproc_file_state_t; } scc_pproc_file_t;
typedef SCC_VEC(scc_pproc_file_state_t *) scc_pproc_file_stack_t; typedef SCC_VEC(scc_pproc_file_t *) scc_pproc_file_stack_t;
typedef SCC_VEC(scc_lexer_tok_ring_t *) scc_pproc_ring_vec_t; typedef SCC_VEC(scc_lexer_tok_ring_t *) scc_pproc_ring_vec_t;
typedef SCC_VEC(scc_cstring_t) scc_pproc_cstr_vec_t;
typedef struct scc_pproc { typedef struct scc_pproc {
scc_lexer_tok_ring_t *org_ring; scc_lexer_tok_ring_t *org_ring;
@@ -35,21 +37,44 @@ typedef struct scc_pproc {
scc_lexer_tok_ring_t expanded_ring; scc_lexer_tok_ring_t expanded_ring;
scc_strpool_t strpool; scc_strpool_t strpool;
int at_line_start; int at_line_start;
int enable;
scc_pproc_cstr_vec_t include_paths;
scc_pproc_macro_table_t macro_table; scc_pproc_macro_table_t macro_table;
scc_pproc_if_stack_t if_stack; scc_pproc_if_stack_t if_stack;
scc_pproc_file_stack_t file_stack; scc_pproc_file_stack_t file_stack;
scc_lexer_tok_ring_t ring; scc_lexer_tok_ring_t ring;
int ring_ref_count; int ring_ref_count;
struct {
int max_include_depth;
} config;
} scc_pproc_t; } scc_pproc_t;
void scc_pproc_init(scc_pproc_t *pp, scc_lexer_tok_ring_t *input); void scc_pproc_init(scc_pproc_t *pp, scc_lexer_tok_ring_t *input);
scc_lexer_tok_ring_t *scc_pproc_to_ring(scc_pproc_t *pp, int ring_size); scc_lexer_tok_ring_t *scc_pproc_to_ring(scc_pproc_t *pp, int ring_size);
void scc_pproc_drop(scc_pproc_t *pp); void scc_pproc_drop(scc_pproc_t *pp);
static inline void scc_pproc_add_include_path(scc_pproc_t *pp,
const scc_cstring_t *path) {
scc_vec_push(pp->include_paths, scc_cstring_copy(path));
}
static inline void scc_pproc_add_include_path_cstr(scc_pproc_t *pp,
const char *path) {
scc_vec_push(pp->include_paths, scc_cstring_from_cstr(path));
}
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);
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,

View File

@@ -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
@@ -208,10 +230,11 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
scc_lexer_tok_t tok = {0}; scc_lexer_tok_t tok = {0};
int ok = 0; int ok = 0;
scc_ring_next(*pp->cur_ring, tok, ok); scc_ring_next(*pp->cur_ring, tok, ok);
Assert(ok == true && tok.type == SCC_TOK_SHARP);
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
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) { scc_get_tok_subtype(tok.type) != SCC_TOK_SUBTYPE_IDENTIFIER) {
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
LOG_ERROR("Invalid preprocessor directive"); LOG_ERROR("Invalid preprocessor directive");
goto ERROR; goto ERROR;
@@ -223,8 +246,34 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
LOG_ERROR("Expected preprocessor keyword, got %s", tok.lexeme); LOG_ERROR("Expected preprocessor keyword, got %s", tok.lexeme);
goto ERROR; goto ERROR;
} }
scc_tok_type_t type = keywords[ret].tok_type; scc_tok_type_t type = keywords[ret].tok_type;
if (scc_vec_size(pp->if_stack) != 0) {
scc_pproc_if_t *top =
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
pp->enable = top->active;
}
// 非条件指令,且当前不活动时直接跳过整行
int is_conditional = 0;
switch (type) {
case SCC_PP_TOK_IFDEF:
case SCC_PP_TOK_IFNDEF:
case SCC_PP_TOK_ELIFDEF:
case SCC_PP_TOK_ELIFNDEF:
case SCC_PP_TOK_ELSE:
case SCC_PP_TOK_ENDIF:
case SCC_PP_TOK_IF:
case SCC_PP_TOK_ELIF:
is_conditional = 1;
break;
default:
break;
}
if (!is_conditional && !pp->enable) {
scc_lexer_skip_until_newline(pp->cur_ring);
return;
}
switch (type) { switch (type) {
case SCC_PP_TOK_DEFINE: { case SCC_PP_TOK_DEFINE: {
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
@@ -270,19 +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_lexer_tok_drop(&tok); scc_lexer_tok_ring_t out_ring;
scc_pproc_parse_include(pp); scc_pproc_parse_line_and_expand(pp, &out_ring);
scc_pproc_parse_include(pp, &tok, &out_ring);
return; return;
case SCC_PP_TOK_IF: }
case SCC_PP_TOK_IFDEF: case SCC_PP_TOK_IFDEF:
case SCC_PP_TOK_IFNDEF: case SCC_PP_TOK_IFNDEF:
case SCC_PP_TOK_ELSE:
case SCC_PP_TOK_ELIF:
case SCC_PP_TOK_ELIFDEF: case SCC_PP_TOK_ELIFDEF:
case SCC_PP_TOK_ELIFNDEF: case SCC_PP_TOK_ELIFNDEF: {
case SCC_PP_TOK_ENDIF: scc_lexer_tok_drop(&tok);
goto ERROR; if (!scc_lexer_next_non_blank(pp->cur_ring, &tok) ||
tok.type != SCC_TOK_IDENT) {
scc_lexer_tok_drop(&tok);
LOG_ERROR("expected identifier");
} else {
scc_pproc_parse_if_defined(pp, type, &tok);
scc_lexer_tok_drop(&tok);
}
scc_lexer_skip_until_newline(pp->cur_ring);
return;
}
case SCC_PP_TOK_ELSE:
case SCC_PP_TOK_ENDIF: {
scc_lexer_tok_drop(&tok);
scc_pproc_parse_if_defined(pp, type, null);
scc_lexer_skip_until_newline(pp->cur_ring);
return;
}
case SCC_PP_TOK_IF:
case SCC_PP_TOK_ELIF: {
scc_lexer_tok_drop(&tok);
scc_lexer_tok_ring_t out_ring;
scc_pproc_parse_line_and_expand(pp, &out_ring);
scc_pproc_parse_if_condition(pp, type, &out_ring);
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;
@@ -298,6 +371,7 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
} }
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
} }
return;
case SCC_PP_TOK_WARNING: case SCC_PP_TOK_WARNING:
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
while (1) { while (1) {
@@ -310,9 +384,11 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
} }
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
} }
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;
} }

View File

@@ -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, &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);
@@ -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, &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");
} }
@@ -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, &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));

173
libs/pproc/src/pproc_if.c Normal file
View 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;
}

View File

@@ -1,11 +1,52 @@
#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,
int is_system) { scc_pos_t *pos, int is_system) {
scc_pproc_file_state_t *file = scc_malloc(sizeof(scc_pproc_file_state_t)); scc_cstring_t fpath = scc_cstring_create();
int ret = 0;
const char *org_fname = pos->name;
if (!is_system) {
const char parent[] = "/../";
// FIXME maybe it can eazy
scc_cstring_append_cstr(&fpath, org_fname, scc_strlen(org_fname));
scc_cstring_append_cstr(&fpath, parent, scc_strlen(parent));
scc_cstring_append(&fpath, fname);
ret = scc_fexists(scc_cstring_as_cstr(&fpath));
if (ret == true) {
goto FOPEN;
}
}
/* system default path and -I includes path */
scc_vec_foreach(pp->include_paths, i) {
scc_cstring_free(&fpath);
scc_cstring_t *syspath = &scc_vec_at(pp->include_paths, i);
scc_cstring_append(&fpath, syspath);
scc_cstring_append_ch(&fpath, '/');
scc_cstring_append(&fpath, fname);
ret = scc_fexists(scc_cstring_as_cstr(&fpath));
if (ret == true) {
goto FOPEN;
}
}
LOG_ERROR("In %s:%d:%d include %c%s%c, the file is not found", org_fname,
pos->line, pos->col, is_system ? '<' : '\"',
scc_cstring_as_cstr(fname), is_system ? '>' : '\"');
return -1;
FOPEN:
if (scc_vec_size(pp->file_stack) >= pp->config.max_include_depth) {
LOG_FATAL("Include depth is too deep, the include depth is %d, set "
"MAX_INCLUDE_DEPTH is %d",
scc_vec_size(pp->file_stack), pp->config.max_include_depth);
}
scc_pproc_file_t *file = scc_malloc(sizeof(scc_pproc_file_t));
Assert(file != null); Assert(file != null);
if (scc_sstream_init(&(file->sstream), fname.data, 1024)) { if (scc_sstream_init(&(file->sstream), scc_cstring_as_cstr(&fpath), 1024)) {
return -1; return -1;
} }
scc_lexer_init(&(file->lexer), scc_sstream_to_ring(&(file->sstream))); scc_lexer_init(&(file->lexer), scc_sstream_to_ring(&(file->sstream)));
@@ -16,29 +57,16 @@ static int switch_file_stack(scc_pproc_t *pp, scc_cstring_t fname,
return 0; return 0;
} }
void scc_pproc_parse_include(scc_pproc_t *pp) { 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_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 &&
@@ -47,7 +75,7 @@ void scc_pproc_parse_include(scc_pproc_t *pp) {
} }
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);
@@ -74,9 +102,10 @@ void scc_pproc_parse_include(scc_pproc_t *pp) {
} }
scc_cstring_free(&line); scc_cstring_free(&line);
int is_system = includename[0] == '<'; int is_system = includename[0] == '<';
if (switch_file_stack(pp, fname, is_system)) { if (switch_file_stack(pp, &fname, &pos, is_system)) {
goto ERROR; // LOG_ERROR()
} }
scc_cstring_free(&fname);
return; return;
ERROR: ERROR:
LOG_ERROR("Invalid include filename need \"FILENAME\" or <FILENAME>"); LOG_ERROR("Invalid include filename need \"FILENAME\" or <FILENAME>");

View File

@@ -1,4 +1,5 @@
#include <pproc_expand.h> #include <pproc_expand.h>
#include <scc_lexer_utils.h>
#include <scc_pproc.h> #include <scc_pproc.h>
static int pproc_next_one_file(scc_pproc_t *pp, scc_lexer_tok_t *out) { static int pproc_next_one_file(scc_pproc_t *pp, scc_lexer_tok_t *out) {
@@ -25,13 +26,31 @@ CONTINUE:
return true; return true;
} }
if (pp->at_line_start && tok.type == SCC_TOK_SHARP) { if (pp->at_line_start) {
if (tok.type == SCC_TOK_SHARP) {
// parse to # // parse to #
scc_pproc_handle_directive(pp); scc_pproc_handle_directive(pp);
pp->at_line_start = true;
goto CONTINUE;
} else if (tok.type == SCC_TOK_BLANK) {
scc_ring_next(*stream, *out, ok);
scc_ring_peek(*stream, tok, ok);
if (ok && tok.type == SCC_TOK_SHARP) {
scc_pproc_handle_directive(pp);
pp->at_line_start = true;
goto CONTINUE;
}
scc_ring_back(*stream, ok);
Assert(ok == true);
scc_ring_peek(*stream, tok, ok);
}
}
if (pp->enable == false) {
scc_lexer_skip_until_newline(stream);
pp->at_line_start = true;
goto CONTINUE; goto CONTINUE;
} }
pp->at_line_start = false;
if (tok.type == SCC_TOK_IDENT) { if (tok.type == SCC_TOK_IDENT) {
// maybe expanded // maybe expanded
scc_pproc_macro_t *macro = scc_pproc_macro_t *macro =
@@ -62,7 +81,7 @@ CONTINUE:
return false; return false;
} }
scc_pproc_file_state_t *file = scc_vec_pop(pp->file_stack); scc_pproc_file_t *file = scc_vec_pop(pp->file_stack);
Assert(file->ring == pp->cur_ring); Assert(file->ring == pp->cur_ring);
scc_lexer_drop_ring(file->ring); scc_lexer_drop_ring(file->ring);
scc_lexer_drop(&(file->lexer)); scc_lexer_drop(&(file->lexer));
@@ -84,9 +103,13 @@ void scc_pproc_init(scc_pproc_t *pp, scc_lexer_tok_ring_t *input) {
pp->cur_ring = pp->org_ring; pp->cur_ring = pp->org_ring;
scc_ring_init(pp->expanded_ring, 0, 0, 0); scc_ring_init(pp->expanded_ring, 0, 0, 0);
scc_pproc_marco_table_init(&pp->macro_table); scc_pproc_marco_table_init(&pp->macro_table);
scc_vec_init(pp->include_paths);
scc_vec_init(pp->if_stack); scc_vec_init(pp->if_stack);
scc_vec_init(pp->file_stack); scc_vec_init(pp->file_stack);
pp->at_line_start = true; pp->at_line_start = true;
pp->enable = true;
pp->config.max_include_depth = 32;
} }
void scc_pproc_add_builtin_macros() { void scc_pproc_add_builtin_macros() {

View File

@@ -17,7 +17,7 @@ static cbool process_input(const char *input, scc_cstring_t *output) {
scc_pproc_init(&pp, scc_lexer_to_ring(&lexer, 8, true)); scc_pproc_init(&pp, scc_lexer_to_ring(&lexer, 8, true));
scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pp, 8); scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pp, 8);
*output = scc_cstring_create(); *output = scc_cstring_from_cstr("");
scc_lexer_tok_t tok; scc_lexer_tok_t tok;
while (1) { while (1) {
scc_ring_next_consume(*tok_ring, tok, ret); scc_ring_next_consume(*tok_ring, tok, ret);
@@ -31,7 +31,6 @@ static cbool process_input(const char *input, scc_cstring_t *output) {
scc_pproc_drop(&pp); scc_pproc_drop(&pp);
scc_lexer_drop(&lexer); scc_lexer_drop(&lexer);
scc_sstream_drop(&mem_stream); scc_sstream_drop(&mem_stream);
return true; return true;
} }
@@ -127,6 +126,41 @@ static void test_define_nested_macros(void) {
CHECK_PP_OUTPUT_EXACT( CHECK_PP_OUTPUT_EXACT(
"#define A 1\n#define B (A + 1)\n#define C (B + 1)\nC\n", "#define A 1\n#define B (A + 1)\n#define C (B + 1)\nC\n",
"((1 + 1) + 1)\n"); "((1 + 1) + 1)\n");
CHECK_PP_OUTPUT_EXACT("#define A\n", "");
CHECK_PP_OUTPUT_EXACT("#undef A\n", "");
CHECK_PP_OUTPUT_EXACT(" # define A 1\nA", "1");
// CHECK_PP_OUTPUT_EXACT(" # define A 1 \nA", "1"); // TODO
CHECK_PP_OUTPUT_EXACT("#define CONCAT(str) __scc_##str\nCONCAT(int)",
"__scc_int");
CHECK_PP_OUTPUT_EXACT("#define CONCAT(str) str##_scc__\nCONCAT(int)",
"int_scc__");
CHECK_PP_OUTPUT_EXACT("#define CONCAT(str) __scc_ ## str\nCONCAT(int)",
"__scc_int");
// TEST_CASE("TODO"); /*FALSE*/
// CHECK_PP_OUTPUT_EXACT("#define str(x) # x\n"
// "str()\n",
// "\"\"\n");
TEST_CASE("TODO");
CHECK_PP_OUTPUT_EXACT("#define x 1\n"
"#define f(a) f(x * (a))\n"
"f(0)\n"
"f(x)",
"f(1 * (0))\n"
"f(1 * (1))");
CHECK_PP_OUTPUT_EXACT("#define x x(0)\n"
"#define f(a) f(x * (a))\n"
"f(f(0))\n"
"f(f(x))\n"
"f(f(a))\n",
"f(x(0) * (f(x(0) * (0))))\n"
"f(x(0) * (f(x(0) * (x(0)))))\n"
"f(x(0) * (f(x(0) * (a))))\n");
} }
static void test_undef_macros(void) { static void test_undef_macros(void) {
@@ -176,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) {
@@ -225,6 +245,289 @@ static void test_edge_cases(void) {
CHECK_PP_OUTPUT_EXACT("#define A B\n#define B C\n#define C 1\nA\n", "1\n"); CHECK_PP_OUTPUT_EXACT("#define A B\n#define B C\n#define C 1\nA\n", "1\n");
} }
static void test_conditional_ifdef(void) {
TEST_CASE("ifdef and ifndef");
// 基本 ifdef / ifndef
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#ifdef FOO\n"
"foo\n"
"#endif\n",
"foo\n");
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#ifndef FOO\n"
"foo\n"
"#endif\n",
"");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#ifdef FOO\n"
"foo\n"
"#endif\n",
"");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#ifndef FOO\n"
"foo\n"
"#endif\n",
"foo\n");
// ifdef + else
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#ifdef FOO\n"
"foo\n"
"#else\n"
"bar\n"
"#endif\n",
"foo\n");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#ifdef FOO\n"
"foo\n"
"#else\n"
"bar\n"
"#endif\n",
"bar\n");
// ifdef + elifdef (C23)
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#ifdef FOO\n"
"foo\n"
"#elifdef FOO\n"
"foo2\n"
"#endif\n",
"foo\n");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#define BAR\n"
"#ifdef FOO\n"
"foo\n"
"#elifdef BAR\n"
"bar\n"
"#else\n"
"none\n"
"#endif\n",
"bar\n");
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
"#undef BAR\n"
"#ifdef FOO\n"
"foo\n"
"#elifdef BAR\n"
"bar\n"
"#else\n"
"none\n"
"#endif\n",
"none\n");
// 嵌套
CHECK_PP_OUTPUT_EXACT("#define A\n"
"#ifdef A\n"
" #ifdef B\n"
" inner\n"
" #endif\n"
" outer\n"
"#endif\n",
" outer\n");
CHECK_PP_OUTPUT_EXACT("#define B\n"
"#ifndef A\n"
" #ifdef B\n"
" inner\n"
" #endif\n"
" outer\n"
"#endif\n",
" inner\n outer\n");
// 外层假,内层真
CHECK_PP_OUTPUT_EXACT("#ifdef __NONE\n"
"#define OUTER\n"
"#endif\n"
"#ifdef OUTER\n"
"should not appear\n"
"#endif\n",
""); // 期望为空
// 更复杂的嵌套条件
CHECK_PP_OUTPUT_EXACT("#define X\n"
"#ifdef X\n"
"x defined\n"
"#ifdef Y\n"
"Y defined\n"
"#else\n"
"Y not defined\n"
"#endif\n"
"after inner\n"
"#else\n"
"X not defined\n"
"#endif\n",
"x defined\nY not defined\nafter inner\n");
}
static void test_simple_number_conditional_if(void) {
TEST_CASE("if and elif with one integer constants");
// 基本 if
CHECK_PP_OUTPUT_EXACT("#if 1\ntrue\n#endif\n", "true\n");
CHECK_PP_OUTPUT_EXACT("#if 0\nfalse\n#endif\n", "");
CHECK_PP_OUTPUT_EXACT("#if 4\ntrue\n#endif\n", "true\n");
CHECK_PP_OUTPUT_EXACT("#if 0\nfalse\n#else\nother\n#endif\n", "other\n");
// if + elif + else
CHECK_PP_OUTPUT_EXACT("#if 0\nzero\n#elif 1\none\n#else\nother\n#endif\n",
"one\n");
CHECK_PP_OUTPUT_EXACT("#if 0\nzero\n#elif 0\none\n#else\nother\n#endif\n",
"other\n");
CHECK_PP_OUTPUT_EXACT("#if 1\nfirst\n#elif 1\nsecond\n#endif\n", "first\n");
// 嵌套
CHECK_PP_OUTPUT_EXACT(
"#if 1\n #if 0\n inner\n #endif\n outer\n#endif\n", " outer\n");
CHECK_PP_OUTPUT_EXACT("#if 0\n #if 1\n inner\n #endif\n "
"outer\n#else\n alternative\n#endif\n",
" alternative\n");
// 与 #ifdef 混合
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
"#if 1\n"
" #ifdef FOO\n"
" foo\n"
" #endif\n"
" bar\n"
"#endif\n",
" foo\n bar\n");
}
static void test_variadic_macros(void) {
TEST_CASE("variadic macros with __VA_ARGS__");
// 基本可变参数宏
CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) x __VA_ARGS__\n"
"FOO(1, 2, 3)\n",
"1 2, 3\n");
// 多参数
CHECK_PP_OUTPUT_EXACT("#define SUM(...) (__VA_ARGS__)\n"
"SUM(1, 2, 3)\n",
"(1, 2, 3)\n");
// 与 printf 结合
CHECK_PP_OUTPUT_EXACT("#define DEBUG(fmt, ...) printf(fmt, __VA_ARGS__)\n"
"DEBUG(\"hello\", 1, 2)\n",
"printf(\"hello\", 1, 2)\n");
// 空可变参数
CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) x __VA_ARGS__\n"
"FOO(1)\n",
"1 \n");
CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) #__VA_ARGS__\nFOO(1);", "\"\";");
}
static void test_gnu_comma_variadic_deletion(void) {
TEST_CASE("GNU comma deletion with ## and __VA_ARGS__");
// 可变参数为空,逗号被删除
CHECK_PP_OUTPUT_EXACT("#define FOO(fmt, ...) printf(fmt, ## __VA_ARGS__)\n"
"FOO(\"hello\")\n",
"printf(\"hello\")\n");
// 可变参数非空,逗号保留
CHECK_PP_OUTPUT_EXACT("#define FOO(fmt, ...) printf(fmt, ## __VA_ARGS__)\n"
"FOO(\"%d\", 42)\n",
"printf(\"%d\",42)\n");
// 带空白变体
CHECK_PP_OUTPUT_EXACT("#define FOO(fmt,...) printf(fmt,##__VA_ARGS__)\n"
"FOO(\"%d\", 42)\n",
"printf(\"%d\",42)\n");
}
static void test_c99_docs(void) {
TEST_CASE("6.10.3.3 The ## operator EXAMPLE");
CHECK_PP_OUTPUT_EXACT("#define hash_hash # ## #\n"
"#define mkstr(a) # a\n"
"#define in_between(a) mkstr(a)\n"
"#define join(c, d) in_between(c hash_hash d)\n"
"char p[] = join(x, y);\n",
"char p[] = \"x ## y\";\n");
// 6.10.3.5 Scope of macrodefinitions
TEST_CASE("EXAMPLE 3 To illustrate the rules for redefinition and "
"reexamination, the sequence");
/*
CHECK_PP_OUTPUT_EXACT(
"#define x 3\n"
"#define f(a) f(x * (a))\n"
"#undef x\n"
"#define x 2\n"
"#define g f\n"
"#define z z[0]\n"
"#define h g(~\n"
"#define m(a) a(w)\n"
"#define w 0,1\n"
"#define t(a) a\n"
"#define p() int\n"
"#define q(x) x\n"
"#define r(x,y) x ## y\n"
"#define str(x) # x\n"
"f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);\n"
"g(x+(3,4)-w) | h 5) & m\n"
" (f)^m(m);\n"
"p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) };\n"
"char c[2][6] = { str(hello), str() };\n",
"f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);\n"
"f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);\n"
"int i[] = { 1, 23, 4, 5, };\n"
"char c[2][6] = { \"hello\", \"\" };\n");
*/
TEST_CASE("EXAMPLE 4 To illustrate the rules for creating character string "
"literals and concatenating tokens, the sequence");
TEST_CASE("EXAMPLE 5 To illustrate the rules for placemarker preprocessing "
"tokens, the sequence");
/*
CHECK_PP_OUTPUT_EXACT("#define t(x,y,z) x ## y ## z\n"
"int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,),\n"
"\t\t\tt(10,,), t(,11,), t(,,12), t(,,) };\n",
"int j[] = { 123, 45, 67, 89,\n"
"\t\t\t10, 11, 12, };\n");
*/
TEST_CASE("EXAMPLE 6 To demonstrate the redefinition rules, the following "
"sequence is valid.");
CHECK_PP_OUTPUT_EXACT(
"#define OBJ_LIKE (1-1)\n"
"#define OBJ_LIKE /* white space */ (1-1) /* other */\n"
"#define FUNC_LIKE(a) (a)\n"
"#define FUNC_LIKE( a )( /* note the white space */ \\\n"
" a /* other stuffonthis line\n"
" */ )\n",
"");
// INVALID
// CHECK_PP_OUTPUT_EXACT(
// "#define OBJ_LIKE (0) // different token sequence\n"
// "#define OBJ_LIKE (1 - 1) // different white space\n"
// "#define FUNC_LIKE(b) ( a ) // different parameter usage\n"
// "#define FUNC_LIKE(b) ( b ) // different parameter spelling\n");
TEST_CASE("EXAMPLE 7 Finally,to show the variable argument list macro "
"facilities:");
CHECK_PP_OUTPUT_EXACT(
"#define debug(...) fprintf(stderr, __VA_ARGS__)\n"
"#define showlist(...) puts(#__VA_ARGS__)\n"
"#define report(test, ...) ((test)?puts(#test): \\\n"
" printf(__VA_ARGS__))\n"
"debug(\"Flag\");\n"
"debug(\"X = %d\\n\", x);\n"
"showlist(The first, second, and third items.);\n"
"report(x>y, \"x is %d but y is %d\", x, y);\n",
"fprintf(stderr, \"Flag\");\n"
"fprintf(stderr, \"X = %d\\n\", x);\n"
"puts(\"The first, second, and third items.\");\n"
"((x>y)?puts(\"x>y\"): printf(\"x is %d but y is %d\", x, y));\n");
}
#define TEST_LIST_CASE(func_name) {#func_name, func_name} #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),
@@ -237,5 +540,11 @@ TEST_LIST = {
TEST_LIST_CASE(test_define_nested_macros), TEST_LIST_CASE(test_define_nested_macros),
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_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},
}; };

View File

@@ -7,7 +7,12 @@
#define __SCC_LOG_IMPL_H__ #define __SCC_LOG_IMPL_H__
#include "color.h" #include "color.h"
#ifndef __SCC__
#include <stdarg.h> #include <stdarg.h>
#else
// TODO
#warning "TODO: implement stdarg.h"
#endif
#ifdef __SCC_LOG_IMPL_USE_STD_IMPL__ #ifdef __SCC_LOG_IMPL_USE_STD_IMPL__
#include <stdio.h> #include <stdio.h>
@@ -21,7 +26,7 @@
#define __smcc_log_unreachable() (__builtin_unreachable()) #define __smcc_log_unreachable() (__builtin_unreachable())
#elif defined _MSC_VER // MSVC #elif defined _MSC_VER // MSVC
#define __smcc_log_unreachable() (__assume(false)) #define __smcc_log_unreachable() (__assume(false))
#elif defined __SMCC_BUILT_IN__ // The SMCC (my compiler) #elif defined __SCC_BUILT_IN__ // The SCC Compiler (my compiler)
#define __smcc_log_unreachable() (__smcc_builtin_unreachable()) #define __smcc_log_unreachable() (__smcc_builtin_unreachable())
#else #else
#define __smcc_log_unreachable() #define __smcc_log_unreachable()
@@ -182,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

@@ -29,14 +29,20 @@
// ==================== 内部辅助宏 (不直接使用) ==================== // ==================== 内部辅助宏 (不直接使用) ====================
#define scc_ring_phys(ring, idx) ((idx) % (ring).cap) #define _scc_ring_phys(ring, idx) ((idx) % (ring).cap)
#define _scc_ring_cap(ring) ((ring).cap)
#define _scc_ring_head(ring) ((ring).head)
#define _scc_ring_probe(ring) ((ring).probe)
#define _scc_ring_tail(ring) ((ring).tail)
#define _scc_ring_size(ring) ((ring).tail - (ring).head)
#define _scc_ring_empty(ring) ((ring).head == (ring).tail)
/** /**
* @brief 确保 probe 位置有数据可用 (尝试填充) * @brief 确保 probe 位置有数据可用 (尝试填充)
* @param ring 环形缓冲区变量 * @param ring 环形缓冲区变量
* @param ok 变量名 (如 int ok_flag) ,宏会将其设置为 true 或 false * @param ok 变量名 (如 int ok_flag) ,宏会将其设置为 true 或 false
*/ */
#define scc_ring_ensure(ring, ok) \ #define _scc_ring_ensure(ring, ok) \
do { \ do { \
ok = 1; \ ok = 1; \
if ((ring).probe < (ring).tail) \ if ((ring).probe < (ring).tail) \
@@ -50,7 +56,7 @@
ok = 0; /* 缓冲区满,无法填充 */ \ ok = 0; /* 缓冲区满,无法填充 */ \
break; \ break; \
} \ } \
usize phys_tail = scc_ring_phys(ring, (ring).tail); \ usize phys_tail = _scc_ring_phys(ring, (ring).tail); \
if ((ring).fill == null || \ if ((ring).fill == null || \
!(ring).fill(&(ring).data[phys_tail], (ring).userdata)) { \ !(ring).fill(&(ring).data[phys_tail], (ring).userdata)) { \
ok = 0; \ ok = 0; \
@@ -110,14 +116,14 @@
*/ */
#define scc_ring_peek(ring, val, ok) \ #define scc_ring_peek(ring, val, ok) \
do { \ do { \
scc_ring_ensure(ring, ok); \ _scc_ring_ensure(ring, ok); \
if (!(ok)) \ if (!(ok)) \
break; \ break; \
if ((ring).probe >= (ring).tail) { \ if ((ring).probe >= (ring).tail) { \
ok = 0; \ ok = 0; \
break; \ break; \
} \ } \
usize _phys = scc_ring_phys(ring, (ring).probe); \ usize _phys = _scc_ring_phys(ring, (ring).probe); \
val = (ring).data[_phys]; \ val = (ring).data[_phys]; \
} while (0) } while (0)
@@ -129,14 +135,14 @@
*/ */
#define scc_ring_next(ring, val, ok) \ #define scc_ring_next(ring, val, ok) \
do { \ do { \
scc_ring_ensure(ring, ok); \ _scc_ring_ensure(ring, ok); \
if (!(ok)) \ if (!(ok)) \
break; \ break; \
if ((ring).probe >= (ring).tail) { \ if ((ring).probe >= (ring).tail) { \
ok = 0; \ ok = 0; \
break; \ break; \
} \ } \
usize _phys = scc_ring_phys(ring, (ring).probe); \ usize _phys = _scc_ring_phys(ring, (ring).probe); \
val = (ring).data[_phys]; \ val = (ring).data[_phys]; \
(ring).probe++; \ (ring).probe++; \
} while (0) } while (0)

View File

@@ -1,8 +1,9 @@
#ifndef __SCC_CORE_TYPE_H__ #ifndef __SCC_CORE_TYPE_H__
#define __SCC_CORE_TYPE_H__ #define __SCC_CORE_TYPE_H__
#ifndef __SCC_BUILTIN_TYPE__ #ifndef __SCC__
#include <assert.h> #include <assert.h>
#include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@@ -32,22 +33,28 @@ 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 /* clang-format off */
#define __scc_i16 typedef __scc_i8 i8;
#define __scc_i32 typedef __scc_i16 i16;
#define __scc_i64 typedef __scc_i32 i32;
#define __scc_u8 typedef __scc_i64 i64;
#define __scc_u16 typedef __scc_u8 u8;
#define __scc_u32 typedef __scc_u16 u16;
#define __scc_u64 typedef __scc_u32 u32;
#define __scc_f32 typedef __scc_u64 u64;
#define __scc_f64
#define __scc_bool typedef __scc_isize isize;
#define __scc_char typedef __scc_usize usize;
#define __scc_void typedef __scc_isize pdiff;
#define __scc_null
#define __scc_isize typedef __scc_f32 f32;
#define __scc_usize typedef __scc_f64 f64;
typedef __scc_bool cbool;
/// void / null
#define null __scc_null
/* clang-format on */
#endif #endif
typedef union scc_cvalue { typedef union scc_cvalue {

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

@@ -7,12 +7,11 @@
// #include <ir_dump.h> // #include <ir_dump.h>
// #include <scc_ast2ir.h> // #include <scc_ast2ir.h>
#include <stdio.h>
typedef struct { typedef struct {
const char *input_file; const char *input_file;
const char *output_file; const char *output_file;
int verbose; int verbose;
scc_argparse_list_t include_paths;
cbool emit_lex; cbool emit_lex;
cbool emit_pp; cbool emit_pp;
cbool emit_ast; cbool emit_ast;
@@ -26,6 +25,7 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
SCC_HINT_DESCRIPTION, SCC_HINT_DESCRIPTION,
SCC_HINT_OUTPUT_FILE, SCC_HINT_OUTPUT_FILE,
SCC_HINT_INPUT_FILE, SCC_HINT_INPUT_FILE,
SCC_HINT_INCLUDE_PATH,
SCC_HINT_VERBOSE, SCC_HINT_VERBOSE,
SCC_HINT_EMIT_LEX, SCC_HINT_EMIT_LEX,
@@ -38,6 +38,7 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
[SCC_HINT_DESCRIPTION] = "A simple C compiler", [SCC_HINT_DESCRIPTION] = "A simple C compiler",
[SCC_HINT_OUTPUT_FILE] = "Output file", [SCC_HINT_OUTPUT_FILE] = "Output file",
[SCC_HINT_INPUT_FILE] = "Input source file", [SCC_HINT_INPUT_FILE] = "Input source file",
[SCC_HINT_INCLUDE_PATH] = "SCC_HINT_INCLUDE_PATH",
[SCC_HINT_VERBOSE] = "Increase verbosity (can be used multiple times)", [SCC_HINT_VERBOSE] = "Increase verbosity (can be used multiple times)",
[SCC_HINT_EMIT_LEX] = "Generate lexer sources tokens and exit", [SCC_HINT_EMIT_LEX] = "Generate lexer sources tokens and exit",
[SCC_HINT_EMIT_PP] = "Generate preprocessed tokens and exit", [SCC_HINT_EMIT_PP] = "Generate preprocessed tokens and exit",
@@ -49,6 +50,7 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
[SCC_HINT_DESCRIPTION] = "一个简单的C编译器", [SCC_HINT_DESCRIPTION] = "一个简单的C编译器",
[SCC_HINT_OUTPUT_FILE] = "输出文件", [SCC_HINT_OUTPUT_FILE] = "输出文件",
[SCC_HINT_INPUT_FILE] = "输入源文件", [SCC_HINT_INPUT_FILE] = "输入源文件",
[SCC_HINT_INCLUDE_PATH] = "SCC_HINT_INCLUDE_PATH",
[SCC_HINT_VERBOSE] = "增加详细输出(可多次使用)", [SCC_HINT_VERBOSE] = "增加详细输出(可多次使用)",
[SCC_HINT_EMIT_LEX] = "生成`源代码的词法单元`并退出", [SCC_HINT_EMIT_LEX] = "生成`源代码的词法单元`并退出",
[SCC_HINT_EMIT_PP] = "生成`预处理后的词法单元`并退出", [SCC_HINT_EMIT_PP] = "生成`预处理后的词法单元`并退出",
@@ -88,6 +90,13 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
scc_argparse_spec_set_required(&arg_input.spec, true); scc_argparse_spec_set_required(&arg_input.spec, true);
scc_argparse_cmd_add_arg(root, &arg_input); scc_argparse_cmd_add_arg(root, &arg_input);
// -I, --include (添加额外的系统头文件搜索路径)
scc_argparse_opt_t opt_include;
scc_argparse_opt_init(&opt_include, 'I', "include",
scc_hints[SCC_HINT_INCLUDE_PATH]);
scc_argparse_spec_setup_list(&opt_include.spec, &(config->include_paths));
scc_argparse_cmd_add_opt(root, &opt_include);
// -v, --verbose (计数) // -v, --verbose (计数)
scc_argparse_opt_t opt_verbose; scc_argparse_opt_t opt_verbose;
scc_argparse_opt_init(&opt_verbose, 'V', "verbose", scc_argparse_opt_init(&opt_verbose, 'V', "verbose",
@@ -175,7 +184,6 @@ int main(int argc, const char **argv, const char **envp) {
SetConsoleOutputCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8); SetConsoleCP(CP_UTF8);
#endif #endif
setbuf(stdout, NULL);
#ifdef _WIN32 #ifdef _WIN32
#define OUTPUT_DEFAULT_FILE "a.exe" #define OUTPUT_DEFAULT_FILE "a.exe"
@@ -190,6 +198,8 @@ int main(int argc, const char **argv, const char **envp) {
.emit_ast = false, .emit_ast = false,
.emit_ir = false, .emit_ir = false,
}; };
scc_vec_init(config.include_paths);
scc_argparse_t argparse; scc_argparse_t argparse;
setup_argparse(&argparse, &config, SCC_ARGPARSE_LANG_ZH); setup_argparse(&argparse, &config, SCC_ARGPARSE_LANG_ZH);
int ret = scc_argparse_parse(&argparse, argc, argv); int ret = scc_argparse_parse(&argparse, argc, argv);
@@ -219,6 +229,15 @@ int main(int argc, const char **argv, const char **envp) {
scc_pproc_t pproc; scc_pproc_t pproc;
scc_pproc_init(&pproc, scc_lexer_to_ring(&lexer, 8, true)); scc_pproc_init(&pproc, scc_lexer_to_ring(&lexer, 8, true));
scc_vec_foreach(config.include_paths, i) {
scc_pproc_add_include_path_cstr(&pproc,
scc_vec_at(config.include_paths, i));
}
scc_lexer_tok_vec_t pproc_tok_vec;
scc_vec_init(pproc_tok_vec);
scc_cstring_t pproc_macro_name = scc_cstring_from_cstr("__SCC__");
scc_pproc_add_object_macro(&(pproc.macro_table), &pproc_macro_name,
&pproc_tok_vec);
if (config.emit_pp) { if (config.emit_pp) {
scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pproc, 8); scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pproc, 8);
if (config.output_file == null) { if (config.output_file == null) {

View File

@@ -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()

View File

@@ -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()