docs(solution): 补充 Lab1 设计说明、修改记录与运行脚本

brkstar 3 weeks ago
parent c2759c27cd
commit 876ba72328

@ -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 生成
- 浮点/数组/控制流的中间表示支持
- 更完整的函数/作用域/类型系统检查

@ -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 文件并成功编译

@ -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 <your_case.sy>
```
### 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/汇编支持。

@ -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."
Loading…
Cancel
Save