fix(frontend): 修复部分实现
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# SysY 编译器课程实验(C++)
|
# SysY 编译器课程实验(C++)
|
||||||
|
|
||||||
本仓库为“并行编译课程实验”提供一个 SysY 编译器的最小可运行示例,实验按 Lab1–Lab6 逐步完成:
|
本仓库为“并行编译课程实验”提供一个 SysY 编译器的最小可运行示例,实验按 Lab1–Lab6 逐步完成:
|
||||||
从前端(ANTLR 词法/语法分析与 AST 构建)到中端(IR 生成与优化),再到后端(ARM64/AArch64 汇编生成与寄存器分配),最后进行循环/并行相关优化。
|
从前端(词法/语法分析与语法树处理)到中端(IR 生成与优化),再到后端(ARM64/AArch64 汇编生成与寄存器分配),最后进行循环/并行相关优化。
|
||||||
|
|
||||||
## 1. 实验介绍
|
## 1. 实验介绍
|
||||||
|
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
| 实验 | 名称 | 任务/目标 |
|
| 实验 | 名称 | 任务/目标 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Lab1 | 语法树构建 | 基于 SysY 源程序完成语法分析与 AST 构建,并按约定输出 AST(JSON 形式) |
|
| Lab1 | 语法树构建 | 基于 SysY 源程序完成语法分析与语法树构建,并按约定输出语法树 |
|
||||||
| Lab2 | 中间表示生成 | 将 AST 翻译为 LLVM 风格的中间表示(IR),并输出 IR |
|
| Lab2 | 中间表示生成 | 将语法树翻译为 LLVM 风格的中间表示(IR),并输出 IR |
|
||||||
| Lab3 | 指令选择与汇编生成 | 将 IR 翻译为目标平台汇编代码(本项目以 ARM64/AArch64 为主) |
|
| Lab3 | 指令选择与汇编生成 | 将 IR 翻译为目标平台汇编代码(本项目以 ARM64/AArch64 为主) |
|
||||||
| Lab4 | 寄存器分配 | 为后端生成的虚拟寄存器分配物理寄存器,完成 spill/reload 等必要处理 |
|
| Lab4 | 寄存器分配 | 为后端生成的虚拟寄存器分配物理寄存器,完成 spill/reload 等必要处理 |
|
||||||
| Lab5 | 基本标量优化 | 实现常见的标量优化(如常量传播、死代码删除、简化 CFG 等) |
|
| Lab5 | 基本标量优化 | 实现常见的标量优化(如常量传播、死代码删除、简化 CFG 等) |
|
||||||
|
|||||||
@@ -40,11 +40,10 @@
|
|||||||
|
|
||||||
| scope | 含义 |
|
| scope | 含义 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `frontend` | 前端(ANTLR 驱动、AST 构建入口等) |
|
| `frontend` | 前端(语法解析、语法树打印等) |
|
||||||
| `ast` | AST 相关 |
|
|
||||||
| `sema` | 语义分析(符号表、常量求值等) |
|
| `sema` | 语义分析(符号表、常量求值等) |
|
||||||
| `ir` | IR 核心结构 |
|
| `ir` | IR 核心结构 |
|
||||||
| `irgen` | AST → IR 生成 |
|
| `irgen` | 语法树 → IR 生成 |
|
||||||
| `mir` | Machine IR(指令选择、寄存器分配、栈帧等) |
|
| `mir` | Machine IR(指令选择、寄存器分配、栈帧等) |
|
||||||
| `backend` | 后端目标相关(如需要可细化 `aarch64`) |
|
| `backend` | 后端目标相关(如需要可细化 `aarch64`) |
|
||||||
| `antlr` | 语法文件/ANTLR 相关 |
|
| `antlr` | 语法文件/ANTLR 相关 |
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Lab1:语法树构建
|
# Lab1:构建antlr规则生成语法树
|
||||||
|
|
||||||
## 1. 本实验定位
|
## 1. 本实验定位
|
||||||
|
|
||||||
@@ -16,13 +16,10 @@ Lab1 聚焦前端第一步:词法/语法分析。
|
|||||||
|
|
||||||
## 3. Lab1 需要补充的内容
|
## 3. Lab1 需要补充的内容
|
||||||
|
|
||||||
1. 必须修改的文件
|
1. 需要修改的文件
|
||||||
- `src/antlr4/SysY.g4`:补全文法规则。
|
- `src/antlr4/SysY.g4`:补全文法规则。
|
||||||
- `src/frontend/AstBuilder.cpp`:同步扩展 parse tree 到 AST 的构建逻辑。
|
- `src/frontend/AntlrDriver.cpp`:解析入口与错误处理。
|
||||||
- `src/ast/AstNodes.h`、`src/ast/AstNodes.cpp`:按新增语法补充或调整 AST 节点定义。
|
- `src/frontend/SyntaxTreePrinter.cpp`:语法树打印逻辑(用于调试验证)。
|
||||||
|
|
||||||
2. 建议同步修改的文件
|
|
||||||
- `src/ast/AstPrinter.cpp`:为新增节点补充文本 AST 输出(`--ast-dot` 仅调试辅助,不是必做要求)。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -64,50 +61,11 @@ java -jar third_party/antlr-4.13.2-complete.jar \
|
|||||||
|
|
||||||
按提供的测试输入回归验证:
|
按提供的测试输入回归验证:
|
||||||
|
|
||||||
1. 运行 `./build/bin/compiler <case.sy>` 检查解析是否成功。
|
1. 运行 `./build/bin/compiler --emit-parse-tree <case.sy>` 检查解析是否成功。
|
||||||
2. 出现报错时优先回查 `SysY.g4` 与对应 AST 构建逻辑。
|
2. 出现报错时优先回查 `SysY.g4` 逻辑。
|
||||||
|
|
||||||
可选输出控制命令:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 仅输出 AST 文本
|
# 仅输出语法树
|
||||||
./build/bin/compiler --emit-ast test/test_case/simple_add.sy
|
./build/bin/compiler --emit-parse-tree test/test_case/simple_add.sy
|
||||||
|
|
||||||
# 仅输出 IR
|
|
||||||
./build/bin/compiler --emit-ir test/test_case/simple_add.sy
|
|
||||||
|
|
||||||
# 同时输出 AST 与 IR(默认行为)
|
|
||||||
./build/bin/compiler --emit-ast --emit-ir test/test_case/simple_add.sy
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 7. AST 输出相关说明(辅助)
|
|
||||||
|
|
||||||
AST 输出是调试手段,可以根据 AST 结果检查语法/词法实现逻辑问题。
|
|
||||||
**此功能完善与否不会影响整个流程的功能,可以选择性的实现**。
|
|
||||||
|
|
||||||
当前仓库中的 AST 文本输出与 `AST -> dot` 导出仅覆盖“已实现的最小语法子集”,并非完整 SysY 通用版本。 后续每新增语法/节点类型时,需要在 `src/ast/AstPrinter.cpp` 中同步补充文本输出与 dot 导出逻辑。
|
|
||||||
|
|
||||||
1. 基础文本 AST 输出(默认)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./build/bin/compiler test/test_case/simple_add.sy
|
|
||||||
```
|
|
||||||
|
|
||||||
2. 图形化 AST 输出(可选)
|
|
||||||
|
|
||||||
依赖安装(Ubuntu/WSL):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y graphviz
|
|
||||||
```
|
|
||||||
|
|
||||||
生成 DOT 与 PNG:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p test/test_result/ast
|
|
||||||
./build/bin/compiler --emit-ast --ast-dot test/test_result/ast/simple_add.ast.dot test/test_case/simple_add.sy
|
|
||||||
dot -Tpng test/test_result/ast/simple_add.ast.dot -o test/test_result/ast/simple_add.ast.png
|
|
||||||
```
|
|
||||||
|
|
||||||
直接查看图片即可
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
# Lab2:从 ANTLR 解析结果生成中间表示(IR)
|
# Lab2:生成中间表示(IR)
|
||||||
|
|
||||||
## 1. 本实验定位
|
## 1. 本实验定位
|
||||||
|
|
||||||
本仓库当前提供了一个“最小可运行”的 ANTLR 解析结果 -> IR 示例链路。
|
|
||||||
Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多 SysY 语法正确翻译为 IR。
|
Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多 SysY 语法正确翻译为 IR。
|
||||||
|
|
||||||
## 2. Lab2 要求
|
## 2. Lab2 要求
|
||||||
@@ -10,11 +9,10 @@ Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多
|
|||||||
需要同学完成:
|
需要同学完成:
|
||||||
|
|
||||||
1. 熟悉 IR 相关数据结构与构建接口。
|
1. 熟悉 IR 相关数据结构与构建接口。
|
||||||
2. 理解当前 ANTLR 解析结果 -> IR 的最小实现流程。
|
2. 理解当前语法树 -> IR 的最小实现流程。
|
||||||
3. 在现有框架上扩展 IR 生成能力,使其覆盖课程要求的Sysy语法。
|
3. 在现有框架上扩展 IR 生成能力,使其覆盖课程要求的Sysy语法。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 3. 当前代码框架(与 Lab2 直接相关)
|
## 3. 当前代码框架(与 Lab2 直接相关)
|
||||||
|
|
||||||
1. IR 定义与打印
|
1. IR 定义与打印
|
||||||
@@ -22,7 +20,7 @@ Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多
|
|||||||
- `src/ir/IRBuilder.cpp`
|
- `src/ir/IRBuilder.cpp`
|
||||||
- `src/ir/IRPrinter.cpp`
|
- `src/ir/IRPrinter.cpp`
|
||||||
|
|
||||||
2. ParseTree -> IR 生成器
|
2. 语法树 -> IR 生成器
|
||||||
- `src/irgen/IRGen.h`
|
- `src/irgen/IRGen.h`
|
||||||
- `src/irgen/IRGenDriver.cpp`
|
- `src/irgen/IRGenDriver.cpp`
|
||||||
- `src/irgen/IRGenFunc.cpp`
|
- `src/irgen/IRGenFunc.cpp`
|
||||||
@@ -49,7 +47,7 @@ Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多
|
|||||||
|
|
||||||
## 5. 当前最小示例实现说明
|
## 5. 当前最小示例实现说明
|
||||||
|
|
||||||
当前 ParseTree -> IR 仅覆盖最小子集:
|
当前语法树 -> IR 仅覆盖最小子集:
|
||||||
|
|
||||||
1. 常量整数、变量引用、二元加法表达式。
|
1. 常量整数、变量引用、二元加法表达式。
|
||||||
2. 局部变量声明(当前采用 LLVM 前端常见的 `alloca/load/store` 内存模型)。
|
2. 局部变量声明(当前采用 LLVM 前端常见的 `alloca/load/store` 内存模型)。
|
||||||
@@ -74,14 +72,14 @@ cmake --build build -j "$(nproc)"
|
|||||||
./build/bin/compiler --emit-ir test/test_case/simple_add.sy
|
./build/bin/compiler --emit-ir test/test_case/simple_add.sy
|
||||||
```
|
```
|
||||||
|
|
||||||
如需打印 ANTLR 语法树:
|
如需打印语法树:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./build/bin/compiler --emit-parse-tree test/test_case/simple_add.sy
|
./build/bin/compiler --emit-parse-tree test/test_case/simple_add.sy
|
||||||
```
|
```
|
||||||
|
|
||||||
推荐使用统一脚本验证 “IR -> LLVM 后端 -> 可执行程序” 整体链路,用于验证 IR 的正确性:
|
推荐使用统一脚本验证 “IR -> LLVM 后端 -> 可执行程序” 整体链路,用于验证 IR 的正确性:
|
||||||
推荐使用统一脚本验证 “IR -> LLVM 后端 -> 可执行程序” 整体链路,用于验证 IR 的正确性:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy out/ir --run
|
./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy out/ir --run
|
||||||
|
|||||||
@@ -8,12 +8,12 @@
|
|||||||
↓
|
↓
|
||||||
Lexer / Parser(ANTLR)
|
Lexer / Parser(ANTLR)
|
||||||
↓
|
↓
|
||||||
AST 构建 + 语义分析(Sema)
|
语法树构建 + 语义分析(Sema)
|
||||||
↓
|
↓
|
||||||
AST(带类型 / 符号 / 常量)
|
语法树(附加语义信息)
|
||||||
|
|
||||||
[ 中端 Middle-end ]
|
[ 中端 Middle-end ]
|
||||||
AST
|
语法树
|
||||||
↓
|
↓
|
||||||
IR 构建(平台无关 IR,LLVM 风格)
|
IR 构建(平台无关 IR,LLVM 风格)
|
||||||
↓
|
↓
|
||||||
@@ -51,10 +51,7 @@
|
|||||||
│ │ └── SysY.g4
|
│ │ └── SysY.g4
|
||||||
│ ├── frontend/
|
│ ├── frontend/
|
||||||
│ │ ├── AntlrDriver.cpp
|
│ │ ├── AntlrDriver.cpp
|
||||||
│ │ └── AstBuilder.cpp
|
│ │ └── SyntaxTreePrinter.cpp
|
||||||
│ ├── ast/
|
|
||||||
│ │ ├── AstNodes.cpp
|
|
||||||
│ │ └── AstPrinter.cpp
|
|
||||||
│ ├── sem/
|
│ ├── sem/
|
||||||
│ │ ├── Sema.cpp
|
│ │ ├── Sema.cpp
|
||||||
│ │ ├── SymbolTable.cpp
|
│ │ ├── SymbolTable.cpp
|
||||||
@@ -103,7 +100,6 @@
|
|||||||
│ └── CLI.cpp
|
│ └── CLI.cpp
|
||||||
├── include/
|
├── include/
|
||||||
│ ├── frontend/
|
│ ├── frontend/
|
||||||
│ ├── ast/
|
|
||||||
│ ├── sem/
|
│ ├── sem/
|
||||||
│ ├── irgen/
|
│ ├── irgen/
|
||||||
│ ├── ir/
|
│ ├── ir/
|
||||||
@@ -154,36 +150,28 @@
|
|||||||
- `SysYBaseVisitor.cpp/.h`、`SysYVisitor.cpp/.h`
|
- `SysYBaseVisitor.cpp/.h`、`SysYVisitor.cpp/.h`
|
||||||
- `*.tokens`、`*.interp`
|
- `*.tokens`、`*.interp`
|
||||||
|
|
||||||
#### 3.2.3 `src/frontend/`:ANTLR 调用与 AST 构建
|
#### 3.2.3 `src/frontend/`:语法解析与语法树输出
|
||||||
|
|
||||||
- `src/frontend/AntlrDriver.cpp`
|
- `src/frontend/AntlrDriver.cpp`
|
||||||
- 读取源代码并调用 ANTLR 生成的 lexer/parser,得到 parse tree。
|
- 读取源代码并调用解析流程,得到语法树。
|
||||||
- 由于语法正确性由测试保证,该模块只需提供“可用的解析入口”,不需要实现复杂的语法错误报告体系。
|
- 由于语法正确性由测试保证,该模块只需提供“可用的解析入口”,不需要实现复杂的语法错误报告体系。
|
||||||
- `src/frontend/AstBuilder.cpp`
|
- `src/frontend/SyntaxTreePrinter.cpp`
|
||||||
- 将 parse tree 转换为 AST(`src/ast/*`),并在 AST 节点上保留必要的定位信息(可选,用于调试/日志)。
|
- 语法树调试打印:用于验证语法规则与前端解析行为是否符合预期。
|
||||||
|
|
||||||
#### 3.2.4 `src/ast/`:抽象语法树(AST)
|
#### 3.2.4 `src/sem/`:语义分析(Sema)
|
||||||
|
|
||||||
- `src/ast/AstNodes.cpp`
|
|
||||||
- AST 节点定义与实现:表达式、语句、声明、函数、类型等。
|
|
||||||
- AST 节点应当能够承载后续阶段附加信息(类型、符号绑定、常量值等)。
|
|
||||||
- `src/ast/AstPrinter.cpp`
|
|
||||||
- AST 调试打印:用于验证 AST 构建是否符合预期,便于定位前端/语义阶段问题。
|
|
||||||
|
|
||||||
#### 3.2.5 `src/sem/`:语义分析(Sema)
|
|
||||||
|
|
||||||
- `src/sem/Sema.cpp`
|
- `src/sem/Sema.cpp`
|
||||||
- 语义分析主流程:符号解析、类型检查、控制流规则检查、必要的隐式转换插入/记录等。
|
- 语义分析主流程:符号解析、类型检查、控制流规则检查、必要的隐式转换插入/记录等。
|
||||||
- 输出为“带类型 / 符号 / 常量信息”的 AST(可通过在 AST 节点上附加属性实现)。
|
- 在语法树基础上完成语义检查,并为后续 IR 生成提供约束。
|
||||||
- `src/sem/SymbolTable.cpp`
|
- `src/sem/SymbolTable.cpp`
|
||||||
- 符号表与作用域管理:支持嵌套作用域、变量/函数/参数/常量的注册与查找。
|
- 符号表与作用域管理:支持嵌套作用域、变量/函数/参数/常量的注册与查找。
|
||||||
- `src/sem/ConstEval.cpp`
|
- `src/sem/ConstEval.cpp`
|
||||||
- 常量求值:用于数组维度、全局初始化、`const` 表达式等需要编译期计算的场景。
|
- 常量求值:用于数组维度、全局初始化、`const` 表达式等需要编译期计算的场景。
|
||||||
|
|
||||||
#### 3.2.6 `src/irgen/`:AST → IR(平台无关,LLVM 风格)
|
#### 3.2.5 `src/irgen/`:语法树 → IR(平台无关,LLVM 风格)
|
||||||
|
|
||||||
- `src/irgen/IRGenDriver.cpp`
|
- `src/irgen/IRGenDriver.cpp`
|
||||||
- 驱动 Visitor 遍历 AST,调度各子模块完成翻译。
|
- 驱动语法树遍历,调度各子模块完成翻译。
|
||||||
- `src/irgen/IRGenFunc.cpp`
|
- `src/irgen/IRGenFunc.cpp`
|
||||||
- 函数翻译:函数定义、参数列表与返回值翻译;创建对应 IR 函数对象。
|
- 函数翻译:函数定义、参数列表与返回值翻译;创建对应 IR 函数对象。
|
||||||
- `src/irgen/IRGenStmt.cpp`
|
- `src/irgen/IRGenStmt.cpp`
|
||||||
@@ -193,7 +181,7 @@
|
|||||||
- `src/irgen/IRGenDecl.cpp`
|
- `src/irgen/IRGenDecl.cpp`
|
||||||
- 声明翻译:处理全局变量、局部变量、数组初始化与空间分配等。
|
- 声明翻译:处理全局变量、局部变量、数组初始化与空间分配等。
|
||||||
|
|
||||||
#### 3.2.7 `src/ir/`:LLVM 风格 IR 核心
|
#### 3.2.6 `src/ir/`:LLVM 风格 IR 核心
|
||||||
|
|
||||||
- `src/ir/Context.cpp`
|
- `src/ir/Context.cpp`
|
||||||
- IR 上下文:管理类型/常量创建与复用、字符串/符号等公共资源。
|
- IR 上下文:管理类型/常量创建与复用、字符串/符号等公共资源。
|
||||||
@@ -228,7 +216,7 @@
|
|||||||
- `src/ir/passes/CFGSimplify.cpp`
|
- `src/ir/passes/CFGSimplify.cpp`
|
||||||
- CFG 简化:删除不可达块、合并空块、简化分支等。
|
- CFG 简化:删除不可达块、合并空块、简化分支等。
|
||||||
|
|
||||||
#### 3.2.8 `src/mir/`:Machine IR
|
#### 3.2.7 `src/mir/`:Machine IR
|
||||||
|
|
||||||
- `src/mir/MIRContext.cpp`
|
- `src/mir/MIRContext.cpp`
|
||||||
- MIR 上下文:保存目标约束、指令集信息等。
|
- MIR 上下文:保存目标约束、指令集信息等。
|
||||||
@@ -254,7 +242,7 @@
|
|||||||
- `src/mir/passes/Peephole.cpp`
|
- `src/mir/passes/Peephole.cpp`
|
||||||
- 窥孔优化:删除冗余 move、合并常见指令模式,提升最终汇编质量。
|
- 窥孔优化:删除冗余 move、合并常见指令模式,提升最终汇编质量。
|
||||||
|
|
||||||
#### 3.2.9 `src/utils/`:日志与命令行
|
#### 3.2.8 `src/utils/`:日志与命令行
|
||||||
|
|
||||||
- `src/utils/Log.cpp`
|
- `src/utils/Log.cpp`
|
||||||
- 日志输出:统一调试信息、阶段信息与错误信息输出。
|
- 日志输出:统一调试信息、阶段信息与错误信息输出。
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
add_subdirectory(ir)
|
add_subdirectory(ir)
|
||||||
add_subdirectory(frontend)
|
add_subdirectory(frontend)
|
||||||
|
add_subdirectory(sem)
|
||||||
add_subdirectory(irgen)
|
add_subdirectory(irgen)
|
||||||
add_subdirectory(mir)
|
add_subdirectory(mir)
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ add_executable(compiler
|
|||||||
)
|
)
|
||||||
target_link_libraries(compiler PRIVATE
|
target_link_libraries(compiler PRIVATE
|
||||||
frontend
|
frontend
|
||||||
|
sem
|
||||||
irgen
|
irgen
|
||||||
mir
|
mir
|
||||||
utils
|
utils
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 调用 ANTLR 生成的 Lexer/Parser,返回 parse tree。
|
// 调用前端解析流程,返回语法树。
|
||||||
#include "frontend/AntlrDriver.h"
|
#include "frontend/AntlrDriver.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
add_library(frontend STATIC
|
add_library(frontend STATIC
|
||||||
AntlrDriver.cpp
|
AntlrDriver.cpp
|
||||||
|
SyntaxTreePrinter.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(frontend PUBLIC
|
target_link_libraries(frontend PUBLIC
|
||||||
|
|||||||
183
src/frontend/SyntaxTreePrinter.cpp
Normal file
183
src/frontend/SyntaxTreePrinter.cpp
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
#include "frontend/SyntaxTreePrinter.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "SysYParser.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string GetTokenName(const antlr4::Token* tok, antlr4::Parser* parser) {
|
||||||
|
if (!tok || !parser) {
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
const int token_type = tok->getType();
|
||||||
|
const auto& vocab = parser->getVocabulary();
|
||||||
|
std::string token_name(vocab.getSymbolicName(token_type));
|
||||||
|
if (token_name.empty()) {
|
||||||
|
token_name = std::string(vocab.getLiteralName(token_type));
|
||||||
|
}
|
||||||
|
if (token_name.empty()) {
|
||||||
|
token_name = std::to_string(token_type);
|
||||||
|
}
|
||||||
|
return token_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KeepImportantToken(const std::string& token_name) {
|
||||||
|
return token_name == "Ident" || token_name == "Number" ||
|
||||||
|
token_name == "Assign" || token_name == "AddOp";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PrettyPrimary(SysYParser::PrimaryContext* primary) {
|
||||||
|
if (!primary) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (primary->Number()) {
|
||||||
|
return primary->Number()->getText();
|
||||||
|
}
|
||||||
|
if (primary->Ident()) {
|
||||||
|
return primary->Ident()->getText();
|
||||||
|
}
|
||||||
|
if (primary->exp()) {
|
||||||
|
return "(" + primary->exp()->getText() + ")";
|
||||||
|
}
|
||||||
|
return primary->getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PrettyAddExp(SysYParser::AddExpContext* add_exp) {
|
||||||
|
if (!add_exp) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const auto terms = add_exp->primary();
|
||||||
|
if (terms.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string out = PrettyPrimary(terms[0]);
|
||||||
|
for (size_t i = 1; i < terms.size(); ++i) {
|
||||||
|
out += " + " + PrettyPrimary(terms[i]);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PrettyExp(SysYParser::ExpContext* exp) {
|
||||||
|
if (!exp || !exp->addExp()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return PrettyAddExp(exp->addExp());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PrettyRuleText(antlr4::ParserRuleContext* rule) {
|
||||||
|
if (!rule) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (auto* var_decl = dynamic_cast<SysYParser::VarDeclContext*>(rule)) {
|
||||||
|
std::string out = "int " + var_decl->Ident()->getText();
|
||||||
|
if (var_decl->exp()) {
|
||||||
|
out += " = " + PrettyExp(var_decl->exp());
|
||||||
|
}
|
||||||
|
out += ";";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
if (auto* ret = dynamic_cast<SysYParser::ReturnStmtContext*>(rule)) {
|
||||||
|
return "return " + PrettyExp(ret->exp()) + ";";
|
||||||
|
}
|
||||||
|
if (dynamic_cast<SysYParser::FuncDefContext*>(rule) != nullptr) {
|
||||||
|
return "int main()";
|
||||||
|
}
|
||||||
|
if (auto* stmt = dynamic_cast<SysYParser::StmtContext*>(rule)) {
|
||||||
|
if (stmt->varDecl()) {
|
||||||
|
return PrettyRuleText(stmt->varDecl());
|
||||||
|
}
|
||||||
|
if (stmt->returnStmt()) {
|
||||||
|
return PrettyRuleText(stmt->returnStmt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (auto* exp = dynamic_cast<SysYParser::ExpContext*>(rule)) {
|
||||||
|
return PrettyExp(exp);
|
||||||
|
}
|
||||||
|
if (auto* add_exp = dynamic_cast<SysYParser::AddExpContext*>(rule)) {
|
||||||
|
return PrettyAddExp(add_exp);
|
||||||
|
}
|
||||||
|
if (auto* primary = dynamic_cast<SysYParser::PrimaryContext*>(rule)) {
|
||||||
|
return PrettyPrimary(primary);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasVisibleNode(antlr4::tree::ParseTree* node, antlr4::Parser* parser) {
|
||||||
|
auto* terminal = dynamic_cast<antlr4::tree::TerminalNode*>(node);
|
||||||
|
if (terminal) {
|
||||||
|
const std::string token_name = GetTokenName(terminal->getSymbol(), parser);
|
||||||
|
return KeepImportantToken(token_name);
|
||||||
|
}
|
||||||
|
for (auto* child : node->children) {
|
||||||
|
if (HasVisibleNode(child, parser)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RuleName(antlr4::tree::ParseTree* node, antlr4::Parser* parser) {
|
||||||
|
auto* rule = dynamic_cast<antlr4::ParserRuleContext*>(node);
|
||||||
|
if (!parser || !rule) {
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
const int idx = rule->getRuleIndex();
|
||||||
|
const auto& names = parser->getRuleNames();
|
||||||
|
if (idx >= 0 && idx < static_cast<int>(names.size())) {
|
||||||
|
return names[static_cast<size_t>(idx)];
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NodeLabel(antlr4::tree::ParseTree* node, antlr4::Parser* parser) {
|
||||||
|
auto* terminal = dynamic_cast<antlr4::tree::TerminalNode*>(node);
|
||||||
|
if (terminal) {
|
||||||
|
return GetTokenName(terminal->getSymbol(), parser) + ": " + node->getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string rule_name = RuleName(node, parser);
|
||||||
|
auto* rule = dynamic_cast<antlr4::ParserRuleContext*>(node);
|
||||||
|
const std::string pretty = PrettyRuleText(rule);
|
||||||
|
if (!pretty.empty()) {
|
||||||
|
return rule_name + " (" + pretty + ")";
|
||||||
|
}
|
||||||
|
return rule_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintSyntaxTreeImpl(antlr4::tree::ParseTree* node, antlr4::Parser* parser,
|
||||||
|
std::ostream& os, const std::string& prefix,
|
||||||
|
bool is_last, bool is_root) {
|
||||||
|
if (!HasVisibleNode(node, parser)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_root) {
|
||||||
|
os << NodeLabel(node, parser) << "\n";
|
||||||
|
} else {
|
||||||
|
os << prefix << (is_last ? "└── " : "├── ") << NodeLabel(node, parser)
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<antlr4::tree::ParseTree*> children;
|
||||||
|
for (auto* child : node->children) {
|
||||||
|
if (HasVisibleNode(child, parser)) {
|
||||||
|
children.push_back(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string child_prefix =
|
||||||
|
is_root ? "" : prefix + (is_last ? " " : "│ ");
|
||||||
|
for (size_t i = 0; i < children.size(); ++i) {
|
||||||
|
PrintSyntaxTreeImpl(children[i], parser, os, child_prefix,
|
||||||
|
i + 1 == children.size(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void PrintSyntaxTree(antlr4::tree::ParseTree* tree, antlr4::Parser* parser,
|
||||||
|
std::ostream& os) {
|
||||||
|
PrintSyntaxTreeImpl(tree, parser, os, "", true, true);
|
||||||
|
}
|
||||||
9
src/frontend/SyntaxTreePrinter.h
Normal file
9
src/frontend/SyntaxTreePrinter.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
#include "antlr4-runtime.h"
|
||||||
|
|
||||||
|
// 以树状缩进形式打印语法树(仅保留关键节点/记号)。
|
||||||
|
void PrintSyntaxTree(antlr4::tree::ParseTree* tree, antlr4::Parser* parser,
|
||||||
|
std::ostream& os);
|
||||||
@@ -162,7 +162,7 @@ class Function : public Value {
|
|||||||
|
|
||||||
class Module {
|
class Module {
|
||||||
public:
|
public:
|
||||||
// 创建函数时显式传入返回类型,便于在 IRGen 中根据 AST 选择类型。
|
// 创建函数时显式传入返回类型,便于在 IRGen 中根据语法树信息选择类型。
|
||||||
Function* CreateFunction(const std::string& name,
|
Function* CreateFunction(const std::string& name,
|
||||||
std::shared_ptr<Type> ret_type);
|
std::shared_ptr<Type> ret_type);
|
||||||
const std::vector<std::unique_ptr<Function>>& functions() const {
|
const std::vector<std::unique_ptr<Function>>& functions() const {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 将 ANTLR parse tree 翻译为极简 IR。
|
// 将语法树翻译为极简 IR。
|
||||||
// 实现拆分在 IRGenFunc/IRGenStmt/IRGenExp/IRGenDecl。
|
// 实现拆分在 IRGenFunc/IRGenStmt/IRGenExp/IRGenDecl。
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -32,7 +32,7 @@ class IRGenImpl {
|
|||||||
private:
|
private:
|
||||||
void GenFuncDef(SysYParser::FuncDefContext& func);
|
void GenFuncDef(SysYParser::FuncDefContext& func);
|
||||||
void GenBlock(SysYParser::BlockContext& block);
|
void GenBlock(SysYParser::BlockContext& block);
|
||||||
void GenStmt(SysYParser::StmtContext& stmt);
|
bool GenStmt(SysYParser::StmtContext& stmt);
|
||||||
void GenVarDecl(SysYParser::VarDeclContext& decl);
|
void GenVarDecl(SysYParser::VarDeclContext& decl);
|
||||||
void GenReturnStmt(SysYParser::ReturnStmtContext& ret);
|
void GenReturnStmt(SysYParser::ReturnStmtContext& ret);
|
||||||
|
|
||||||
|
|||||||
@@ -6,27 +6,22 @@
|
|||||||
#include "ir/IR.h"
|
#include "ir/IR.h"
|
||||||
|
|
||||||
void IRGenImpl::GenBlock(SysYParser::BlockContext& block) {
|
void IRGenImpl::GenBlock(SysYParser::BlockContext& block) {
|
||||||
for (auto* stmt : block.stmt()) {
|
|
||||||
if (stmt && stmt->varDecl()) {
|
|
||||||
const std::string name = stmt->varDecl()->Ident()->getText();
|
|
||||||
auto* slot = builder_.CreateAllocaI32(ir::DefaultContext().NextTemp());
|
|
||||||
locals_[name] = slot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto* stmt : block.stmt()) {
|
for (auto* stmt : block.stmt()) {
|
||||||
if (stmt) {
|
if (stmt) {
|
||||||
GenStmt(*stmt);
|
if (GenStmt(*stmt)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRGenImpl::GenVarDecl(SysYParser::VarDeclContext& decl) {
|
void IRGenImpl::GenVarDecl(SysYParser::VarDeclContext& decl) {
|
||||||
const std::string name = decl.Ident()->getText();
|
const std::string name = decl.Ident()->getText();
|
||||||
auto it = locals_.find(name);
|
if (locals_.find(name) != locals_.end()) {
|
||||||
if (it == locals_.end()) {
|
throw std::runtime_error("[irgen] 重复定义变量: " + name);
|
||||||
throw std::runtime_error("[irgen] 变量栈槽未创建: " + name);
|
|
||||||
}
|
}
|
||||||
|
auto* slot = builder_.CreateAllocaI32(ir::DefaultContext().NextTemp());
|
||||||
|
locals_[name] = slot;
|
||||||
|
|
||||||
ir::Value* init = nullptr;
|
ir::Value* init = nullptr;
|
||||||
if (decl.exp()) {
|
if (decl.exp()) {
|
||||||
@@ -34,5 +29,5 @@ void IRGenImpl::GenVarDecl(SysYParser::VarDeclContext& decl) {
|
|||||||
} else {
|
} else {
|
||||||
init = ir::DefaultContext().GetConstInt(0);
|
init = ir::DefaultContext().GetConstInt(0);
|
||||||
}
|
}
|
||||||
builder_.CreateStore(init, it->second);
|
builder_.CreateStore(init, slot);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,12 @@
|
|||||||
|
|
||||||
std::unique_ptr<ir::Module> GenerateIR(antlr4::tree::ParseTree* tree) {
|
std::unique_ptr<ir::Module> GenerateIR(antlr4::tree::ParseTree* tree) {
|
||||||
if (!tree) {
|
if (!tree) {
|
||||||
throw std::runtime_error("[irgen] parse tree 为空");
|
throw std::runtime_error("[irgen] 语法树为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* cu = dynamic_cast<SysYParser::CompUnitContext*>(tree);
|
auto* cu = dynamic_cast<SysYParser::CompUnitContext*>(tree);
|
||||||
if (!cu) {
|
if (!cu) {
|
||||||
throw std::runtime_error("[irgen] parse tree 根节点不是 compUnit");
|
throw std::runtime_error("[irgen] 语法树根节点不是 compUnit");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto module = std::make_unique<ir::Module>();
|
auto module = std::make_unique<ir::Module>();
|
||||||
|
|||||||
@@ -5,14 +5,14 @@
|
|||||||
#include "SysYParser.h"
|
#include "SysYParser.h"
|
||||||
#include "ir/IR.h"
|
#include "ir/IR.h"
|
||||||
|
|
||||||
void IRGenImpl::GenStmt(SysYParser::StmtContext& stmt) {
|
bool IRGenImpl::GenStmt(SysYParser::StmtContext& stmt) {
|
||||||
if (stmt.varDecl()) {
|
if (stmt.varDecl()) {
|
||||||
GenVarDecl(*stmt.varDecl());
|
GenVarDecl(*stmt.varDecl());
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (stmt.returnStmt()) {
|
if (stmt.returnStmt()) {
|
||||||
GenReturnStmt(*stmt.returnStmt());
|
GenReturnStmt(*stmt.returnStmt());
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
throw std::runtime_error("[irgen] 暂不支持的语句类型");
|
throw std::runtime_error("[irgen] 暂不支持的语句类型");
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/main.cpp
11
src/main.cpp
@@ -1,10 +1,13 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "frontend/AntlrDriver.h"
|
#include "frontend/AntlrDriver.h"
|
||||||
|
#include "frontend/SyntaxTreePrinter.h"
|
||||||
#include "ir/IR.h"
|
#include "ir/IR.h"
|
||||||
#include "irgen/IRGen.h"
|
#include "irgen/IRGen.h"
|
||||||
#include "mir/MIR.h"
|
#include "mir/MIR.h"
|
||||||
|
#include "sem/Sema.h"
|
||||||
#include "utils/CLI.h"
|
#include "utils/CLI.h"
|
||||||
#include "utils/Log.h"
|
#include "utils/Log.h"
|
||||||
|
|
||||||
@@ -19,10 +22,16 @@ int main(int argc, char** argv) {
|
|||||||
auto antlr = ParseFileWithAntlr(opts.input);
|
auto antlr = ParseFileWithAntlr(opts.input);
|
||||||
bool need_blank_line = false;
|
bool need_blank_line = false;
|
||||||
if (opts.emit_parse_tree) {
|
if (opts.emit_parse_tree) {
|
||||||
std::cout << antlr.tree->toStringTree(antlr.parser.get()) << "\n";
|
PrintSyntaxTree(antlr.tree, antlr.parser.get(), std::cout);
|
||||||
need_blank_line = true;
|
need_blank_line = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* comp_unit = dynamic_cast<SysYParser::CompUnitContext*>(antlr.tree);
|
||||||
|
if (!comp_unit) {
|
||||||
|
throw std::runtime_error("[main] 语法树根节点不是 compUnit");
|
||||||
|
}
|
||||||
|
RunSema(*comp_unit);
|
||||||
|
|
||||||
auto module = GenerateIR(antlr.tree);
|
auto module = GenerateIR(antlr.tree);
|
||||||
if (opts.emit_ir) {
|
if (opts.emit_ir) {
|
||||||
ir::IRPrinter printer;
|
ir::IRPrinter printer;
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ add_library(sem STATIC
|
|||||||
|
|
||||||
target_link_libraries(sem PUBLIC
|
target_link_libraries(sem PUBLIC
|
||||||
build_options
|
build_options
|
||||||
ast
|
${ANTLR4_RUNTIME_TARGET}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,58 +1,75 @@
|
|||||||
// 极简语义分析:只检查变量是否先声明再使用。
|
|
||||||
// 如需扩展,可在此基础上加入:
|
|
||||||
// - 常量折叠/类型检查
|
|
||||||
// - 函数签名/参数数量校验
|
|
||||||
// - 控制流相关检查(return 覆盖、break/continue 合法性等)
|
|
||||||
#include "sem/Sema.h"
|
#include "sem/Sema.h"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include "ast/AstNodes.h"
|
|
||||||
#include "sem/SymbolTable.h"
|
#include "sem/SymbolTable.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class SemaVisitor {
|
void CheckExpr(SysYParser::ExpContext& exp, const SymbolTable& table);
|
||||||
public:
|
|
||||||
explicit SemaVisitor(SymbolTable& table) : table_(table) {}
|
|
||||||
|
|
||||||
void CheckBlock(const ast::Block& block) {
|
void CheckPrimary(SysYParser::PrimaryContext& primary,
|
||||||
for (const auto& item : block.items) {
|
const SymbolTable& table) {
|
||||||
if (auto decl = dynamic_cast<ast::VarDecl*>(item.get())) {
|
if (primary.Number()) {
|
||||||
table_.Add(decl->name);
|
return;
|
||||||
if (decl->init) CheckExpr(*decl->init);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (auto ret = dynamic_cast<ast::ReturnStmt*>(item.get())) {
|
|
||||||
CheckExpr(*ret->value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckExpr(const ast::Expr& expr) {
|
if (primary.Ident()) {
|
||||||
if (auto var = dynamic_cast<const ast::VarExpr*>(&expr)) {
|
const std::string name = primary.Ident()->getText();
|
||||||
if (!table_.Contains(var->name)) {
|
if (!table.Contains(name)) {
|
||||||
throw std::runtime_error("[sema] 使用了未定义的变量: " + var->name);
|
throw std::runtime_error("[sema] 使用了未定义的变量: " + name);
|
||||||
}
|
|
||||||
} else if (auto bin = dynamic_cast<const ast::BinaryExpr*>(&expr)) {
|
|
||||||
CheckExpr(*bin->lhs);
|
|
||||||
CheckExpr(*bin->rhs);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
if (primary.exp()) {
|
||||||
SymbolTable& table_;
|
CheckExpr(*primary.exp(), table);
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("[sema] 暂不支持的 primary 形式");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckExpr(SysYParser::ExpContext& exp, const SymbolTable& table) {
|
||||||
|
if (!exp.addExp()) {
|
||||||
|
throw std::runtime_error("[sema] 非法表达式");
|
||||||
|
}
|
||||||
|
const auto& terms = exp.addExp()->primary();
|
||||||
|
for (auto* term : terms) {
|
||||||
|
CheckPrimary(*term, table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::shared_ptr<ast::CompUnit> RunSema(std::shared_ptr<ast::CompUnit> ast) {
|
void RunSema(SysYParser::CompUnitContext& comp_unit) {
|
||||||
if (!ast || !ast->func || !ast->func->body) return ast;
|
auto* func = comp_unit.funcDef();
|
||||||
|
if (!func || !func->block()) {
|
||||||
|
throw std::runtime_error("[sema] 缺少 main 函数定义");
|
||||||
|
}
|
||||||
|
|
||||||
SymbolTable table;
|
SymbolTable table;
|
||||||
SemaVisitor visitor(table);
|
|
||||||
visitor.CheckBlock(*ast->func->body);
|
for (auto* stmt : func->block()->stmt()) {
|
||||||
return ast;
|
if (!stmt) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (auto* decl = stmt->varDecl()) {
|
||||||
|
const std::string name = decl->Ident()->getText();
|
||||||
|
if (table.Contains(name)) {
|
||||||
|
throw std::runtime_error("[sema] 重复定义变量: " + name);
|
||||||
|
}
|
||||||
|
if (decl->exp()) {
|
||||||
|
CheckExpr(*decl->exp(), table);
|
||||||
|
}
|
||||||
|
table.Add(name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (auto* ret = stmt->returnStmt()) {
|
||||||
|
CheckExpr(*ret->exp(), table);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("[sema] 暂不支持的语句类型");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
// 语义检查
|
// 基于语法树的极简语义检查。
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include "SysYParser.h"
|
||||||
|
|
||||||
namespace ast {
|
// 目前仅检查:
|
||||||
struct CompUnit;
|
// - 变量先声明后使用
|
||||||
}
|
// - 局部变量不允许重复定义
|
||||||
|
void RunSema(SysYParser::CompUnitContext& comp_unit);
|
||||||
// 返回经过检查的 AST(当前直接返回原 AST)。
|
|
||||||
std::shared_ptr<ast::CompUnit> RunSema(std::shared_ptr<ast::CompUnit> ast);
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ void PrintHelp(std::ostream& os) {
|
|||||||
<< "\n"
|
<< "\n"
|
||||||
<< "选项:\n"
|
<< "选项:\n"
|
||||||
<< " -h, --help 打印帮助信息并退出\n"
|
<< " -h, --help 打印帮助信息并退出\n"
|
||||||
<< " --emit-parse-tree 仅在显式模式下启用 ANTLR 语法树输出\n"
|
<< " --emit-parse-tree 仅在显式模式下启用语法树输出\n"
|
||||||
<< " --emit-ir 仅在显式模式下启用 IR 输出\n"
|
<< " --emit-ir 仅在显式模式下启用 IR 输出\n"
|
||||||
<< " --emit-asm 仅在显式模式下启用 AArch64 汇编输出\n"
|
<< " --emit-asm 仅在显式模式下启用 AArch64 汇编输出\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
|
|||||||
1
test/run_tests.sh
Normal file → Executable file
1
test/run_tests.sh
Normal file → Executable file
@@ -2,4 +2,3 @@
|
|||||||
# - 批量编译 test/test_case/ 下的 *.sy 用例
|
# - 批量编译 test/test_case/ 下的 *.sy 用例
|
||||||
# - 将产物与日志写入 test/test_result/(例如 .ll/.s、运行输出、diff 结果)
|
# - 将产物与日志写入 test/test_result/(例如 .ll/.s、运行输出、diff 结果)
|
||||||
# - 汇总通过/失败信息并给出统计
|
# - 汇总通过/失败信息并给出统计
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user