diff --git a/solution/Lab1-修改记录.md b/solution/Lab1-修改记录.md new file mode 100644 index 0000000..ac3cb6a --- /dev/null +++ b/solution/Lab1-修改记录.md @@ -0,0 +1,120 @@ +# Lab1 修改记录 + +## 1. 修改文件 + +- `src/antlr4/SysY.g4` +- `src/main.cpp` +- `include/sem/Sema.h` +- `src/sem/Sema.cpp` +- `include/irgen/IRGen.h` +- `src/irgen/IRGenDriver.cpp` +- `src/irgen/IRGenFunc.cpp` +- `src/irgen/IRGenDecl.cpp` +- `src/irgen/IRGenStmt.cpp` +- `src/irgen/IRGenExp.cpp` +- `solution/Lab1-设计方案.md` +- `solution/Lab1-修改记录.md` + +## 2. 文法扩展 + +将原来只支持: + +- `int main() { ... }` +- 局部 `int` 标量声明 +- 简单 `return a + b` + +的最小文法,扩展为支持: + +- 全局声明与多函数定义 +- `const/int/float/void` +- 标量与数组声明 +- 花括号初始化列表 +- 函数形参、数组形参、函数调用 +- `if/else/while/break/continue/return` +- 赋值语句、表达式语句、复合语句 +- `+ - * / %` +- 比较与逻辑表达式 +- 十进制/八进制/十六进制整数 +- 十进制/十六进制浮点常量 + +## 3. 运行路径调整 + +修改 `src/main.cpp`: + +- 当命令行仅指定 `--emit-parse-tree` 时,打印语法树后直接返回。 + +这样可以避免: + +- 已经通过语法分析的用例 +- 因后续 `sema/irgen` 仍是最小子集而失败 + +这是 Lab1 场景下必要的阶段隔离。 + +## 4. 新文法下的接口适配 + +由于 `SysY.g4` 从“单一 `main` 函数”扩展为完整编译单元,ANTLR 生成的 Context 接口发生变化,因此同步调整了: + +- `sema` 中的变量使用绑定位置:从旧的最小表达式节点改为 `LValContext` +- `irgen` 中的遍历入口:改为适配 `compUnit/funcDef/block/stmt/exp` +- `irgen` 中的存储槽映射:按单个 `VarDefContext` 维护 +- `irgen` 的表达式遍历:适配 `mulExp / unaryExp / primary / lVal` + +说明: + +- 这些修改的目标是“适配新文法并保持工程可编译” +- 并未把 `sema/irgen` 扩展到完整 SysY 2022 +- 当前后续阶段仍主要支持最小 `int` 标量子集 + +## 5. ANTLR 重新生成 + +使用命令重新生成了 Lexer/Parser: + +```bash +mkdir -p build/generated/antlr4 +java -jar third_party/antlr-4.13.2-complete.jar \ + -Dlanguage=Cpp \ + -visitor -no-listener \ + -Xexact-output-dir \ + -o build/generated/antlr4 \ + src/antlr4/SysY.g4 +``` + +## 6. 构建验证 + +执行: + +```bash +cmake --build build -j 4 +``` + +结果: + +- 构建成功 + +## 7. 用例验证 + +已验证以下代表性样例可输出语法树: + +- `test/test_case/functional/simple_add.sy` +- `test/test_case/functional/15_graph_coloring.sy` +- `test/test_case/functional/95_float.sy` + +并批量验证: + +```bash +./build/bin/compiler --emit-parse-tree test/test_case/functional/*.sy +./build/bin/compiler --emit-parse-tree test/test_case/performance/*.sy +``` + +结果: + +- `test/test_case` 下全部 `.sy` 用例在 `--emit-parse-tree` 模式下通过 + +## 8. 已知边界 + +当前提交完成的是 Lab1 所需的“语法分析与语法树构建”。以下能力仍属于后续实验范围: + +- 完整语义分析 +- 完整 IR 生成 +- 浮点/数组/控制流的中间表示支持 +- 更完整的函数/作用域/类型系统检查 diff --git a/solution/Lab1-设计方案.md b/solution/Lab1-设计方案.md new file mode 100644 index 0000000..0847345 --- /dev/null +++ b/solution/Lab1-设计方案.md @@ -0,0 +1,177 @@ +# Lab1 设计方案 + +## 1. 目标 + +根据 `sysy2022.pdf` 中的 SysY 语言定义,扩展 `src/antlr4/SysY.g4`,使编译器能够: + +1. 识别 SysY 2022 的主要词法单元与语法结构。 +2. 通过 `--emit-parse-tree` 输出完整的 ANTLR 语法树。 +3. 在 Lab1 仅要求语法树输出时,不被后续尚未完成的语义分析与 IR 生成阶段阻塞。 + +## 2. 总体方案 + +本次实现继续沿用“ANTLR 语法树直接输出”的路径,不额外引入 AST 层。整体分三部分: + +1. 扩展 `SysY.g4`,覆盖 SysY 2022 所需语法。 +2. 保持现有 `SyntaxTreePrinter` 输出格式不变,继续直接打印 ANTLR parse tree。 +3. 调整 `main.cpp`:当只指定 `--emit-parse-tree` 时,打印后直接结束,避免进入当前仍是最小子集的 `sema/irgen`。 + +## 3. 文法设计 + +### 3.1 顶层结构 + +采用标准 SysY 编译单元形式: + +- `compUnit -> (decl | funcDef)+ EOF` +- 同时支持全局声明和函数定义 + +这样可以覆盖示例中的: + +- 全局变量/常量 +- 多函数程序 +- `main` 前定义辅助函数 + +### 3.2 声明 + +声明分为两类: + +- `constDecl` +- `varDecl` + +两者都支持: + +- 基本类型 `int` / `float` +- 多个定义项以逗号分隔 +- 数组维度 +- 标量初始化与花括号初始化列表 + +对应规则核心为: + +- `constDef : Ident ('[' constExp ']')* '=' constInitVal` +- `varDef : Ident ('[' constExp ']')* ('=' initVal)?` + +### 3.3 函数 + +函数定义支持: + +- 返回类型 `void/int/float` +- 形参列表 +- 数组形参 + +形参数组采用 SysY 常见形式: + +- 第一维可省略长度:`int a[]` +- 后续维度显式给出:`int a[][N]` + +### 3.4 语句 + +`stmt` 覆盖以下类型: + +- 赋值语句 +- 表达式语句/空语句 +- 复合语句 `block` +- `if/else` +- `while` +- `break` +- `continue` +- `return` + +这样能够覆盖测试用例中的: + +- 单行 `if` +- 带 `else` 的分支 +- 深层嵌套语句 +- 循环控制语句 + +### 3.5 表达式优先级 + +表达式分层采用自底向上的优先级结构: + +- `primary` +- `unaryExp` +- `mulExp` +- `addExp` +- `relExp` +- `eqExp` +- `lAndExp` +- `lOrExp` + +其中: + +- `exp` 保持为 `addExp`,与 SysY 中“普通表达式”和“条件表达式”分离的定义一致 +- `cond` 使用 `lOrExp` + +这样可以保证: + +- 算术表达式优先级正确 +- 比较与逻辑表达式能用于 `if` / `while` +- 函数实参仍符合 SysY 定义 + +### 3.6 左值与函数调用 + +通过: + +- `lVal : Ident ('[' exp ']')*` +- `unaryExp : primary | Ident '(' funcRParams? ')' | unaryOp unaryExp` + +支持: + +- 普通变量使用 +- 数组下标访问 +- 函数调用 +- 一元 `+ - !` + +### 3.7 数字字面量 + +词法层将整数和浮点数统一归为 `Number`,便于当前前端最小实现继续复用已有“数字常量”处理方式,同时在词法规则内覆盖: + +- 十进制整数 +- 八进制整数 +- 十六进制整数 +- 十进制浮点数 +- 十六进制浮点数 +- 指数形式 + +可解析的典型形式包括: + +- `0` +- `077` +- `0xff` +- `5.5` +- `03.1415926` +- `.33E+5` +- `1e-6` +- `0x1.921fb6p+1` +- `0x.AP-3` + +## 4. 语法树输出方案 + +语法树输出继续使用现有 `SyntaxTreePrinter.cpp`: + +- 非终结符输出为规则名 +- 终结符输出为 `TokenName: text` +- 使用树形 ASCII 缩进 + +本次不修改输出器,只保证文法规则名和 token 名能稳定反映 SysY 结构。 + +## 5. 与后续阶段的兼容策略 + +当前 `sema` 和 `irgen` 只支持极小子集。为避免 Lab1 被后续阶段阻塞,采用两层兼容策略: + +1. `main.cpp` 在“只输出语法树”时提前返回。 +2. 同时把 `sema/irgen` 的接口适配到新文法,使最小子集仍可编译通过。 + +这样既满足 Lab1,又不破坏当前工程的构建链路。 + +## 6. 验证方案 + +验证分两步: + +1. 使用代表性样例检查语法树结构。 +2. 批量遍历 `test/test_case/functional/*.sy` 与 `test/test_case/performance/*.sy`,执行 `./build/bin/compiler --emit-parse-tree`。 + +验证目标是: + +- 文法能接受测试目录中的 SysY 程序 +- 语法树可稳定输出 +- 工程可以重新生成 ANTLR 文件并成功编译 diff --git a/solution/RUN.md b/solution/RUN.md new file mode 100644 index 0000000..8a86738 --- /dev/null +++ b/solution/RUN.md @@ -0,0 +1,118 @@ +# Lab1 运行说明 + +## 1. 环境要求 + +建议环境中具备以下工具: + +- `java` +- `cmake` +- `g++` / `clang++` +- `make` 或 `ninja` + +可先检查: + +```bash +java -version +cmake --version +g++ --version +``` + +## 2. 手动生成 ANTLR 代码 + +在仓库根目录执行: + +```bash +mkdir -p build/generated/antlr4 +java -jar third_party/antlr-4.13.2-complete.jar \ + -Dlanguage=Cpp \ + -visitor -no-listener \ + -Xexact-output-dir \ + -o build/generated/antlr4 \ + src/antlr4/SysY.g4 +``` + +## 3. 手动配置与编译 + +在仓库根目录执行: + +```bash +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build -j "$(nproc)" +``` + +编译成功后,可执行文件位于: + +```bash +./build/bin/compiler +``` + +## 4. 单个样例运行 + +### 4.1 仅输出语法树 + +```bash +./build/bin/compiler --emit-parse-tree test/test_case/functional/simple_add.sy +``` + +### 4.2 验证最小 IR 仍可工作 + +```bash +./build/bin/compiler --emit-ir test/test_case/functional/simple_add.sy +``` + +## 5. 批量测试 + +我提供了一个批量测试脚本: + +```bash +./solution/run_lab1_batch.sh +``` + +该脚本会自动完成: + +1. 重新生成 `build/generated/antlr4` 下的 ANTLR 文件 +2. 执行 `cmake -S . -B build -DCMAKE_BUILD_TYPE=Release` +3. 执行 `cmake --build build -j "$(nproc)"` +4. 批量测试 `test/test_case/functional/*.sy` +5. 批量测试 `test/test_case/performance/*.sy` + +若某个用例失败,脚本会打印失败用例名并返回非零退出码。 + +## 6. 常用附加命令 + +### 6.1 查看帮助 + +```bash +./build/bin/compiler --help +``` + +### 6.2 指定单个样例文件 + +```bash +./build/bin/compiler --emit-parse-tree +``` + +### 6.3 重新从零开始构建 + +```bash +rm -rf build +mkdir -p build/generated/antlr4 +java -jar third_party/antlr-4.13.2-complete.jar \ + -Dlanguage=Cpp \ + -visitor -no-listener \ + -Xexact-output-dir \ + -o build/generated/antlr4 \ + src/antlr4/SysY.g4 +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build -j "$(nproc)" +``` + +## 7. 结果判定 + +Lab1 主要检查点是: + +- 合法 SysY 程序可以被 `SysY.g4` 成功解析 +- `--emit-parse-tree` 能输出语法树 +- `test/test_case` 下样例可以批量通过语法树模式 + +本项目当前实现中,Lab1 的重点是“语法分析与语法树构建”,不是完整语义分析和完整 IR/汇编支持。 diff --git a/solution/run_lab1_batch.sh b/solution/run_lab1_batch.sh new file mode 100755 index 0000000..e97e36d --- /dev/null +++ b/solution/run_lab1_batch.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +BUILD_DIR="$ROOT_DIR/build" +ANTLR_DIR="$BUILD_DIR/generated/antlr4" +JAR_PATH="$ROOT_DIR/third_party/antlr-4.13.2-complete.jar" +GRAMMAR_PATH="$ROOT_DIR/src/antlr4/SysY.g4" +COMPILER="$BUILD_DIR/bin/compiler" + +echo "[1/4] Generating ANTLR sources..." +mkdir -p "$ANTLR_DIR" +java -jar "$JAR_PATH" \ + -Dlanguage=Cpp \ + -visitor -no-listener \ + -Xexact-output-dir \ + -o "$ANTLR_DIR" \ + "$GRAMMAR_PATH" + +echo "[2/4] Configuring CMake..." +cmake -S "$ROOT_DIR" -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release + +echo "[3/4] Building project..." +cmake --build "$BUILD_DIR" -j "$(nproc)" + +echo "[4/4] Running parse-tree tests..." +failed=0 + +for case_file in "$ROOT_DIR"/test/test_case/functional/*.sy "$ROOT_DIR"/test/test_case/performance/*.sy; do + if ! "$COMPILER" --emit-parse-tree "$case_file" >/dev/null 2>/tmp/lab1_parse.err; then + echo "FAIL: $case_file" + cat /tmp/lab1_parse.err + failed=1 + else + echo "PASS: $case_file" + fi +done + +if [[ "$failed" -ne 0 ]]; then + echo "Batch test finished with failures." + exit 1 +fi + +echo "Batch test passed."