用 C 语言编写引导编译器 ethcc_v0,再用它编译 ethcc 语言自身实现的 ethcc_v1,最终实现编译器自举。
当前阶段:Step 11 完成 — v1 已具备完整的编译器前端和代码生成能力,可通过 stdin/stdout 编译 .ec 源文件。
| 步骤 | 功能 | 状态 |
|---|---|---|
| Step 0 | 基础验证,极简 ethcc 程序 | ✅ |
| Step 1-4 | 词法分析器(token、关键字、注释、比较运算符) | ✅ |
| Step 5 | 基础语法分析器(AST、递归下降、表达式) | ✅ |
| Step 6 | 增强语法分析器(if-else、多语句块) | ✅ |
| Step 7 | while 循环 | ✅ |
| Step 8 | for 循环(脱糖为 while) | ✅ |
| Step 9 | 数组访问 a[i] |
✅ |
| Step 10 | 代码生成器(AST → C 代码) | ✅ |
| Step 11 | 文件 I/O 与 CLI(stdin/stdout) | ✅ |
| Step 12 | 函数调用代码生成 | ⬜ |
| Step 13 | 语义分析 / 符号表 | ⬜ |
| Step 14 | 整合与完善 | ⬜ |
| Step 15 | 真正自举(v1 编译自身) | ⬜ |
┌──────────────────────────────────────────┐
│ ethcc_v0 (C, 1722 行) │
│ GCC 编译 │
│ ├── 词法分析器 ├── 语法分析器 │
│ ├── AST (指针) ├── 代码生成器 → C │
│ └── 支持:函数调用、参数、char/string │
└──────────────┬───────────────────────────┘
│ 编译 .ec 源文件
▼
┌──────────────────────────────────────────┐
│ ethcc_v1 (ethcc 语言, 1524 行) │
│ 由 ethcc_v0 编译 │
│ ├── 词法分析器 ├── 语法分析器 │
│ ├── AST (并行数组) ├── 代码生成器 → C │
│ └── 支持:if/while/for、数组、表达式 │
└──────────────────────────────────────────┘
ethcc 是 C 语言的一个简化子集,当前支持:
- 类型:仅
int,支持一维数组int a[10] - 控制流:
if/if-else、while、for - 表达式:
+-*/、比较运算符(<><=>===!=) - 函数:无参函数定义与 return 语句
- 数组:一维数组声明与访问
a[i]
暂不支持:函数调用、函数参数、&& ||、char/string 类型、指针。
ether_cc/
├── stage1/
│ └── ethcc_v0.c # C 编写的引导编译器 (1722 行)
├── stage2/
│ ├── ethcc_v1_step5.ec # 基础语法分析器
│ ├── ethcc_v1_step6.ec # if-else、比较运算符
│ ├── ethcc_v1_step7.ec # while 循环
│ ├── ethcc_v1_step8.ec # for 循环
│ ├── ethcc_v1_step9.ec # 数组访问
│ ├── ethcc_v1_step10.ec # 代码生成器
│ └── ethcc_v1_step11.ec # 当前最新 — stdin/stdout + CLI
├── examples/
│ ├── hello.ec # 最小程序 (return 42)
│ ├── while_test.ec # while 循环求和
│ ├── simple_if.ec # if-else 分支
│ ├── math.ec # 多函数运算
│ ├── recursion.ec # 递归斐波那契/阶乘
│ └── new_features.ec # 数组+逻辑运算
├── runtime/
│ └── runtime.c # 运行时函数(未链接)
├── build/ # 编译产物
├── Makefile
└── README.md
make # 编译 v0 + 最新 v1 step11
make v1-step11 # 仅编译 v1 step11
make test-examples # 运行示例测试(通过 v0)
make test-v1-compile # 使用 v1 编译 hello.ec
make clean # 清理v1 从 stdin 读取 .ec 源文件,生成 C 代码到 stdout:
./build/ethcc_v1_step11 < input.ec > output.c
gcc -o output output.c
./output示例:
echo 'int main() { return 42; }' | ./build/ethcc_v1_step11 | head -n -1 > hello.c
gcc -o hello hello.c && ./hello # 返回 42
head -n -1用于过滤 v0 运行时注入的result: N行。
v0 作为引导编译器存在若干限制,v1 的代码需要针对性规避:
| v0 限制 | v1 应对方案 |
|---|---|
| 不支持前向声明 | 函数按调用顺序定义(callee 先于 caller) |
| 不支持指针 | AST 使用并行数组存储,节点索引代替指针 |
| 不支持数组参数 | 使用全局扁平化缓冲区(cg_stack[2560]) |
| 不支持二维数组 | 一维数组模拟:base * stride + offset |
| 代码生成器变量名冲突 | 各作用域使用不同变量名前缀 |
v1 使用 6 个并行数组存储抽象语法树,完全不依赖指针:
ast_node_type[node_idx] — 节点类型
ast_node_value[node_idx] — 数值或块索引
ast_node_left[node_idx] — 左子节点索引
ast_node_right[node_idx] — 右子节点索引
ast_node_name_idx[node_idx] — 标识符在字符串池中的索引
ast_node_index[node_idx] — 数组标记 / else 分支索引
代码块采用扁平化存储:block_children[block_idx * 50 + child_pos]
代码生成器将 AST 转换为 C 代码,特点:
- goto 标签控制流(if-else、while 循环)
- 临时变量命名
t0, t1, ...、标签命名l0, l1, ... - 槽位保存/恢复机制避免数组参数依赖
- 生成代码包含
#include <stdio.h>和<stdlib.h>
# 编译 .ec 文件(生成可执行文件)
build/ethcc_v0 source.ec -o output
# 仅查看生成的 C 代码(不编译运行)
build/ethcc_v0 source.ec -tv0 的工作流程:读取 .ec 文件 → 解析 → 生成 C 代码 → 调用 GCC 编译 → 产生可执行文件。
每一步都是一个可独立编译运行的里程碑:
- 从最小可行版本开始(仅 main 函数)
- 逐步增加词法分析、语法分析能力
- v1 自身用 ethcc 语言编写,确保能被当前 v0 编译
- 避免在新步骤引入 v0 不支持的复杂特性
- 最终目标:v1 编译自身的 .ec 源码,实现完全自举
本项目用于学习和实验编译器自举概念。