9e41b09318
- codegen: 声明 CRT 的 malloc/strlen/memcpy
- str+str 拼接: strlen(l)+strlen(r)+1 → malloc → memcpy×2 → 返回指针
- 新增集成测试 08_str_concat.l ("Hello, " + "World!" → "Hello, World!")
- 修复自报告 §5-6 字符串拼接不工作的 bug
11 KiB
11 KiB
L Language 架构分析报告 (v0.1+)
日期: 2026-06-05 01:00 | 自动生成 | 自上次报告后有 4 个提交 上次报告: [docs/architecture-analysis-report-2026-06-05.md](/D:\Code\doing_exercises\programs\L Language\docs\architecture-analysis-report-2026-06-05.md) (v0.1 基线
3b7bab1)
变更摘要 (自上次报告)
- P0:
let mut+ 赋值语句 - P0: 字符串类型 + 字面量 +
print_str - 架构: LLVM 目标初始化解耦 ->
target.h/c - 技术债: codegen
malloc-> arena - 技术债: 添加
.codegraphignore
上次报告提出的 4 项 P0 建议已全部落地。本次报告反映 v0.1 + 4 commits 后的当前快照,新增 588 行代码(net),2 个集成测试程序(06_mut_while.l, 07_hello_str.l)。
| Commit | 功能 |
|---|---|
f8c5e18 |
技术债修复:codegen malloc->arena + .codegraphignore |
bd02a49 |
let mut + 赋值语句 -- while 循环可修改变量 |
9a53d97 |
字符串类型 + 字面量 + print_str |
9ff2990 |
LLVM 目标初始化解耦 -- 抽取 target.h/c |
1. 当前架构全景
1.1 编译流水线
源码(.l) -> Lexer(词法) -> Parser(语法) -> Sema(语义) -> Codegen(LLVM IR) -> Target(obj) -> 链接(.exe)
Token[] AstNode* 类型标注 LLVMModuleRef .o 文件 GCC/ld
一条完整的手写单趟编译器流水线,无依赖生成器。Codegen 层与 Target 层已通过 target.h/c 解耦。
1.2 模块清单与指标
| 模块 | 文件 | 行数 | 关键数据结构 | 新增功能 |
|---|---|---|---|---|
| lexer/ | lexer.c(131) token.c(44) + .h(49) | 224 | Token {kind, start, length, line, col} | TOK_MUT, TOK_STR, TOK_STR_LIT, TOK_ASSIGN |
| ast/ | ast.c(107) ast.h(93) | 200 | AstNode {kind, type, as{union}} | AST_ASSIGN_STMT, is_mut, str_val |
| parser/ | parser.c(314) parser.h(10) | 324 | Parser {tokens, pos, filename, arena} | let mut 解析, 赋值语句, 字符串字面量 |
| sema/ | sema.c(294) symbol.c(46) + .h(41) | 381 | Scope {head, parent}, Symbol {is_mut} | 可变性检查, TYPE_STR, print_str, 赋值检查 |
| codegen/ | codegen.c(365) target.c(30) + .h(26) | 421 | CgCtx {builder, var_table, fn_table} | Store(赋值), GlobalStringPtr(str) |
| driver/ | main.c(133) error.c(46) + .h(18) | 197 | 流水线串联 | 调用 target_* 接口 |
| util/ | arena.c(39) arena.h(13) | 52 | Arena {memory, offset, capacity} | 8MB bump allocator |
| include/ | l_lang.h | 34 | TypeKind, 公共声明 | TYPE_STR 新增 |
总计: 22 源文件, ~1,833 行实现代码
1.3 技术选型评估
| 选择 | 评价 | 最新状态 |
|---|---|---|
| C17 + CMake + MinGW | 轻量,学习友好 | 稳定 |
| Arena bump allocator | 极简内存管理,无 GC 开销 | 已统一(codegen 原先混用 malloc) |
| LLVM-C API (19.x) | 成熟但版本差异需处理 | 已适配 LLVMContext API |
| 手写 Lexer/Parser | 0 外部依赖,完整控制 | 稳定,30 种 Token 类型 |
| Pratt 表达式解析 | 优雅处理优先级,易扩展 | 无变化 |
GCC 链接 (system()) |
简单但平台绑定 | 待改进 |
| 分离 target.h/c | 关注点分离,可测试 | 新建模块 |
2. 功能清单与成熟度
2.1 v0.1 基线功能
- 基本类型: i64, f64, bool, void
- 算术运算: + - * / %
- 比较运算: == != < > <= >=
- 逻辑运算: && || !
- 变量声明: let 不可变,类型标注 + 类型推断
- 控制流: if/else, while
- 函数定义与调用: 参数、返回类型、递归
- 内置函数: print_i64, print_f64, print_bool
- 注释: // 行注释, /* */ 块注释
- 错误报告: 词法/语法即时终止, 语义错误批量输出
- 测试: 65 单元测试 + 5 集成测试
2.2 v0.1+ 新增功能
- let mut 可变变量 --
let mut x: i64 = 0;声明可变变量 - 赋值语句 --
x = x + 1;对可变变量赋值(LLVMBuildStore) - 可变性检查 -- 对不可变变量赋值报 "不能对不可变变量赋值(需用 let mut 声明)"
- 字符串类型 str -- 类型系统新增 TYPE_STR
- 字符串字面量 --
"Hello, L Language!"双引号字面量,LLVMBuildGlobalStringPtr - 字符串拼接 (语义) --
"a" + "b"类型检查为 str + str -> str - print_str 内置函数 -- 委托 printf("%%s\n", str)
- target.h/c 独立模块 -- LLVM 目标初始化与 Codegen 解耦
- 集成测试: 06_mut_while.l (可变 while 循环), 07_hello_str.l (字符串输出)
2.3 实现细节
let mut + 赋值 贯穿全流水线:
- Lexer:
TOK_MUT(关键字),TOK_ASSIGN(分隔符) - AST:
AST_ASSIGN_STMT节点,let_stmt.is_mut标志 - Parser:
let mut IDENT = expr;完整解析;IDENT = expr;赋值语句(标识符后紧跟=时触发) - Sema:
Symbol.is_mut标记; 赋值时检查可变性 + 类型一致性 - Codegen:
LLVMBuildStore向现有 alloca 写入新值
字符串类型 实现:
- Lexer:
TOK_STR(类型关键字),TOK_STR_LIT(双引号字面量) - AST:
literal.str_val(arena 拷贝),AST_LITERAL_EXPR支持 TYPE_STR - Parser:
TOK_STR_LIT创建ast_make_literal_str() - Sema: TYPE_STR 加入类型系统;
+对 str+str 做类型检查 - Codegen:
LLVMBuildGlobalStringPtr生成全局常量;print_str委派 printf - 限制: 运行时字符串拼接(str + str)在 codegen 中直接返回左操作数,语义层验证通过但运行时行为不正确
3. 与 Rust 对标:缺失功能清单
按 "学习价值 + 实现难度" 排序。已完成项标注 ✓。
P0: 短期(1-2 天/项)
| # | 功能 | Rust 启发 | 改动范围 | 学习价值 |
|---|---|---|---|---|
| ✓ | let mut + 赋值 | 默认不可变 | lexer/parser/sema/codegen | 不可变性贯穿全流水线 |
| ✓ | 字符串类型 + 字面量 | &str/String | lexer/parser/sema/codegen | LLVM 全局常量和指针 |
| 1 | 复合赋值 += -= *= /= | 复合赋值运算符 | lexer(4 Token) + parser(desugar) + sema(mut check) | 语法糖去糖 |
| 2 | 修复 str+str 运行时拼接 | -- | codegen(strcat/memcpy) | 运行时内存操作 |
| 3 | for 循环 + Range | for i in 0..10 {} | lexer(for/in/..) + parser + sema + codegen->while | 去糖为 while 循环 |
| 4 | 结构体 struct | struct 具名域 | lexer(struct/.) + parser + sema + codegen(GEP) | GEP 指针计算 |
P1: 中期(3-5 天/项)
| # | 功能 | 改动范围 | 学习价值 |
|---|---|---|---|
| 5 | 数组 + 索引 [i64; N], arr[i] | lexer/parser/sema/codegen(GEP) | GEP 多维指针计算 |
| 6 | 枚举 (C 风格) | lexer(enum) + parser + sema + codegen(i64) | 为代数类型铺路 |
| 7 | match 表达式 | lexer/parser/sema(穷举检查) + codegen | 模式匹配是 PL 核心 |
| 8 | 类型别名 type Meters = i64 | lexer/parser/sema(展开) | 名称等价 vs 结构等价 |
P2: 中后期(1-2 周/项)
| # | 功能 | 学习价值 |
|---|---|---|
| 9 | 模块系统 mod + use | 从单文件到多文件跳跃 |
| 10 | 自定义 IR 层 (三地址码/SSA) | "IR 是编译器的核心抽象" |
| 11 | 泛型 (单态化) | Rust 零成本抽象的基石 |
| 12 | 解释器模式 (walk AST) | 快速验证语义,无需 LLVM |
P3: 长期(2-4 周/项)
| # | 功能 | 备注 |
|---|---|---|
| 13 | trait / 接口 | 需泛型 + 虚表 |
| 14 | Option/Result 错误处理 | 需泛型 + enum |
| 15 | 所有权 / 借用检查 | 借用检查器,极度复杂 |
| 16 | 自举 (L 编译 L) | 终极考验 |
Rust 设计哲学吸收建议
| Rust 特性 | 决策 | 理由 |
|---|---|---|
| 默认不可变 (let vs let mut) | 已加 | 编译期检查,零实现成本 |
| 表达式 vs 语句 | 建议加 | if/match/block 作为表达式 |
| 模式匹配 | 建议加 | 最优雅的控制流之一 |
| 代数数据类型 | 有条件 | 需要泛型支持 |
| Option/Result | 有条件 | 依赖泛型 + enum |
| 所有权 / 借用 | 暂不加 | 借用检查器,远超当前阶段 |
| 生命周期标注 | 暂不加 | 所有权系统的伴生 |
| trait 系统 | 暂不加 | 需泛型 + 虚表 |
| 宏系统 | 暂不加 | 自举后才考虑 |
4. 架构改进:已完成 vs 待实施
4.1 已完成 ✓
-
代码生成与链接解耦 (来自上次报告 4.1-1)
src/codegen/target.h/c新增 (30+15 行)- 接口: target_init(), target_get_default_triple(), target_create_machine(), target_emit_obj()
- main.c 减少 25 行 inline LLVM Target API 调用
-
内存管理统一 (来自上次报告 7-1)
- codegen.c 所有 malloc 替换为 arena_alloc()
- 消除内存泄漏风险
-
索引排除 (来自上次报告 7-5)
- .codegraphignore 排除 build/
4.2 待实施
-
错误类型统一 (上次报告 4.1-2)
- 当前: ErrorInfo 和 ErrorList 分散多个模块
- 建议: 统一为 CompilerError 结构体
-
测试框架扩展 (上次报告 4.1-3)
- 当前: 无 codegen 层测试
- 建议: test_codegen.c + LLVMVerifyModule
-
调用约定抽象 (上次报告 4.2-6)
- 建议: 抽 abi.h 封装参数传递和返回值处理
5. 技术债务与风险
| # | 问题 | 状态 | 影响 | 建议 |
|---|---|---|---|---|
| 1 | codegen malloc 混用 arena | 已修复 | -- | -- |
| 2 | 无 .codegraphignore | 已修复 | -- | -- |
| 3 | system("gcc ...") 链接 | 仍存在 | 平台不兼容 | 封装 Linker 接口,支持 ld/lld |
| 4 | 无 codegen 层测试 | 仍存在 | IR 正确性无形式验证 | test_codegen.c |
| 5 | 无优化 pass (mem2reg) | 仍存在 | alloca 效率低 | LLVMRunPassManager |
| 6 | 字符串运行时拼接不工作 | 新增 | "a"+"b" 语义通过但运行时错误 | codegen 中实现 strcat/memcpy |
| 7 | CHANGELOG 未更新 | 新增 | 4 个新 commit 未记录 | 添加 v0.2.0 条目 |
6. 推荐开发路线图
v0.2 目标
| 优先级 | 功能 | 预计工时 | 备注 |
|---|---|---|---|
| P0 | 修复 str+str 运行时拼接 | 0.5 天 | 阻塞后续 |
| P0 | 复合赋值 += -= *= /= | 0.5 天 | 语法糖,改动面小 |
| P0 | 结构体 struct | 2-3 天 | 解锁 GEP 和复合类型 |
| P0 | for 循环 + range | 1 天 | 去糖为 while |
| P1 | test_codegen.c | 1 天 | 技术债补偿 |
v0.3 目标
| 优先级 | 功能 | 预计工时 |
|---|---|---|
| P1 | 数组 + 索引 | 2 天 |
| P1 | 枚举 (C 风格) | 1 天 |
| P2 | match 表达式 | 2-3 天 |
| P1 | 类型别名 | 0.5 天 |
| P2 | 解释器模式 | 2 天 |
长期
| 优先级 | 功能 | 预计工时 |
|---|---|---|
| P2 | 自定义 IR 层 | 3-5 天 |
| P2 | 模块系统 | 3-5 天 |
| P3 | 泛型 (单态化) | 5-7 天 |
| P3 | trait / 接口 | 5-7 天 |
| P3 | 自举尝试 | 数周 |
7. 度量汇总
| 指标 | v0.1 基线 | 当前 (v0.1+) | 变化 |
|---|---|---|---|
| 源文件数 | 19 | 22 | +3 |
| 实现代码行 | ~1,300 | ~1,833 | +~530 |
| Token 类型 | 25 | 30 | +5 |
| AST 节点类型 | 13 | 15 | +2 |
| 类型系统 | 4 种 | 5 种 (+str) | +1 |
| 内置函数 | 3 | 4 (+print_str) | +1 |
| 集成测试 | 5 程序 | 7 程序 | +2 |
| 单元测试 | 65 | 65 | 无变化 |
| LLVM 模块 | 单体 codegen.c | codegen.c + target.c | 解耦 |
| 报告 P0 完成度 | -- | 4/4 | 100% |
本报告由 Codex 自动生成。自上次报告后代码有显著变化 (4 commits, +588/-53 行),上次报告的 4 项 P0 建议全部实现。