feat(compiler): 添加对裸机环境的支持并改进构建配置

- 更新README描述为C语言裸机环境的自举编译器
- 修改justfile中的构建命令,添加--dev参数并将输出文件名从scc.exe改为scc
- 在AST定义中添加SCC_AST_UNKNOWN节点类型
- 修复位置日志格式化中的类型错误
- 实现标准输出流支持,当输出文件为'-'时输出到控制台
- 支持基于环境变量LANG的语言本地化选择

fix(lexer): 改进标记打印功能

- 修复换行符的显示,将实际换行符显示为"\n"
- 改进文件输出逻辑,支持标准输出流

refactor(build): 更新cbuild工具的包含路径

- 将包含路径从"scc_libs"更改为"scc_include"

test: 添加简单的包含测试用例

- 新增测试文件tests/simple/12_include.c用于测试头文件包含功能
This commit is contained in:
zzy
2026-02-26 16:07:19 +08:00
parent 13de9d713b
commit 4e2176b7f0
8 changed files with 54 additions and 21 deletions

View File

@@ -2,7 +2,7 @@
> `scc` > `scc`
这是一个简单的C语言编译器可以从C99子集编程语言生成可执行代码。该语言支持基本操作如算术运算、逻辑运算、条件语句if/else、循环语句while/for、分支语句switch/case、函数调用以及内联汇编调用asm 这是一个简单的C语言裸机环境的自举编译器可以从C99子集编程语言生成可执行代码。该语言支持基本操作如算术运算、逻辑运算、条件语句(if/else)、循环语句(while/for)、分支语句(switch/case)、函数调用以及内联汇编调用(asm)
## Builder ## Builder

View File

@@ -19,10 +19,10 @@ clean:
cbuild clean cbuild clean
build: build:
cbuild build -cclang cbuild build -cclang --dev
build-install: build build-install: build
cp ./build/dev/scc.exe ./scc.exe cp ./build/dev/scc ./scc
test-scc: test-scc:
# windows: (Get-Content a.txt -Raw) -replace '\x1b\[[0-9;]*[a-zA-Z]', '' | Set-Content clean.txt # windows: (Get-Content a.txt -Raw) -replace '\x1b\[[0-9;]*[a-zA-Z]', '' | Set-Content clean.txt

View File

@@ -8,6 +8,7 @@
* @brief AST 节点类型枚举 * @brief AST 节点类型枚举
*/ */
typedef enum { typedef enum {
SCC_AST_UNKNOWN,
// 声明 // 声明
scc_ast_decl_t_BEGIN, // 声明开始 scc_ast_decl_t_BEGIN, // 声明开始
SCC_AST_DECL_VAR, // 变量声明 SCC_AST_DECL_VAR, // 变量声明

View File

@@ -10,7 +10,7 @@ extern logger_t __scc_usr_log;
do { \ do { \
char _full_msg[LOGGER_MAX_BUF_SIZE]; \ char _full_msg[LOGGER_MAX_BUF_SIZE]; \
int _n = snprintf_(_full_msg, sizeof(_full_msg), \ int _n = snprintf_(_full_msg, sizeof(_full_msg), \
"%s:%lu:%lu: ", (pos).name, (pos).line, (pos).col); \ "%s:%u:%u: ", (pos).name, (pos).line, (pos).col); \
snprintf_(_full_msg + _n, sizeof(_full_msg) - _n, fmt, ##__VA_ARGS__); \ snprintf_(_full_msg + _n, sizeof(_full_msg) - _n, fmt, ##__VA_ARGS__); \
__scc_usr_log.handler(&__scc_usr_log, level, null, 0, null, "%s", \ __scc_usr_log.handler(&__scc_usr_log, level, null, 0, null, "%s", \
_full_msg); \ _full_msg); \

View File

@@ -0,0 +1 @@

View File

@@ -36,7 +36,8 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
static const char *scc_hints_en[] = { static const char *scc_hints_en[] = {
[SCC_HINT_PROG_NAME] = "scc", [SCC_HINT_PROG_NAME] = "scc",
[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 (`-` means standard output stream file)",
[SCC_HINT_INPUT_FILE] = "Input source file", [SCC_HINT_INPUT_FILE] = "Input source file",
[SCC_HINT_INCLUDE_PATH] = "Add directory to the include search paths", [SCC_HINT_INCLUDE_PATH] = "Add directory to the include search paths",
[SCC_HINT_VERBOSE] = "Increase verbosity (can be used multiple times)", [SCC_HINT_VERBOSE] = "Increase verbosity (can be used multiple times)",
@@ -48,7 +49,7 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
static const char *scc_hints_zh[] = { static const char *scc_hints_zh[] = {
[SCC_HINT_PROG_NAME] = "scc", [SCC_HINT_PROG_NAME] = "scc",
[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] = "增加详细输出(可多次使用)",
@@ -147,10 +148,11 @@ static void print_ring(scc_lexer_tok_ring_t *ring, int verbose) {
if (verbose == 0) { if (verbose == 0) {
scc_printf("%s ", scc_get_tok_name(tok.type)); scc_printf("%s ", scc_get_tok_name(tok.type));
} else if (verbose >= 1) { } else if (verbose >= 1) {
scc_printf("token [%-8s] `%s` at %s:%d:%d\n", scc_printf(
scc_get_tok_name(tok.type), "token [%-8s] `%s` at %s:%d:%d\n", scc_get_tok_name(tok.type),
scc_cstring_as_cstr(&tok.lexeme), tok.loc.name, tok.type != SCC_TOK_ENDLINE ? scc_cstring_as_cstr(&tok.lexeme)
tok.loc.line, tok.loc.col); : "\\n",
tok.loc.name, tok.loc.line, tok.loc.col);
} }
scc_lexer_tok_drop(&tok); scc_lexer_tok_drop(&tok);
} }
@@ -159,20 +161,28 @@ static void print_ring(scc_lexer_tok_ring_t *ring, int verbose) {
static void print_file(scc_lexer_tok_ring_t *ring, const char *file_name) { static void print_file(scc_lexer_tok_ring_t *ring, const char *file_name) {
scc_lexer_tok_t tok = {0}; scc_lexer_tok_t tok = {0};
int ret = 0; int ret = 0;
scc_file_t fp = scc_fopen(file_name, SCC_FILE_WRITE); scc_file_t fp = null;
if (fp == null) { cbool is_stdout = scc_strcmp(file_name, "-") == 0;
LOG_FATAL("Failed to open file %s", file_name); if (!is_stdout) {
return; fp = scc_fopen(file_name, SCC_FILE_WRITE);
if (fp == null) {
LOG_FATAL("Failed to open file %s", file_name);
return;
}
} }
while (1) { while (1) {
scc_ring_next_consume(*ring, tok, ret); scc_ring_next_consume(*ring, tok, ret);
if (ret == false || tok.type == SCC_TOK_EOF) { if (ret == false || tok.type == SCC_TOK_EOF) {
break; break;
} }
usize ret = scc_fwrite(fp, scc_cstring_as_cstr(&tok.lexeme), if (is_stdout) {
scc_cstring_len(&tok.lexeme)); scc_printf("%s", scc_cstring_as_cstr(&tok.lexeme));
if (ret != scc_cstring_len(&tok.lexeme)) { } else {
LOG_FATAL("Failed to write to file %s", file_name); 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_lexer_tok_drop(&tok);
} }
@@ -190,6 +200,13 @@ int main(int argc, const char **argv, const char **envp) {
#else #else
#define OUTPUT_DEFAULT_FILE "a.out" #define OUTPUT_DEFAULT_FILE "a.out"
#endif #endif
scc_argparse_lang_t argparse_lang = SCC_ARGPARSE_LANG_EN;
for (const char **env = envp; *env != null; env++) {
const char *env_str = *env;
if (scc_strcmp(env_str, "LANG=zh_CN.UTF-8") == 0) {
argparse_lang = SCC_ARGPARSE_LANG_ZH;
}
}
scc_config_t config = { scc_config_t config = {
.input_file = null, .input_file = null,
@@ -201,11 +218,11 @@ int main(int argc, const char **argv, const char **envp) {
scc_vec_init(config.include_paths); 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, argparse_lang);
int ret = scc_argparse_parse(&argparse, argc, argv); int ret = scc_argparse_parse(&argparse, argc, argv);
if (ret != 0) { if (ret != 0) {
scc_argparse_drop(&argparse); scc_argparse_drop(&argparse);
return ret; return 0;
} }
scc_argparse_drop(&argparse); scc_argparse_drop(&argparse);

View File

@@ -0,0 +1,6 @@
#include <stdio.h>
int main(void) {
printf("hello world");
return 0;
}

View File

@@ -692,7 +692,15 @@ class SccCompiler(Compiler):
self, source: Path, output: Path, includes: list[Path], flags: list[str] self, source: Path, output: Path, includes: list[Path], flags: list[str]
): ):
# cmd = ["clang"] + flags + ["-c", str(source), "-o", str(output)] # cmd = ["clang"] + flags + ["-c", str(source), "-o", str(output)]
cmd = ["scc", "--emit-pp", "-o", str(output), str(source), "-I", "scc_libs"] cmd = [
"scc",
"--emit-pp",
"-o",
str(output),
str(source),
"-I",
"scc_include",
]
for inc in includes: for inc in includes:
cmd += ["-I", f"{inc}"] cmd += ["-I", f"{inc}"]
self.run(cmd) self.run(cmd)