Compare commits

..

11 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
zzy
a52ff33e30 feat(ast): 更新AST字面量表示方式
更新AST定义以使用词素字符串代替常量值,
并修改AST转储功能以正确显示字面量内容。

BREAKING CHANGE: AST表达式结构体中literal成员从value改为lexme字段。

refactor(pproc): 重构宏展开和文件包含逻辑

将宏展开函数重构为独立接口,实现文件包含处理逻辑,
改进预处理器的状态管理机制。

fix(sstream): 修复文件流初始化错误码返回

修正文件打开失败时的错误码返回值,确保调用方能正确处理异常情况。
2026-02-19 15:56:05 +08:00
zzy
27a87d17ab feat(lexer): 改进预处理器token测试用例并修复##符号处理
- 将"##" token从SCC_TOK_SHARP修正为SCC_TOK_SHARP_SHARP
- 添加更多预处理器指令测试用例,包括宏定义、错误和警告指令
- 修正序列测试中的##符号处理

fix(pproc): 完善预处理器指令处理逻辑

- 实现#error和#warning指令的具体处理逻辑
- 添加对字符串字面量的错误和警告消息输出
- 优化未处理指令的错误处理流程

fix(pproc): 修复词法分析器流处理边界条件

- 在scc_pproc.c中添加对token获取失败的检查
- 防止在流结束时出现未处理的边界情况
2026-02-19 12:14:56 +08:00
zzy
08a60e6e8a feat: 添加预处理器宏定义的字符串化和连接操作支持
- 实现了 # 和 ## 预处理器操作符的功能
- 添加了 token 深拷贝和移动函数以支持宏展开
- 修改预处理器展开逻辑以正确处理宏参数替换
- 增加了宏参数分割时对空白字符的处理

fix: 修复预处理器宏展开中的内存管理和逻辑错误

- 修正了宏展开集合的数据结构初始化方式
- 修复了函数式宏调用时括号匹配的判断逻辑
- 改进了宏参数解析过程中空白字符的处理
- 解决了 token 在宏展开过程中的所有权管理问题

chore: 为 justfile 添加文件统计命令并优化构建配置

- 新增 count-file 命令用于统计代码文件数量
- 调整了输出文件的默认命名规则
- 优化了词法分析器 token 释放时的字段重置逻辑
2026-02-19 11:20:01 +08:00
26 changed files with 1505 additions and 328 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

@@ -11,5 +11,29 @@ count:
# you need download `tokei` it can download by cargo
tokei libs runtime src -e tests
build_lexer:
python ./tools/cbuild/cbuild.py --path libs/lexer build
count-file:
# you need download `tokei` it can download by cargo
tokei libs runtime src -e tests --files
clean:
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_t;
typedef SCC_VEC(const char *) scc_argparse_list_t;
// 约束规范结构体
struct scc_argparse_spec {
scc_argparse_val_type_t value_type; // 值类型
@@ -56,6 +57,7 @@ struct scc_argparse_spec {
const char **str_store; // 字符串存储
char **str_alloc_store; // 字符串存储使用alloc需要free
void **ptr_store; // 通用指针存储
scc_argparse_list_t *vec_store; // 新增:指向字符串向量的指针
} store;
// 枚举值约束
@@ -215,6 +217,15 @@ static inline void scc_argparse_spec_setup_choices(scc_argparse_spec_t *spec,
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) \
static inline void scc_argparse_spec_set_##attr(scc_argparse_spec_t *spec, \
cbool flag) { \

View File

@@ -63,6 +63,10 @@ static inline void parse_cmd(scc_optparse_t *optparse,
min_args = 0;
max_args = 0;
break;
case SCC_ARGPARSE_VAL_TYPE_LIST: // 列表类型
min_args = 1;
max_args = 65535; // FIXME maybe INT_MAX ?
break;
default:
min_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;
}
if (parser->need_version && scc_strcmp(opt->long_name, "version") == 0) {
// TODO default version print
}
if (opt->spec.flag_store_as_count) {
(*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;
}
if (ctx->result.value) {
if (!ctx->result.value) {
return SCC_ARGPARSE_ERR_NONE;
}
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;
}

View File

@@ -2,6 +2,7 @@
#define __SCC_AST_DEF_H__
#include <scc_core.h>
#include <scc_pos.h>
/**
* @brief AST 节点类型枚举
@@ -310,7 +311,7 @@ struct scc_ast_expr {
} compound_literal;
// 字面量
struct {
scc_cvalue_t value;
scc_cstring_t lexme;
} literal;
// 标识符
struct {

View File

@@ -392,16 +392,10 @@ static void dump_expr_impl(scc_ast_expr_t *expr, scc_tree_dump_ctx_t *ctx) {
PRINT_QUOTED_VALUE(ctx, get_op_str(expr->unary.op));
break;
case SCC_AST_EXPR_INT_LITERAL:
PRINT_VALUE(ctx, " %lld", expr->literal.value.i);
break;
case SCC_AST_EXPR_FLOAT_LITERAL:
PRINT_VALUE(ctx, " %f", expr->literal.value.f);
break;
case SCC_AST_EXPR_CHAR_LITERAL:
PRINT_VALUE(ctx, " '%c'", (char)expr->literal.value.ch);
break;
case SCC_AST_EXPR_STRING_LITERAL:
PRINT_VALUE(ctx, " \"%s\"", expr->literal.value.cstr.data);
PRINT_VALUE(ctx, " %s", expr->literal.lexme);
break;
case SCC_AST_EXPR_IDENTIFIER:
if (expr->identifier.name) {

View File

@@ -42,44 +42,44 @@ typedef enum scc_cstd {
// WARNING: Using Binary Search To Fast Find Keyword
// 你必须确保其中是按照字典序排列
#define SCC_CKEYWORD_TABLE \
X(asm , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ASM , SCC_CEXT_SCC) \
X(atomic , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ATOMIC , SCC_CEXT_SCC) \
X(auto , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_AUTO , SCC_CEXT_SCC) \
X(bool , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_BOOL , SCC_CEXT_SCC) \
X(break , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_BREAK , SCC_CSTD_C89) \
X(case , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CASE , SCC_CSTD_C89) \
X(char , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CHAR , SCC_CSTD_C89) \
X(complex , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_COMPLEX , SCC_CEXT_SCC) \
X(const , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CONST , SCC_CSTD_C89) \
X(continue , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CONTINUE , SCC_CSTD_C89) \
X(default , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_DEFAULT , SCC_CSTD_C89) \
X(do , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_DO , SCC_CSTD_C89) \
X(double , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_DOUBLE , SCC_CSTD_C89) \
X(else , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ELSE , SCC_CSTD_C89) \
X(enum , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ENUM , SCC_CSTD_C89) \
X(extern , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_EXTERN , SCC_CSTD_C89) \
X(float , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_FLOAT , SCC_CSTD_C89) \
X(for , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_FOR , SCC_CSTD_C89) \
X(goto , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_GOTO , SCC_CSTD_C89) \
X(if , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_IF , SCC_CSTD_C89) \
X(inline , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_INLINE , SCC_CSTD_C99) \
X(int , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_INT , SCC_CSTD_C89) \
X(long , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_LONG , SCC_CSTD_C89) \
X(register , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_REGISTER , SCC_CSTD_C89) \
X(restrict , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_RESTRICT , SCC_CSTD_C99) \
X(return , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_RETURN , SCC_CSTD_C89) \
X(short , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SHORT , SCC_CSTD_C89) \
X(signed , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SIGNED , SCC_CSTD_C89) \
X(sizeof , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SIZEOF , SCC_CSTD_C89) \
X(static , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_STATIC , SCC_CSTD_C89) \
X(struct , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_STRUCT , SCC_CSTD_C89) \
X(switch , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SWITCH , SCC_CSTD_C89) \
X(typedef , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_TYPEDEF , SCC_CSTD_C89) \
X(union , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_UNION , SCC_CSTD_C89) \
X(unsigned , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_UNSIGNED , SCC_CSTD_C89) \
X(void , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_VOID , SCC_CSTD_C89) \
X(volatile , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_VOLATILE , SCC_CSTD_C89) \
X(while , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_WHILE , SCC_CSTD_C89) \
X(asm , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ASM , SCC_CEXT_SCC) \
X(atomic , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ATOMIC , SCC_CEXT_SCC) \
X(auto , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_AUTO , SCC_CEXT_SCC) \
X(bool , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_BOOL , SCC_CEXT_SCC) \
X(break , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_BREAK , SCC_CSTD_C89) \
X(case , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CASE , SCC_CSTD_C89) \
X(char , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CHAR , SCC_CSTD_C89) \
X(complex , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_COMPLEX , SCC_CEXT_SCC) \
X(const , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CONST , SCC_CSTD_C89) \
X(continue , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CONTINUE , SCC_CSTD_C89) \
X(default , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_DEFAULT , SCC_CSTD_C89) \
X(do , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_DO , SCC_CSTD_C89) \
X(double , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_DOUBLE , SCC_CSTD_C89) \
X(else , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ELSE , SCC_CSTD_C89) \
X(enum , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ENUM , SCC_CSTD_C89) \
X(extern , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_EXTERN , SCC_CSTD_C89) \
X(float , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_FLOAT , SCC_CSTD_C89) \
X(for , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_FOR , SCC_CSTD_C89) \
X(goto , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_GOTO , SCC_CSTD_C89) \
X(if , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_IF , SCC_CSTD_C89) \
X(inline , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_INLINE , SCC_CSTD_C99) \
X(int , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_INT , SCC_CSTD_C89) \
X(long , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_LONG , SCC_CSTD_C89) \
X(register , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_REGISTER , SCC_CSTD_C89) \
X(restrict , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_RESTRICT , SCC_CSTD_C99) \
X(return , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_RETURN , SCC_CSTD_C89) \
X(short , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SHORT , SCC_CSTD_C89) \
X(signed , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SIGNED , SCC_CSTD_C89) \
X(sizeof , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SIZEOF , SCC_CSTD_C89) \
X(static , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_STATIC , SCC_CSTD_C89) \
X(struct , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_STRUCT , SCC_CSTD_C89) \
X(switch , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SWITCH , SCC_CSTD_C89) \
X(typedef , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_TYPEDEF , SCC_CSTD_C89) \
X(union , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_UNION , SCC_CSTD_C89) \
X(unsigned , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_UNSIGNED , SCC_CSTD_C89) \
X(void , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_VOID , SCC_CSTD_C89) \
X(volatile , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_VOLATILE , SCC_CSTD_C89) \
X(while , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_WHILE , SCC_CSTD_C89) \
// KEYWORD_TABLE
#define SCC_CTOK_TABLE \
@@ -145,9 +145,9 @@ typedef enum scc_cstd {
// END
/* clang-format on */
// 定义TokenType枚举
typedef enum scc_tok_type {
// must first becase the unknown token must be 0
/* clang-format off */
// must first becase the unknown token must be 0
#define X(str, subtype, tok) tok,
SCC_CTOK_TABLE
#undef X
@@ -159,11 +159,11 @@ typedef enum scc_tok_type {
#define X(name, subtype, tok, std) tok,
SCC_CKEYWORD_TABLE
#undef X
/* clang-format on*/
} scc_tok_type_t;
typedef enum scc_tok_subtype {
SCC_TOK_SUBTYPE_INVALID, // 错误占位
SCC_TOK_SUBTYPE_KEYWORD, // 关键字
SCC_TOK_SUBTYPE_OPERATOR, // 操作符
SCC_TOK_SUBTYPE_IDENTIFIER, // 标识符
SCC_TOK_SUBTYPE_LITERAL, // 字面量
@@ -187,6 +187,11 @@ scc_tok_subtype_t scc_get_tok_subtype(scc_tok_type_t type);
const char *scc_get_tok_name(scc_tok_type_t type);
static inline void scc_lexer_tok_drop(scc_lexer_tok_t *tok) {
tok->type = SCC_TOK_UNKNOWN;
tok->loc.col = 0;
tok->loc.line = 0;
tok->loc.name = null;
tok->loc.offset = 0;
scc_cstring_free(&tok->lexeme);
}
@@ -195,4 +200,20 @@ static inline cbool scc_lexer_tok_match(const scc_lexer_tok_t *tok,
return tok->type == type;
}
// 深拷贝 token
static inline scc_lexer_tok_t scc_lexer_tok_copy(const scc_lexer_tok_t *src) {
scc_lexer_tok_t dst = *src;
dst.lexeme = scc_cstring_copy(&src->lexeme);
return dst;
}
// 移动 token源 token 不再拥有 lexeme
static inline void scc_lexer_tok_move(scc_lexer_tok_t *dst,
scc_lexer_tok_t *src) {
*dst = *src;
src->lexeme.data = null;
src->lexeme.size = 0;
src->lexeme.cap = 0;
}
#endif /* __SCC_LEXER_TOKEN_H__ */

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) {
scc_lexer_tok_t tok;
cbool ok;
while (scc_lexer_peek_non_blank(stream, &tok)) {
if (tok.type == SCC_TOK_ENDLINE)
break;
while (1) {
scc_ring_next_consume(*stream, tok, ok);
if (!ok)
break;
scc_tok_type_t type = tok.type;
scc_lexer_tok_drop(&tok);
if (type == SCC_TOK_ENDLINE)
break;
}
}

View File

@@ -301,8 +301,7 @@ void test_identifiers() {
void test_preprocessor() {
TEST_CASE("Preprocessor directives - just the # token");
TEST_TOKEN("#", SCC_TOK_SHARP);
TEST_TOKEN("##", SCC_TOK_SHARP); // 第一个 # 是 token第二个 # 将是下一个
// token在序列测试中验证
TEST_TOKEN("##", SCC_TOK_SHARP_SHARP);
// 多 token 序列测试 #include 等
TEST_SEQUENCE("#include <stdio.h>", SCC_TOK_SHARP, SCC_TOK_IDENT,
@@ -311,6 +310,18 @@ void test_preprocessor() {
TEST_SEQUENCE("#define FOO 123", SCC_TOK_SHARP, SCC_TOK_IDENT,
SCC_TOK_BLANK, SCC_TOK_IDENT, SCC_TOK_BLANK,
SCC_TOK_INT_LITERAL);
TEST_SEQUENCE("#define FOO(x) x + 1", SCC_TOK_SHARP, SCC_TOK_IDENT,
SCC_TOK_BLANK, SCC_TOK_IDENT, SCC_TOK_L_PAREN, SCC_TOK_IDENT,
SCC_TOK_R_PAREN, SCC_TOK_BLANK, SCC_TOK_IDENT, SCC_TOK_BLANK,
SCC_TOK_ADD, SCC_TOK_BLANK, SCC_TOK_INT_LITERAL);
TEST_SEQUENCE("#undef FOO", SCC_TOK_SHARP, SCC_TOK_IDENT, SCC_TOK_BLANK,
SCC_TOK_IDENT);
TEST_SEQUENCE("#error \"This is an error\"", SCC_TOK_SHARP, SCC_TOK_IDENT,
SCC_TOK_BLANK, SCC_TOK_STRING_LITERAL);
TEST_SEQUENCE("#warning \"This is an warning\"\n", SCC_TOK_SHARP,
SCC_TOK_IDENT, SCC_TOK_BLANK, SCC_TOK_STRING_LITERAL,
SCC_TOK_ENDLINE);
}
void test_edge_cases() {
@@ -348,7 +359,7 @@ void test_sequences() {
TEST_SEQUENCE("<<=", SCC_TOK_ASSIGN_L_SH);
TEST_SEQUENCE("...", SCC_TOK_ELLIPSIS);
TEST_SEQUENCE("->", SCC_TOK_DEREF);
TEST_SEQUENCE("##", SCC_TOK_SHARP, SCC_TOK_SHARP); // 两个预处理记号
TEST_SEQUENCE("##", SCC_TOK_SHARP_SHARP); // 两个预处理记号
TEST_CASE("Comments and whitespace interleaved");
TEST_SEQUENCE("/* comment */ a // line comment\n b", SCC_TOK_BLOCK_COMMENT,
@@ -371,18 +382,18 @@ void test_error_recovery() {
// 测试未闭合的字符字面量:词法分析器可能继续直到遇到换行或 EOF
// 这里假设它会产生一个 SCC_TOK_CHAR_LITERAL 但包含到结束
// 但标准 C 中未闭合是错误,我们可能返回 UNKNOWN
TEST_CASE("Unterminated character literal");
TEST_TOKEN("'a", SCC_TOK_UNKNOWN); // 取决于实现,可能为 CHAR_LITERAL
// 更可靠的测试:序列中下一个 token 是什么
TEST_SEQUENCE("'a b", SCC_TOK_UNKNOWN,
SCC_TOK_IDENT); // 假设第一个 token 是错误
// TEST_CASE("Unterminated character literal");
// TEST_TOKEN("'a", SCC_TOK_UNKNOWN); // 取决于实现,可能为 CHAR_LITERAL
// // 更可靠的测试:序列中下一个 token 是什么
// TEST_SEQUENCE("'a b", SCC_TOK_UNKNOWN,
// SCC_TOK_IDENT); // 假设第一个 token 是错误
TEST_CASE("Unterminated string literal");
TEST_TOKEN("\"hello", SCC_TOK_UNKNOWN); // 同样
// TEST_CASE("Unterminated string literal");
// TEST_TOKEN("\"hello", SCC_TOK_UNKNOWN); // 同样
TEST_CASE("Unterminated block comment");
TEST_SEQUENCE("/* comment",
SCC_TOK_BLOCK_COMMENT); // 直到 EOF可能仍为注释
// TEST_CASE("Unterminated block comment");
// TEST_SEQUENCE("/* comment",
// SCC_TOK_BLOCK_COMMENT); // 直到 EOF可能仍为注释
}
// ============================ 主测试列表 ============================

View File

@@ -8,9 +8,9 @@
typedef struct {
scc_pproc_macro_table_t *macro_table;
scc_pproc_macro_table_t *expanded_set;
scc_lexer_tok_ring_t *input;
scc_lexer_tok_vec_t output;
scc_pproc_macro_table_t expanded_set;
int need_rescan;
} scc_pproc_expand_t;
@@ -22,5 +22,12 @@ scc_lexer_array_to_ring(scc_lexer_tok_vec_t *array) {
}
void scc_pproc_expand_macro(scc_pproc_expand_t *expand_ctx);
void scc_pproc_expand_by_src(scc_pproc_macro_table_t *macro_table,
scc_lexer_tok_ring_t *input,
scc_lexer_tok_ring_t *output,
const scc_pproc_macro_t *macro);
void scc_pproc_expand_by_vec(scc_pproc_macro_table_t *macro_table,
scc_lexer_tok_vec_t *input,
scc_lexer_tok_ring_t *output);
#endif /* __SCC_PPROC_EXPAND_H__ */

View File

@@ -15,41 +15,66 @@
// 条件编译状态栈
typedef struct {
int active; // 当前层级是否有效(即应该输出 token
int skip; // 当前层级是否跳过(即不输出 token
// 可根据需要增加状态,如 #if 的结果、#elif 已执行等
} scc_pproc_if_state_t;
typedef SCC_VEC(scc_pproc_if_state_t) scc_pproc_if_stack_t;
cbool found_true;
cbool seen_else;
cbool active;
} scc_pproc_if_t;
typedef SCC_VEC(scc_pproc_if_t) scc_pproc_if_stack_t;
// 文件包含栈
typedef struct {
scc_lexer_t *lexer; // 当前文件的 lexer
scc_lexer_tok_ring_t *tok_ring; // 当前文件的 token 环(由 lexer 提供)
// 可能还需要保存当前位置等
} scc_pproc_file_state_t;
typedef SCC_VEC(scc_pproc_file_state_t) scc_pproc_file_stack_t;
scc_sstream_t sstream;
scc_lexer_t lexer;
scc_lexer_tok_ring_t *ring;
} scc_pproc_file_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_cstring_t) scc_pproc_cstr_vec_t;
typedef struct scc_pproc {
scc_lexer_tok_ring_t *org_ring;
scc_lexer_tok_ring_t *cur_ring;
scc_lexer_tok_ring_t expanded_ring;
scc_strpool_t strpool;
int at_line_start;
int enable;
scc_pproc_cstr_vec_t include_paths;
scc_pproc_macro_table_t macro_table;
scc_pproc_if_stack_t if_stack;
scc_pproc_file_stack_t file_stack;
scc_lexer_tok_ring_t ring;
int ring_ref_count;
struct {
int max_include_depth;
} config;
} scc_pproc_t;
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);
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_expand_by_src(scc_pproc_t *pp, const scc_pproc_macro_t *macro);
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,
scc_lexer_tok_vec_t *args, int need_full);
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_pproc.h>
static const struct {
const char *name;
scc_tok_type_t tok_type;
@@ -59,14 +62,16 @@ void scc_pproc_parse_macro_arguments(scc_lexer_tok_ring_t *ring,
if (tok.type == SCC_TOK_R_PAREN) {
depth--;
}
if (depth > 0 || need_full) {
ok = depth > 0 || need_full;
if (ok) {
scc_vec_push(*args, tok);
} else {
scc_lexer_tok_drop(&tok);
}
if (tok.type == SCC_TOK_L_PAREN) {
depth++;
}
if (!ok) {
scc_lexer_tok_drop(&tok);
}
} while (depth);
}
@@ -146,6 +151,25 @@ void scc_pproc_parse_object_macro(scc_pproc_t *pp,
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
6.10 Preprocessing directives
@@ -206,10 +230,11 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
scc_lexer_tok_t tok = {0};
int ok = 0;
scc_ring_next(*pp->cur_ring, tok, ok);
Assert(ok == true && tok.type == SCC_TOK_SHARP);
scc_lexer_tok_drop(&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);
LOG_ERROR("Invalid preprocessor directive");
goto ERROR;
@@ -221,8 +246,34 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
LOG_ERROR("Expected preprocessor keyword, got %s", tok.lexeme);
goto ERROR;
}
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) {
case SCC_PP_TOK_DEFINE: {
scc_lexer_tok_drop(&tok);
@@ -268,24 +319,80 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
scc_lexer_tok_drop(&tok);
return;
}
case SCC_PP_TOK_INCLUDE:
case SCC_PP_TOK_IF:
case SCC_PP_TOK_INCLUDE: {
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;
}
case SCC_PP_TOK_IFDEF:
case SCC_PP_TOK_IFNDEF:
case SCC_PP_TOK_ELSE:
case SCC_PP_TOK_ELIF:
case SCC_PP_TOK_ELIFDEF:
case SCC_PP_TOK_ELIFNDEF:
case SCC_PP_TOK_ENDIF:
case SCC_PP_TOK_ELIFNDEF: {
scc_lexer_tok_drop(&tok);
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_EMBED:
goto ERROR;
case SCC_PP_TOK_ERROR:
scc_lexer_tok_drop(&tok);
while (1) {
ok = scc_lexer_next_non_blank(pp->cur_ring, &tok);
if (tok.type == SCC_TOK_ENDLINE || ok == false) {
return;
}
if (scc_get_tok_subtype(tok.type) == SCC_TOK_SUBTYPE_LITERAL) {
LOG_ERROR(scc_cstring_as_cstr(&tok.lexeme));
}
scc_lexer_tok_drop(&tok);
}
return;
case SCC_PP_TOK_WARNING:
scc_lexer_tok_drop(&tok);
while (1) {
ok = scc_lexer_next_non_blank(pp->cur_ring, &tok);
if (tok.type == SCC_TOK_ENDLINE || ok == false) {
return;
}
if (scc_get_tok_subtype(tok.type) == SCC_TOK_SUBTYPE_LITERAL) {
LOG_WARN(scc_cstring_as_cstr(&tok.lexeme));
}
scc_lexer_tok_drop(&tok);
}
return;
case SCC_PP_TOK_PRAGMA:
LOG_WARN("Pragma ignored");
scc_lexer_skip_until_newline(pp->cur_ring);
return;
default:
LOG_WARN("Unhandled directive: %s", scc_cstring_as_cstr(&tok.lexeme));
break;
}
ERROR:
LOG_WARN("Unhandled directive: %s", scc_cstring_as_cstr(&tok.lexeme));
scc_lexer_skip_until_newline(pp->cur_ring);
}

View File

@@ -2,11 +2,12 @@
#include <scc_pproc.h>
static scc_lexer_tok_t stringify_argument(scc_lexer_tok_vec_t *arg_tokens) {
// WRITE BY AI
scc_cstring_t str = scc_cstring_create();
scc_cstring_append_ch(&str, '\"'); // 左引号
int need_space = 0; // 是否需要插入空格
for (usize i = 0; i < arg_tokens->size; i++) {
scc_vec_foreach(*arg_tokens, i) {
scc_lexer_tok_t *tok = &scc_vec_at(*arg_tokens, i);
if (tok->type == SCC_TOK_BLANK) {
need_space = 1; // 标记遇到空白
@@ -16,7 +17,6 @@ static scc_lexer_tok_t stringify_argument(scc_lexer_tok_vec_t *arg_tokens) {
// 需要空格且当前不是第一个有效token插入一个空格
if (need_space && i > 0) {
scc_cstring_append_ch(&str, ' ');
need_space = 0;
}
// 对字符串/字符常量内的 " 和 \ 进行转义
@@ -36,6 +36,40 @@ static scc_lexer_tok_t stringify_argument(scc_lexer_tok_vec_t *arg_tokens) {
return result;
}
static scc_lexer_tok_t concatenate_tokens(const scc_lexer_tok_t *left,
const scc_lexer_tok_t *right) {
Assert(left != null && right != null);
scc_cstring_t new_lex = scc_cstring_create();
scc_cstring_append(&new_lex, &left->lexeme);
scc_cstring_append(&new_lex, &right->lexeme);
scc_lexer_t lexer;
scc_sstream_t sstream;
// new_lex 所有权转移
scc_sstream_init_by_buffer(&sstream, scc_cstring_as_cstr(&new_lex),
scc_cstring_len(&new_lex), true, 8);
scc_lexer_init(&lexer, scc_sstream_to_ring(&sstream));
scc_lexer_tok_ring_t *ring = scc_lexer_to_ring(&lexer, 8, true);
scc_lexer_tok_t result;
int ok;
scc_ring_next_consume(*ring, result, ok);
if (!ok) {
scc_lexer_tok_drop(&result);
return result;
}
scc_ring_next_consume(*ring, result, ok);
if (ok) {
scc_lexer_tok_drop(&result);
return result;
}
scc_lexer_drop_ring(ring);
scc_lexer_drop(&lexer);
scc_sstream_drop(&sstream);
return result;
}
static inline void scc_copy_expand(scc_pproc_expand_t *expand_ctx,
scc_pproc_expand_t *copyed_ctx,
scc_lexer_tok_ring_t *ring) {
@@ -47,56 +81,94 @@ static inline void scc_copy_expand(scc_pproc_expand_t *expand_ctx,
scc_vec_init(copyed_ctx->output);
}
void scc_pproc_expand_by_src(scc_pproc_t *pp, const scc_pproc_macro_t *macro) {
scc_pproc_expand_t ctx;
void scc_pproc_expand_by_src(scc_pproc_macro_table_t *macro_table,
scc_lexer_tok_ring_t *input,
scc_lexer_tok_ring_t *output,
const scc_pproc_macro_t *macro) {
scc_lexer_tok_vec_t expaned_buffer;
scc_vec_init(expaned_buffer);
int ok;
scc_lexer_tok_t tok;
scc_ring_next_consume(*pp->cur_ring, tok, ok);
scc_ring_next_consume(*input, tok, ok);
if (macro->type == SCC_PP_MACRO_NONE || ok == false) {
UNREACHABLE();
} else if (macro->type == SCC_PP_MACRO_OBJECT) {
scc_vec_push(expaned_buffer, tok);
} else if (macro->type == SCC_PP_MACRO_FUNCTION) {
scc_vec_push(expaned_buffer, tok);
scc_pproc_parse_macro_arguments(pp->cur_ring, &expaned_buffer, true);
scc_pproc_parse_macro_arguments(input, &expaned_buffer, true);
}
scc_lexer_tok_ring_t ring = scc_lexer_array_to_ring(&expaned_buffer);
scc_pproc_expand_by_vec(macro_table, &expaned_buffer, output);
}
void scc_pproc_expand_by_vec(scc_pproc_macro_table_t *macro_table,
scc_lexer_tok_vec_t *input,
scc_lexer_tok_ring_t *output) {
scc_pproc_expand_t ctx;
scc_lexer_tok_ring_t ring = scc_lexer_array_to_ring(input);
ctx.input = &ring;
ctx.macro_table = &pp->macro_table;
ctx.macro_table = macro_table;
ctx.need_rescan = false;
scc_vec_init(ctx.output);
scc_pproc_macro_table_t expanded_set;
ctx.expanded_set = &expanded_set;
scc_pproc_marco_table_init(&ctx.expanded_set);
scc_pproc_marco_table_init(ctx.expanded_set);
scc_pproc_expand_macro(&ctx);
pp->expanded_ring = scc_lexer_array_to_ring(&ctx.output);
scc_pproc_macro_table_drop(&ctx.expanded_set);
*output = scc_lexer_array_to_ring(&ctx.output);
scc_pproc_macro_table_drop(ctx.expanded_set);
}
static inline void
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_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_lexer_tok_t *raw_arg = &scc_vec_at(*raw_args, i);
if (raw_arg->type == SCC_TOK_COMMA) {
scc_lexer_tok_drop(raw_arg);
scc_vec_push(*splited_params, arg);
scc_vec_init(arg);
} else {
if (raw_arg->type == SCC_TOK_BLANK ||
scc_get_tok_subtype(raw_arg->type) ==
SCC_TOK_SUBTYPE_EMPTYSPACE) {
scc_lexer_tok_drop(raw_arg);
if (raw_arg->type == SCC_TOK_L_PAREN) {
depth++;
} else if (raw_arg->type == SCC_TOK_R_PAREN) {
depth--;
}
if (depth != 0 || raw_arg->type != SCC_TOK_COMMA ||
(is_variadic && scc_vec_size(*splited_params) == named_count - 1)) {
if (scc_vec_size(arg) == 0 && raw_arg->type == SCC_TOK_BLANK) {
scc_lexer_tok_drop(raw_arg);
} else {
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) &&
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);
if (is_variadic && scc_vec_size(*splited_params) == named_count - 1) {
scc_vec_init(arg);
scc_vec_push(*splited_params, arg);
}
}
static inline void
@@ -116,6 +188,7 @@ expand_arguments(scc_pproc_macro_extened_params_t *expanded_params,
scc_lexer_tok_ring_t ring = scc_lexer_array_to_ring(&expanded_param);
scc_copy_expand(expand_ctx, &ctx, &ring);
scc_pproc_expand_macro(&ctx);
scc_ring_free(ring);
scc_vec_push(*expanded_params, ctx.output);
}
}
@@ -133,8 +206,63 @@ expanded_params_free(scc_pproc_macro_extened_params_t *expanded_params) {
scc_vec_free(*expanded_params);
}
static void rescan(scc_pproc_expand_t *expand_ctx,
const scc_pproc_macro_t *macro,
scc_lexer_tok_vec_t *tok_buffer) {
scc_pproc_macro_t *expanded_macro =
scc_pproc_macro_new(&macro->name, macro->type);
if (expanded_macro == null) {
LOG_FATAL("Out of memory");
}
scc_pproc_macro_table_set(expand_ctx->expanded_set, expanded_macro);
scc_pproc_expand_t rescan_ctx;
scc_lexer_tok_ring_t ring = scc_lexer_array_to_ring(tok_buffer);
scc_copy_expand(expand_ctx, &rescan_ctx, &ring);
scc_pproc_expand_macro(&rescan_ctx);
scc_ring_free(ring);
scc_vec_foreach(rescan_ctx.output, i) {
scc_vec_push(expand_ctx->output, scc_vec_at(rescan_ctx.output, i));
}
scc_pproc_macro_table_remove(expand_ctx->expanded_set, &macro->name);
}
static int find_params(const scc_lexer_tok_t *tok,
const scc_pproc_macro_t *macro) {
scc_vec_foreach(macro->params, j) {
if (scc_cstring_cmp(&(tok->lexeme),
&(scc_vec_at(macro->params, j).lexeme)) == 0) {
return j;
}
}
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,
const scc_pproc_macro_t *macro) {
scc_lexer_tok_vec_t tok_buffer;
scc_vec_init(tok_buffer);
Assert(macro->type == SCC_PP_MACRO_FUNCTION);
scc_lexer_tok_vec_t raw_args;
scc_vec_init(raw_args);
@@ -143,7 +271,7 @@ static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
// collect, fill and expand arg
scc_pproc_macro_extened_params_t 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_vec_init(expanded_params);
@@ -151,62 +279,183 @@ static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
// replace
scc_vec_foreach(macro->replaces, i) {
scc_lexer_tok_t tok = scc_vec_at(macro->replaces, i);
scc_lexer_tok_t prev_tok = {0};
if (i >= 1) {
prev_tok = scc_vec_at(macro->replaces, i - 1);
}
scc_lexer_tok_t tok =
scc_lexer_tok_copy(&scc_vec_at(macro->replaces, i));
if (tok.type == SCC_TOK_BLANK) {
scc_cstring_free(&tok.lexeme);
tok.lexeme = scc_cstring_from_cstr(" ");
scc_vec_push(expand_ctx->output, tok);
scc_vec_push(tok_buffer, tok);
continue;
}
scc_vec_foreach(macro->params, j) {
if (scc_cstring_cmp(&tok.lexeme,
&(scc_vec_at(macro->params, j).lexeme)) == 0) {
if (j >= scc_vec_size(expanded_params)) {
LOG_ERROR("Invalid macro parameter");
goto CONTINUE;
}
if (prev_tok.type == SCC_TOK_SHARP) {
if (tok.type == SCC_TOK_SHARP) {
// # stringify
scc_lexer_tok_t out =
stringify_argument(&scc_vec_at(splited_params, j));
scc_vec_pop(expand_ctx->output);
scc_vec_push(expand_ctx->output, out);
scc_lexer_tok_drop(&tok);
int right_idx = got_right_non_blank(i, &macro->replaces);
if (right_idx >= (int)macro->replaces.size) {
LOG_WARN("generate empty stringify");
scc_cstring_free(&tok.lexeme);
tok.lexeme = scc_cstring_from_cstr("");
scc_vec_push(tok_buffer, tok);
break;
}
int j = find_params(&scc_vec_at(macro->replaces, right_idx), macro);
Assert(j != -1 && j < (int)scc_vec_size(splited_params));
tok = stringify_argument(&scc_vec_at(splited_params, j));
scc_vec_push(tok_buffer, tok);
i = right_idx;
continue;
} else if (tok.type == SCC_TOK_SHARP_SHARP) {
// ## contact
scc_lexer_tok_drop(&tok);
int left_idx = got_left_non_blank(i, &macro->replaces);
int right_idx = got_right_non_blank(i, &macro->replaces);
if (left_idx < 0 || right_idx >= (int)macro->replaces.size) {
LOG_FATAL("Invalid ## operator");
}
while (i++ < right_idx) {
scc_lexer_tok_drop(&scc_vec_pop(tok_buffer));
}
scc_lexer_tok_t *left_tok = &scc_vec_at(macro->replaces, left_idx);
scc_lexer_tok_t *right_tok =
&scc_vec_at(macro->replaces, right_idx);
if (left_tok->type == SCC_TOK_COMMA &&
scc_strcmp(scc_cstring_as_cstr(&(right_tok->lexeme)),
"__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_vec_size(left_vec)
? &scc_vec_at(left_vec, scc_vec_size(left_vec) - 1)
: null;
scc_lexer_tok_t *right =
scc_vec_size(right_vec) ? &scc_vec_at(right_vec, 0) : null;
scc_vec_foreach(left_vec, k) {
if (k + 1 >= scc_vec_size(left_vec)) {
continue;
}
scc_lexer_tok_t tok =
scc_lexer_tok_copy(&scc_vec_at(left_vec, k));
scc_vec_push(tok_buffer, tok);
}
scc_lexer_tok_t concate_tok = concatenate_tokens(left, right);
if (concate_tok.type == SCC_TOK_UNKNOWN) {
LOG_FATAL("Invalid ## token");
}
scc_vec_push(tok_buffer, concate_tok);
scc_vec_foreach(right_vec, k) {
if (k == 0) {
continue;
}
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;
} else {
int j = find_params(&tok, macro);
if (j != -1) {
Assert(j < (int)scc_vec_size(expanded_params));
scc_lexer_tok_vec_t expanded_param =
scc_vec_at(expanded_params, j);
scc_lexer_tok_drop(&tok);
scc_vec_foreach(expanded_param, k) {
tok = scc_vec_at(expanded_param, k);
tok.lexeme = scc_cstring_copy(&tok.lexeme);
scc_vec_push(expand_ctx->output, tok);
tok = scc_lexer_tok_copy(&scc_vec_at(expanded_param, k));
scc_vec_push(tok_buffer, tok);
}
}
goto CONTINUE;
}
}
tok.lexeme = scc_cstring_copy(&tok.lexeme);
scc_vec_push(expand_ctx->output, tok);
CONTINUE:
continue;
}
}
scc_vec_push(tok_buffer, tok);
}
expanded_params_free(&splited_params);
expanded_params_free(&expanded_params);
rescan(expand_ctx, macro, &tok_buffer);
}
static inline void expand_object_macro(scc_pproc_expand_t *expand_ctx,
const scc_pproc_macro_t *macro) {
scc_lexer_tok_vec_t tok_buffer;
scc_vec_init(tok_buffer);
scc_vec_foreach(macro->replaces, i) {
scc_lexer_tok_t tok = scc_vec_at(macro->replaces, i);
scc_lexer_tok_t tok =
scc_lexer_tok_copy(&scc_vec_at(macro->replaces, i));
if (tok.type == SCC_TOK_BLANK) {
scc_cstring_free(&tok.lexeme);
tok.lexeme = scc_cstring_from_cstr(" ");
} else {
tok.lexeme = scc_cstring_copy(&tok.lexeme);
} else if (tok.type == SCC_TOK_SHARP_SHARP) {
// ## contact
int left_idx = got_left_non_blank(i, &macro->replaces);
int right_idx = got_right_non_blank(i, &macro->replaces);
if (left_idx < 0 ||
right_idx >= (int)scc_vec_size(macro->replaces)) {
LOG_FATAL("Invalid ## operator");
}
scc_vec_push(expand_ctx->output, tok);
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 concate_tok = concatenate_tokens(left, right);
while (i++ < right_idx) {
scc_lexer_tok_drop(&scc_vec_pop(tok_buffer));
}
if (concate_tok.type == SCC_TOK_UNKNOWN) {
LOG_FATAL("Invalid ## token");
}
scc_vec_push(tok_buffer, concate_tok);
i = right_idx;
continue;
}
scc_vec_push(tok_buffer, tok);
}
rescan(expand_ctx, macro, &tok_buffer);
}
void scc_pproc_expand_macro(scc_pproc_expand_t *expand_ctx) {
@@ -225,30 +474,27 @@ void scc_pproc_expand_macro(scc_pproc_expand_t *expand_ctx) {
scc_pproc_macro_t *macro =
scc_pproc_macro_table_get(expand_ctx->macro_table, &tok.lexeme);
if (macro == null || scc_pproc_macro_table_get(
&expand_ctx->expanded_set, &macro->name)) {
if (macro == null ||
scc_pproc_macro_table_get(expand_ctx->expanded_set, &macro->name)) {
scc_vec_push(expand_ctx->output, tok);
continue;
}
expand_ctx->need_rescan = true;
scc_pproc_macro_t *expanded_macro =
scc_pproc_macro_new(&macro->name, macro->type);
if (expanded_macro == null) {
LOG_FATAL("Out of memory");
}
scc_pproc_macro_table_set(&expand_ctx->expanded_set, expanded_macro);
if (macro->type == SCC_PP_MACRO_OBJECT) {
expand_object_macro(expand_ctx, macro);
} else if (macro->type == SCC_PP_MACRO_FUNCTION) {
scc_lexer_tok_t expect_tok;
scc_ring_peek(*expand_ctx->input, expect_tok, ok);
if (ok == false || expect_tok.type != SCC_TOK_L_PAREN) {
scc_vec_push(expand_ctx->output, tok);
continue;
}
expand_function_macro(expand_ctx, macro);
} else {
UNREACHABLE();
}
RESCAN:
scc_pproc_macro_table_remove(&expand_ctx->expanded_set, &macro->name);
// TODO expand # and ##
continue;
}
if (expand_ctx->need_rescan) {
expand_ctx->need_rescan = false;

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

@@ -0,0 +1,113 @@
#include <pproc_expand.h>
#include <scc_core_ring.h>
#include <scc_pproc.h>
static int switch_file_stack(scc_pproc_t *pp, scc_cstring_t *fname,
scc_pos_t *pos, int is_system) {
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);
if (scc_sstream_init(&(file->sstream), scc_cstring_as_cstr(&fpath), 1024)) {
return -1;
}
scc_lexer_init(&(file->lexer), scc_sstream_to_ring(&(file->sstream)));
file->ring = scc_lexer_to_ring(&(file->lexer), 8, true);
pp->cur_ring = file->ring;
scc_vec_push(pp->file_stack, file);
return 0;
}
void scc_pproc_parse_include(scc_pproc_t *pp, scc_lexer_tok_t *include_tok,
scc_lexer_tok_ring_t *tok_ring) {
int ok;
scc_lexer_tok_t tok;
scc_pos_t pos = include_tok->loc;
scc_lexer_tok_drop(include_tok);
scc_cstring_t line = scc_cstring_create();
while (1) {
scc_ring_next_consume(*tok_ring, tok, ok);
if (!ok)
break;
if (scc_get_tok_subtype(tok.type) != SCC_TOK_SUBTYPE_EMPTYSPACE &&
scc_get_tok_subtype(tok.type) != SCC_TOK_SUBTYPE_COMMENT) {
scc_cstring_append(&line, &tok.lexeme);
}
scc_lexer_tok_drop(&tok);
}
scc_ring_free(*tok_ring);
const char *includename = scc_cstring_as_cstr(&line);
int len = scc_cstring_len(&line);
if (len < 2) {
goto ERROR;
} else if (len == 2) {
goto ERROR;
} else {
if (includename[0] == '\"') {
if (includename[len - 1] != '\"') {
goto ERROR;
}
} else if (includename[0] == '<') {
if (includename[len - 1] != '>') {
goto ERROR;
}
} else {
goto ERROR;
}
}
scc_cstring_t fname = scc_cstring_create();
for (int i = 1; i < len - 1; i++) {
scc_cstring_append_ch(&fname, includename[i]);
}
scc_cstring_free(&line);
int is_system = includename[0] == '<';
if (switch_file_stack(pp, &fname, &pos, is_system)) {
// LOG_ERROR()
}
scc_cstring_free(&fname);
return;
ERROR:
LOG_ERROR("Invalid include filename need \"FILENAME\" or <FILENAME>");
scc_cstring_free(&line);
}

View File

@@ -22,8 +22,6 @@ void scc_pproc_macro_drop(scc_pproc_macro_t *macro) {
if (!macro)
return;
scc_cstring_free(&macro->name);
// 释放参数列表
for (usize i = 0; i < macro->params.size; ++i) {
scc_lexer_tok_drop(&scc_vec_at(macro->params, i));
@@ -36,6 +34,8 @@ void scc_pproc_macro_drop(scc_pproc_macro_t *macro) {
}
scc_vec_free(macro->replaces);
scc_cstring_free(&macro->name);
scc_free(macro);
}

View File

@@ -1,10 +1,12 @@
#include <pproc_expand.h>
#include <scc_lexer_utils.h>
#include <scc_pproc.h>
static int pproc_next(scc_pproc_t *pp, scc_lexer_tok_t *out) {
static int pproc_next_one_file(scc_pproc_t *pp, scc_lexer_tok_t *out) {
CONTINUE:
scc_lexer_tok_ring_t *stream = pp->cur_ring;
scc_lexer_tok_t tok = {0};
int ok = 0;
CONTINUE:
if (pp->expanded_ring.cap) {
scc_ring_next_consume(pp->expanded_ring, *out, ok);
if (ok == false) {
@@ -15,25 +17,40 @@ CONTINUE:
}
}
scc_ring_peek(*stream, tok, ok);
if (tok.type == SCC_TOK_BLANK ||
scc_get_tok_subtype(tok.type) == SCC_TOK_SUBTYPE_COMMENT) {
scc_cstring_free(&tok.lexeme);
scc_ring_next_consume(*stream, *out, ok);
out->lexeme = scc_cstring_from_cstr(" ");
return true;
} else if (tok.type == SCC_TOK_ENDLINE) {
if (ok == false) {
return false;
}
if (tok.type == SCC_TOK_ENDLINE) {
scc_ring_next_consume(*stream, *out, ok);
pp->at_line_start = 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 #
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;
}
pp->at_line_start = false;
if (tok.type == SCC_TOK_IDENT) {
// maybe expanded
scc_pproc_macro_t *macro =
@@ -42,7 +59,8 @@ CONTINUE:
scc_ring_next_consume(*stream, *out, ok);
return ok;
}
scc_pproc_expand_by_src(pp, macro);
scc_pproc_expand_by_src(&pp->macro_table, pp->cur_ring,
&pp->expanded_ring, macro);
goto CONTINUE;
} else {
// continue
@@ -52,14 +70,50 @@ CONTINUE:
return false;
}
static int pproc_next(scc_pproc_t *pp, scc_lexer_tok_t *tok) {
CONTINUE:
int ret = pproc_next_one_file(pp, tok);
if (ret != 0) {
return true;
}
if (scc_vec_size(pp->file_stack) == 0) {
return false;
}
scc_pproc_file_t *file = scc_vec_pop(pp->file_stack);
Assert(file->ring == pp->cur_ring);
scc_lexer_drop_ring(file->ring);
scc_lexer_drop(&(file->lexer));
scc_sstream_drop(&(file->sstream));
scc_free(file);
if (scc_vec_size(pp->file_stack) == 0) {
pp->cur_ring = pp->org_ring;
} else {
pp->cur_ring =
scc_vec_at(pp->file_stack, scc_vec_size(pp->file_stack) - 1)->ring;
}
goto CONTINUE;
}
void scc_pproc_init(scc_pproc_t *pp, scc_lexer_tok_ring_t *input) {
Assert(pp != null && input != null);
pp->cur_ring = input;
pp->org_ring = input;
pp->cur_ring = pp->org_ring;
scc_ring_init(pp->expanded_ring, 0, 0, 0);
scc_pproc_marco_table_init(&pp->macro_table);
scc_vec_init(pp->include_paths);
scc_vec_init(pp->if_stack);
scc_vec_init(pp->file_stack);
pp->at_line_start = true;
pp->enable = true;
pp->config.max_include_depth = 32;
}
void scc_pproc_add_builtin_macros() {
// TODO
}
static cbool fill_token(scc_lexer_tok_t *tok, void *userdata) {

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

View File

@@ -84,13 +84,13 @@ int scc_sstream_init(scc_sstream_t *stream, const char *fname, int ring_size) {
scc_file_t file = scc_fopen(fname, SCC_FILE_READ);
if (file == null) {
LOG_ERROR("Failed to open file: %s", fname);
return 0;
return 1;
}
usize fsize = scc_fsize(file);
if (fsize == 0) {
LOG_WARN("file size is 0");
scc_fclose(file);
return 0;
return 2;
}
char *buffer = (char *)scc_malloc(fsize);
scc_memset(buffer, 0, fsize);

View File

@@ -7,7 +7,12 @@
#define __SCC_LOG_IMPL_H__
#include "color.h"
#ifndef __SCC__
#include <stdarg.h>
#else
// TODO
#warning "TODO: implement stdarg.h"
#endif
#ifdef __SCC_LOG_IMPL_USE_STD_IMPL__
#include <stdio.h>
@@ -21,7 +26,7 @@
#define __smcc_log_unreachable() (__builtin_unreachable())
#elif defined _MSC_VER // MSVC
#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())
#else
#define __smcc_log_unreachable()
@@ -182,13 +187,10 @@ void log_set_handler(logger_t *logger, log_handler handler);
* 利用数组大小不能为负的特性
* 或使用 _Static_assert (C11)
*/
#ifdef static_assert
#undef static_assert
#endif
#if __STDC_VERSION__ >= 201112L
#define static_assert _Static_assert
#define StaticAssert static_assert
#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
#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 位置有数据可用 (尝试填充)
* @param ring 环形缓冲区变量
* @param ok 变量名 (如 int ok_flag) ,宏会将其设置为 true 或 false
*/
#define scc_ring_ensure(ring, ok) \
#define _scc_ring_ensure(ring, ok) \
do { \
ok = 1; \
if ((ring).probe < (ring).tail) \
@@ -50,7 +56,7 @@
ok = 0; /* 缓冲区满,无法填充 */ \
break; \
} \
usize phys_tail = scc_ring_phys(ring, (ring).tail); \
usize phys_tail = _scc_ring_phys(ring, (ring).tail); \
if ((ring).fill == null || \
!(ring).fill(&(ring).data[phys_tail], (ring).userdata)) { \
ok = 0; \
@@ -110,14 +116,14 @@
*/
#define scc_ring_peek(ring, val, ok) \
do { \
scc_ring_ensure(ring, ok); \
_scc_ring_ensure(ring, ok); \
if (!(ok)) \
break; \
if ((ring).probe >= (ring).tail) { \
ok = 0; \
break; \
} \
usize _phys = scc_ring_phys(ring, (ring).probe); \
usize _phys = _scc_ring_phys(ring, (ring).probe); \
val = (ring).data[_phys]; \
} while (0)
@@ -129,14 +135,14 @@
*/
#define scc_ring_next(ring, val, ok) \
do { \
scc_ring_ensure(ring, ok); \
_scc_ring_ensure(ring, ok); \
if (!(ok)) \
break; \
if ((ring).probe >= (ring).tail) { \
ok = 0; \
break; \
} \
usize _phys = scc_ring_phys(ring, (ring).probe); \
usize _phys = _scc_ring_phys(ring, (ring).probe); \
val = (ring).data[_phys]; \
(ring).probe++; \
} while (0)

View File

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

View File

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

View File

@@ -7,12 +7,11 @@
// #include <ir_dump.h>
// #include <scc_ast2ir.h>
#include <stdio.h>
typedef struct {
const char *input_file;
const char *output_file;
int verbose;
scc_argparse_list_t include_paths;
cbool emit_lex;
cbool emit_pp;
cbool emit_ast;
@@ -26,6 +25,7 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
SCC_HINT_DESCRIPTION,
SCC_HINT_OUTPUT_FILE,
SCC_HINT_INPUT_FILE,
SCC_HINT_INCLUDE_PATH,
SCC_HINT_VERBOSE,
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_OUTPUT_FILE] = "Output 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_EMIT_LEX] = "Generate lexer sources 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_OUTPUT_FILE] = "输出文件",
[SCC_HINT_INPUT_FILE] = "输入源文件",
[SCC_HINT_INCLUDE_PATH] = "SCC_HINT_INCLUDE_PATH",
[SCC_HINT_VERBOSE] = "增加详细输出(可多次使用)",
[SCC_HINT_EMIT_LEX] = "生成`源代码的词法单元`并退出",
[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_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 (计数)
scc_argparse_opt_t opt_verbose;
scc_argparse_opt_init(&opt_verbose, 'V', "verbose",
@@ -143,27 +152,54 @@ static void print_ring(scc_lexer_tok_ring_t *ring, int verbose) {
scc_cstring_as_cstr(&tok.lexeme), tok.loc.name,
tok.loc.line, tok.loc.col);
}
scc_lexer_tok_drop(&tok);
}
}
static void print_file(scc_lexer_tok_ring_t *ring, const char *file_name) {
scc_lexer_tok_t tok = {0};
int ret = 0;
scc_file_t fp = scc_fopen(file_name, SCC_FILE_WRITE);
if (fp == null) {
LOG_FATAL("Failed to open file %s", file_name);
return;
}
while (1) {
scc_ring_next_consume(*ring, tok, ret);
if (ret == false || tok.type == SCC_TOK_EOF) {
break;
}
usize ret = scc_fwrite(fp, scc_cstring_as_cstr(&tok.lexeme),
scc_cstring_len(&tok.lexeme));
if (ret != scc_cstring_len(&tok.lexeme)) {
LOG_FATAL("Failed to write to file %s", file_name);
}
scc_lexer_tok_drop(&tok);
}
scc_fclose(fp);
}
int main(int argc, const char **argv, const char **envp) {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
#endif
setbuf(stdout, NULL);
#ifdef _WIN32
#define OUTPUT_DEFAULT_FILE "a.exe"
#else
#define OUTPUT_DEFAULT_FILE "a.out"
#endif
scc_config_t config = {
.input_file = NULL,
#ifdef _WIN32
.output_file = "a.exe",
#else
.output_file = "a.out",
#endif
.input_file = null,
.verbose = 0,
.output_file = null,
.emit_ast = false,
.emit_ir = false,
};
scc_vec_init(config.include_paths);
scc_argparse_t argparse;
setup_argparse(&argparse, &config, SCC_ARGPARSE_LANG_ZH);
int ret = scc_argparse_parse(&argparse, argc, argv);
@@ -181,16 +217,35 @@ int main(int argc, const char **argv, const char **envp) {
scc_lexer_t lexer;
scc_lexer_init(&lexer, scc_sstream_to_ring(&sstream));
if (config.emit_lex) {
scc_lexer_tok_ring_t *tok_ring = scc_lexer_to_ring(&lexer, 8, false);
scc_lexer_tok_ring_t *tok_ring = scc_lexer_to_ring(
&lexer, 8, config.output_file == null ? false : true);
if (config.output_file == null) {
print_ring(tok_ring, config.verbose);
} else {
print_file(tok_ring, config.output_file);
}
return 0;
}
scc_pproc_t pproc;
scc_pproc_init(&pproc, scc_lexer_to_ring(&lexer, 8, false));
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) {
scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pproc, 8);
if (config.output_file == null) {
print_ring(tok_ring, config.verbose);
} else {
print_file(tok_ring, config.output_file);
}
return 0;
}
scc_pproc_drop(&pproc);

View File

@@ -1,4 +1,4 @@
"""cbuild.py - 优化的轻量C构建系统"""
"""cbuild.py - 优化的轻量C构建系统""" # pylint: disable=too-many-lines
from abc import ABC, abstractmethod
import tomllib
@@ -517,7 +517,7 @@ class BuildCache:
with open(self.cache_file, "r", encoding="utf-8") as f:
data = json.load(f)
self.cache = {k: CacheEntry(**v) for k, v in data.items()}
except OSError, json.JSONDecodeError, TypeError:
except (OSError, json.JSONDecodeError, TypeError):
self.cache = {}
def save(self):
@@ -564,6 +564,7 @@ class Compiler(ABC):
def __init__(self):
self.recorded = []
self.recording = False
self.dry_run = False
def enable_recording(self, enable=True):
"""启用命令记录"""
@@ -580,6 +581,8 @@ class Compiler(ABC):
"""运行命令"""
self.record(cmd)
logger.debug("执行命令: %s", cmd)
if self.dry_run:
return # 只打印,不执行
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
@@ -679,6 +682,25 @@ class ClangCompiler(Compiler):
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):
"""虚拟编译器(用于测试)"""
@@ -1018,12 +1040,18 @@ def create_parser():
parser = argparse.ArgumentParser(description="轻量C构建系统", prog="cbuild")
parser.add_argument("--verbose", "-v", action="store_true", help="详细输出")
parser.add_argument("--path", "-p", default=".", help="项目路径")
subparsers = parser.add_subparsers(dest="command", required=True, metavar="COMMAND")
def add_common_args(subparser):
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(
@@ -1102,6 +1130,7 @@ def create_parser():
def main():
"""主函数"""
# print("current cwd: " + os.getcwd())
parser = create_parser()
args = parser.parse_args()
@@ -1117,9 +1146,12 @@ def main():
compiler_map = {
"gcc": GccCompiler(),
"clang": ClangCompiler(),
"scc": SccCompiler(),
}
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:
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()