feat(pproc): 实现C语言预处理器功能并重构项目依赖
- 新增预处理器库(pproc),替代原有的pprocessor模块 - 实现完整的宏定义解析功能,支持对象宏和函数宏 - 添加条件编译指令处理(#if、#ifdef、#ifndef、#else、#elif、#endif) - 实现宏展开机制,包括嵌套宏和递归宏处理 - 添加宏定义测试用例,覆盖基本功能和复杂场景 - 在cbuild.toml中更新依赖配置,移除parser、ast、ast2ir、ir等未完成模块 - 新增lexer工具函数用于token流处理 - 添加宏定义表管理功能,支持宏的创建、查找、删除操作 - 实现宏参数解析和替换列表处理
This commit is contained in:
187
src/main.c
187
src/main.c
@@ -1,41 +1,22 @@
|
||||
#include <argparse.h>
|
||||
#include <lexer.h>
|
||||
#include <parser.h>
|
||||
#include <pprocessor.h>
|
||||
#include <scc_lexer.h>
|
||||
#include <scc_pproc.h>
|
||||
|
||||
#include <ast_dump.h>
|
||||
#include <ir_dump.h>
|
||||
#include <scc_ast2ir.h>
|
||||
// #include <scc_parser.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;
|
||||
cbool emit_lex;
|
||||
cbool emit_pp;
|
||||
cbool emit_ast;
|
||||
cbool emit_ir;
|
||||
} scc_config_t;
|
||||
|
||||
static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
|
||||
@@ -46,16 +27,20 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
|
||||
SCC_HINT_OUTPUT_FILE,
|
||||
SCC_HINT_INPUT_FILE,
|
||||
SCC_HINT_VERBOSE,
|
||||
|
||||
SCC_HINT_EMIT_LEX,
|
||||
SCC_HINT_EMIT_PP,
|
||||
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_LEX] = "Generate lexer sources tokens and exit",
|
||||
[SCC_HINT_EMIT_PP] = "Generate preprocessed tokens and exit",
|
||||
[SCC_HINT_EMIT_AST] = "Generate AST and exit",
|
||||
[SCC_HINT_EMIT_IR] = "Generate IR and exit",
|
||||
};
|
||||
@@ -65,8 +50,10 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
|
||||
[SCC_HINT_OUTPUT_FILE] = "输出文件",
|
||||
[SCC_HINT_INPUT_FILE] = "输入源文件",
|
||||
[SCC_HINT_VERBOSE] = "增加详细输出(可多次使用)",
|
||||
[SCC_HINT_EMIT_AST] = "生成 AST 并退出",
|
||||
[SCC_HINT_EMIT_IR] = "生成 IR 并退出",
|
||||
[SCC_HINT_EMIT_LEX] = "生成`源代码的词法单元`并退出",
|
||||
[SCC_HINT_EMIT_PP] = "生成`预处理后的词法单元`并退出",
|
||||
[SCC_HINT_EMIT_AST] = "生成`抽象语法树`并退出",
|
||||
[SCC_HINT_EMIT_IR] = "生成`中间代码`并退出",
|
||||
};
|
||||
|
||||
const char **scc_hints;
|
||||
@@ -103,22 +90,35 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
|
||||
|
||||
// -v, --verbose (计数)
|
||||
scc_argparse_opt_t opt_verbose;
|
||||
scc_argparse_opt_init(&opt_verbose, 'v', "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
|
||||
// --emit-lex
|
||||
scc_argparse_opt_t opt_lex;
|
||||
scc_argparse_opt_init(&opt_lex, 0, "emit-lex",
|
||||
scc_hints[SCC_HINT_EMIT_LEX]);
|
||||
scc_argparse_spec_setup_bool(&opt_lex.spec, &(config->emit_lex));
|
||||
scc_argparse_cmd_add_opt(root, &opt_lex);
|
||||
|
||||
// --emit-pp
|
||||
scc_argparse_opt_t opt_pp;
|
||||
scc_argparse_opt_init(&opt_pp, 0, "emit-pp", scc_hints[SCC_HINT_EMIT_PP]);
|
||||
scc_argparse_spec_setup_bool(&opt_pp.spec, &(config->emit_pp));
|
||||
scc_argparse_cmd_add_opt(root, &opt_pp);
|
||||
|
||||
// -T, --emit-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_spec_setup_bool(&opt_ast.spec, &(config->emit_ast));
|
||||
scc_argparse_cmd_add_opt(root, &opt_ast);
|
||||
|
||||
// -R, --ir
|
||||
// -R, --emit-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_spec_setup_bool(&opt_ir.spec, &(config->emit_ir));
|
||||
scc_argparse_cmd_add_opt(root, &opt_ir);
|
||||
}
|
||||
|
||||
@@ -127,17 +127,42 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static void print_ring(scc_lexer_tok_ring_t *ring, int verbose) {
|
||||
scc_lexer_tok_t tok = {0};
|
||||
int ret = 0;
|
||||
while (1) {
|
||||
scc_ring_next_consume(*ring, tok, ret);
|
||||
if (ret == false || tok.type == SCC_TOK_EOF) {
|
||||
break;
|
||||
}
|
||||
if (verbose == 0) {
|
||||
scc_printf("%s ", scc_get_tok_name(tok.type));
|
||||
} else if (verbose >= 1) {
|
||||
scc_printf("token [%-8s] `%s` at %s:%d:%d\n",
|
||||
scc_get_tok_name(tok.type),
|
||||
scc_cstring_as_cstr(&tok.lexeme), tok.loc.name,
|
||||
tok.loc.line, tok.loc.col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv, const char **envp) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
SetConsoleCP(CP_UTF8);
|
||||
#endif
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
scc_config_t config = {
|
||||
.input_file = NULL,
|
||||
#ifdef _WIN32
|
||||
.output_file = "a.exe",
|
||||
#else
|
||||
.output_file = "a.out",
|
||||
#endif
|
||||
.verbose = 0,
|
||||
.dump_ast = false,
|
||||
.dump_ir = false,
|
||||
.emit_ast = false,
|
||||
.emit_ir = false,
|
||||
};
|
||||
scc_argparse_t argparse;
|
||||
setup_argparse(&argparse, &config, SCC_ARGPARSE_LANG_ZH);
|
||||
@@ -148,51 +173,57 @@ int main(int argc, const char **argv, const char **envp) {
|
||||
}
|
||||
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_sstream_t sstream;
|
||||
if (scc_sstream_init(&sstream, config.input_file, 1024)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
print_ring(tok_ring, config.verbose);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_pproc_init(&pproc, scc_lexer_to_ring(&lexer, 8, false));
|
||||
if (config.emit_pp) {
|
||||
scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pproc, 8);
|
||||
print_ring(tok_ring, config.verbose);
|
||||
}
|
||||
|
||||
scc_ir_builder_t ir_builder;
|
||||
scc_ast2ir(translation_unit, &ir_builder);
|
||||
scc_pproc_drop(&pproc);
|
||||
scc_lexer_drop(&lexer);
|
||||
scc_sstream_drop(&sstream);
|
||||
|
||||
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_parser_t parser;
|
||||
// scc_parser_init(&parser, &lexer_stream, null);
|
||||
// scc_ast_translation_unit_t *translation_unit =
|
||||
// scc_parse_translation_unit(&parser);
|
||||
|
||||
// if (config.emit_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.emit_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;
|
||||
|
||||
Reference in New Issue
Block a user