feat(argparse): 添加选择类型支持和错误处理优化

添加了 SCC_ARGPARSE_ERR_PNT_DEFAULT 错误类型用于默认操作处理,
实现了 scc_argparse_spec_setup_choices 函数支持枚举选择,
重构了错误处理流程使返回值更加一致。
修复了长选项名称匹配的逻辑错误。

feat(lexer): 添加换行符和注释符号的词法标记

新增 SCC_TOK_ENDLINE 和 SCC_TOK_SHARP 标记类型,
改进词法分析器对换行符和井号的识别处理。

feat(scc_core): 添加常用宏定义

添加 scc_min 和 scc_max 宏定义提供基础数值比较功能。

feat(main): 实现编译器主程序和命令行接口

创建主程序入口实现完整的编译流程,
集成预处理器、词法分析、语法分析和IR生成模块,
添加AST和IR输出功能支持调试查看中间表示。

chore(build): 配置项目构建依赖关系

创建 cbuild.toml 配置文件定义项目包信息和依赖库,
建立编译器各组件库之间的依赖关系管理。
This commit is contained in:
zzy
2026-02-13 17:26:50 +08:00
parent ffee07a03d
commit 088050c903
10 changed files with 370 additions and 22 deletions

199
src/main.c Normal file
View File

@@ -0,0 +1,199 @@
#include <argparse.h>
#include <lexer.h>
#include <parser.h>
#include <pprocessor.h>
#include <ast_dump.h>
#include <ir_dump.h>
#include <scc_ast2ir.h>
#include <stdio.h>
static scc_probe_stream_t *from_file_stream(FILE *fp) {
if (fseek(fp, 0, SEEK_END) != 0) {
perror("fseek failed");
return NULL;
}
usize fsize = ftell(fp);
if (fseek(fp, 0, SEEK_SET)) {
perror("fseek failed");
return NULL;
}
char *buffer = (char *)scc_malloc(fsize);
scc_memset(buffer, 0, fsize);
usize read_ret = fread(buffer, 1, fsize, fp);
fclose(fp);
scc_probe_stream_t *stream =
scc_mem_probe_stream_alloc(buffer, read_ret, true);
return stream;
}
typedef struct {
const char *input_file;
const char *output_file;
int verbose;
cbool dump_ast;
cbool dump_ir;
} scc_config_t;
static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
scc_argparse_lang_t lang) {
enum {
SCC_HINT_PROG_NAME,
SCC_HINT_DESCRIPTION,
SCC_HINT_OUTPUT_FILE,
SCC_HINT_INPUT_FILE,
SCC_HINT_VERBOSE,
SCC_HINT_EMIT_AST,
SCC_HINT_EMIT_IR,
};
static const char *scc_hints_en[] = {
[SCC_HINT_PROG_NAME] = "scc",
[SCC_HINT_DESCRIPTION] = "A simple C compiler",
[SCC_HINT_OUTPUT_FILE] = "Output file",
[SCC_HINT_INPUT_FILE] = "Input source file",
[SCC_HINT_VERBOSE] = "Increase verbosity (can be used multiple times)",
[SCC_HINT_EMIT_AST] = "Generate AST and exit",
[SCC_HINT_EMIT_IR] = "Generate IR and exit",
};
static const char *scc_hints_zh[] = {
[SCC_HINT_PROG_NAME] = "scc",
[SCC_HINT_DESCRIPTION] = "一个简单的C编译器",
[SCC_HINT_OUTPUT_FILE] = "输出文件",
[SCC_HINT_INPUT_FILE] = "输入源文件",
[SCC_HINT_VERBOSE] = "增加详细输出(可多次使用)",
[SCC_HINT_EMIT_AST] = "生成 AST 并退出",
[SCC_HINT_EMIT_IR] = "生成 IR 并退出",
};
const char **scc_hints;
switch (lang) {
case SCC_ARGPARSE_LANG_EN:
scc_hints = scc_hints_en;
break;
case SCC_ARGPARSE_LANG_ZH:
scc_hints = scc_hints_zh;
break;
default:
scc_hints = scc_hints_en;
break;
}
scc_argparse_init(argparse, scc_hints[SCC_HINT_PROG_NAME],
scc_hints[SCC_HINT_DESCRIPTION]);
argparse->lang = lang;
scc_argparse_cmd_t *root = scc_argparse_get_root(argparse);
// -o, --output
scc_argparse_opt_t opt_output;
scc_argparse_opt_init(&opt_output, 'o', "output",
scc_hints[SCC_HINT_OUTPUT_FILE]);
scc_argparse_spec_setup_string(&opt_output.spec, &(config->output_file));
scc_argparse_cmd_add_opt(root, &opt_output);
// input file (必需)
scc_argparse_arg_t arg_input;
scc_argparse_arg_init(&arg_input, "input", scc_hints[SCC_HINT_INPUT_FILE]);
scc_argparse_spec_setup_string(&arg_input.spec, &(config->input_file));
scc_argparse_spec_set_required(&arg_input.spec, true);
scc_argparse_cmd_add_arg(root, &arg_input);
// -v, --verbose (计数)
scc_argparse_opt_t opt_verbose;
scc_argparse_opt_init(&opt_verbose, 'v', "verbose",
scc_hints[SCC_HINT_VERBOSE]);
scc_argparse_spec_setup_count(&opt_verbose.spec, &(config->verbose));
scc_argparse_cmd_add_opt(root, &opt_verbose);
// -T, --ast
scc_argparse_opt_t opt_ast;
scc_argparse_opt_init(&opt_ast, 'T', "emit-ast",
scc_hints[SCC_HINT_EMIT_AST]);
scc_argparse_spec_setup_bool(&opt_ast.spec, &(config->dump_ast));
scc_argparse_cmd_add_opt(root, &opt_ast);
// -R, --ir
scc_argparse_opt_t opt_ir;
scc_argparse_opt_init(&opt_ir, 'R', "emit-ir", scc_hints[SCC_HINT_EMIT_IR]);
scc_argparse_spec_setup_bool(&opt_ir.spec, &(config->dump_ir));
scc_argparse_cmd_add_opt(root, &opt_ir);
}
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
int main(int argc, const char **argv, const char **envp) {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
#endif
scc_config_t config = {
.input_file = NULL,
.output_file = "a.exe",
.verbose = 0,
.dump_ast = false,
.dump_ir = false,
};
scc_argparse_t argparse;
setup_argparse(&argparse, &config, SCC_ARGPARSE_LANG_ZH);
int ret = scc_argparse_parse(&argparse, argc, argv);
if (ret != 0) {
scc_argparse_drop(&argparse);
return ret;
}
scc_argparse_drop(&argparse);
setbuf(stdout, NULL);
FILE *fp = fopen(config.input_file, "r");
if (!fp) {
perror("fopen");
scc_argparse_drop(&argparse);
return 1;
}
scc_pproc_t pproc;
scc_probe_stream_t *source_code_stream = from_file_stream(fp);
// scc_probe_stream_t *pprocessed_code_stream =
// scc_pproc_init(&pproc, source_code_stream);
scc_lexer_t lexer;
scc_lexer_init(&lexer, source_code_stream);
scc_lexer_stream_t lexer_stream;
scc_lexer_to_stream(&lexer, &lexer_stream, false);
scc_parser_t parser;
scc_parser_init(&parser, &lexer_stream, null);
scc_ast_translation_unit_t *translation_unit =
scc_parse_translation_unit(&parser);
if (config.dump_ast) {
scc_tree_dump_ctx_t tree_dump;
scc_tree_dump_ctx_init(&tree_dump, true);
scc_ast_dump_node(&tree_dump, (scc_ast_node_t *)translation_unit);
scc_tree_dump_ctx_drop(&tree_dump);
return 0;
}
scc_ir_builder_t ir_builder;
scc_ast2ir(translation_unit, &ir_builder);
if (config.dump_ir) {
scc_ir_dump_ctx_t ir_dump_ctx;
scc_tree_dump_ctx_t tree_dump; // 仅为 ir dump 辅助
scc_tree_dump_ctx_init(&tree_dump, true);
scc_ir_dump_ctx_init(&ir_dump_ctx, &tree_dump, &ir_builder.cprog,
&ir_builder.ctx);
// scc_ir_dump_cprog(&ir_dump_ctx);
scc_ir_dump_cprog_linear(&ir_dump_ctx);
scc_tree_dump_ctx_drop(&tree_dump);
return 0;
}
scc_printf("output exe at %s", config.output_file);
return 0;
}