编译实践Lv4~Lv9

Lv4 引入符号表统一记录常量、变量的属性与内存标识 1 2 3 4 5 6 struct SymbolInfo { bool is_const; // 常量 or 变量 int const_val; // 常量值(编译期已知) string alloc_name; // 变量的 alloc 指令名,如 "@x" }; unordered_map<string, SymbolInfo> symtab; 操作: AddConst(name, val) — 插入常量(同时做重复定义检查) AddVar(name, ...) — 插入变量 + 生成 alloc i32 指令 Lookup(name) — 查询符号 当前仅支持单层作用域(函数体 Block),无嵌套 Block,因此使用单一 unordered_map。若后续实验引入嵌套作用域,可扩展为栈式符号表(进入 Block 时 push 新表,离开时 pop)。 Lv5 Lv4 用 is_return bool 区分两种语句,Lv5 需要支持 4 种,改用 kind 枚举,重构StmtAST : 1 2 3 4 5 6 7 8 9 class StmtAST : public BaseAST { public: enum Kind { RETURN, ASSIGN, EXP_STMT, BLOCK }; Kind kind; unique_ptr<BaseAST> exp; // RETURN / EXP_STMT unique_ptr<BaseAST> lval; // ASSIGN unique_ptr<BaseAST> assign_exp; // ASSIGN unique_ptr<BaseAST> block; // BLOCK }; 测试遇到一个问题, ...

July 16, 2026 · 13 min · farmer3-c

编译实践Lv3.表达式

目的 实现一个能够处理表达式 (一元/二元) 的编译器,编译器将可以处理如下的 SysY 程序: int main() { return 1 + 2 * -3; } 实现 Lv3.1. 一元表达式 新增/变更的语法规范: Stmt ::= "return" Exp ";"; Exp ::= UnaryExp; PrimaryExp ::= "(" Exp ")" | Number; Number ::= INT_CONST; UnaryExp ::= PrimaryExp | UnaryOp UnaryExp; UnaryOp ::= "+" | "-" | "!"; 设计 AST 时,我只为 ::= 左侧的符号设计一种 AST, 使其涵盖 ::= 右侧的所有规则。 AST.h 添加 ExpAST, PrimaryExpAST, UnaryExpAST,我使用bool标志区别实现右侧的多种规则,更新 StmtAST,原来只支持num,改成exp。 sysy.y 添加语法规则 for Exp, UnaryExp, PrimaryExp, UnaryOp; 更新 Stmt rule ...

July 13, 2026 · 6 min · farmer3-c

编译实践Lv2.初试目标代码生成

目的 实现一个能处理 main 函数和 return 语句的编译器, 同时输出编译后的 RISC-V 汇编。 编译器会将如下的 SysY 程序: int main() { // 摊牌了, 我是注释 return 0; } 编译为对应的 RISC-V 汇编: .text .globl main main: li a0, 0 ret 或: .text .globl main main: li t0, 0 mv a0, t0 ret 实现 Lv2.1. 处理 Koopa IR 这一节的任务是建立内存形式的 Koopa IR。 libkoopa 中的接口并没有我定义的AST数据类型,所以可以先把 Program Dump 到 string 中,再用koopa_parse_from_string解析这个 string,得到内存形式的 Koopa IR。 Lv2.2. 目标代码生成 这一节的任务是生成 RISC-V 汇编。 可以写一个头文件和cpp文件(ASMGenerator.h,ASMGenerator.cpp)专门用来生成汇编。在ASMGenerator.h中声明需要用到的函数, class ASMGenerator { public: ASMGenerator(std::ostream &os) : os(os) {} void Generate(const koopa_raw_program_t &program); private: // DFS 遍历族 void Visit(const koopa_raw_program_t &program); void Visit(const koopa_raw_slice_t &slice); void Visit(const koopa_raw_function_t &func); void Visit(const koopa_raw_basic_block_t &bb); void Visit(const koopa_raw_value_t &value); // 辅助 void EmitPrologue(); void EmitEpilogue(); // 共享状态 std::ostream &os; int frame_size = 0; // 对齐后的栈帧大小 std::string cur_func; // 当前函数名(已去 @) }; 在ASMGenerator.cpp分别实现这些函数, ...

June 30, 2026 · 2 min · farmer3-c

编译实践Lv1.main函数

目的 实现一个能处理 main 函数和 return 语句的编译器.编译器可以将如下的 SysY 程序: int main() { // 注释也应该被删掉哦 return 0; } 编译为对应的 Koopa IR: fun @main(): i32 { %entry: ret 0 } 实现 Lv1.2. 词法/语法分析初见 我使用项目提供的c++模板,直接执行: make build/compiler -koopa hello.c -o hello.koopa 输出: int main() { return 0; } 这是因为默认的模板使用string存放ast,所以输出的ast也是string形式: CompUnit : FuncDef { ast = unique_ptr<string>($1); } ; Lv1.3. 解析 main 函数 这里设计一个ast用于输出程序的语法结构,教程写的已经很详细了:写一个头文件来定义AST,对于头文件重复声明的问题,加上#pragma once就可以解决。 我是这样定义基类和子类进行构造的: // 所有 AST 的基类 class BaseAST { public: virtual ~BaseAST() = default; virtual void Dump() const = 0; }; // CompUnit 是 BaseAST class CompUnitAST : public BaseAST { public: // 用智能指针管理对象 std::unique_ptr<BaseAST> func_def; void Dump() const override { std::cout << "CompUnitAST { "; func_def->Dump(); std::cout << " }"; } }; …… 同时,修改参数类型的相关声明:比如%parse-param { std::unique_ptr<string> &ast } 改为%parse-param { std::unique_ptr<BaseAST> &ast } ...

June 10, 2026 · 2 min · farmer3-c

编译实践Lv0. 环境配置

目的 配置实验环境的 Docker 容器, 并学习 Docker 的基本使用方法. 认识编译实践中用到的编译器中间表示: Koopa IR. 认识编译实践中开发的编译器的目标架构: RISC-V. 配置实验环境的 Docker 容器, 并学习 Docker 的基本使用方法. 安装Docker Desktop 使用docker 一、镜像相关常用命令 镜像是容器的 “模板”,所有容器都基于镜像创建,这部分是基础操作。 1. 拉取镜像(下载到本地) 如果本地没有该镜像,先从 Docker 仓库拉取(如果是私有仓库,需先docker login): powershell 1 2 3 4 5 # 拉取指定镜像(格式:docker pull 镜像名:标签,latest是默认标签可省略) docker pull maxxing/compiler-dev:latest # 简写(省略latest,效果同上) docker pull maxxing/compiler-dev 作用:把远程仓库的maxxing/compiler-dev镜像下载到本地,后续创建容器无需重复下载; 场景:首次使用该镜像、需要更新镜像版本时。 2. 查看本地镜像 powershell 1 2 3 4 5 # 查看所有本地镜像(重点看REPOSITORY、TAG、IMAGE ID) docker images # 过滤查看指定镜像(只显示maxxing/compiler-dev) docker images maxxing/compiler-dev 输出示例: 1 2 REPOSITORY TAG IMAGE ID CREATED SIZE maxxing/compiler-dev latest 5bcecbbe34b6 3 months ago 4.14GB 场景:确认镜像是否已下载、查看镜像 ID / 大小等信息。 ...

February 14, 2026 · 6 min · farmer3-c