jing 1 week ago
commit 7f1b0aaead

@ -1,95 +0,0 @@
---
name: git-submit
description: 执行 Git 提交和推送工作流
---
# Git 提交/推送工作流Codex CLI
当用户明确要求提交/推送时使用。
## 操作流程
### 1检查当前状态不需要用户确认
- 查看分支/干净程度:`git status -sb`
- 查看变更概要:`git diff --stat`
如发现明显问题,优先在提交前处理:
- 误把生成物/大文件加入:先移除或补 `.gitignore`
- 变更跨度过大:提醒用户是否需要拆分提交
### 2起草提交信息中文默认不需要用户确认
根据对话历史和修改的文件信息,按下方规范直接生成提交信息并继续执行后续流程。
- 如果用户已经明确给出提交信息,直接使用用户提供的版本
- 如果用户没有指定提交信息,按下方规范自动生成最合适的一条
- **不要**为了 commit message 再额外等待用户确认,除非用户明确要求先看 message 再提交
#### Git Commit Message 规范
##### 1格式
```text
<type>(<scope>): <subject>
```
说明:
- `<type>`:提交类型(必填)
- `<scope>`:影响范围/模块(必填)
- `<subject>`:一句话说明“做了什么”(必填)
---
##### 2type 列表
统一使用小写:
| type | 含义 |
|---|---|
| `feat` | 新功能 |
| `fix` | 修复 bug |
| `docs` | 文档变更 |
| `style` | 仅格式/风格(不改语义) |
| `refactor` | 重构(不改变外部行为) |
| `perf` | 性能优化 |
| `test` | 测试相关 |
| `build` | 构建系统 |
| `ci` | CI 相关 |
| `chore` | 杂项维护 |
| `revert` | 回滚提交 |
---
##### 3scope 列表
建议从以下范围中选择(保持一致即可):
| scope | 含义 |
|---|---|
| `frontend` | 前端ANTLR 驱动、AST 构建入口等) |
| `ast` | AST 相关 |
| `sema` | 语义分析(符号表、常量求值等) |
| `ir` | IR 核心结构 |
| `irgen` | AST → IR 生成 |
| `mir` | Machine IR指令选择、寄存器分配、栈帧等 |
| `backend` | 后端目标相关(如需要可细化 `aarch64` |
| `antlr` | 语法文件/ANTLR 相关 |
| `build` | 构建配置 |
| `test` | 测试 |
| `doc` | 文档 |
| `misc` | 其他无法归类但需要说明的范围 |
提交信息示例:
```text
refactor(irgen): 简化 AST → IR 构建流程
```
### 3后续流程均不需要用户确认
- 暂存所有改动:`git add -A`
- 单行摘要:`git commit -m "<type>(<scope>): <subject>"`,需要补充说明时用多行:`git commit -m "<summary>" -m "<detail 1>" -m "<detail 2>"`
- 推送:`git push`,推送完成后立即停止,不要再运行其他命令

@ -1,126 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
msg_file="${1:-}"
if [[ -z "${msg_file}" || ! -f "${msg_file}" ]]; then
echo "错误未找到提交信息文件commit-msg hook 未收到有效参数)。" >&2
exit 1
fi
# 读取第一条“有效提交信息”(跳过空行与注释行),并兼容 CRLF。
first_line="$(
awk '
{
sub(/\r$/, "")
if ($0 ~ /^[[:space:]]*#/) next
if ($0 ~ /^[[:space:]]*$/) next
print
exit
}
' "${msg_file}"
)"
allowed_types=(
feat fix docs style refactor perf test build ci chore revert
)
allowed_scopes=(
frontend ast sema ir irgen mir backend antlr build test doc dev
)
print_allowed() {
local IFS="|"
echo "允许的 type${allowed_types[*]}" >&2
echo "允许的 scope${allowed_scopes[*]}" >&2
}
fail() {
local reason="$1"
local extra="${2:-}"
echo "错误:提交信息不符合规范:${reason}" >&2
echo "" >&2
echo "当前提交信息:" >&2
echo " ${first_line}" >&2
echo "" >&2
echo "规范格式:" >&2
echo " <type>(<scope>): <subject>" >&2
echo "" >&2
echo "注意:" >&2
echo " 1) 冒号必须是英文 ':'(不是中文 '')。" >&2
echo " 2) 冒号后必须严格跟 1 个空格(': ')。" >&2
echo "" >&2
print_allowed
echo "" >&2
echo "示例:" >&2
echo " feat(irgen): add constant folding" >&2
echo " fix(sema): handle null symbol" >&2
echo " docs(doc): update build instructions" >&2
if [[ -n "${extra}" ]]; then
echo "" >&2
echo "${extra}" >&2
fi
echo "" >&2
echo "参考文档doc/Git Commit Message 规范.md" >&2
exit 1
}
if [[ -z "${first_line}" ]]; then
fail "提交信息为空(第一行不能为空)。"
fi
# 允许 merge 提交信息git merge 默认生成的 message
if [[ "${first_line}" == "Merge "* ]]; then
exit 0
fi
# 允许 fixup!/squash!(用于 rebase --autosquash 等流程)。
if [[ "${first_line}" == "fixup!"* || "${first_line}" == "squash!"* ]]; then
exit 0
fi
# 先做一些常见错误的定向提示。
if [[ "${first_line}" == *""* ]]; then
fail "检测到中文冒号 ''。" "请把中文冒号替换为英文冒号 ':',并确保冒号后有且仅有 1 个空格。"
fi
if [[ "${first_line}" =~ ^[A-Za-z]+\([A-Za-z]+\):[^[:space:]].*$ ]]; then
fail "冒号后缺少空格。" "正确写法应为 ': '(英文冒号 + 1 个空格)。"
fi
if [[ "${first_line}" =~ ^[A-Za-z]+\([A-Za-z]+\):[[:space:]]{2,}.*$ ]]; then
fail "冒号后空格数量不正确(多于 1 个空格)。" "请确保冒号后严格是 1 个空格(': ')。"
fi
type=""
scope=""
subject=""
if [[ "${first_line}" =~ ^([A-Za-z]+)\(([A-Za-z]+)\):\ (.+)$ ]]; then
type="${BASH_REMATCH[1]}"
scope="${BASH_REMATCH[2]}"
subject="${BASH_REMATCH[3]}"
else
fail "格式不正确。" "请按 '<type>(<scope>): <subject>' 格式填写。"
fi
if [[ "${subject}" =~ ^[[:space:]] ]]; then
fail "冒号后的空格数量不正确(多于 1 个空格)。" "请确保冒号后严格是 1 个空格(': ')。"
fi
case "${type}" in
feat | fix | docs | style | refactor | perf | test | build | ci | chore | revert) ;;
*)
fail "type 不合法:${type}" "请使用允许的 type小写。"
;;
esac
case "${scope}" in
frontend | ast | sema | ir | irgen | mir | backend | antlr | build | test | doc | dev) ;;
*)
fail "scope 不合法:${scope}" "请使用允许的 scope严格限制。"
;;
esac
exit 0

@ -33,7 +33,7 @@ sudo apt install -y build-essential cmake git openjdk-11-jre
### 2.3 安装 LLVM 工具链
`scripts/verify_ir_with_llvm.sh` 在 `--run` 模式下会调用 LLVM 工具链(`llc` 与 `clang`)将生成的 IR 编译并运行。
`scripts/verify_ir.sh` 在 `--run` 模式下会调用 LLVM 工具链(`llc` 与 `clang`)将生成的 IR 编译并运行。
```bash
sudo apt update
@ -91,7 +91,7 @@ cmake --build build -j "$(nproc)"
跑完整编译流程自检:从 SysY 源码生成 AArch64 汇编,完成汇编、链接,并在 QEMU 下运行结果程序:
```bash
./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run
./scripts/verify_asm.sh test/test_case/simple_add.sy test/test_result/asm --run
```
如果最终看到 `退出码: 3`,说明当前最小子集示例 `return a + b` 的完整链路已经跑通。

@ -85,5 +85,5 @@ cmake --build build -j "$(nproc)"
```bash
./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy out/ir --run
./scripts/verify_ir.sh test/test_case/simple_add.sy test/test_result/ir --run
```

@ -49,7 +49,7 @@ Lab3 的目标是在该示例基础上扩展后端语义覆盖范围,逐步把
2. 视实现需要可能修改
- `src/main.cpp`(当需要调整输出阶段行为时)
- `src/utils/CLI.cpp`(当需要扩展后端相关命令行选项时)
- `scripts/verify_asm_with_qemu.sh`(当需要扩展统一验证脚本时)
- `scripts/verify_asm.sh`(当需要扩展统一验证脚本时)
## 5. 当前最小示例实现说明
@ -82,7 +82,7 @@ cmake --build build -j "$(nproc)"
推荐使用统一脚本验证 “源码 -> 汇编 -> 可执行程序” 整体链路,用于验证后端代码生成的正确性:
```bash
./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run
./scripts/verify_asm.sh test/test_case/simple_add.sy test/test_result/asm --run
```
若最终输出 `退出码: 3`,说明当前最小子集示例 `return a + b` 的完整后端链路已经跑通。

@ -61,7 +61,7 @@ Lab4 的目标是在 Lab3 示例基础上,把“固定寄存器 + 栈槽”的
- `src/mir/MIRBasicBlock.cpp`(当需要扩展基本块级活跃性或辅助接口时)
- `src/main.cpp`(当需要调整后端阶段行为时)
- `src/utils/CLI.cpp`(当需要扩展后端调试相关命令行选项时)
- `scripts/verify_asm_with_qemu.sh`(当需要扩展统一验证脚本时)
- `scripts/verify_asm.sh`(当需要扩展统一验证脚本时)
## 5. 当前最小示例实现说明
@ -138,7 +138,7 @@ cmake --build build -j "$(nproc)"
推荐继续使用统一脚本验证 “源码 -> 汇编 -> 可执行程序” 整体链路,用于做最小回归:
```bash
./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run
./scripts/verify_asm.sh test/test_case/simple_add.sy test/test_result/asm --run
```
建议在功能回归之外,再观察优化前后汇编输出差异。可按自己的实现方式保留调试日志、优化开关,或直接对比生成的汇编文本,重点关注:

@ -210,8 +210,8 @@ cmake --build build -j "$(nproc)"
### 8.2 语义回归
```bash
./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy out/ir --run
./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run
./scripts/verify_ir.sh test/test_case/simple_add.sy test/test_result/ir --run
./scripts/verify_asm.sh test/test_case/simple_add.sy test/test_result/asm --run
```
目标:优化后程序行为与优化前保持一致。

@ -157,8 +157,8 @@ cmake --build build -j "$(nproc)"
### 9.1 功能回归
```bash
./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy out/ir --run
./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run
./scripts/verify_ir.sh test/test_case/simple_add.sy test/test_result/ir --run
./scripts/verify_asm.sh test/test_case/simple_add.sy test/test_result/asm --run
```
### 9.2 优化效果对比(示例)
@ -170,5 +170,3 @@ cmake --build build -j "$(nproc)"
```
---

Binary file not shown.

@ -1,30 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [[ -z "${repo_root}" ]]; then
echo "错误:当前目录不是 Git 仓库,无法配置 hooks。" >&2
exit 1
fi
cd "${repo_root}"
hooks_dir=".githooks"
if [[ ! -d "${hooks_dir}" ]]; then
echo "错误:未找到 ${hooks_dir} 目录,请确认仓库已包含 hooks 文件。" >&2
exit 1
fi
git config core.hooksPath "${hooks_dir}"
echo "已启用本仓库 Git hooks" >&2
echo " core.hooksPath=${hooks_dir}" >&2
echo "" >&2
echo "现在执行 git commit 时会校验提交信息是否符合:" >&2
echo " doc/Git Commit Message 规范.md" >&2
echo "" >&2
echo "查看当前配置:" >&2
echo " git config --get core.hooksPath" >&2
echo "" >&2
echo "如需取消(恢复默认 .git/hooks " >&2
echo " git config --unset core.hooksPath" >&2

@ -8,7 +8,7 @@ if [[ $# -lt 1 || $# -gt 3 ]]; then
fi
input=$1
out_dir="out/asm"
out_dir="test/test_result/asm"
run_exec=false
shift
@ -44,7 +44,7 @@ mkdir -p "$out_dir"
base=$(basename "$input")
stem=${base%.sy}
asm_file="$out_dir/$stem.s"
exe="$out_dir/$stem.exe"
exe="$out_dir/$stem"
"$compiler" --emit-asm "$input" > "$asm_file"
echo "汇编已生成: $asm_file"

@ -1,5 +1,5 @@
#!/usr/bin/env bash
# ./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy --run
# ./scripts/verify_ir.sh test/test_case/simple_add.sy --run
set -euo pipefail
@ -9,7 +9,7 @@ if [[ $# -lt 1 || $# -gt 3 ]]; then
fi
input=$1
out_dir="out/ir"
out_dir="test/test_result/ir"
run_exec=false
shift
@ -53,7 +53,7 @@ if [[ "$run_exec" == true ]]; then
exit 1
fi
obj="$out_dir/$stem.o"
exe="$out_dir/$stem.exe"
exe="$out_dir/$stem"
llc -filetype=obj "$out_file" -o "$obj"
clang "$obj" -o "$exe"
echo "运行 $exe ..."

@ -9,20 +9,17 @@
#include "SysYLexer.h"
#include "SysYParser.h"
#include "antlr4-runtime.h"
#include "utils/Log.h"
namespace {
bool HasParsePrefix(const std::string& msg) {
return msg.rfind("[parse]", 0) == 0;
}
class ParseErrorListener : public antlr4::BaseErrorListener {
public:
void syntaxError(antlr4::Recognizer* /*recognizer*/, antlr4::Token* /*offendingSymbol*/,
size_t line, size_t charPositionInLine,
const std::string& msg, std::exception_ptr /*e*/) override {
throw std::runtime_error("[parse] 暂不支持的语法/词法 @" + std::to_string(line) + ":" +
std::to_string(charPositionInLine) + " - " + msg);
throw std::runtime_error(FormatErrorAt("parse", line, charPositionInLine,
"暂不支持的语法/词法 - " + msg));
}
};
@ -31,7 +28,7 @@ class ParseErrorListener : public antlr4::BaseErrorListener {
AntlrResult ParseFileWithAntlr(const std::string& path) {
std::ifstream fin(path);
if (!fin.is_open()) {
throw std::runtime_error("[parse] 无法打开输入文件: " + path);
throw std::runtime_error(FormatError("parse", "无法打开输入文件: " + path));
}
std::ostringstream ss;
ss << fin.rdbuf();
@ -53,18 +50,18 @@ AntlrResult ParseFileWithAntlr(const std::string& path) {
} catch (const std::exception& ex) {
const std::string msg = ex.what();
if (!msg.empty()) {
if (HasParsePrefix(msg)) {
if (HasErrorPrefix(msg, "parse")) {
throw;
}
throw std::runtime_error("[parse] 暂不支持的语法/词法 - " + msg);
throw std::runtime_error(
FormatError("parse", "暂不支持的语法/词法 - " + msg));
}
if (auto* tok = parser->getCurrentToken()) {
throw std::runtime_error("[parse] 暂不支持的语法/词法 @" +
std::to_string(tok->getLine()) + ":" +
std::to_string(tok->getCharPositionInLine()) +
" near token '" + tok->getText() + "'");
throw std::runtime_error(
FormatErrorAt("parse", tok->getLine(), tok->getCharPositionInLine(),
"暂不支持的语法/词法 near token '" + tok->getText() + "'"));
}
throw std::runtime_error("[parse] 暂不支持的语法/词法");
throw std::runtime_error(FormatError("parse", "暂不支持的语法/词法"));
}
AntlrResult result;

@ -4,6 +4,7 @@
#include "SysYParser.h"
#include "ir/IR.h"
#include "utils/Log.h"
void IRGenImpl::GenBlock(SysYParser::BlockContext& block) {
for (auto* item : block.blockItem()) {
@ -24,7 +25,7 @@ bool IRGenImpl::GenBlockItem(SysYParser::BlockItemContext& item) {
if (item.stmt()) {
return GenStmt(*item.stmt());
}
throw std::runtime_error("[irgen] 暂不支持的 blockItem 类型");
throw std::runtime_error(FormatError("irgen", "暂不支持的语句或声明"));
}
void IRGenImpl::GenDecl(SysYParser::DeclContext& decl) {
@ -32,12 +33,12 @@ void IRGenImpl::GenDecl(SysYParser::DeclContext& decl) {
GenVarDecl(*decl.varDecl());
return;
}
throw std::runtime_error("[irgen] 暂不支持的声明类型");
throw std::runtime_error(FormatError("irgen", "暂不支持的声明类型"));
}
void IRGenImpl::GenVarDecl(SysYParser::VarDeclContext& decl) {
if (storage_map_.find(&decl) != storage_map_.end()) {
throw std::runtime_error("[irgen] 声明重复生成存储槽位");
throw std::runtime_error(FormatError("irgen", "声明重复生成存储槽位"));
}
auto* slot = builder_.CreateAllocaI32(module_.context().NextTemp());
storage_map_[&decl] = slot;

@ -1,10 +1,10 @@
#include "irgen/IRGen.h"
#include <memory>
#include <stdexcept>
#include "SysYParser.h"
#include "ir/IR.h"
#include "utils/Log.h"
std::unique_ptr<ir::Module> GenerateIR(SysYParser::CompUnitContext& tree,
const SemanticContext& sema) {

@ -4,10 +4,11 @@
#include "SysYParser.h"
#include "ir/IR.h"
#include "utils/Log.h"
ir::Value* IRGenImpl::GenExpr(SysYParser::ExpContext& expr) {
if (!expr.addExp()) {
throw std::runtime_error("[irgen] 非法表达式");
throw std::runtime_error(FormatError("irgen", "非法表达式"));
}
return GenAddExpr(*expr.addExp());
}
@ -16,7 +17,7 @@ ir::Value* IRGenImpl::GenAddExpr(SysYParser::AddExpContext& add) {
// 当前表达式层次仍是最小实现,直接贴合 addExp -> primary 的语法形状。
const auto& terms = add.primary();
if (terms.empty()) {
throw std::runtime_error("[irgen] 空加法表达式");
throw std::runtime_error(FormatError("irgen", "空加法表达式"));
}
ir::Value* acc = GenPrimary(*terms[0]);
@ -35,18 +36,20 @@ ir::Value* IRGenImpl::GenPrimary(SysYParser::PrimaryContext& primary) {
if (primary.Ident()) {
auto* decl = sema_.ResolveVarUse(&primary);
if (!decl) {
throw std::runtime_error("[irgen] 变量使用缺少语义绑定: " +
primary.Ident()->getText());
throw std::runtime_error(
FormatError("irgen",
"变量使用缺少语义绑定: " + primary.Ident()->getText()));
}
auto it = storage_map_.find(decl);
if (it == storage_map_.end()) {
throw std::runtime_error("[irgen] 变量声明缺少存储槽位: " +
primary.Ident()->getText());
throw std::runtime_error(
FormatError("irgen",
"变量声明缺少存储槽位: " + primary.Ident()->getText()));
}
return builder_.CreateLoad(it->second, module_.context().NextTemp());
}
if (primary.exp()) {
return GenExpr(*primary.exp());
}
throw std::runtime_error("[irgen] 暂不支持的 primary 形式");
throw std::runtime_error(FormatError("irgen", "暂不支持的表达式形式"));
}

@ -4,6 +4,7 @@
#include "SysYParser.h"
#include "ir/IR.h"
#include "utils/Log.h"
namespace {
@ -11,8 +12,9 @@ void VerifyFunctionStructure(const ir::Function& func) {
// 当前 IRGen 仍是单入口、顺序生成;这里在生成结束后补一层块终结校验。
for (const auto& bb : func.blocks()) {
if (!bb || !bb->HasTerminator()) {
throw std::runtime_error("[irgen] 基本块未正确终结: " +
(bb ? bb->name() : std::string("<null>")));
throw std::runtime_error(
FormatError("irgen", "基本块未正确终结: " +
(bb ? bb->name() : std::string("<null>"))));
}
}
}
@ -27,17 +29,17 @@ IRGenImpl::IRGenImpl(ir::Module& module, const SemanticContext& sema)
void IRGenImpl::Gen(SysYParser::CompUnitContext& cu) {
if (!cu.funcDef()) {
throw std::runtime_error("[irgen] 缺少 main 定义");
throw std::runtime_error(FormatError("irgen", "缺少 main 定义"));
}
GenFuncDef(*cu.funcDef());
}
void IRGenImpl::GenFuncDef(SysYParser::FuncDefContext& func) {
if (!func.block()) {
throw std::runtime_error("[irgen] 函数体为空");
throw std::runtime_error(FormatError("irgen", "函数体为空"));
}
if (!func.Ident()) {
throw std::runtime_error("[irgen] 缺少函数名");
throw std::runtime_error(FormatError("irgen", "缺少函数名"));
}
func_ = module_.CreateFunction(

@ -4,18 +4,19 @@
#include "SysYParser.h"
#include "ir/IR.h"
#include "utils/Log.h"
bool IRGenImpl::GenStmt(SysYParser::StmtContext& stmt) {
if (stmt.returnStmt()) {
GenReturnStmt(*stmt.returnStmt());
return true;
}
throw std::runtime_error("[irgen] 暂不支持的语句类型");
throw std::runtime_error(FormatError("irgen", "暂不支持的语句类型"));
}
void IRGenImpl::GenReturnStmt(SysYParser::ReturnStmtContext& ret) {
if (!ret.exp()) {
throw std::runtime_error("[irgen] return 缺少表达式");
throw std::runtime_error(FormatError("irgen", "return 缺少表达式"));
}
ir::Value* v = GenExpr(*ret.exp());
builder_.CreateRet(v);

@ -28,7 +28,7 @@ int main(int argc, char** argv) {
auto* comp_unit = dynamic_cast<SysYParser::CompUnitContext*>(antlr.tree);
if (!comp_unit) {
throw std::runtime_error("[main] 语法树根节点不是 compUnit");
throw std::runtime_error(FormatError("main", "语法树根节点不是 compUnit"));
}
auto sema = RunSema(*comp_unit);
@ -52,7 +52,7 @@ int main(int argc, char** argv) {
mir::PrintAsm(*machine_func, std::cout);
}
} catch (const std::exception& ex) {
std::cerr << "error: " << ex.what() << "\n";
PrintException(std::cerr, ex);
return 1;
}
return 0;

@ -3,6 +3,8 @@
#include <stdexcept>
#include <vector>
#include "utils/Log.h"
namespace mir {
namespace {
@ -17,8 +19,7 @@ void RunFrameLowering(MachineFunction& function) {
for (const auto& slot : function.frame_slots()) {
cursor += slot.size;
if (-cursor < -256) {
throw std::runtime_error(
"Lab3 MVP 后端暂不支持超过 64 个 i32 栈槽的函数");
throw std::runtime_error(FormatError("mir", "暂不支持过大的栈帧"));
}
}

@ -4,6 +4,7 @@
#include <unordered_map>
#include "ir/IR.h"
#include "utils/Log.h"
namespace mir {
namespace {
@ -20,8 +21,8 @@ void EmitValueToReg(const ir::Value* value, PhysReg target,
auto it = slots.find(value);
if (it == slots.end()) {
throw std::runtime_error("Lab3 MVP 后端找不到值对应的栈槽: " +
value->name());
throw std::runtime_error(
FormatError("mir", "找不到值对应的栈槽: " + value->name()));
}
block.Append(Opcode::LoadStack,
@ -41,7 +42,8 @@ void LowerInstruction(const ir::Instruction& inst, MachineFunction& function,
auto& store = static_cast<const ir::StoreInst&>(inst);
auto dst = slots.find(store.ptr());
if (dst == slots.end()) {
throw std::runtime_error("Lab3 MVP 后端要求 store 目标必须来自 alloca");
throw std::runtime_error(
FormatError("mir", "暂不支持对非栈变量地址进行写入"));
}
EmitValueToReg(store.value(), PhysReg::W8, slots, block);
block.Append(Opcode::StoreStack,
@ -52,7 +54,8 @@ void LowerInstruction(const ir::Instruction& inst, MachineFunction& function,
auto& load = static_cast<const ir::LoadInst&>(inst);
auto src = slots.find(load.ptr());
if (src == slots.end()) {
throw std::runtime_error("Lab3 MVP 后端要求 load 源必须来自 alloca");
throw std::runtime_error(
FormatError("mir", "暂不支持对非栈变量地址进行读取"));
}
int dst_slot = function.CreateFrameIndex();
block.Append(Opcode::LoadStack,
@ -83,10 +86,10 @@ void LowerInstruction(const ir::Instruction& inst, MachineFunction& function,
}
case ir::Opcode::Sub:
case ir::Opcode::Mul:
throw std::runtime_error("Lab3 MVP 后端暂不支持 add 以外的二元运算");
throw std::runtime_error(FormatError("mir", "暂不支持该二元运算"));
}
throw std::runtime_error("Lab3 MVP 后端遇到未知 IR 指令");
throw std::runtime_error(FormatError("mir", "暂不支持该 IR 指令"));
}
} // namespace
@ -95,19 +98,19 @@ std::unique_ptr<MachineFunction> LowerToMIR(const ir::Module& module) {
DefaultContext();
if (module.functions().size() != 1) {
throw std::runtime_error("Lab3 MVP 后端只支持单函数 main");
throw std::runtime_error(FormatError("mir", "暂不支持多个函数"));
}
const auto& func = *module.functions().front();
if (func.name() != "main") {
throw std::runtime_error("Lab3 MVP 后端只支持 main 函数");
throw std::runtime_error(FormatError("mir", "暂不支持非 main 函数"));
}
auto machine_func = std::make_unique<MachineFunction>(func.name());
ValueSlotMap slots;
const auto* entry = func.entry();
if (!entry) {
throw std::runtime_error("IR 函数缺少入口基本块");
throw std::runtime_error(FormatError("mir", "IR 函数缺少入口基本块"));
}
for (const auto& inst : entry->instructions()) {

@ -2,6 +2,8 @@
#include <stdexcept>
#include "utils/Log.h"
namespace mir {
namespace {
@ -24,7 +26,7 @@ void RunRegAlloc(MachineFunction& function) {
for (const auto& inst : function.entry().instructions()) {
for (const auto& operand : inst.operands()) {
if (operand.kind() == Operand::Kind::Reg && !IsAllowedReg(operand.reg())) {
throw std::runtime_error("Lab3 MVP 后端发现未预着色的寄存器");
throw std::runtime_error(FormatError("mir", "寄存器分配失败"));
}
}
}

@ -4,6 +4,7 @@
#include <string>
#include "sem/SymbolTable.h"
#include "utils/Log.h"
namespace {
@ -20,7 +21,7 @@ void CheckPrimary(SysYParser::PrimaryContext& primary,
const std::string name = primary.Ident()->getText();
auto* decl = table.Lookup(name);
if (!decl) {
throw std::runtime_error("[sema] 使用了未定义的变量: " + name);
throw std::runtime_error(FormatError("sema", "使用了未定义的变量: " + name));
}
sema.BindVarUse(&primary, decl);
return;
@ -31,13 +32,13 @@ void CheckPrimary(SysYParser::PrimaryContext& primary,
return;
}
throw std::runtime_error("[sema] 暂不支持的 primary 形式");
throw std::runtime_error(FormatError("sema", "暂不支持的表达式形式"));
}
void CheckExpr(SysYParser::ExpContext& exp, const SymbolTable& table,
SemanticContext& sema) {
if (!exp.addExp()) {
throw std::runtime_error("[sema] 非法表达式");
throw std::runtime_error(FormatError("sema", "非法表达式"));
}
const auto& terms = exp.addExp()->primary();
for (auto* term : terms) {
@ -50,10 +51,10 @@ void CheckExpr(SysYParser::ExpContext& exp, const SymbolTable& table,
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit) {
auto* func = comp_unit.funcDef();
if (!func || !func->block()) {
throw std::runtime_error("[sema] 缺少 main 函数定义");
throw std::runtime_error(FormatError("sema", "缺少 main 函数定义"));
}
if (!func->Ident() || func->Ident()->getText() != "main") {
throw std::runtime_error("[sema] 入口函数必须命名为 main");
throw std::runtime_error(FormatError("sema", "入口函数必须命名为 main"));
}
SymbolTable table;
@ -62,7 +63,8 @@ SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit) {
const auto& items = func->block()->blockItem();
if (items.empty()) {
throw std::runtime_error("[sema] main 函数不能为空,且必须以 return 结束");
throw std::runtime_error(
FormatError("sema", "main 函数不能为空,且必须以 return 结束"));
}
for (size_t i = 0; i < items.size(); ++i) {
@ -71,12 +73,13 @@ SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit) {
continue;
}
if (seen_return) {
throw std::runtime_error("[sema] return 必须是 main 函数中的最后一条语句");
throw std::runtime_error(
FormatError("sema", "return 必须是 main 函数中的最后一条语句"));
}
if (auto* decl = item->decl() ? item->decl()->varDecl() : nullptr) {
const std::string name = decl->Ident()->getText();
if (table.Contains(name)) {
throw std::runtime_error("[sema] 重复定义变量: " + name);
throw std::runtime_error(FormatError("sema", "重复定义变量: " + name));
}
if (decl->exp()) {
CheckExpr(*decl->exp(), table, sema);
@ -89,15 +92,16 @@ SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit) {
CheckExpr(*ret->exp(), table, sema);
seen_return = true;
if (i + 1 != items.size()) {
throw std::runtime_error("[sema] return 必须是 main 函数中的最后一条语句");
throw std::runtime_error(
FormatError("sema", "return 必须是 main 函数中的最后一条语句"));
}
continue;
}
throw std::runtime_error("[sema] 暂不支持的 blockItem 类型");
throw std::runtime_error(FormatError("sema", "暂不支持的语句或声明"));
}
if (!seen_return) {
throw std::runtime_error("[sema] main 函数必须包含 return 语句");
throw std::runtime_error(FormatError("sema", "main 函数必须包含 return 语句"));
}
return sema;

@ -5,17 +5,20 @@
#include "utils/CLI.h"
#include <cstring>
#include <stdexcept>
#include <string>
#include <cstring>
#include "utils/Log.h"
CLIOptions ParseCLI(int argc, char** argv) {
CLIOptions opt;
bool explicit_emit = false;
if (argc <= 1) {
throw std::runtime_error(
"用法: compiler [--help] [--emit-parse-tree] [--emit-ir] [--emit-asm] <input.sy>");
throw std::runtime_error(FormatError(
"cli",
"用法: compiler [--help] [--emit-parse-tree] [--emit-ir] [--emit-asm] <input.sy>"));
}
for (int i = 1; i < argc; ++i) {
@ -59,23 +62,25 @@ CLIOptions ParseCLI(int argc, char** argv) {
}
if (arg[0] == '-') {
throw std::runtime_error(std::string("未知参数: ") + arg +
"(使用 --help 查看用法)");
throw std::runtime_error(
FormatError("cli", std::string("未知参数: ") + arg +
"(使用 --help 查看用法)"));
}
if (!opt.input.empty()) {
throw std::runtime_error(
"参数过多:当前只支持 1 个输入文件(使用 --help 查看用法)");
throw std::runtime_error(FormatError(
"cli", "参数过多:当前只支持 1 个输入文件(使用 --help 查看用法)"));
}
opt.input = arg;
}
if (opt.input.empty() && !opt.show_help) {
throw std::runtime_error("缺少输入文件:请提供 <input.sy>(使用 --help 查看用法)");
throw std::runtime_error(
FormatError("cli", "缺少输入文件:请提供 <input.sy>(使用 --help 查看用法)"));
}
if (!opt.emit_parse_tree && !opt.emit_ir && !opt.emit_asm) {
throw std::runtime_error(
"未选择任何输出:请使用 --emit-parse-tree / --emit-ir / --emit-asm");
throw std::runtime_error(FormatError(
"cli", "未选择任何输出:请使用 --emit-parse-tree / --emit-ir / --emit-asm"));
}
return opt;
}

@ -5,6 +5,48 @@
#include "utils/Log.h"
#include <ostream>
#include <string>
namespace {
bool IsCLIError(const std::string_view msg) {
return HasErrorPrefix(msg, "cli");
}
} // namespace
void LogInfo(const std::string_view msg, std::ostream& os) {
os << "[info] " << msg << "\n";
}
void LogError(const std::string_view msg, std::ostream& os) {
os << "[error] " << msg << "\n";
}
std::string FormatError(const std::string_view stage,
const std::string_view msg) {
return "[" + std::string(stage) + "] " + std::string(msg);
}
std::string FormatErrorAt(const std::string_view stage, const std::size_t line,
const std::size_t column,
const std::string_view msg) {
return "[" + std::string(stage) + "] @" + std::to_string(line) + ":" +
std::to_string(column) + " - " + std::string(msg);
}
bool HasErrorPrefix(const std::string_view msg, const std::string_view stage) {
const std::string prefix = "[" + std::string(stage) + "]";
return msg.rfind(prefix, 0) == 0;
}
void PrintException(std::ostream& os, const std::exception& ex) {
LogError(ex.what(), os);
if (IsCLIError(ex.what())) {
os << "\n";
PrintHelp(os);
}
}
void PrintHelp(std::ostream& os) {
os << "SysY Compiler (课程实验最小可运行示例)\n"

@ -1,11 +1,20 @@
// 轻量日志接口。
#pragma once
#include <cstddef>
#include <exception>
#include <iosfwd>
#include <iostream>
#include <string>
#include <string_view>
#define LOG_INFO(msg) std::cerr << "[info] " << msg << "\n"
#define LOG_ERROR(msg) std::cerr << "[error] " << msg << "\n"
void LogInfo(std::string_view msg, std::ostream& os);
void LogError(std::string_view msg, std::ostream& os);
std::string FormatError(std::string_view stage, std::string_view msg);
std::string FormatErrorAt(std::string_view stage, std::size_t line,
std::size_t column, std::string_view msg);
bool HasErrorPrefix(std::string_view msg, std::string_view stage);
void PrintException(std::ostream& os, const std::exception& ex);
// 打印命令行帮助信息(用于 `compiler --help`)。
void PrintHelp(std::ostream& os);

Loading…
Cancel
Save