test(frontend): 完善 Lab1 批量验证脚本与反例测试

brkstar 3 weeks ago
parent dcaada11bc
commit 5756a2952c

@ -16,6 +16,9 @@
- `solution/Lab1-修改记录.md`
- `solution/RUN.md`
- `solution/run_lab1_batch.sh`
- `test/test_case/negative/missing_semicolon.sy`
- `test/test_case/negative/missing_rparen.sy`
- `test/test_case/negative/unexpected_else.sy`
## 2. 文法扩展
@ -116,9 +119,31 @@ cmake --build build -j 4
补充更新了 `solution/run_lab1_batch.sh`
- 默认使用 `COMPILER_PARSE_ONLY=ON` 进行 Lab1 构建
- 新增可选参数 `--save-tree`
- 启用后,会在仓库根目录下创建 `test_tree/`
- 并按照 `functional/`、`performance/` 的目录结构保存每个样例对应的语法树
- 增加正例总数、通过数、失败数统计
- 增加反例总数、通过数、失败数统计
- 增加失败样例列表打印,便于直接汇报“覆盖样例数 + 通过率”
## 9. 反例测试补充
新增目录:
- `test/test_case/negative`
新增负例样例:
- `missing_semicolon.sy`
- `missing_rparen.sy`
- `unexpected_else.sy`
这些反例用于证明:
- 合法程序可以成功解析
- 非法程序会触发 `parse` 错误
- 报错信息包含位置信息,便于定位问题
同时同步更新了:
@ -126,7 +151,7 @@ cmake --build build -j 4
- `solution/Lab1-设计方案.md`
- `solution/Lab1-修改记录.md`
## 9. 已知边界
## 10. 已知边界
当前提交完成的是 Lab1 所需的“语法分析与语法树构建”。以下能力仍属于后续实验范围:

@ -160,25 +160,30 @@
1. `main.cpp` 在“只输出语法树”时提前返回。
2. 同时把 `sema/irgen` 的接口适配到新文法,使最小子集仍可编译通过。
3. `solution/run_lab1_batch.sh` 默认使用 `COMPILER_PARSE_ONLY=ON` 配置 CMake确保批量验证只依赖前端解析与语法树打印。
这样既满足 Lab1又不破坏当前工程的构建链路。
## 6. 验证方案
验证分步:
验证分步:
1. 使用代表性样例检查语法树结构。
2. 批量遍历 `test/test_case/functional/*.sy``test/test_case/performance/*.sy`,执行 `./build/bin/compiler --emit-parse-tree`
3. 增加 `test/test_case/negative/*.sy` 反例,验证非法输入会触发 `parse` 错误。
另外补充一个批量自动化脚本 `solution/run_lab1_batch.sh`,用于统一执行:
- ANTLR 文件重新生成
- CMake 配置与编译
- 所有 `.sy` 用例的语法树回归
- `parse-only` 模式下的 CMake 配置与编译
- 所有正例 `.sy` 用例的语法树回归
- 所有反例 `.sy` 用例的错误回归
- 在需要时通过 `--save-tree` 将语法树保存到 `test_tree/`
- 输出正例/反例/总体统计信息与失败列表
验证目标是:
- 文法能接受测试目录中的 SysY 程序
- 语法树可稳定输出
- 非法输入能稳定报出 `parse` 错误
- 工程可以重新生成 ANTLR 文件并成功编译

@ -68,6 +68,14 @@ cmake --build build -j "$(nproc)"
./solution/run_lab1_batch.sh
```
该脚本默认使用 **parse-only 构建模式**
```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=ON
```
这样即使 `sem` / `irgen` / `mir` 还没有完成Lab1 的语法树验证也不会被后续实验模块阻塞。
如果希望在批量测试时把每个样例的语法树保存到 `test_tree/` 目录,可以加可选项:
```bash
@ -77,38 +85,66 @@ cmake --build build -j "$(nproc)"
该脚本会自动完成:
1. 重新生成 `build/generated/antlr4` 下的 ANTLR 文件
2. 执行 `cmake -S . -B build -DCMAKE_BUILD_TYPE=Release`
2. 执行 `cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=ON`
3. 执行 `cmake --build build -j "$(nproc)"`
4. 批量测试 `test/test_case/functional/*.sy`
5. 批量测试 `test/test_case/performance/*.sy`
6. 批量测试 `test/test_case/negative/*.sy`,确认非法输入会触发 `parse` 报错
若使用 `--save-tree`,还会额外:
6. 在仓库根目录下创建 `test_tree/`
7. 将语法树按测试集目录结构保存,例如:
7. 在仓库根目录下创建 `test_tree/`
8. 将语法树按测试集目录结构保存,例如:
```bash
test_tree/functional/simple_add.tree
test_tree/performance/fft0.tree
```
脚本结束时会输出:
- 正例总数 / 通过数 / 失败数
- 反例总数 / 通过数 / 失败数
- 总覆盖样例数与整体通过情况
- 失败样例列表
若某个用例失败,脚本会打印失败用例名并返回非零退出码。
## 6. 常用附加命令
## 6. 反例测试说明
新增了负例目录:
```bash
test/test_case/negative
```
当前提供了 3 个非法样例:
- `missing_semicolon.sy`
- `missing_rparen.sy`
- `unexpected_else.sy`
这些样例用于验证:
- 合法输入能够成功输出语法树
- 非法输入能够触发 `parse` 报错
- 报错信息带有位置,便于定位问题
## 7. 常用附加命令
### 6.1 查看帮助
### 7.1 查看帮助
```bash
./build/bin/compiler --help
```
### 6.2 指定单个样例文件
### 7.2 指定单个样例文件
```bash
./build/bin/compiler --emit-parse-tree <your_case.sy>
```
### 6.3 重新从零开始构建
### 7.3 重新从零开始构建
```bash
rm -rf build
@ -123,12 +159,13 @@ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j "$(nproc)"
```
## 7. 结果判定
## 8. 结果判定
Lab1 主要检查点是:
- 合法 SysY 程序可以被 `SysY.g4` 成功解析
- `--emit-parse-tree` 能输出语法树
- `test/test_case` 下样例可以批量通过语法树模式
- `test/test_case` 下正例可以批量通过语法树模式
- `test/test_case/negative` 下反例会稳定触发 `parse` 报错
本项目当前实现中Lab1 的重点是“语法分析与语法树构建”,不是完整语义分析和完整 IR/汇编支持。

@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
shopt -s nullglob
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
BUILD_DIR="$ROOT_DIR/build"
@ -10,6 +11,38 @@ GRAMMAR_PATH="$ROOT_DIR/src/antlr4/SysY.g4"
COMPILER="$BUILD_DIR/bin/compiler"
SAVE_TREE=false
TREE_DIR="$ROOT_DIR/test_tree"
POSITIVE_CASES=(
"$ROOT_DIR"/test/test_case/functional/*.sy
"$ROOT_DIR"/test/test_case/performance/*.sy
)
NEGATIVE_CASES=(
"$ROOT_DIR"/test/test_case/negative/*.sy
)
positive_total=0
positive_passed=0
positive_failed=0
negative_total=0
negative_passed=0
negative_failed=0
failed_cases=()
print_summary() {
local total passed failed
total=$((positive_total + negative_total))
passed=$((positive_passed + negative_passed))
failed=$((positive_failed + negative_failed))
echo
echo "Summary:"
echo " Positive cases: total=$positive_total, passed=$positive_passed, failed=$positive_failed"
echo " Negative cases: total=$negative_total, passed=$negative_passed, failed=$negative_failed"
echo " Overall: total=$total, passed=$passed, failed=$failed"
if (( ${#failed_cases[@]} > 0 )); then
echo "Failed cases:"
printf ' - %s\n' "${failed_cases[@]}"
fi
}
while [[ $# -gt 0 ]]; do
case "$1" in
@ -35,20 +68,20 @@ java -jar "$JAR_PATH" \
"$GRAMMAR_PATH"
echo "[2/4] Configuring CMake..."
cmake -S "$ROOT_DIR" -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release
cmake -S "$ROOT_DIR" -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=ON
echo "[3/4] Building project..."
cmake --build "$BUILD_DIR" -j "$(nproc)"
echo "[4/4] Running parse-tree tests..."
failed=0
echo "[4/4] Running parse-tree tests in parse-only mode..."
if [[ "$SAVE_TREE" == true ]]; then
rm -rf "$TREE_DIR"
mkdir -p "$TREE_DIR"
fi
for case_file in "$ROOT_DIR"/test/test_case/functional/*.sy "$ROOT_DIR"/test/test_case/performance/*.sy; do
for case_file in "${POSITIVE_CASES[@]}"; do
((positive_total += 1))
if [[ "$SAVE_TREE" == true ]]; then
rel_path="${case_file#"$ROOT_DIR"/test/test_case/}"
rel_dir="$(dirname "$rel_path")"
@ -60,22 +93,51 @@ for case_file in "$ROOT_DIR"/test/test_case/functional/*.sy "$ROOT_DIR"/test/tes
echo "FAIL: $case_file"
cat /tmp/lab1_parse.err
rm -f "$out_file"
failed=1
((positive_failed += 1))
failed_cases+=("$case_file")
else
echo "PASS: $case_file -> $out_file"
((positive_passed += 1))
fi
else
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
((positive_failed += 1))
failed_cases+=("$case_file")
else
echo "PASS: $case_file"
((positive_passed += 1))
fi
fi
done
if [[ "$failed" -ne 0 ]]; then
if (( ${#NEGATIVE_CASES[@]} > 0 )); then
echo
echo "Running negative parse tests..."
for case_file in "${NEGATIVE_CASES[@]}"; do
((negative_total += 1))
if "$COMPILER" --emit-parse-tree "$case_file" >/tmp/lab1_negative.out 2>/tmp/lab1_negative.err; then
echo "FAIL: $case_file (expected parse failure, but parsing succeeded)"
((negative_failed += 1))
failed_cases+=("$case_file")
else
if grep -q '^\[error\] \[parse\]' /tmp/lab1_negative.err; then
echo "PASS: $case_file -> expected parse error"
((negative_passed += 1))
else
echo "FAIL: $case_file (did not report parse error as expected)"
cat /tmp/lab1_negative.err
((negative_failed += 1))
failed_cases+=("$case_file")
fi
fi
done
fi
print_summary
if (( positive_failed + negative_failed > 0 )); then
echo "Batch test finished with failures."
exit 1
fi

@ -0,0 +1,3 @@
int main( {
return 0;
}

@ -0,0 +1,4 @@
int main() {
int a = 1
return a;
}

@ -0,0 +1,3 @@
int main() {
else return 0;
}
Loading…
Cancel
Save