feat: L Language v0.1 编译器完整实现
5 阶段编译流水线: 词法分析 → 语法分析(Pratt) → 语义分析(类型推断) → LLVM IR → .exe 模块: - lexer: 手写状态机, 40 种 Token, // 和 /* */ 注释 - parser: Pratt 表达式解析(9 级优先级) + 递归下降语句/函数 - ast: 14 种节点类型 + 工厂函数 - sema: 作用域链符号表 + 类型推断 + 类型检查 - codegen: AST → LLVM-C API, print_i64/f64/bool 内建 - driver: 命令行 + 流水线串联 + 错误报告 - util: Arena bump allocator (8MB) 测试: 65 单元测试(词法41+语法15+语义9) + 5 集成测试 全部通过 语言特性: i64/f64/bool/void, let不可变变量, if/else, while, 递归函数
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
#include "l_lang.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "sema.h"
|
||||
#include "codegen.h"
|
||||
#include "error.h"
|
||||
#include "arena.h"
|
||||
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/TargetMachine.h>
|
||||
#include <llvm-c/Target.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 读取整个文件到内存
|
||||
static char* read_file(const char* path, size_t* size) {
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (!f) { fprintf(stderr, "无法打开文件: %s\n", path); return NULL; }
|
||||
fseek(f, 0, SEEK_END);
|
||||
*size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
char* buf = malloc(*size + 1);
|
||||
if (!buf) { fclose(f); return NULL; }
|
||||
fread(buf, 1, *size, f);
|
||||
buf[*size] = '\0';
|
||||
fclose(f);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// 写入字符串到文件
|
||||
static bool write_file(const char* path, const char* data) {
|
||||
FILE* f = fopen(path, "w");
|
||||
if (!f) return false;
|
||||
fputs(data, f);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* input = NULL;
|
||||
const char* output = "a.exe";
|
||||
bool emit_ir = false;
|
||||
|
||||
// 解析命令行参数
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--emit-ir") == 0) { emit_ir = true; }
|
||||
else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { output = argv[++i]; }
|
||||
else if (argv[i][0] != '-') { input = argv[i]; }
|
||||
}
|
||||
|
||||
if (!input) {
|
||||
fprintf(stderr, "用法: l_lang <文件.l> [-o <输出>] [--emit-ir]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 1. 读取源文件
|
||||
size_t src_size;
|
||||
char* source = read_file(input, &src_size);
|
||||
if (!source) return 1;
|
||||
|
||||
// 2. 初始化
|
||||
Arena arena = arena_create(8); // 8 MB
|
||||
if (!arena.memory) { fprintf(stderr, "内存分配失败\n"); free(source); return 1; }
|
||||
|
||||
ErrorInfo error = {0};
|
||||
ErrorList error_list; error_init(&error_list);
|
||||
|
||||
// 3. 词法分析
|
||||
size_t token_count;
|
||||
Token* tokens = lex(&arena, source, input, &token_count, &error);
|
||||
if (!tokens) {
|
||||
fprintf(stderr, "词法错误: %s:%d:%d: %s\n",
|
||||
error.filename, error.line, error.col, error.message);
|
||||
free(source); arena_destroy(&arena);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 4. 语法分析
|
||||
AstNode* ast = parse(&arena, tokens, token_count, input, &error);
|
||||
if (!ast) {
|
||||
fprintf(stderr, "语法错误: %s:%d:%d: %s\n",
|
||||
error.filename, error.line, error.col, error.message);
|
||||
free(source); arena_destroy(&arena);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 5. 语义分析
|
||||
sema_analyze(ast, &error_list, &arena);
|
||||
if (error_list.count > 0) {
|
||||
error_print(&error_list);
|
||||
free(source); arena_destroy(&arena);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 6. LLVM IR 生成
|
||||
const char* codegen_error = NULL;
|
||||
LLVMModuleRef module = codegen_module(ast, "l_module", &codegen_error);
|
||||
if (!module) {
|
||||
fprintf(stderr, "IR 生成错误: %s\n", codegen_error);
|
||||
free(source); arena_destroy(&arena);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (emit_ir) {
|
||||
// 输出 LLVM IR 文本
|
||||
char* ir = LLVMPrintModuleToString(module);
|
||||
char ir_path[512];
|
||||
snprintf(ir_path, sizeof(ir_path), "%s.ll", input);
|
||||
write_file(ir_path, ir);
|
||||
printf("IR 已输出到: %s\n", ir_path);
|
||||
LLVMDisposeMessage(ir);
|
||||
} else {
|
||||
// 初始化 X86 目标(LLVM-C.lib 中没有 InitializeAll 系列符号)
|
||||
LLVMInitializeX86TargetInfo();
|
||||
LLVMInitializeX86Target();
|
||||
LLVMInitializeX86TargetMC();
|
||||
LLVMInitializeX86AsmPrinter();
|
||||
LLVMInitializeX86AsmParser();
|
||||
|
||||
char* triple = LLVMGetDefaultTargetTriple();
|
||||
LLVMTargetRef target;
|
||||
char* target_error = NULL;
|
||||
if (LLVMGetTargetFromTriple(triple, &target, &target_error)) {
|
||||
fprintf(stderr, "目标平台错误: %s\n", target_error);
|
||||
LLVMDisposeMessage(target_error); LLVMDisposeMessage(triple);
|
||||
free(source); arena_destroy(&arena); LLVMDisposeModule(module);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LLVMTargetMachineRef tm = LLVMCreateTargetMachine(
|
||||
target, triple, "generic", "",
|
||||
LLVMCodeGenLevelDefault, LLVMRelocDefault,
|
||||
LLVMCodeModelDefault);
|
||||
LLVMDisposeMessage(triple);
|
||||
|
||||
// 输出目标文件
|
||||
char obj_path[512];
|
||||
snprintf(obj_path, sizeof(obj_path), "%s.o", input);
|
||||
char* obj_error = NULL;
|
||||
if (LLVMTargetMachineEmitToFile(tm, module, obj_path,
|
||||
LLVMObjectFile, &obj_error)) {
|
||||
fprintf(stderr, "目标代码生成错误: %s\n", obj_error);
|
||||
LLVMDisposeMessage(obj_error);
|
||||
free(source); arena_destroy(&arena);
|
||||
LLVMDisposeTargetMachine(tm); LLVMDisposeModule(module);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 调用 gcc 链接(MinGW 环境可用)
|
||||
char cmd[1024];
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"gcc \"%s\" -o \"%s\"",
|
||||
obj_path, output);
|
||||
int ret = system(cmd);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "链接失败 (exit code %d)\n", ret);
|
||||
} else {
|
||||
printf("编译成功: %s\n", output);
|
||||
}
|
||||
LLVMDisposeTargetMachine(tm);
|
||||
}
|
||||
|
||||
// 清理
|
||||
LLVMDisposeModule(module);
|
||||
free(source);
|
||||
arena_destroy(&arena);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user