!40 refactor: 优化和重构字符串扫描器,完善词法分析功能
Merge pull request !40 from Luke/feature/optimize-lexical-error-messages
This commit is contained in:
commit
6f21cec444
@ -11,7 +11,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 任务标题
|
label: 任务标题
|
||||||
description: 简要描述维护内容
|
description: 简要描述维护内容
|
||||||
placeholder: "如:升级 JDK 版本"
|
placeholder: "如: 升级 JDK 版本"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@ -20,6 +20,6 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 任务详情
|
label: 任务详情
|
||||||
description: 描述执行步骤或相关脚本命令
|
description: 描述执行步骤或相关脚本命令
|
||||||
placeholder: "如:修改 pom.xml…"
|
placeholder: "如: 修改 pom.xml…"
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@ -20,7 +20,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 优化方案
|
label: 优化方案
|
||||||
description: 详细描述改进思路与实现方式
|
description: 详细描述改进思路与实现方式
|
||||||
placeholder: "如:缓存机制、算法优化…"
|
placeholder: "如: 缓存机制、算法优化…"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@ -29,6 +29,6 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 预期收益
|
label: 预期收益
|
||||||
description: 描述优化后带来的效益或体验提升
|
description: 描述优化后带来的效益或体验提升
|
||||||
placeholder: "如:响应时间缩短 30%…"
|
placeholder: "如: 响应时间缩短 30%…"
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@ -32,6 +32,6 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 修复截止日期
|
label: 修复截止日期
|
||||||
description: 填写计划完成的日期 (YYYY-MM-DD)
|
description: 填写计划完成的日期 (YYYY-MM-DD)
|
||||||
placeholder: "例如:2025-06-20"
|
placeholder: "例如: 2025-06-20"
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@ -20,7 +20,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 已尝试方法
|
label: 已尝试方法
|
||||||
description: 列出已尝试过的解决方案或思路
|
description: 列出已尝试过的解决方案或思路
|
||||||
placeholder: "如:查阅文档、尝试示例代码…"
|
placeholder: "如: 查阅文档、尝试示例代码…"
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
@ -29,6 +29,6 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 期望答案
|
label: 期望答案
|
||||||
description: 描述希望获得的解答或帮助方向
|
description: 描述希望获得的解答或帮助方向
|
||||||
placeholder: "如:最佳实践、配置示例…"
|
placeholder: "如: 最佳实践、配置示例…"
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@ -20,7 +20,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 重构原因
|
label: 重构原因
|
||||||
description: 说明当前存在的痛点或待改进之处
|
description: 说明当前存在的痛点或待改进之处
|
||||||
placeholder: "如:变量命名不规范、函数职责过多…"
|
placeholder: "如: 变量命名不规范、函数职责过多…"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@ -29,6 +29,6 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 预期效果
|
label: 预期效果
|
||||||
description: 说明重构后带来的好处或验证方式
|
description: 说明重构后带来的好处或验证方式
|
||||||
placeholder: "如:提高性能、增强可读性…"
|
placeholder: "如: 提高性能、增强可读性…"
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@ -3,14 +3,14 @@
|
|||||||
https://gitee.com/jcnc-org/snow/blob/main/doc/Git-Management/Git-Management.md
|
https://gitee.com/jcnc-org/snow/blob/main/doc/Git-Management/Git-Management.md
|
||||||
提交 PR 后,请根据实际情况删除不适用的项。
|
提交 PR 后,请根据实际情况删除不适用的项。
|
||||||
|
|
||||||
1. 请在右侧面板中:
|
1. 请在右侧面板中:
|
||||||
- 关联 Issue
|
- 关联 Issue
|
||||||
- 选择 PR 类型(bug 修复 / 新功能 / 文档 / 优化 等)
|
- 选择 PR 类型(bug 修复 / 新功能 / 文档 / 优化 等)
|
||||||
- 添加必要的标签和审查人
|
- 添加必要的标签和审查人
|
||||||
- 请添加里程碑
|
- 请添加里程碑
|
||||||
- 如必要请设置优先级
|
- 如必要请设置优先级
|
||||||
|
|
||||||
2. 在下面的“检查清单”里,用 `- [x]` 标记已完成,用 `- [ ]` 标记未完成。例如:
|
2. 在下面的“检查清单”里,用 `- [x]` 标记已完成,用 `- [ ]` 标记未完成。例如:
|
||||||
- [x] 已阅读并遵守项目规范
|
- [x] 已阅读并遵守项目规范
|
||||||
- [ ] 本地通过所有测试
|
- [ ] 本地通过所有测试
|
||||||
- [ ] 文档已更新(如有必要)
|
- [ ] 文档已更新(如有必要)
|
||||||
|
|||||||
122
README.md
122
README.md
@ -86,11 +86,11 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
|||||||
|
|
||||||
## 开发环境安装
|
## 开发环境安装
|
||||||
|
|
||||||
1. **开发环境准备**:
|
1. **开发环境准备**:
|
||||||
1. 安装集成开发环境 [IntelliJ IDEA](https://www.jetbrains.com/idea/download)
|
1. 安装集成开发环境 [IntelliJ IDEA](https://www.jetbrains.com/idea/download)
|
||||||
2. 安装 Java 开发工具 [Graalvm-jdk-24](https://www.graalvm.org/downloads/)
|
2. 安装 Java 开发工具 [Graalvm-jdk-24](https://www.graalvm.org/downloads/)
|
||||||
|
|
||||||
2. **获取源码**:
|
2. **获取源码**:
|
||||||
将项目源码下载或克隆到本地目录。
|
将项目源码下载或克隆到本地目录。
|
||||||
```bash
|
```bash
|
||||||
git clone https://gitee.com/jcnc-org/snow.git
|
git clone https://gitee.com/jcnc-org/snow.git
|
||||||
@ -285,37 +285,37 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
|||||||
|
|
||||||
独立编译不依赖 `.cloud` 文件,而是直接使用 `Snow` 编译器进行 `.snow` 文件的编译和执行。
|
独立编译不依赖 `.cloud` 文件,而是直接使用 `Snow` 编译器进行 `.snow` 文件的编译和执行。
|
||||||
|
|
||||||
#### 独立编译步骤:
|
#### 独立编译步骤:
|
||||||
|
|
||||||
1. **运行编译器:**
|
1. **运行编译器: **
|
||||||
你可以通过以下命令来编译单个或多个 `.snow` 文件,或者递归编译一个目录中的所有 `.snow` 源文件为`.water`虚拟机指令。
|
你可以通过以下命令来编译单个或多个 `.snow` 文件,或者递归编译一个目录中的所有 `.snow` 源文件为`.water`虚拟机指令。
|
||||||
|
|
||||||
* **单个文件编译:**
|
* **单个文件编译: **
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
Snow complete [SnowCode].snow
|
Snow complete [SnowCode].snow
|
||||||
```
|
```
|
||||||
|
|
||||||
* **多个文件编译:**
|
* **多个文件编译: **
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
Snow complete [SnowCode1].snow [SnowCode2].snow [SnowCode3].snow -o [Name]
|
Snow complete [SnowCode1].snow [SnowCode2].snow [SnowCode3].snow -o [Name]
|
||||||
```
|
```
|
||||||
|
|
||||||
* **目录递归编译:**
|
* **目录递归编译: **
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
Snow -d path/to/source_dir
|
Snow -d path/to/source_dir
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **查看编译输出:**
|
2. **查看编译输出: **
|
||||||
编译过程会输出源代码、抽象语法树(AST)、中间表示(IR)以及虚拟机指令等内容。你可以看到如下几个分段输出:
|
编译过程会输出源代码、抽象语法树(AST)、中间表示(IR)以及虚拟机指令等内容。你可以看到如下几个分段输出:
|
||||||
|
|
||||||
* **AST**(抽象语法树)部分以 JSON 格式输出。
|
* **AST**(抽象语法树)部分以 JSON 格式输出。
|
||||||
* **IR**(中间表示)部分会列出逐行的中间代码。
|
* **IR**(中间表示)部分会列出逐行的中间代码。
|
||||||
* **VM code**(虚拟机指令)会展示虚拟机的字节码指令。
|
* **VM code**(虚拟机指令)会展示虚拟机的字节码指令。
|
||||||
|
|
||||||
3. **默认执行模式:**
|
3. **默认执行模式: **
|
||||||
编译器会在 **DEBUG 模式** 下运行,显示详细的执行过程和状态,并且在虚拟机中执行编译后的代码,最后会打印出所有局部变量的值。
|
编译器会在 **DEBUG 模式** 下运行,显示详细的执行过程和状态,并且在虚拟机中执行编译后的代码,最后会打印出所有局部变量的值。
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -324,30 +324,30 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
|||||||
|
|
||||||
集成编译需要使用 `.cloud` 文件来指定项目的配置和结构,适用于项目标准化、依赖管理、构建管理和项目分发等场景。
|
集成编译需要使用 `.cloud` 文件来指定项目的配置和结构,适用于项目标准化、依赖管理、构建管理和项目分发等场景。
|
||||||
|
|
||||||
#### 集成编译命令:
|
#### 集成编译命令:
|
||||||
|
|
||||||
1. **基本用法:**
|
1. **基本用法: **
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
snow [OPTIONS] <command>
|
snow [OPTIONS] <command>
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **命令选项:**
|
2. **命令选项: **
|
||||||
|
|
||||||
* `-h, --help`:显示帮助信息并退出。
|
* `-h, --help`: 显示帮助信息并退出。
|
||||||
* `-v, --version`:打印 Snow 编程语言的版本并退出。
|
* `-v, --version`: 打印 Snow 编程语言的版本并退出。
|
||||||
|
|
||||||
3. **可用命令:**
|
3. **可用命令: **
|
||||||
|
|
||||||
* `compile`:将 `.snow` 源文件编译成虚拟机字节码文件(`.water`)。此命令会使用 `.cloud` 文件来指导编译过程。
|
* `compile`: 将 `.snow` 源文件编译成虚拟机字节码文件(`.water`)。此命令会使用 `.cloud` 文件来指导编译过程。
|
||||||
* `clean`:清理构建输出和本地缓存,移除中间产物,释放磁盘空间。
|
* `clean`: 清理构建输出和本地缓存,移除中间产物,释放磁盘空间。
|
||||||
* `version`:打印 Snow 的版本。
|
* `version`: 打印 Snow 的版本。
|
||||||
* `run`:运行已编译的虚拟机字节码文件(`.water`)。
|
* `run`: 运行已编译的虚拟机字节码文件(`.water`)。
|
||||||
* `init`:初始化一个新项目,生成 `project.cloud` 文件。
|
* `init`: 初始化一个新项目,生成 `project.cloud` 文件。
|
||||||
* `generate`:根据 `project.cloud` 生成项目目录结构。
|
* `generate`: 根据 `project.cloud` 生成项目目录结构。
|
||||||
* `build`:构建当前项目,按顺序解析依赖、编译和打包。
|
* `build`: 构建当前项目,按顺序解析依赖、编译和打包。
|
||||||
|
|
||||||
4. **例如,执行集成编译命令:**
|
4. **例如,执行集成编译命令: **
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
snow compile [SnowCode].snow
|
snow compile [SnowCode].snow
|
||||||
@ -355,14 +355,14 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
|||||||
|
|
||||||
* 此命令会使用 `.cloud` 文件中的配置信息来指导编译过程,并生成 `.water`。
|
* 此命令会使用 `.cloud` 文件中的配置信息来指导编译过程,并生成 `.water`。
|
||||||
|
|
||||||
5. **使用帮助:**
|
5. **使用帮助: **
|
||||||
如果你需要了解某个命令的详细选项,可以使用:
|
如果你需要了解某个命令的详细选项,可以使用:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
snow <command> --help
|
snow <command> --help
|
||||||
```
|
```
|
||||||
|
|
||||||
例如,查看 `compile` 命令的具体选项:
|
例如,查看 `compile` 命令的具体选项:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
snow compile --help
|
snow compile --help
|
||||||
@ -372,7 +372,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
|||||||
|
|
||||||
## 示例代码片段
|
## 示例代码片段
|
||||||
|
|
||||||
以下是一个简单的 Snow 代码示例,演示模块定义,导入和函数声明的基本语法:
|
以下是一个简单的 Snow 代码示例,演示模块定义,导入和函数声明的基本语法:
|
||||||
|
|
||||||
```snow
|
```snow
|
||||||
module: Math
|
module: Math
|
||||||
@ -407,10 +407,10 @@ module: Math
|
|||||||
end module
|
end module
|
||||||
```
|
```
|
||||||
|
|
||||||
上述代码定义了一个名为 `Math` 的模块,其中包含两个函数:
|
上述代码定义了一个名为 `Math` 的模块,其中包含两个函数:
|
||||||
|
|
||||||
* `main`:不接收任何参数,返回类型为 `int`。在函数体内调用了 `Math.factorial(6)`,然后返回 `0`。
|
* `main`: 不接收任何参数,返回类型为 `int`。在函数体内调用了 `Math.factorial(6)`,然后返回 `0`。
|
||||||
* `factorial`:接收一个 `int` 类型的参数 `n`,返回类型为 `int`。函数体内先声明并初始化局部变量 `num1` 为 `1`,然后通过一个
|
* `factorial`: 接收一个 `int` 类型的参数 `n`,返回类型为 `int`。函数体内先声明并初始化局部变量 `num1` 为 `1`,然后通过一个
|
||||||
`loop` 循环(从 `counter = 1` 到 `counter <= n`)依次将 `num1` 乘以 `counter`,循环结束后返回 `num1`,即 `n` 的阶乘值。
|
`loop` 循环(从 `counter = 1` 到 `counter <= n`)依次将 `num1` 乘以 `counter`,循环结束后返回 `num1`,即 `n` 的阶乘值。
|
||||||
|
|
||||||
|
|
||||||
@ -418,45 +418,45 @@ end module
|
|||||||
|
|
||||||
## 项目结构说明
|
## 项目结构说明
|
||||||
|
|
||||||
* `compiler/`:Snow 编译器源代码目录
|
* `compiler/`: Snow 编译器源代码目录
|
||||||
|
|
||||||
* `lexer/`:词法分析模块,负责将源码切分为 Token
|
* `lexer/`: 词法分析模块,负责将源码切分为 Token
|
||||||
* `parser/`:语法分析模块,将 Token 流解析为 AST(含模块/函数/语句解析)
|
* `parser/`: 语法分析模块,将 Token 流解析为 AST(含模块/函数/语句解析)
|
||||||
* `semantic/`:语义分析模块,负责符号表管理、类型检查等
|
* `semantic/`: 语义分析模块,负责符号表管理、类型检查等
|
||||||
* `ir/`:中间表示(IR)模块,生成并管理三地址码形式的中间代码
|
* `ir/`: 中间表示(IR)模块,生成并管理三地址码形式的中间代码
|
||||||
* `backend/`:编译器后端模块,将 IR 翻译为虚拟机指令,包含寄存器分配和指令生成器
|
* `backend/`: 编译器后端模块,将 IR 翻译为虚拟机指令,包含寄存器分配和指令生成器
|
||||||
|
|
||||||
* `vm/`:虚拟机相关源代码目录
|
* `vm/`: 虚拟机相关源代码目录
|
||||||
|
|
||||||
* `commands/`:定义 SnowVM 指令集的具体实现
|
* `commands/`: 定义 SnowVM 指令集的具体实现
|
||||||
* `engine/`:核心执行引擎,提供指令执行和寄存器/栈管理
|
* `engine/`: 核心执行引擎,提供指令执行和寄存器/栈管理
|
||||||
* `execution/`:执行流程控制(按指令顺序执行、分支跳转等)
|
* `execution/`: 执行流程控制(按指令顺序执行、分支跳转等)
|
||||||
* `io/`:输入输出辅助类(加载指令、文件解析等)
|
* `io/`: 输入输出辅助类(加载指令、文件解析等)
|
||||||
* `gui/`:Swing 可视化调试面板,实时展示局部变量表
|
* `gui/`: Swing 可视化调试面板,实时展示局部变量表
|
||||||
* `factories/`、`utils/`:指令创建、日志调试等公共工具
|
* `factories/`、`utils/`: 指令创建、日志调试等公共工具
|
||||||
|
|
||||||
* `pkg/`:内置构建与包管理器 **snow pkg**
|
* `pkg/`: 内置构建与包管理器 **snow pkg**
|
||||||
|
|
||||||
* `dsl/`:`.cloud` 描述文件解析器
|
* `dsl/`: `.cloud` 描述文件解析器
|
||||||
* `tasks/`:预设任务实现(`clean · compile · run · package · publish` 等)
|
* `tasks/`: 预设任务实现(`clean · compile · run · package · publish` 等)
|
||||||
* `resolver/`:本地/远程仓库解析与缓存
|
* `resolver/`: 本地/远程仓库解析与缓存
|
||||||
* `lifecycle/`:任务生命周期钩子(pre/post 脚本等)
|
* `lifecycle/`: 任务生命周期钩子(pre/post 脚本等)
|
||||||
* `model/`:项目、依赖、版本等模型
|
* `model/`: 项目、依赖、版本等模型
|
||||||
* `utils/`:文件、日志、校验和等通用工具
|
* `utils/`: 文件、日志、校验和等通用工具
|
||||||
* `doc/`:开发者文档与示例 `.cloud` 配置
|
* `doc/`: 开发者文档与示例 `.cloud` 配置
|
||||||
|
|
||||||
* `cli/`:独立的命令行前端
|
* `cli/`: 独立的命令行前端
|
||||||
|
|
||||||
* `commands/`:`compile` / `run` / `pkg` 等子命令实现
|
* `commands/`: `compile` / `run` / `pkg` 等子命令实现
|
||||||
* `api/`:公共选项解析、终端交互抽象
|
* `api/`: 公共选项解析、终端交互抽象
|
||||||
* `utils/`:终端颜色、进度条、异常格式化等
|
* `utils/`: 终端颜色、进度条、异常格式化等
|
||||||
* `SnowCLI.java`:CLI 主入口
|
* `SnowCLI.java`: CLI 主入口
|
||||||
|
|
||||||
|
|
||||||
## 版权声明
|
## 版权声明
|
||||||
|
|
||||||
版权所有 © 2025 许轲(Luke),代表 SnowLang 项目。
|
版权所有 © 2025 许轲(Luke),代表 SnowLang 项目。
|
||||||
仓库地址:<https://gitee.com/jcnc-org/snow>
|
仓库地址: <https://gitee.com/jcnc-org/snow>
|
||||||
本项目依据 [Apache 2.0 许可证](LICENSE) 进行许可和发布。
|
本项目依据 [Apache 2.0 许可证](LICENSE) 进行许可和发布。
|
||||||
|
|
||||||
“SnowLang 项目”为由许轲(Luke)发起的独立开源项目。
|
“SnowLang 项目”为由许轲(Luke)发起的独立开源项目。
|
||||||
@ -464,7 +464,7 @@ end module
|
|||||||
|
|
||||||
## 加入我们
|
## 加入我们
|
||||||
|
|
||||||
- 微信:`xuxiaolankaka`
|
- 微信: `xuxiaolankaka`
|
||||||
- QQ:`1399528359`
|
- QQ: `1399528359`
|
||||||
- 邮箱:`luke.k.xu [at] hotmail.com`
|
- 邮箱: `luke.k.xu [at] hotmail.com`
|
||||||
|
|
||||||
|
|||||||
@ -2,27 +2,27 @@
|
|||||||
|
|
||||||
在执行 `build-project2tar.ps1` 脚本之前,您需要确保 PowerShell 的执行策略允许运行脚本。默认情况下,PowerShell 可能阻止未签名的脚本执行。因此,您需要设置适当的执行策略。
|
在执行 `build-project2tar.ps1` 脚本之前,您需要确保 PowerShell 的执行策略允许运行脚本。默认情况下,PowerShell 可能阻止未签名的脚本执行。因此,您需要设置适当的执行策略。
|
||||||
|
|
||||||
#### 步骤 1:以管理员身份打开 PowerShell
|
#### 步骤 1: 以管理员身份打开 PowerShell
|
||||||
|
|
||||||
* 在 Windows 系统中,搜索 **PowerShell**,右键点击 **Windows PowerShell**,并选择 **以管理员身份运行**。
|
* 在 Windows 系统中,搜索 **PowerShell**,右键点击 **Windows PowerShell**,并选择 **以管理员身份运行**。
|
||||||
|
|
||||||
#### 步骤 2:设置 PowerShell 执行策略
|
#### 步骤 2: 设置 PowerShell 执行策略
|
||||||
|
|
||||||
为了允许执行 PowerShell 脚本,您需要调整当前用户的执行策略。输入以下命令并按 Enter:
|
为了允许执行 PowerShell 脚本,您需要调整当前用户的执行策略。输入以下命令并按 Enter:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 解释:
|
#### 解释:
|
||||||
|
|
||||||
* `-Scope CurrentUser`:此参数指定该执行策略仅对当前用户有效,而不会影响系统范围内的其他用户。
|
* `-Scope CurrentUser`: 此参数指定该执行策略仅对当前用户有效,而不会影响系统范围内的其他用户。
|
||||||
* `-ExecutionPolicy RemoteSigned`:此策略表示:
|
* `-ExecutionPolicy RemoteSigned`: 此策略表示:
|
||||||
|
|
||||||
* 本地创建的脚本可以直接运行。
|
* 本地创建的脚本可以直接运行。
|
||||||
* 从互联网下载的脚本必须具备有效的数字签名才能运行。没有签名的脚本将无法执行,除非您先解除阻止该脚本。
|
* 从互联网下载的脚本必须具备有效的数字签名才能运行。没有签名的脚本将无法执行,除非您先解除阻止该脚本。
|
||||||
|
|
||||||
#### 步骤 3:运行 `build-project2tar.ps1` 脚本
|
#### 步骤 3: 运行 `build-project2tar.ps1` 脚本
|
||||||
|
|
||||||
设置完成后,您可以在 PowerShell 中运行 `build-project2tar.ps1` 脚本。确保您已经切换到包含该脚本的目录,或提供完整的文件路径来执行它。
|
设置完成后,您可以在 PowerShell 中运行 `build-project2tar.ps1` 脚本。确保您已经切换到包含该脚本的目录,或提供完整的文件路径来执行它。
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ $parentDir = Split-Path -Parent $scriptDir
|
|||||||
$tarPath = Join-Path $parentDir $tarName
|
$tarPath = Join-Path $parentDir $tarName
|
||||||
|
|
||||||
# 输出开始创建 tar 包的消息
|
# 输出开始创建 tar 包的消息
|
||||||
Write-Output "开始创建 tar 包:$tarName 到 $parentDir ..."
|
Write-Output "开始创建 tar 包: $tarName 到 $parentDir ..."
|
||||||
|
|
||||||
# 如果存在旧 tar 包,先删除它
|
# 如果存在旧 tar 包,先删除它
|
||||||
if (Test-Path $tarPath) {
|
if (Test-Path $tarPath) {
|
||||||
@ -26,7 +26,7 @@ if (-not (Get-Command $tarCommand -ErrorAction SilentlyContinue)) {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# 执行打包操作:切换到 org\jcnc 目录下再压缩 snow 文件夹
|
# 执行打包操作: 切换到 org\jcnc 目录下再压缩 snow 文件夹
|
||||||
try {
|
try {
|
||||||
# 构建命令并执行
|
# 构建命令并执行
|
||||||
$tarCommandArgs = "-cf", $tarPath, "-C", "$scriptDir\..\src\main\java\org\jcnc", "snow"
|
$tarCommandArgs = "-cf", $tarPath, "-C", "$scriptDir\..\src\main\java\org\jcnc", "snow"
|
||||||
@ -34,7 +34,7 @@ try {
|
|||||||
|
|
||||||
& $tarCommand @tarCommandArgs
|
& $tarCommand @tarCommandArgs
|
||||||
} catch {
|
} catch {
|
||||||
Write-Error "❌ 创建 tar 包失败。错误信息:$_"
|
Write-Error "❌ 创建 tar 包失败。错误信息: $_"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 1. 版本控制基础
|
## 1. 版本控制基础
|
||||||
|
|
||||||
本项目使用 Git 进行版本控制,并遵循以下基本原则:
|
本项目使用 Git 进行版本控制,并遵循以下基本原则:
|
||||||
|
|
||||||
* 所有代码更改必须通过 Git 提交,并推送至远程仓库。
|
* 所有代码更改必须通过 Git 提交,并推送至远程仓库。
|
||||||
* 每次提交必须包括清晰、简洁且具描述性的提交信息,确保团队成员能够轻松理解变更的目的和内容。
|
* 每次提交必须包括清晰、简洁且具描述性的提交信息,确保团队成员能够轻松理解变更的目的和内容。
|
||||||
@ -10,23 +10,23 @@
|
|||||||
|
|
||||||
## 2. 分支管理
|
## 2. 分支管理
|
||||||
|
|
||||||
本项目采用以下分支策略进行代码管理:
|
本项目采用以下分支策略进行代码管理:
|
||||||
|
|
||||||
### 2.1 主分支 (`main`)
|
### 2.1 主分支 (`main`)
|
||||||
|
|
||||||
* **用途**:`main` 分支始终保持项目的稳定版本,且此分支的代码随时可以部署到生产环境。
|
* **用途**: `main` 分支始终保持项目的稳定版本,且此分支的代码随时可以部署到生产环境。
|
||||||
* **更新规则**:仅允许经过充分测试并审查的代码合并到 `main` 分支。每次从 `dev` 或 `release` 分支合并到 `main` 时,必须打上版本标签。
|
* **更新规则**: 仅允许经过充分测试并审查的代码合并到 `main` 分支。每次从 `dev` 或 `release` 分支合并到 `main` 时,必须打上版本标签。
|
||||||
|
|
||||||
### 2.2 开发分支 (`dev`)
|
### 2.2 开发分支 (`dev`)
|
||||||
|
|
||||||
* **用途**:`dev` 分支是所有开发工作的集成分支。所有的新功能开发应首先合并至 `dev` 分支,并经过集成测试后再合并到 `main`。
|
* **用途**: `dev` 分支是所有开发工作的集成分支。所有的新功能开发应首先合并至 `dev` 分支,并经过集成测试后再合并到 `main`。
|
||||||
* **更新规则**:所有功能开发完成后,应合并至 `dev` 分支进行集成测试,确认没有问题后再合并到 `main`。
|
* **更新规则**: 所有功能开发完成后,应合并至 `dev` 分支进行集成测试,确认没有问题后再合并到 `main`。
|
||||||
|
|
||||||
### 2.3 功能分支 (`feature/*`)
|
### 2.3 功能分支 (`feature/*`)
|
||||||
|
|
||||||
* **用途**:每个新功能的开发都应从 `dev` 分支创建一个独立的功能分支。
|
* **用途**: 每个新功能的开发都应从 `dev` 分支创建一个独立的功能分支。
|
||||||
* **命名规范**:`feature/功能描述`,例如:`feature/ast-folding`、`feature/user-cli`。所有分支名称应使用小写字母,并且使用破折号(`-`)分隔单词。
|
* **命名规范**: `feature/功能描述`,例如: `feature/ast-folding`、`feature/user-cli`。所有分支名称应使用小写字母,并且使用破折号(`-`)分隔单词。
|
||||||
* **开发流程**:
|
* **开发流程**:
|
||||||
|
|
||||||
1. 从 `dev` 分支拉取最新代码。
|
1. 从 `dev` 分支拉取最新代码。
|
||||||
2. 完成功能开发后,在本地提交代码并推送至远程仓库。
|
2. 完成功能开发后,在本地提交代码并推送至远程仓库。
|
||||||
@ -34,9 +34,9 @@
|
|||||||
|
|
||||||
### 2.4 修复分支 (`bugfix/*`)
|
### 2.4 修复分支 (`bugfix/*`)
|
||||||
|
|
||||||
* **用途**:用于修复 Bug,修复分支可以从 `dev` 或 `main` 分支创建。
|
* **用途**: 用于修复 Bug,修复分支可以从 `dev` 或 `main` 分支创建。
|
||||||
* **命名规范**:`bugfix/bug描述`,例如:`bugfix/fix-ast-error`。
|
* **命名规范**: `bugfix/bug描述`,例如: `bugfix/fix-ast-error`。
|
||||||
* **开发流程**:
|
* **开发流程**:
|
||||||
|
|
||||||
1. 从 `dev` 或 `main` 分支拉取最新代码。
|
1. 从 `dev` 或 `main` 分支拉取最新代码。
|
||||||
2. 完成修复后,提交修改并推送至远程仓库。
|
2. 完成修复后,提交修改并推送至远程仓库。
|
||||||
@ -44,9 +44,9 @@
|
|||||||
|
|
||||||
### 2.5 发布分支 (`release/*`)
|
### 2.5 发布分支 (`release/*`)
|
||||||
|
|
||||||
* **用途**:当 `dev` 分支的功能开发完成且准备发布时,应创建一个 `release` 分支进行发布准备。
|
* **用途**: 当 `dev` 分支的功能开发完成且准备发布时,应创建一个 `release` 分支进行发布准备。
|
||||||
* **命名规范**:`release/vX.X.X`,例如:`release/v1.0.0`。
|
* **命名规范**: `release/vX.X.X`,例如: `release/v1.0.0`。
|
||||||
* **开发流程**:
|
* **开发流程**:
|
||||||
|
|
||||||
1. 从 `dev` 分支创建 `release` 分支。
|
1. 从 `dev` 分支创建 `release` 分支。
|
||||||
2. 在 `release` 分支上进行版本发布的最终准备工作,如文档更新、版本号调整等。
|
2. 在 `release` 分支上进行版本发布的最终准备工作,如文档更新、版本号调整等。
|
||||||
@ -54,24 +54,24 @@
|
|||||||
|
|
||||||
### 2.6 热修复分支 (`hotfix/*`)
|
### 2.6 热修复分支 (`hotfix/*`)
|
||||||
|
|
||||||
* **用途**:当生产环境中发现紧急问题(如 Bug 或系统崩溃等),需在 `main` 分支上进行快速修复时,应创建一个 `hotfix` 分支进行修复。
|
* **用途**: 当生产环境中发现紧急问题(如 Bug 或系统崩溃等),需在 `main` 分支上进行快速修复时,应创建一个 `hotfix` 分支进行修复。
|
||||||
* **命名规范**:`hotfix/bug描述`,例如:`hotfix/fix-production-crash`。
|
* **命名规范**: `hotfix/bug描述`,例如: `hotfix/fix-production-crash`。
|
||||||
* **开发流程**:
|
* **开发流程**:
|
||||||
|
|
||||||
1. 从 `main` 分支创建 `hotfix` 分支,确保该分支包含生产环境中最新的稳定版本。
|
1. 从 `main` 分支创建 `hotfix` 分支,确保该分支包含生产环境中最新的稳定版本。
|
||||||
2. 在 `hotfix` 分支上进行问题修复和相关调整。
|
2. 在 `hotfix` 分支上进行问题修复和相关调整。
|
||||||
3. 完成修复后,提交修改并推送至远程仓库。
|
3. 完成修复后,提交修改并推送至远程仓库。
|
||||||
4. 创建拉取请求(PR),将 `hotfix` 分支合并至 `main` 分支并打上版本标签,确保生产环境修复生效。
|
4. 创建拉取请求(PR),将 `hotfix` 分支合并至 `main` 分支并打上版本标签,确保生产环境修复生效。
|
||||||
5. 将修复后的变更合并回 `dev` 分支,确保所有的修复和调整同步到开发分支,防止后续开发中出现同样的问题。
|
5. 将修复后的变更合并回 `dev` 分支,确保所有的修复和调整同步到开发分支,防止后续开发中出现同样的问题。
|
||||||
6. **回滚策略**:如果热修复未能解决问题,立即回滚合并,删除 `hotfix` 分支并通知团队,确保不影响生产环境。
|
6. **回滚策略**: 如果热修复未能解决问题,立即回滚合并,删除 `hotfix` 分支并通知团队,确保不影响生产环境。
|
||||||
|
|
||||||
## 3. 提交规范
|
## 3. 提交规范
|
||||||
|
|
||||||
为确保提交信息清晰且易于理解,遵循以下提交规范:
|
为确保提交信息清晰且易于理解,遵循以下提交规范:
|
||||||
|
|
||||||
### 3.1 提交信息格式
|
### 3.1 提交信息格式
|
||||||
|
|
||||||
提交信息应简洁且具有描述性,格式如下:
|
提交信息应简洁且具有描述性,格式如下:
|
||||||
|
|
||||||
```
|
```
|
||||||
[类型] 描述
|
[类型] 描述
|
||||||
@ -81,20 +81,20 @@
|
|||||||
|
|
||||||
#### 提交类型
|
#### 提交类型
|
||||||
|
|
||||||
* `feat`:新增功能
|
* `feat`: 新增功能
|
||||||
* `fix`:修复 Bug
|
* `fix`: 修复 Bug
|
||||||
* `docs`:文档更新
|
* `docs`: 文档更新
|
||||||
* `style`:代码格式调整(不影响功能)
|
* `style`: 代码格式调整(不影响功能)
|
||||||
* `refactor`:代码重构
|
* `refactor`: 代码重构
|
||||||
* `test`:增加/修改测试
|
* `test`: 增加/修改测试
|
||||||
* `chore`:工具配置等其他杂项任务
|
* `chore`: 工具配置等其他杂项任务
|
||||||
* `ci`:持续集成相关改动
|
* `ci`: 持续集成相关改动
|
||||||
* `perf`:性能优化
|
* `perf`: 性能优化
|
||||||
|
|
||||||
#### 示例
|
#### 示例
|
||||||
|
|
||||||
* `feat: 添加 IR 折叠功能`
|
* `feat: 添加 IR 折叠功能`
|
||||||
* `fix: 修复问题 Y(原因:X bug,解决方案:Z)`
|
* `fix: 修复问题 Y(原因: X bug,解决方案: Z)`
|
||||||
* `docs: 更新 API 文档`
|
* `docs: 更新 API 文档`
|
||||||
* `refactor: 优化 AST 逻辑`
|
* `refactor: 优化 AST 逻辑`
|
||||||
|
|
||||||
@ -114,7 +114,7 @@
|
|||||||
### 4.2 代码审查
|
### 4.2 代码审查
|
||||||
|
|
||||||
* 所有 PR 必须经过至少一名开发者的代码审查。
|
* 所有 PR 必须经过至少一名开发者的代码审查。
|
||||||
* 审查时应关注以下方面:
|
* 审查时应关注以下方面:
|
||||||
|
|
||||||
* 代码是否符合项目的编码规范。
|
* 代码是否符合项目的编码规范。
|
||||||
* 是否提供了足够的单元测试覆盖。
|
* 是否提供了足够的单元测试覆盖。
|
||||||
@ -124,14 +124,14 @@
|
|||||||
|
|
||||||
## 5. 版本发布
|
## 5. 版本发布
|
||||||
|
|
||||||
版本发布基于 Git 标签,发布流程如下:
|
版本发布基于 Git 标签,发布流程如下:
|
||||||
|
|
||||||
### 5.1 打标签
|
### 5.1 打标签
|
||||||
|
|
||||||
每当版本准备发布时,应在 `main` 分支上打上版本标签:
|
每当版本准备发布时,应在 `main` 分支上打上版本标签:
|
||||||
|
|
||||||
* **版本号规则**:采用语义化版本控制(SemVer)格式,版本号由三部分组成:`主版本号.次版本号.修订号`(例如:`v1.0.0`)。
|
* **版本号规则**: 采用语义化版本控制(SemVer)格式,版本号由三部分组成: `主版本号.次版本号.修订号`(例如: `v1.0.0`)。
|
||||||
* **标签命令**:
|
* **标签命令**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git tag v1.0.0
|
git tag v1.0.0
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
## 2. 前置条件
|
## 2. 前置条件
|
||||||
|
|
||||||
1. 操作系统:Linux/macOS/Windows
|
1. 操作系统: Linux/macOS/Windows
|
||||||
2. Java 项目(Maven)
|
2. Java 项目(Maven)
|
||||||
3. GraalVM(建议 24+ 版本)
|
3. GraalVM(建议 24+ 版本)
|
||||||
|
|
||||||
@ -14,10 +14,10 @@
|
|||||||
|
|
||||||
### 3.1 安装 GraalVM
|
### 3.1 安装 GraalVM
|
||||||
|
|
||||||
1. 下载对应平台的 GraalVM Community 版本:[https://www.graalvm.org/downloads/](https://www.graalvm.org/downloads/)
|
1. 下载对应平台的 GraalVM Community 版本: [https://www.graalvm.org/downloads/](https://www.graalvm.org/downloads/)
|
||||||
2. 解压并配置环境变量:
|
2. 解压并配置环境变量:
|
||||||
|
|
||||||
3. 验证安装:
|
3. 验证安装:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
java -version
|
java -version
|
||||||
@ -44,12 +44,12 @@ Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 24.0.1+9.1 (build 24.0.1+9-jvmc
|
|||||||
|
|
||||||
## 4. Maven 项目配置文件
|
## 4. Maven 项目配置文件
|
||||||
|
|
||||||
通过将以下配置文件添加到 `pom.xml` 中,为 Native Image 启用 Maven 插件:
|
通过将以下配置文件添加到 `pom.xml` 中,为 Native Image 启用 Maven 插件:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<profiles>
|
<profiles>
|
||||||
<!--
|
<!--
|
||||||
原生镜像构建:Linux 平台
|
原生镜像构建: Linux 平台
|
||||||
- 使用 GraalVM 的 native-image 工具,生成静态链接的可执行文件
|
- 使用 GraalVM 的 native-image 工具,生成静态链接的可执行文件
|
||||||
- 依赖 musl libc,需提前安装并配置 musl-gcc 工具链
|
- 依赖 musl libc,需提前安装并配置 musl-gcc 工具链
|
||||||
-->
|
-->
|
||||||
@ -111,7 +111,7 @@ Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 24.0.1+9.1 (build 24.0.1+9-jvmc
|
|||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
原生镜像构建:Windows 平台
|
原生镜像构建: Windows 平台
|
||||||
- 使用 GraalVM 的 native-image 工具,生成 Windows 可执行文件
|
- 使用 GraalVM 的 native-image 工具,生成 Windows 可执行文件
|
||||||
- Windows 上不使用 musl,因此不配置静态链接
|
- Windows 上不使用 musl,因此不配置静态链接
|
||||||
-->
|
-->
|
||||||
@ -169,6 +169,6 @@ Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 24.0.1+9.1 (build 24.0.1+9-jvmc
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
3. 等待 Native Image 构建完成:这个过程可能较慢(数分钟)。
|
3. 等待 Native Image 构建完成: 这个过程可能较慢(数分钟)。
|
||||||
4. 可执行文件即可直接运行,无需 JVM。
|
4. 可执行文件即可直接运行,无需 JVM。
|
||||||
> 生成的可执行文件位于 target/ 目录。
|
> 生成的可执行文件位于 target/ 目录。
|
||||||
|
|||||||
@ -7,22 +7,22 @@
|
|||||||
|
|
||||||
## 1.2 背景与目标读者
|
## 1.2 背景与目标读者
|
||||||
|
|
||||||
自 ChatGPT 3.5 诞生以来,越来越多的个人和企业将 LLM 生成的代码融入日常开发与自动化。然而,实践证明:LLM 生成的代码虽然“看起来没问题”,却常因对底层语言细节把握不准导致微妙 Bug——比如运算符优先级混淆、作用域处理错误、甚至基础库调用都无法编译通过。
|
自 ChatGPT 3.5 诞生以来,越来越多的个人和企业将 LLM 生成的代码融入日常开发与自动化。然而,实践证明: LLM 生成的代码虽然“看起来没问题”,却常因对底层语言细节把握不准导致微妙 Bug——比如运算符优先级混淆、作用域处理错误、甚至基础库调用都无法编译通过。
|
||||||
|
|
||||||
于是,我萌生了这样一个想法:**能否设计一门编程语言,让 LLM 在“语法层面”能够更精准、高效地理解和生成代码?**
|
于是,我萌生了这样一个想法: **能否设计一门编程语言,让 LLM 在“语法层面”能够更精准、高效地理解和生成代码?**
|
||||||
|
|
||||||
传统编程语言的设计多偏重“计算机”的执行效率,程序员往往在抽象表达和底层性能之间不断权衡。Ruby 之父松本行弘提出“为人而不是为机器编程”的理念。而在 LLM 日益普及的今天,我们应当承认:大型模型正成为“超级程序员”,它们不仅是助手,更是“代码生产的中介”。如果一门语言的语法和设计能最大化发挥 LLM 的推理与生成能力,人机协作的效率将被极大提升。
|
传统编程语言的设计多偏重“计算机”的执行效率,程序员往往在抽象表达和底层性能之间不断权衡。Ruby 之父松本行弘提出“为人而不是为机器编程”的理念。而在 LLM 日益普及的今天,我们应当承认: 大型模型正成为“超级程序员”,它们不仅是助手,更是“代码生产的中介”。如果一门语言的语法和设计能最大化发挥 LLM 的推理与生成能力,人机协作的效率将被极大提升。
|
||||||
|
|
||||||
因此,**Snow** 的使命是:让编程语言不再“让人头疼”,而是“让 LLM 更加从容地书写”。目标受众涵盖 LLM 爱好者、开发者、对编译原理感兴趣的学生,以及对性能有追求的工程师甚至是初学者——每个人都能在 Snow 中找到乐趣与成长。
|
因此,**Snow** 的使命是: 让编程语言不再“让人头疼”,而是“让 LLM 更加从容地书写”。目标受众涵盖 LLM 爱好者、开发者、对编译原理感兴趣的学生,以及对性能有追求的工程师甚至是初学者——每个人都能在 Snow 中找到乐趣与成长。
|
||||||
|
|
||||||
## 1.3 文章目的
|
## 1.3 文章目的
|
||||||
|
|
||||||
本文将带你完整体验 Snow 从零到 v0.1 的诞生历程,围绕四个核心目标:
|
本文将带你完整体验 Snow 从零到 v0.1 的诞生历程,围绕四个核心目标:
|
||||||
|
|
||||||
* **兴趣驱动的坚持**:用真实经历激励更多人相信“兴趣是最好的动力”,哪怕工作再忙,也能靠热爱坚持探索。
|
* **兴趣驱动的坚持**: 用真实经历激励更多人相信“兴趣是最好的动力”,哪怕工作再忙,也能靠热爱坚持探索。
|
||||||
* **从规划到实践的拆解**:详解在有限资源下,如何一步步拆解出词法分析、语法解析、AST 构建、解释执行等关键模块,以“先可用后完备”为原则,稳步推进。
|
* **从规划到实践的拆解**: 详解在有限资源下,如何一步步拆解出词法分析、语法解析、AST 构建、解释执行等关键模块,以“先可用后完备”为原则,稳步推进。
|
||||||
* **经验与反思**:不仅有“成功输出 1+1=2”的成就感,也有调试死循环、运算符冲突等踩坑经历,全方位展示编程语言设计的挑战与思考。
|
* **经验与反思**: 不仅有“成功输出 1+1=2”的成就感,也有调试死循环、运算符冲突等踩坑经历,全方位展示编程语言设计的挑战与思考。
|
||||||
* **激励与号召**:希望 Snow 成为开源社区的新起点,邀请更多伙伴参与,见证从 v0.2、v1.0 甚至到未来并发、标准库、包管理等更大梦想的实现。
|
* **激励与号召**: 希望 Snow 成为开源社区的新起点,邀请更多伙伴参与,见证从 v0.2、v1.0 甚至到未来并发、标准库、包管理等更大梦想的实现。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -31,18 +31,18 @@
|
|||||||
## 2.1 现有工具的痛点
|
## 2.1 现有工具的痛点
|
||||||
|
|
||||||
* **编程语言过于灵活,缺乏规范**
|
* **编程语言过于灵活,缺乏规范**
|
||||||
常见的编程语言如 Python、PHP、Ruby,虽然语法简洁、上手快,但“灵活性”本身也带来了不少隐患:变量类型可以随意变化,作用域和命名规则宽松,团队协作时代码风格极易失控,隐蔽 Bug 难以及时发现。自动化运维和日常数据处理脚本往往随手一写,维护与交接时却漏洞百出、沟通成本高。许多开发者都在思考:如果有一门语法严谨、行为可预测,并天然适合团队协作与 LLM 生成的编程语言,是不是能让代码质量和工程效率都上一个台阶?
|
常见的编程语言如 Python、PHP、Ruby,虽然语法简洁、上手快,但“灵活性”本身也带来了不少隐患: 变量类型可以随意变化,作用域和命名规则宽松,团队协作时代码风格极易失控,隐蔽 Bug 难以及时发现。自动化运维和日常数据处理脚本往往随手一写,维护与交接时却漏洞百出、沟通成本高。许多开发者都在思考: 如果有一门语法严谨、行为可预测,并天然适合团队协作与 LLM 生成的编程语言,是不是能让代码质量和工程效率都上一个台阶?
|
||||||
|
|
||||||
* **缺乏专为 LLM 设计的编程语言**
|
* **缺乏专为 LLM 设计的编程语言**
|
||||||
当下主流编程语言,基本都是“为人类程序员”而设计,很少考虑 LLM 的生成和推理习惯。比如:部分语法容易混淆,作用域和可见性规则不直观,LLM 在生成时不仅需要大量提示,结果还常常不理想。缺少一门语法清晰、特征单一、对 LLM 友好的编程语言,导致自动化和智能生成代码场景下,仍然存在很多不可控和效率瓶颈。
|
当下主流编程语言,基本都是“为人类程序员”而设计,很少考虑 LLM 的生成和推理习惯。比如: 部分语法容易混淆,作用域和可见性规则不直观,LLM 在生成时不仅需要大量提示,结果还常常不理想。缺少一门语法清晰、特征单一、对 LLM 友好的编程语言,导致自动化和智能生成代码场景下,仍然存在很多不可控和效率瓶颈。
|
||||||
|
|
||||||
## 2.2 触发想法的场景
|
## 2.2 触发想法的场景
|
||||||
|
|
||||||
* **对高效与规范的需求日益突出**
|
* **对高效与规范的需求日益突出**
|
||||||
在实际开发和运维工作中,我们经常要写各种自动化脚本。由于编程语言过于灵活,代码风格极易失控,维护起来痛苦不堪。团队中常常讨论:能否有一门语法严谨、易于规范化、适合团队协作的编程语言?大家都希望提升代码质量,减少后期返工。
|
在实际开发和运维工作中,我们经常要写各种自动化脚本。由于编程语言过于灵活,代码风格极易失控,维护起来痛苦不堪。团队中常常讨论: 能否有一门语法严谨、易于规范化、适合团队协作的编程语言?大家都希望提升代码质量,减少后期返工。
|
||||||
|
|
||||||
* **自研编程语言的大胆设想**
|
* **自研编程语言的大胆设想**
|
||||||
随着 LLM 在自动化、辅助编程中的应用普及,越来越多场景下希望直接“让 LLM 写代码”。但事实是,不管是让 LLM 生成 Python 还是 PHP,总要写很多提示,还要人工修正各种细节。由此引发思考:如果有一门对 LLM 友好的编程语言,语法特征清晰、行为可预测,能不能大幅提升代码自动生成与落地的效率?
|
随着 LLM 在自动化、辅助编程中的应用普及,越来越多场景下希望直接“让 LLM 写代码”。但事实是,不管是让 LLM 生成 Python 还是 PHP,总要写很多提示,还要人工修正各种细节。由此引发思考: 如果有一门对 LLM 友好的编程语言,语法特征清晰、行为可预测,能不能大幅提升代码自动生成与落地的效率?
|
||||||
|
|
||||||
|
|
||||||
## 2.3 项目愿景
|
## 2.3 项目愿景
|
||||||
@ -62,12 +62,12 @@
|
|||||||
|
|
||||||
## 3.1 为什么开源
|
## 3.1 为什么开源
|
||||||
|
|
||||||
* **获得社区反馈,检验设计思路**:闭门造车易“自嗨”,开源能快速获得用户和专家的多视角建议。
|
* **获得社区反馈,检验设计思路**: 闭门造车易“自嗨”,开源能快速获得用户和专家的多视角建议。
|
||||||
* **边开源边完善,更吸引贡献者**:功能精简但可用时就发布,容易吸引早期用户参与共建。
|
* **边开源边完善,更吸引贡献者**: 功能精简但可用时就发布,容易吸引早期用户参与共建。
|
||||||
|
|
||||||
## 3.2 开源准备工作
|
## 3.2 开源准备工作
|
||||||
|
|
||||||
* **许可证选择**:
|
* **许可证选择**:
|
||||||
采用 [Apache-2.0](https://gitee.com/jcnc-org/snow/blob/main/LICENSE),最大程度降低贡献门槛。
|
采用 [Apache-2.0](https://gitee.com/jcnc-org/snow/blob/main/LICENSE),最大程度降低贡献门槛。
|
||||||
|
|
||||||
## 3.3 项目运行输出
|
## 3.3 项目运行输出
|
||||||
@ -248,11 +248,11 @@ Process has ended
|
|||||||
|
|
||||||
## 4.1 v0.2 初步目标
|
## 4.1 v0.2 初步目标
|
||||||
|
|
||||||
1. **完善变量作用域与高级函数调用**:
|
1. **完善变量作用域与高级函数调用**:
|
||||||
支持函数参数、返回值、本地与全局变量隔离,以及闭包基础,为并发/异步打基础。
|
支持函数参数、返回值、本地与全局变量隔离,以及闭包基础,为并发/异步打基础。
|
||||||
2. **完善错误提示与调试信息**:
|
2. **完善错误提示与调试信息**:
|
||||||
报错更精准,方便新手调试。
|
报错更精准,方便新手调试。
|
||||||
3. **预计发布日期:2025 年 7 月 30 日**
|
3. **预计发布日期: 2025 年 7 月 30 日**
|
||||||
|
|
||||||
## 4.2 v0.3 目标
|
## 4.2 v0.3 目标
|
||||||
1. **IDE的支持**
|
1. **IDE的支持**
|
||||||
@ -262,11 +262,11 @@ Process has ended
|
|||||||
|
|
||||||
## 4.3 v1.0 长期规划
|
## 4.3 v1.0 长期规划
|
||||||
|
|
||||||
1. **初步标准库**:
|
1. **初步标准库**:
|
||||||
I/O、字符串、JSON、文件系统,满足日常脚本需求。
|
I/O、字符串、JSON、文件系统,满足日常脚本需求。
|
||||||
2. **包管理与模块加载**:
|
2. **包管理与模块加载**:
|
||||||
设计 `snowpkg`,支持一键安装依赖、自动模块导入。
|
设计 `snowpkg`,支持一键安装依赖、自动模块导入。
|
||||||
3. **社区协作与贡献**:
|
3. **社区协作与贡献**:
|
||||||
开设设计讨论区、每月线上分享,鼓励贡献代码与案例,让更多人参与 Snow 的成长。
|
开设设计讨论区、每月线上分享,鼓励贡献代码与案例,让更多人参与 Snow 的成长。
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -275,15 +275,15 @@ Process has ended
|
|||||||
|
|
||||||
## 5.1 学习收获与成就感
|
## 5.1 学习收获与成就感
|
||||||
|
|
||||||
回望从零到 v0.1 的历程,我最开始设计了虚拟机,然后设计的编译器,最震撼的是:让一个想法变成可运行的代码,哪怕只输出一句“Hello, Snow!”也足以令人热血沸腾。每一次 Snow 在屏幕上输出,都让我更深刻理解了编译原理的乐趣。
|
回望从零到 v0.1 的历程,我最开始设计了虚拟机,然后设计的编译器,最震撼的是: 让一个想法变成可运行的代码,哪怕只输出一句“Hello, Snow!”也足以令人热血沸腾。每一次 Snow 在屏幕上输出,都让我更深刻理解了编译原理的乐趣。
|
||||||
|
|
||||||
## 5.2 技术敬畏与情感共鸣
|
## 5.2 技术敬畏与情感共鸣
|
||||||
|
|
||||||
也许有人会说“输出一句话算什么”,但其实,每一个简单的表达式背后,都凝结了无数技术细节:多字符运算符的处理、优先级解析、AST 与符号表、作用域管理、底层 GC 可行性……每一环都让人敬畏计算机科学之美。
|
也许有人会说“输出一句话算什么”,但其实,每一个简单的表达式背后,都凝结了无数技术细节: 多字符运算符的处理、优先级解析、AST 与符号表、作用域管理、底层 GC 可行性……每一环都让人敬畏计算机科学之美。
|
||||||
|
|
||||||
## 5.3 欢迎你的加入
|
## 5.3 欢迎你的加入
|
||||||
|
|
||||||
真诚邀请所有对编程语言、编译原理、LLM 应用感兴趣的小伙伴:
|
真诚邀请所有对编程语言、编译原理、LLM 应用感兴趣的小伙伴:
|
||||||
|
|
||||||
1. 在 Gitee 提交 Issue,反馈使用体验和建议;
|
1. 在 Gitee 提交 Issue,反馈使用体验和建议;
|
||||||
2. Fork 仓库、贡献 PR,参与语法和功能共建;
|
2. Fork 仓库、贡献 PR,参与语法和功能共建;
|
||||||
@ -294,7 +294,7 @@ Process has ended
|
|||||||
> 微信: xuxiaolankaka
|
> 微信: xuxiaolankaka
|
||||||
> QQ: 1399528359
|
> QQ: 1399528359
|
||||||
|
|
||||||
对于从未写过语言的初学者,我想说:**不要害怕,从 Hello World 开始,你会发现编译原理其实很有趣。** 让我们一起,把 Snow 打造为兼顾 LLM 友好和人类易用的创新编程语言。也许,下一个改变编程世界的创举,就在我们手中诞生。
|
对于从未写过语言的初学者,我想说: **不要害怕,从 Hello World 开始,你会发现编译原理其实很有趣。** 让我们一起,把 Snow 打造为兼顾 LLM 友好和人类易用的创新编程语言。也许,下一个改变编程世界的创举,就在我们手中诞生。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Snow 语言现状和下一阶段开发路线图
|
# Snow 语言现状和下一阶段开发路线图
|
||||||
|
|
||||||
> 日期:2025-06-11
|
> 日期: 2025-06-11
|
||||||
|
|
||||||
## 1. 代码结构与职责
|
## 1. 代码结构与职责
|
||||||
| 层次 | 主要包/目录 | 说明 |
|
| 层次 | 主要包/目录 | 说明 |
|
||||||
@ -30,7 +30,7 @@
|
|||||||
6. **VM**
|
6. **VM**
|
||||||
|
|
||||||
* 栈-基 / 寄存器混合架构
|
* 栈-基 / 寄存器混合架构
|
||||||
* 96 条已实现指令(按数据宽度泛化:B/S/I/L/F/D)
|
* 96 条已实现指令(按数据宽度泛化: B/S/I/L/F/D)
|
||||||
* 运行时启动器 `VMLauncher`
|
* 运行时启动器 `VMLauncher`
|
||||||
|
|
||||||
|
|
||||||
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
## 4. 下一阶段开发路线图
|
## 4. 下一阶段开发路线图
|
||||||
|
|
||||||
> 优先级:P0 = 当前版本必须,P1 = 下一个小版本,P2 = 中长期
|
> 优先级: P0 = 当前版本必须,P1 = 下一个小版本,P2 = 中长期
|
||||||
|
|
||||||
| 优先级 | 功能 | 关键任务 |
|
| 优先级 | 功能 | 关键任务 |
|
||||||
|--------|------------------|----------------------------------------------------------------------------------------------------------------------|
|
|--------|------------------|----------------------------------------------------------------------------------------------------------------------|
|
||||||
@ -58,18 +58,18 @@
|
|||||||
| **P0** | **一元表达式解析** | \* 实现 `UnaryOperatorParselet`(`-`, `+`, `!`)<br>\* 对应 `UnaryOpGenerator` 注册 |
|
| **P0** | **一元表达式解析** | \* 实现 `UnaryOperatorParselet`(`-`, `+`, `!`)<br>\* 对应 `UnaryOpGenerator` 注册 |
|
||||||
| **P1** | **数组与切片** | \* 设计 `ArrayType`(元素类型 + 维度)<br>\* 新增 `IndexExpressionNode`、`NewArrayNode`<br>\* VM 扩充 `ALOAD/ASTORE` 指令 |
|
| **P1** | **数组与切片** | \* 设计 `ArrayType`(元素类型 + 维度)<br>\* 新增 `IndexExpressionNode`、`NewArrayNode`<br>\* VM 扩充 `ALOAD/ASTORE` 指令 |
|
||||||
| **P1** | **基础标准库** | \* `print/println`, 文件读写<br>\* 编译期内置绑定到 VM calls |
|
| **P1** | **基础标准库** | \* `print/println`, 文件读写<br>\* 编译期内置绑定到 VM calls |
|
||||||
| **P1** | **测试与 CI** | \* JUnit5 单测:Lexer / Parser / Semantic / VM<br>\* CI/CD 自动构建、示例编译运行 |
|
| **P1** | **测试与 CI** | \* JUnit5 单测: Lexer / Parser / Semantic / VM<br>\* CI/CD 自动构建、示例编译运行 |
|
||||||
| **P2** | **结构体 / 简单面向对象** | \* 结构体 语法、记录类型布局<br>\* 方法调度:静态 or 虚表 |
|
| **P2** | **结构体 / 简单面向对象** | \* 结构体 语法、记录类型布局<br>\* 方法调度: 静态 or 虚表 |
|
||||||
| **P2** | **优化管线** | \* 常量折叠、公共子表达式消除<br>\* 简易死代码清除 |
|
| **P2** | **优化管线** | \* 常量折叠、公共子表达式消除<br>\* 简易死代码清除 |
|
||||||
| **P2** | **错误与异常系统** | \* 语法:`try … catch … end`<br>\* VM:展开-收缩栈,异常表 |
|
| **P2** | **错误与异常系统** | \* 语法: `try … catch … end`<br>\* VM: 展开-收缩栈,异常表 |
|
||||||
| **P2** | **包管理 & CLI** | \* `snowc` 命令:`build`, `run`, `test`<br>\* 本地缓存 `.snowpkg`与包版本语义 |
|
| **P2** | **包管理 & CLI** | \* `snowc` 命令: `build`, `run`, `test`<br>\* 本地缓存 `.snowpkg`与包版本语义 |
|
||||||
|
|
||||||
|
|
||||||
## 5.1 里程碑排期
|
## 5.1 里程碑排期
|
||||||
|
|
||||||
| 时间 | 目标 |
|
| 时间 | 目标 |
|
||||||
|---------|----------------------------------------|
|
|---------|----------------------------------------|
|
||||||
| 2025-07 | 发布 **v0.2.0**:布尔类型 + 一元运算、20+ 单元测试 |
|
| 2025-07 | 发布 **v0.2.0**: 布尔类型 + 一元运算、20+ 单元测试 |
|
||||||
| 2025-08 | 发布 **v0.3.0**:数组/切片 & 基础标准库;引入 CLI |
|
| 2025-08 | 发布 **v0.3.0**: 数组/切片 & 基础标准库;引入 CLI |
|
||||||
| 2025-10 | 发布 **v0.4.0**:结构体支持、首批优化 Pass、>80% 覆盖率 |
|
| 2025-10 | 发布 **v0.4.0**: 结构体支持、首批优化 Pass、>80% 覆盖率 |
|
||||||
| 2026-11 | 发布 **v1.0.0**:异常系统、稳定包管理、文档完善 |
|
| 2026-11 | 发布 **v1.0.0**: 异常系统、稳定包管理、文档完善 |
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
## 0 · 符号约定
|
## 0 · 符号约定
|
||||||
|
|
||||||
* ⟦ … ⟧:可选项(0 或 1 次)
|
* ⟦ … ⟧: 可选项(0 或 1 次)
|
||||||
* { … }\*:可重复项(0 次或多次)
|
* { … }\*: 可重复项(0 次或多次)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -19,12 +19,12 @@
|
|||||||
identifier ::= [A-Za-z_][A-Za-z0-9_]* ;
|
identifier ::= [A-Za-z_][A-Za-z0-9_]* ;
|
||||||
```
|
```
|
||||||
|
|
||||||
* **区分大小写**:Foo 与 foo 为不同标识符。
|
* **区分大小写**: Foo 与 foo 为不同标识符。
|
||||||
* 保留字 (见 §1.3) **禁止** 用作标识符。
|
* 保留字 (见 §1.3) **禁止** 用作标识符。
|
||||||
|
|
||||||
### 1.2 分隔符与强制空白
|
### 1.2 分隔符与强制空白
|
||||||
|
|
||||||
相邻两个标记之间 **推荐** 至少以 1 个空白字符分隔,除非记号本身带有定界符 (( ) , : = < > 等)。示例:
|
相邻两个标记之间 **推荐** 至少以 1 个空白字符分隔,除非记号本身带有定界符 (( ) , : = < > 等)。示例:
|
||||||
|
|
||||||
```snow
|
```snow
|
||||||
module: Foo // 推荐
|
module: Foo // 推荐
|
||||||
@ -44,22 +44,22 @@ end loop break continue self
|
|||||||
|
|
||||||
### 1.4 文字量 (Literal)
|
### 1.4 文字量 (Literal)
|
||||||
|
|
||||||
* **整型**:123 0 -42
|
* **整型**: 123 0 -42
|
||||||
* **浮点**:3.14 0.0
|
* **浮点**: 3.14 0.0
|
||||||
* **布尔**:true false
|
* **布尔**: true false
|
||||||
* **字符串**:未来版本保留;当前规范未定义。
|
* **字符串**: 未来版本保留;当前规范未定义。
|
||||||
|
|
||||||
### 1.5 注释
|
### 1.5 注释
|
||||||
|
|
||||||
* **单行注释**:以 // 起,至当行行尾。
|
* **单行注释**: 以 // 起,至当行行尾。
|
||||||
* **多行注释**:/* … */ 可跨行。**不可嵌套**;嵌套会在最内层 */ 处终止外层,导致编译错误。
|
* **多行注释**: /* … */ 可跨行。**不可嵌套**;嵌套会在最内层 */ 处终止外层,导致编译错误。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 1.6 换行与缩进
|
### 1.6 换行与缩进
|
||||||
|
|
||||||
* **只有换行有语义**:以行末冒号(:)打开一个块时(如 module:、function:、if、loop: 等),块体**必须另起新行**。
|
* **只有换行有语义**: 以行末冒号(:)打开一个块时(如 module:、function:、if、loop: 等),块体**必须另起新行**。
|
||||||
* **缩进没有语义**:缩进仅用于提高代码可读性,对语法无影响。缩进不一致不会报错。
|
* **缩进没有语义**: 缩进仅用于提高代码可读性,对语法无影响。缩进不一致不会报错。
|
||||||
* 块体通过显式关闭关键字(如 end module、end function 等)结束。
|
* 块体通过显式关闭关键字(如 end module、end function 等)结束。
|
||||||
* 若关闭关键字与开始关键字之间的缩进不一致,不会报错,仍以关闭关键字为准。
|
* 若关闭关键字与开始关键字之间的缩进不一致,不会报错,仍以关闭关键字为准。
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ end module
|
|||||||
* **允许** 不同文件夹下声明同名模块,但模块全名(含包路径,用点分隔)在全项目中必须唯一。
|
* **允许** 不同文件夹下声明同名模块,但模块全名(含包路径,用点分隔)在全项目中必须唯一。
|
||||||
* 若项目中出现重复的模块全名,编译阶段将报重定义错误。
|
* 若项目中出现重复的模块全名,编译阶段将报重定义错误。
|
||||||
|
|
||||||
例如:
|
例如:
|
||||||
> src/util/math.snow // module: util.math
|
> src/util/math.snow // module: util.math
|
||||||
> src/core/math.snow // module: core.math
|
> src/core/math.snow // module: core.math
|
||||||
>
|
>
|
||||||
@ -95,15 +95,15 @@ import: <ModuleA>⟦ as <AliasA>⟧, <ModuleB>⟦ as <AliasB>⟧, …
|
|||||||
```
|
```
|
||||||
|
|
||||||
* **别名 (Alias)** 可为任何合法标识符,放在 as 关键字之后。
|
* **别名 (Alias)** 可为任何合法标识符,放在 as 关键字之后。
|
||||||
* **重复导入**:对同一模块多次导入(无论是否带 alias)只解析一次,其余忽略告警。
|
* **重复导入**: 对同一模块多次导入(无论是否带 alias)只解析一次,其余忽略告警。
|
||||||
* **循环依赖**:当前规范未定义,若出现编译器可拒绝或延迟解析。
|
* **循环依赖**: 当前规范未定义,若出现编译器可拒绝或延迟解析。
|
||||||
* **子模块**(诸如 A.B)暂不支持。
|
* **子模块**(诸如 A.B)暂不支持。
|
||||||
|
|
||||||
### 2.3 全路径引用
|
### 2.3 全路径引用
|
||||||
|
|
||||||
* 跨模块引用必须使用 _Prefix_._Name_,其中 *Prefix* 是模块名或导入时的别名。
|
* 跨模块引用必须使用 _Prefix_._Name_,其中 *Prefix* 是模块名或导入时的别名。
|
||||||
例如:Math.Point 或 M.sin。
|
例如: Math.Point 或 M.sin。
|
||||||
* **解析顺序**:未加前缀的标识符只在本模块查找;找不到则视为编译错误,不会隐式搜索导入模块。
|
* **解析顺序**: 未加前缀的标识符只在本模块查找;找不到则视为编译错误,不会隐式搜索导入模块。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -111,27 +111,27 @@ import: <ModuleA>⟦ as <AliasA>⟧, <ModuleB>⟦ as <AliasB>⟧, …
|
|||||||
|
|
||||||
### 3.1 作用域层级
|
### 3.1 作用域层级
|
||||||
|
|
||||||
1. **模块顶层**:全局变量、结构体、模块级函数。
|
1. **模块顶层**: 全局变量、结构体、模块级函数。
|
||||||
2. **结构体内部**:字段、方法、构造函数。
|
2. **结构体内部**: 字段、方法、构造函数。
|
||||||
3. **函数/方法**:形参与局部变量。
|
3. **函数/方法**: 形参与局部变量。
|
||||||
4. **局部嵌套块** (if / loop 等) 的局部变量。
|
4. **局部嵌套块** (if / loop 等) 的局部变量。
|
||||||
|
|
||||||
### 3.2 唯一性规则
|
### 3.2 唯一性规则
|
||||||
|
|
||||||
* **模块顶层唯一**:同一模块的 *结构体名*、*模块级函数名*、*全局变量名* **不得重名**。
|
* **模块顶层唯一**: 同一模块的 *结构体名*、*模块级函数名*、*全局变量名* **不得重名**。
|
||||||
* **结构体内部**:字段名与方法名不得相同;**允许有多个与结构体名同名的函数(即构造函数重载),但其参数签名必须互不相同。**
|
* **结构体内部**: 字段名与方法名不得相同;**允许有多个与结构体名同名的函数(即构造函数重载),但其参数签名必须互不相同。**
|
||||||
* **构造函数重载**:结构体内部可以声明多个与结构体名同名的函数作为构造函数,参数类型与数量必须不同,否则属于 DuplicateName 错误。
|
* **构造函数重载**: 结构体内部可以声明多个与结构体名同名的函数作为构造函数,参数类型与数量必须不同,否则属于 DuplicateName 错误。
|
||||||
* **跨层级遮蔽**:内层可定义与外层同名标识符(除关键字限制外),遵循最近作用域原则。
|
* **跨层级遮蔽**: 内层可定义与外层同名标识符(除关键字限制外),遵循最近作用域原则。
|
||||||
|
|
||||||
### 3.3 访问控制约定
|
### 3.3 访问控制约定
|
||||||
|
|
||||||
* **私有成员与方法**:以单个下划线 `_` 开头的变量名、字段名或方法名,**仅在其所属结构体或模块内部可见**。外部不可访问,编译器应报错 `AccessDenied`。
|
* **私有成员与方法**: 以单个下划线 `_` 开头的变量名、字段名或方法名,**仅在其所属结构体或模块内部可见**。外部不可访问,编译器应报错 `AccessDenied`。
|
||||||
- 例如 `_foo`, `_barMethod`。
|
- 例如 `_foo`, `_barMethod`。
|
||||||
* **公有成员与方法**:非下划线开头的变量、字段、方法,默认为公有。可在模块外部通过模块名/别名前缀访问。
|
* **公有成员与方法**: 非下划线开头的变量、字段、方法,默认为公有。可在模块外部通过模块名/别名前缀访问。
|
||||||
|
|
||||||
#### 影响范围
|
#### 影响范围
|
||||||
- **模块级变量与函数**:`globals` 和 `function` 语句声明的以 `_` 开头者,仅限本模块访问。
|
- **模块级变量与函数**: `globals` 和 `function` 语句声明的以 `_` 开头者,仅限本模块访问。
|
||||||
- **结构体字段与方法**:声明为 `_name`、`_doSomething` 的,仅结构体本身或其方法体可访问。
|
- **结构体字段与方法**: 声明为 `_name`、`_doSomething` 的,仅结构体本身或其方法体可访问。
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 访问示例
|
#### 访问示例
|
||||||
@ -220,7 +220,7 @@ struct: StructName
|
|||||||
end struct
|
end struct
|
||||||
```
|
```
|
||||||
|
|
||||||
**实例化**:
|
**实例化**:
|
||||||
|
|
||||||
* declare p: Point = Point(1, 2)
|
* declare p: Point = Point(1, 2)
|
||||||
* declare p2: Point = Point(3)
|
* declare p2: Point = Point(3)
|
||||||
@ -243,7 +243,7 @@ function: FuncName
|
|||||||
end function
|
end function
|
||||||
```
|
```
|
||||||
|
|
||||||
* **返回检查**:若 returns: 指定类型非 void,所有控制流路径必须显式 return 该类型值。
|
* **返回检查**: 若 returns: 指定类型非 void,所有控制流路径必须显式 return 该类型值。
|
||||||
* 在 loop 内或经 break 跳出后能到达的路径亦计入检查;若缺失,编译错误。
|
* 在 loop 内或经 break 跳出后能到达的路径亦计入检查;若缺失,编译错误。
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -287,7 +287,7 @@ loop:
|
|||||||
end loop
|
end loop
|
||||||
```
|
```
|
||||||
|
|
||||||
* **作用域**:init 块声明的变量仅在本 loop 的 init/cond/step/body 有效。
|
* **作用域**: init 块声明的变量仅在本 loop 的 init/cond/step/body 有效。
|
||||||
* break 立即终止当前循环;continue 跳过剩余 body,执行 step → cond。
|
* break 立即终止当前循环;continue 跳过剩余 body,执行 step → cond。
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -296,7 +296,7 @@ end loop
|
|||||||
|
|
||||||
### 6.1 数值类型
|
### 6.1 数值类型
|
||||||
|
|
||||||
Snow-Lang 支持下列**数值类型**,用于声明变量、参数、结构体字段、函数返回等:
|
Snow-Lang 支持下列**数值类型**,用于声明变量、参数、结构体字段、函数返回等:
|
||||||
|
|
||||||
| 类型名 | 关键字 | 位数 | 描述 |
|
| 类型名 | 关键字 | 位数 | 描述 |
|
||||||
|----------|--------|----|---------------------------|
|
|----------|--------|----|---------------------------|
|
||||||
@ -315,21 +315,19 @@ Snow-Lang 支持下列**数值类型**,用于声明变量、参数、结构体
|
|||||||
|
|
||||||
#### 数值字面量后缀
|
#### 数值字面量后缀
|
||||||
|
|
||||||
为指定具体类型,可在数值字面量后加后缀字母(大小写均可):
|
为指定具体类型,可在数值字面量后加后缀字母(大小写均可):
|
||||||
|
|
||||||
| 后缀 | 类型 | 例子 |
|
| 后缀 | 类型 | 例子 |
|
||||||
|----|--------|----------|
|
|----|--------|----------|
|
||||||
| b | byte | 7b, -2B |
|
| b | byte | 7b, -2B |
|
||||||
| s | short | 123s |
|
| s | short | 123s |
|
||||||
| i | int | 100i |
|
|
||||||
| l | long | 5l, 123L |
|
| l | long | 5l, 123L |
|
||||||
| f | float | 3.14f |
|
| f | float | 3.14f |
|
||||||
| d | double | 1.0d |
|
|
||||||
|
|
||||||
- 没有后缀的整数字面量自动为 `int`。
|
- 没有后缀的整数字面量自动为 `int`。
|
||||||
- 没有后缀的浮点字面量自动为 `double`。
|
- 没有后缀的浮点字面量自动为 `double`。
|
||||||
|
|
||||||
**示例:**
|
**示例: **
|
||||||
```snow
|
```snow
|
||||||
declare a: int = 7 // int (默认)
|
declare a: int = 7 // int (默认)
|
||||||
declare b: long = 9l // long
|
declare b: long = 9l // long
|
||||||
@ -405,17 +403,17 @@ declare a: Point = Point(1, 2)
|
|||||||
|
|
||||||
## 7 · 名字解析算法(概览)
|
## 7 · 名字解析算法(概览)
|
||||||
|
|
||||||
1. **输入**:未限定前缀的标识符 N,当前作用域 S。
|
1. **输入**: 未限定前缀的标识符 N,当前作用域 S。
|
||||||
2. 自内向外遍历作用域链查找 N;首次匹配即确定绑定。
|
2. 自内向外遍历作用域链查找 N;首次匹配即确定绑定。
|
||||||
3. 若遍历至模块顶层仍未匹配,编译错误 *UnresolvedIdentifier*。
|
3. 若遍历至模块顶层仍未匹配,编译错误 *UnresolvedIdentifier*。
|
||||||
4. 限定名 Prefix.N:直接在模块表中查 Prefix(包括别名),成功后在该模块顶层查找 N;找不到即 *UnresolvedQualifiedIdentifier*。
|
4. 限定名 Prefix.N: 直接在模块表中查 Prefix(包括别名),成功后在该模块顶层查找 N;找不到即 *UnresolvedQualifiedIdentifier*。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8 · 编译单位与入口
|
## 8 · 编译单位与入口
|
||||||
|
|
||||||
* **单一入口**:编译器需指定某模块某函数作为启动入口。
|
* **单一入口**: 编译器需指定某模块某函数作为启动入口。
|
||||||
* **模块初始化**:globals 块中的带初值变量在程序启动时自顶向下按出现顺序初始化;不同模块按依赖拓扑顺序初始化(循环依赖未定义)。
|
* **模块初始化**: globals 块中的带初值变量在程序启动时自顶向下按出现顺序初始化;不同模块按依赖拓扑顺序初始化(循环依赖未定义)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -445,7 +443,7 @@ module: RectExample
|
|||||||
declare x: int
|
declare x: int
|
||||||
declare y: int
|
declare y: int
|
||||||
|
|
||||||
// 构造函数1:两个参数
|
// 构造函数1: 两个参数
|
||||||
function: Point
|
function: Point
|
||||||
params:
|
params:
|
||||||
declare x: int
|
declare x: int
|
||||||
@ -456,7 +454,7 @@ module: RectExample
|
|||||||
end body
|
end body
|
||||||
end function
|
end function
|
||||||
|
|
||||||
// 构造函数2:一个参数
|
// 构造函数2: 一个参数
|
||||||
function: Point
|
function: Point
|
||||||
params:
|
params:
|
||||||
declare xy: int
|
declare xy: int
|
||||||
@ -553,8 +551,6 @@ end module
|
|||||||
|
|
||||||
## 11 · 构造函数重载示例
|
## 11 · 构造函数重载示例
|
||||||
|
|
||||||
**示例:**
|
|
||||||
|
|
||||||
````snow
|
````snow
|
||||||
struct: Point
|
struct: Point
|
||||||
declare x: int
|
declare x: int
|
||||||
@ -31,14 +31,14 @@ end module
|
|||||||
|
|
||||||
### 数据类型
|
### 数据类型
|
||||||
|
|
||||||
bool 类型:
|
bool 类型:
|
||||||
|
|
||||||
两种值:`true` 和 `false`
|
两种值: `true` 和 `false`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
数值类型:
|
数值类型:
|
||||||
|
|
||||||
| Number | keyword |
|
| Number | keyword |
|
||||||
|----------|---------|
|
|----------|---------|
|
||||||
@ -51,7 +51,7 @@ bool 类型:
|
|||||||
|
|
||||||
默认整数的类型为 int,浮点数的类型为 double。
|
默认整数的类型为 int,浮点数的类型为 double。
|
||||||
|
|
||||||
数值字面量后缀:
|
数值字面量后缀:
|
||||||
|
|
||||||
| 数值字面量后缀 | 例子 |
|
| 数值字面量后缀 | 例子 |
|
||||||
|---------|----|
|
|---------|----|
|
||||||
@ -64,7 +64,7 @@ bool 类型:
|
|||||||
|
|
||||||
|
|
||||||
### 变量
|
### 变量
|
||||||
定义变量的形式如下,中括号表示可选:
|
定义变量的形式如下,中括号表示可选:
|
||||||
|
|
||||||
```snow
|
```snow
|
||||||
declare name: type [= initial_value]
|
declare name: type [= initial_value]
|
||||||
@ -72,24 +72,24 @@ declare name: type [= initial_value]
|
|||||||
|
|
||||||
其中 `name` 是变量名,`type` 是变量类型,`initial_value` 是初始值
|
其中 `name` 是变量名,`type` 是变量类型,`initial_value` 是初始值
|
||||||
|
|
||||||
例:
|
例:
|
||||||
|
|
||||||
```snow
|
```snow
|
||||||
declare x: int
|
declare x: int
|
||||||
declare y: long = 123456789
|
declare y: long = 123456789
|
||||||
```
|
```
|
||||||
|
|
||||||
读取变量值的方法,直接写变量的名字即可:
|
读取变量值的方法,直接写变量的名字即可:
|
||||||
```snow
|
```snow
|
||||||
x
|
x
|
||||||
```
|
```
|
||||||
|
|
||||||
设置变量值的方法,先写变量名,后面接 `=`,最后写一个表达式即可:
|
设置变量值的方法,先写变量名,后面接 `=`,最后写一个表达式即可:
|
||||||
```snow
|
```snow
|
||||||
x = 10
|
x = 10
|
||||||
```
|
```
|
||||||
|
|
||||||
于是可以通过这种方式让变量参与计算并保存结果:
|
于是可以通过这种方式让变量参与计算并保存结果:
|
||||||
```snow
|
```snow
|
||||||
x = y + 1
|
x = y + 1
|
||||||
```
|
```
|
||||||
@ -99,7 +99,7 @@ x = y + 1
|
|||||||
|
|
||||||
## 流程控制
|
## 流程控制
|
||||||
### if
|
### if
|
||||||
if 语句的形式如下,else 是可选的:
|
if 语句的形式如下,else 是可选的:
|
||||||
|
|
||||||
```snow
|
```snow
|
||||||
if cond then
|
if cond then
|
||||||
@ -111,7 +111,7 @@ end if
|
|||||||
|
|
||||||
cond 可以是表达式(结果为 bool 类型)或者 bool 字面量
|
cond 可以是表达式(结果为 bool 类型)或者 bool 字面量
|
||||||
|
|
||||||
例:
|
例:
|
||||||
|
|
||||||
```snow
|
```snow
|
||||||
module: Main
|
module: Main
|
||||||
@ -131,7 +131,7 @@ end module
|
|||||||
```
|
```
|
||||||
|
|
||||||
### loop
|
### loop
|
||||||
loop 语句的形式如下:
|
loop 语句的形式如下:
|
||||||
```snow
|
```snow
|
||||||
loop:
|
loop:
|
||||||
init:
|
init:
|
||||||
@ -150,7 +150,7 @@ loop:
|
|||||||
end loop
|
end loop
|
||||||
```
|
```
|
||||||
|
|
||||||
例子(求 1 ~ 100 的和):
|
例子(求 1 ~ 100 的和):
|
||||||
```snow
|
```snow
|
||||||
module: Main
|
module: Main
|
||||||
function: main
|
function: main
|
||||||
@ -176,7 +176,7 @@ end module
|
|||||||
```
|
```
|
||||||
|
|
||||||
## 函数
|
## 函数
|
||||||
函数的形式如下:
|
函数的形式如下:
|
||||||
```snow
|
```snow
|
||||||
function: add
|
function: add
|
||||||
parameter:
|
parameter:
|
||||||
@ -197,7 +197,7 @@ end function
|
|||||||
通过 import 语句导入模块,
|
通过 import 语句导入模块,
|
||||||
snow 会自动将同名模块的函数合并。
|
snow 会自动将同名模块的函数合并。
|
||||||
|
|
||||||
在我们最初的例子中,就用了 module 这个关键字。让我们回忆一下:
|
在我们最初的例子中,就用了 module 这个关键字。让我们回忆一下:
|
||||||
|
|
||||||
```snow
|
```snow
|
||||||
module: Main
|
module: Main
|
||||||
@ -213,7 +213,7 @@ end module
|
|||||||
|
|
||||||
可以看到模块名是 Main,里面有函数 main。
|
可以看到模块名是 Main,里面有函数 main。
|
||||||
|
|
||||||
假如现在有一个模块 Math,代码如下:
|
假如现在有一个模块 Math,代码如下:
|
||||||
```snow
|
```snow
|
||||||
// Math.snow
|
// Math.snow
|
||||||
module: Math
|
module: Math
|
||||||
@ -229,7 +229,7 @@ module: Math
|
|||||||
end module
|
end module
|
||||||
```
|
```
|
||||||
|
|
||||||
可以使用 import 来导入 Math 模块:
|
可以使用 import 来导入 Math 模块:
|
||||||
```snow
|
```snow
|
||||||
// main.snow
|
// main.snow
|
||||||
module: Main
|
module: Main
|
||||||
@ -244,7 +244,7 @@ module: Main
|
|||||||
end module
|
end module
|
||||||
```
|
```
|
||||||
|
|
||||||
可以同时导入多个模块,用逗号(半角)分隔模块名即可:
|
可以同时导入多个模块,用逗号(半角)分隔模块名即可:
|
||||||
```snow
|
```snow
|
||||||
// main.snow
|
// main.snow
|
||||||
module: Main
|
module: Main
|
||||||
|
|||||||
@ -1,17 +1,9 @@
|
|||||||
module: Main
|
module: Main
|
||||||
function: main
|
function: main
|
||||||
return_type: int
|
|
||||||
body:
|
|
||||||
foo()
|
|
||||||
|
|
||||||
return 0
|
|
||||||
end body
|
|
||||||
end function
|
|
||||||
|
|
||||||
function: foo
|
|
||||||
return_type: void
|
return_type: void
|
||||||
body:
|
body:
|
||||||
|
declare abc!:int =1
|
||||||
|
|
||||||
end body
|
end body
|
||||||
end function
|
end function
|
||||||
end module
|
end module
|
||||||
|
|||||||
@ -7,34 +7,34 @@ module: Main
|
|||||||
5 == 7b
|
5 == 7b
|
||||||
5 == 7l
|
5 == 7l
|
||||||
5 == 7f
|
5 == 7f
|
||||||
5 == 7d
|
5 == 7.0
|
||||||
|
|
||||||
5b == 5b
|
5b == 5b
|
||||||
5b == 5s
|
5b == 5s
|
||||||
5b == 5l
|
5b == 5l
|
||||||
5b == 5f
|
5b == 5f
|
||||||
5b == 5d
|
5b == 5.0
|
||||||
|
|
||||||
5s == 5s
|
5s == 5s
|
||||||
5s == 5l
|
5s == 5l
|
||||||
5s == 5f
|
5s == 5f
|
||||||
5s == 5d
|
5s == 5.0
|
||||||
|
|
||||||
5l == 5l
|
5l == 5l
|
||||||
5l == 5f
|
5l == 5f
|
||||||
5l == 5d
|
5l == 5.0
|
||||||
|
|
||||||
5f == 5f
|
5f == 5f
|
||||||
5f == 5d
|
5f == 5.0
|
||||||
|
|
||||||
5d == 5d
|
5.0 == 5.0
|
||||||
|
|
||||||
declare b: byte = 8b
|
declare b: byte = 8b
|
||||||
declare s: short = 8s
|
declare s: short = 8s
|
||||||
declare i: int = 8
|
declare i: int = 8
|
||||||
declare l: long = 8l
|
declare l: long = 8l
|
||||||
declare f: float = 8f
|
declare f: float = 8f
|
||||||
declare d: double = 8d
|
declare d: double = 8
|
||||||
|
|
||||||
b == b
|
b == b
|
||||||
b == s
|
b == s
|
||||||
|
|||||||
8
pom.xml
8
pom.xml
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<!--
|
<!--
|
||||||
Java 编译插件:
|
Java 编译插件:
|
||||||
- 使用 Maven 自带的 maven-compiler-plugin 进行源码编译
|
- 使用 Maven 自带的 maven-compiler-plugin 进行源码编译
|
||||||
- <fork>true</fork> 表示在单独的进程中执行 javac,提高兼容性
|
- <fork>true</fork> 表示在单独的进程中执行 javac,提高兼容性
|
||||||
-->
|
-->
|
||||||
@ -45,7 +45,7 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Jar 打包插件:
|
Jar 打包插件:
|
||||||
- 使用 maven-jar-plugin 将编译产物打包成可执行 JAR
|
- 使用 maven-jar-plugin 将编译产物打包成可执行 JAR
|
||||||
- 在 MANIFEST 中指定主类,支持命令行直接 java -jar 调用
|
- 在 MANIFEST 中指定主类,支持命令行直接 java -jar 调用
|
||||||
-->
|
-->
|
||||||
@ -71,7 +71,7 @@
|
|||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<!--
|
<!--
|
||||||
原生镜像构建:Linux 平台
|
原生镜像构建: Linux 平台
|
||||||
- 使用 GraalVM 的 native-image 工具,生成静态链接的可执行文件
|
- 使用 GraalVM 的 native-image 工具,生成静态链接的可执行文件
|
||||||
- 依赖 musl libc,需提前安装并配置 musl-gcc 工具链
|
- 依赖 musl libc,需提前安装并配置 musl-gcc 工具链
|
||||||
-->
|
-->
|
||||||
@ -133,7 +133,7 @@
|
|||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
原生镜像构建:Windows 平台
|
原生镜像构建: Windows 平台
|
||||||
- 使用 GraalVM 的 native-image 工具,生成 Windows 可执行文件
|
- 使用 GraalVM 的 native-image 工具,生成 Windows 可执行文件
|
||||||
- Windows 上不使用 musl,因此不配置静态链接
|
- Windows 上不使用 musl,因此不配置静态链接
|
||||||
-->
|
-->
|
||||||
|
|||||||
@ -13,13 +13,13 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:构建当前项目(包含依赖解析、编译、打包)。
|
* CLI 命令: 构建当前项目(包含依赖解析、编译、打包)。
|
||||||
* <p>
|
* <p>
|
||||||
* 该命令会依次执行依赖解析、源码编译和产物打包阶段。
|
* 该命令会依次执行依赖解析、源码编译和产物打包阶段。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 用法示例:
|
* 用法示例:
|
||||||
* $ snow build
|
* $ snow build
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -6,13 +6,13 @@ import org.jcnc.snow.pkg.lifecycle.LifecyclePhase;
|
|||||||
import org.jcnc.snow.pkg.tasks.CleanTask;
|
import org.jcnc.snow.pkg.tasks.CleanTask;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:清理构建输出和本地缓存目录。
|
* CLI 命令: 清理构建输出和本地缓存目录。
|
||||||
* <p>
|
* <p>
|
||||||
* 用于清除项目生成的 build、dist 等中间产物,保持工作区整洁。
|
* 用于清除项目生成的 build、dist 等中间产物,保持工作区整洁。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 用法示例:
|
* 用法示例:
|
||||||
* $ snow clean
|
* $ snow clean
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -13,17 +13,17 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:编译当前项目。
|
* CLI 命令: 编译当前项目。
|
||||||
*
|
*
|
||||||
* <p>工作模式说明:</p>
|
* <p>工作模式说明: </p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><strong>Cloud 模式</strong>
|
* <li><strong>Cloud 模式</strong>
|
||||||
* - 项目根目录存在 {@code project.cloud} 时触发;
|
* - 项目根目录存在 {@code project.cloud} 时触发;
|
||||||
* - 解析 build 区块,自动推导源码目录与输出文件名;
|
* - 解析 build 区块,自动推导源码目录与输出文件名;
|
||||||
* - 用法:{@code snow compile [run]}</li>
|
* - 用法: {@code snow compile [run]}</li>
|
||||||
* <li><strong>Local 模式</strong>
|
* <li><strong>Local 模式</strong>
|
||||||
* - 未检测到 {@code project.cloud} 时回退;
|
* - 未检测到 {@code project.cloud} 时回退;
|
||||||
* - 保持向后兼容:{@code snow compile [run] [-o <name>] [-d <srcDir>] [file.snow …]}</li>
|
* - 保持向后兼容: {@code snow compile [run] [-o <name>] [-d <srcDir>] [file.snow …]}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>两种模式均将最终参数交由 {@link CompileTask} 处理。</p>
|
* <p>两种模式均将最终参数交由 {@link CompileTask} 处理。</p>
|
||||||
@ -67,12 +67,12 @@ public final class CompileCommand implements CLICommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 源码目录:build.srcDir -> 默认 src */
|
/* 源码目录: build.srcDir -> 默认 src */
|
||||||
String srcDir = project.getBuild().get("srcDir", "src");
|
String srcDir = project.getBuild().get("srcDir", "src");
|
||||||
argList.add("-d");
|
argList.add("-d");
|
||||||
argList.add(srcDir);
|
argList.add(srcDir);
|
||||||
|
|
||||||
/* 输出名称:build.output -> fallback to artifact */
|
/* 输出名称: build.output -> fallback to artifact */
|
||||||
String output = project.getBuild().get("output", project.getArtifact());
|
String output = project.getBuild().get("output", project.getArtifact());
|
||||||
argList.add("-o");
|
argList.add("-o");
|
||||||
argList.add(output);
|
argList.add(output);
|
||||||
|
|||||||
@ -12,19 +12,19 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:根据 project.cloud 生成项目目录结构。
|
* CLI 命令: 根据 project.cloud 生成项目目录结构。
|
||||||
* <p>
|
* <p>
|
||||||
* 负责解析云项目描述文件,并通过 {@link GenerateTask}
|
* 负责解析云项目描述文件,并通过 {@link GenerateTask}
|
||||||
* 在 INIT 生命周期阶段内生成基础目录结构。
|
* 在 INIT 生命周期阶段内生成基础目录结构。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 用法示例:
|
* 用法示例:
|
||||||
* $ snow generate
|
* $ snow generate
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 注意事项:
|
* 注意事项:
|
||||||
* - 若当前目录不存在 project.cloud,则提示用户先执行 `snow init`。
|
* - 若当前目录不存在 project.cloud,则提示用户先执行 `snow init`。
|
||||||
* - 执行成功后,会输出已创建的目录/文件。
|
* - 执行成功后,会输出已创建的目录/文件。
|
||||||
* </p>
|
* </p>
|
||||||
|
|||||||
@ -8,13 +8,13 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:初始化项目配置文件。
|
* CLI 命令: 初始化项目配置文件。
|
||||||
* <p>
|
* <p>
|
||||||
* 用于快速生成 DSL 配置文件(project.cloud)。
|
* 用于快速生成 DSL 配置文件(project.cloud)。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 用法示例:
|
* 用法示例:
|
||||||
* $ snow init
|
* $ snow init
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -8,13 +8,13 @@ import org.jcnc.snow.pkg.resolver.DependencyResolver;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:解析并下载项目依赖到本地缓存。
|
* CLI 命令: 解析并下载项目依赖到本地缓存。
|
||||||
* <p>
|
* <p>
|
||||||
* 适用于离线使用和依赖预热场景,会自动读取项目描述文件并处理依赖缓存。
|
* 适用于离线使用和依赖预热场景,会自动读取项目描述文件并处理依赖缓存。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 用法示例:
|
* 用法示例:
|
||||||
* $ snow install
|
* $ snow install
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -10,14 +10,14 @@ import org.jcnc.snow.pkg.tasks.PublishTask;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:将已构建的项目包发布到远程仓库。
|
* CLI 命令: 将已构建的项目包发布到远程仓库。
|
||||||
* <p>
|
* <p>
|
||||||
* 用于持续集成、交付或分发场景。
|
* 用于持续集成、交付或分发场景。
|
||||||
* 支持自动读取 DSL 项目描述文件,并注册和执行发布生命周期阶段的任务。
|
* 支持自动读取 DSL 项目描述文件,并注册和执行发布生命周期阶段的任务。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 用法示例:
|
* 用法示例:
|
||||||
* $ snow publish
|
* $ snow publish
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -4,14 +4,14 @@ import org.jcnc.snow.cli.api.CLICommand;
|
|||||||
import org.jcnc.snow.pkg.tasks.RunTask;
|
import org.jcnc.snow.pkg.tasks.RunTask;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 命令:运行已编译的 VM 字节码文件(.water)。
|
* CLI 命令: 运行已编译的 VM 字节码文件(.water)。
|
||||||
* <p>
|
* <p>
|
||||||
* 仅解析参数并委托给 {@link RunTask},
|
* 仅解析参数并委托给 {@link RunTask},
|
||||||
* 将 VM 运行逻辑下沉至 pkg 层,保持 CLI 无状态、薄封装。
|
* 将 VM 运行逻辑下沉至 pkg 层,保持 CLI 无状态、薄封装。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 用法示例:
|
* 用法示例:
|
||||||
* $ snow run main.water
|
* $ snow run main.water
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -4,13 +4,13 @@ import org.jcnc.snow.cli.SnowCLI;
|
|||||||
import org.jcnc.snow.cli.api.CLICommand;
|
import org.jcnc.snow.cli.api.CLICommand;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CLI 子命令:输出当前 Snow 工具的版本号。
|
* CLI 子命令: 输出当前 Snow 工具的版本号。
|
||||||
* <p>
|
* <p>
|
||||||
* 用于显示当前 CLI 工具版本,便于诊断、升级、兼容性确认等场景。
|
* 用于显示当前 CLI 工具版本,便于诊断、升级、兼容性确认等场景。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 用法示例:
|
* 用法示例:
|
||||||
* $ snow version
|
* $ snow version
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import java.util.Map;
|
|||||||
* 各虚拟寄存器实际对应的物理寄存器或栈槽号。采用简单的线性扫描分配策略。
|
* 各虚拟寄存器实际对应的物理寄存器或栈槽号。采用简单的线性扫描分配策略。
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* 分配过程如下:
|
* 分配过程如下:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>优先为函数参数分配槽号,从 0 开始,按参数顺序递增。</li>
|
* <li>优先为函数参数分配槽号,从 0 开始,按参数顺序递增。</li>
|
||||||
* <li>遍历函数体的每条指令,为尚未分配的目标寄存器及其操作数分配新的槽号。</li>
|
* <li>遍历函数体的每条指令,为尚未分配的目标寄存器及其操作数分配新的槽号。</li>
|
||||||
@ -28,8 +28,8 @@ public final class RegisterAllocator {
|
|||||||
/**
|
/**
|
||||||
* 虚拟寄存器到槽号的分配映射表。
|
* 虚拟寄存器到槽号的分配映射表。
|
||||||
* <p>
|
* <p>
|
||||||
* 键:虚拟寄存器 {@link IRVirtualRegister};
|
* 键: 虚拟寄存器 {@link IRVirtualRegister};
|
||||||
* 值:对应分配的槽编号 {@link Integer}。
|
* 值: 对应分配的槽编号 {@link Integer}。
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
private final Map<IRVirtualRegister, Integer> map = new HashMap<>();
|
private final Map<IRVirtualRegister, Integer> map = new HashMap<>();
|
||||||
@ -37,7 +37,7 @@ public final class RegisterAllocator {
|
|||||||
/**
|
/**
|
||||||
* 为指定 IR 函数分配所有虚拟寄存器的槽号。
|
* 为指定 IR 函数分配所有虚拟寄存器的槽号。
|
||||||
* <p>
|
* <p>
|
||||||
* 分配顺序说明:
|
* 分配顺序说明:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>首先为所有参数分配槽号。</li>
|
* <li>首先为所有参数分配槽号。</li>
|
||||||
* <li>然后线性遍历函数体,为每个指令涉及的虚拟寄存器(目标或操作数)分配槽号,
|
* <li>然后线性遍历函数体,为每个指令涉及的虚拟寄存器(目标或操作数)分配槽号,
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import java.util.stream.Collectors;
|
|||||||
* 仅负责根据指令类型分发到对应的 {@link InstructionGenerator} 子类完成实际生成。
|
* 仅负责根据指令类型分发到对应的 {@link InstructionGenerator} 子类完成实际生成。
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* 工作流程简述:
|
* 工作流程简述:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>接收一组已注册的 IR 指令生成器,并建立类型到生成器的映射表。</li>
|
* <li>接收一组已注册的 IR 指令生成器,并建立类型到生成器的映射表。</li>
|
||||||
* <li>遍历 IR 函数体的每条指令,根据类型找到对应的生成器,调用其 generate 方法生成 VM 指令。</li>
|
* <li>遍历 IR 函数体的每条指令,根据类型找到对应的生成器,调用其 generate 方法生成 VM 指令。</li>
|
||||||
@ -28,8 +28,8 @@ public final class VMCodeGenerator {
|
|||||||
/**
|
/**
|
||||||
* 指令类型到生成器的注册表(调度表)。
|
* 指令类型到生成器的注册表(调度表)。
|
||||||
* <p>
|
* <p>
|
||||||
* 键:IR 指令类型(Class对象),
|
* 键: IR 指令类型(Class对象),
|
||||||
* 值:对应的指令生成器实例。
|
* 值: 对应的指令生成器实例。
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
private final Map<Class<? extends IRInstruction>, InstructionGenerator<? extends IRInstruction>> registry;
|
private final Map<Class<? extends IRInstruction>, InstructionGenerator<? extends IRInstruction>> registry;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import org.jcnc.snow.vm.engine.VMOpCode;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* VMProgramBuilder:构建线性 VM 程序(即按顺序存放所有 VM 指令)。
|
* VMProgramBuilder: 构建线性 VM 程序(即按顺序存放所有 VM 指令)。
|
||||||
* <p>
|
* <p>
|
||||||
* 本类用于编译器后端,将所有生成的 VM 指令(包括分支和调用指令)统一存储、管理。
|
* 本类用于编译器后端,将所有生成的 VM 指令(包括分支和调用指令)统一存储、管理。
|
||||||
* 支持符号(如函数入口、标签地址)的延迟解析与回填(fix-up)机制,
|
* 支持符号(如函数入口、标签地址)的延迟解析与回填(fix-up)机制,
|
||||||
@ -22,7 +22,7 @@ public final class VMProgramBuilder {
|
|||||||
/** 未解析目标的分支指令(JUMP/IC_* 等待修补) */
|
/** 未解析目标的分支指令(JUMP/IC_* 等待修补) */
|
||||||
private record BranchFix(int index, String label) {}
|
private record BranchFix(int index, String label) {}
|
||||||
|
|
||||||
/** 占位符:用于表示尚未确定的符号地址 */
|
/** 占位符: 用于表示尚未确定的符号地址 */
|
||||||
private static final String PLACEHOLDER = "-1";
|
private static final String PLACEHOLDER = "-1";
|
||||||
|
|
||||||
/** 按顺序存放的 VM 指令文本 */
|
/** 按顺序存放的 VM 指令文本 */
|
||||||
@ -102,7 +102,7 @@ public final class VMProgramBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成 CALL 指令。
|
* 生成 CALL 指令。
|
||||||
* 支持延迟修补:若目标已知,直接写入地址;否则写入占位并登记 fix-up。
|
* 支持延迟修补: 若目标已知,直接写入地址;否则写入占位并登记 fix-up。
|
||||||
* @param target 目标函数名
|
* @param target 目标函数名
|
||||||
* @param nArgs 参数个数
|
* @param nArgs 参数个数
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -5,22 +5,22 @@
|
|||||||
## 项目简介
|
## 项目简介
|
||||||
|
|
||||||
**后端模块(Backend)** 是 [Snow 编译器]() 的核心组成部分之一,承接中间表示(IR)与运行时虚拟机(VM)之间的桥梁。
|
**后端模块(Backend)** 是 [Snow 编译器]() 的核心组成部分之一,承接中间表示(IR)与运行时虚拟机(VM)之间的桥梁。
|
||||||
它主要完成以下工作:
|
它主要完成以下工作:
|
||||||
|
|
||||||
1. **寄存器分配**:将 IR 中的虚拟寄存器映射为物理槽号或栈槽;
|
1. **寄存器分配**: 将 IR 中的虚拟寄存器映射为物理槽号或栈槽;
|
||||||
2. **指令生成与调度**:为每条 IR 指令分配对应的 VM 指令生成器,并负责调用它们输出指令文本;
|
2. **指令生成与调度**: 为每条 IR 指令分配对应的 VM 指令生成器,并负责调用它们输出指令文本;
|
||||||
3. **程序构建**:管理函数入口、标签定义、延迟修补(fix-up)CALL/JUMP 等控制流指令地址;
|
3. **程序构建**: 管理函数入口、标签定义、延迟修补(fix-up)CALL/JUMP 等控制流指令地址;
|
||||||
4. **操作码与常量映射**:提供 IR 操作码到 VM 操作码的映射工具,以及根据常量类型选择 PUSH/STORE 指令。
|
4. **操作码与常量映射**: 提供 IR 操作码到 VM 操作码的映射工具,以及根据常量类型选择 PUSH/STORE 指令。
|
||||||
|
|
||||||
后端模块设计注重可扩展性:各类 IR → VM 的转换逻辑被拆分到不同的 `*Generator` 类中,新增指令只需注册新的生成器;寄存器分配和程序构建也各司其职,职责清晰,便于维护和测试。
|
后端模块设计注重可扩展性: 各类 IR → VM 的转换逻辑被拆分到不同的 `*Generator` 类中,新增指令只需注册新的生成器;寄存器分配和程序构建也各司其职,职责清晰,便于维护和测试。
|
||||||
|
|
||||||
## 核心功能
|
## 核心功能
|
||||||
|
|
||||||
* **线性扫描寄存器分配**:`RegisterAllocator` 按参数优先、指令顺序为 IR 虚拟寄存器分配连续槽号。
|
* **线性扫描寄存器分配**: `RegisterAllocator` 按参数优先、指令顺序为 IR 虚拟寄存器分配连续槽号。
|
||||||
* **指令生成器体系**:`InstructionGenerator<T>` 接口 + 多个 `*Generator` 实现,支持二元运算、跳转、调用、标签、常量加载、一元运算、返回等指令。
|
* **指令生成器体系**: `InstructionGenerator<T>` 接口 + 多个 `*Generator` 实现,支持二元运算、跳转、调用、标签、常量加载、一元运算、返回等指令。
|
||||||
* **生成器调度**:`VMCodeGenerator` 建立类型到生成器的映射,遍历 IR 函数体并分发调用,实现多态化指令生成。
|
* **生成器调度**: `VMCodeGenerator` 建立类型到生成器的映射,遍历 IR 函数体并分发调用,实现多态化指令生成。
|
||||||
* **程序构建与延迟修补**:`VMProgramBuilder` 维护指令流、符号地址表及待修补列表,实现对 `CALL`/`JUMP` 等指令的延迟地址填充。
|
* **程序构建与延迟修补**: `VMProgramBuilder` 维护指令流、符号地址表及待修补列表,实现对 `CALL`/`JUMP` 等指令的延迟地址填充。
|
||||||
* **操作码与常量映射**:`IROpCodeMapper` 提供 IR 操作码到 VM 指令名的静态映射;`OpHelper` 基于反射获取 VMOpCode 常量,并根据值类型(I/L/F…)选择合适的 PUSH/STORE 操作码。
|
* **操作码与常量映射**: `IROpCodeMapper` 提供 IR 操作码到 VM 指令名的静态映射;`OpHelper` 基于反射获取 VMOpCode 常量,并根据值类型(I/L/F…)选择合适的 PUSH/STORE 操作码。
|
||||||
|
|
||||||
## 模块结构
|
## 模块结构
|
||||||
|
|
||||||
@ -51,6 +51,6 @@ backend/
|
|||||||
|
|
||||||
* JDK 24 或更高版本
|
* JDK 24 或更高版本
|
||||||
* Maven 构建管理
|
* Maven 构建管理
|
||||||
* 推荐 IDE:IntelliJ IDEA
|
* 推荐 IDE: IntelliJ IDEA
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
* 并自动进行类型提升。
|
* 并自动进行类型提升。
|
||||||
* 同时实现 "+0 → MOV" 的 Peephole 优化,避免多余的 PUSH/ADD 序列。
|
* 同时实现 "+0 → MOV" 的 Peephole 优化,避免多余的 PUSH/ADD 序列。
|
||||||
* </p>
|
* </p>
|
||||||
* <p>类型提升优先级:D > F > L > I > S > B</p>
|
* <p>类型提升优先级: D > F > L > I > S > B</p>
|
||||||
*/
|
*/
|
||||||
public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationInstruction> {
|
public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationInstruction> {
|
||||||
|
|
||||||
@ -143,11 +143,11 @@ public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationIn
|
|||||||
// ① 条件跳转;成立 → lblTrue
|
// ① 条件跳转;成立 → lblTrue
|
||||||
out.emitBranch(branchOp, lblTrue);
|
out.emitBranch(branchOp, lblTrue);
|
||||||
|
|
||||||
// ② 不成立:压 0
|
// ② 不成立: 压 0
|
||||||
out.emit(OpHelper.opcode("I_PUSH") + " 0");
|
out.emit(OpHelper.opcode("I_PUSH") + " 0");
|
||||||
out.emitBranch(OpHelper.opcode("JUMP"), lblEnd);
|
out.emitBranch(OpHelper.opcode("JUMP"), lblEnd);
|
||||||
|
|
||||||
// ③ 成立分支:压 1
|
// ③ 成立分支: 压 1
|
||||||
out.emit(lblTrue + ":");
|
out.emit(lblTrue + ":");
|
||||||
out.emit(OpHelper.opcode("I_PUSH") + " 1");
|
out.emit(OpHelper.opcode("I_PUSH") + " 1");
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@ public class CmpJumpGenerator implements InstructionGenerator<IRCompareJumpInstr
|
|||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* @param ins IR 条件比较跳转指令
|
* @param ins IR 条件比较跳转指令
|
||||||
* @param out VMProgramBuilder:用于发出 VM 指令
|
* @param out VMProgramBuilder: 用于发出 VM 指令
|
||||||
* @param slotMap 虚拟寄存器到 VM 槽位的映射表
|
* @param slotMap 虚拟寄存器到 VM 槽位的映射表
|
||||||
* @param currentFn 当前处理的函数名(调试用,当前未使用)
|
* @param currentFn 当前处理的函数名(调试用,当前未使用)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -20,8 +20,8 @@ public final class IROpCodeMapper {
|
|||||||
/**
|
/**
|
||||||
* IR 操作码到 VM 指令名的静态映射表。
|
* IR 操作码到 VM 指令名的静态映射表。
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>键:IR 操作码({@link IROpCode} 枚举项)</li>
|
* <li>键: IR 操作码({@link IROpCode} 枚举项)</li>
|
||||||
* <li>值:对应虚拟机指令名(字符串)</li>
|
* <li>值: 对应虚拟机指令名(字符串)</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* 使用 {@link EnumMap},查找和存储高效。
|
* 使用 {@link EnumMap},查找和存储高效。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,25 +1,25 @@
|
|||||||
package org.jcnc.snow.compiler.backend.utils;
|
package org.jcnc.snow.compiler.backend.utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工具类:提供基本数值类型的提升与类型转换辅助功能。
|
* 工具类: 提供基本数值类型的提升与类型转换辅助功能。
|
||||||
* <p>
|
* <p>
|
||||||
* 在进行数值类型运算、比较等操作时,低优先级的类型会被提升为高优先级类型参与运算。
|
* 在进行数值类型运算、比较等操作时,低优先级的类型会被提升为高优先级类型参与运算。
|
||||||
* 例如 int + long 运算,int 会被提升为 long,最终运算结果类型为 long。
|
* 例如 int + long 运算,int 会被提升为 long,最终运算结果类型为 long。
|
||||||
* <p>
|
* <p>
|
||||||
* 类型优先级从高到低依次为:
|
* 类型优先级从高到低依次为:
|
||||||
* D(double):6
|
* D(double): 6
|
||||||
* F(float) :5
|
* F(float) : 5
|
||||||
* L(long) :4
|
* L(long) : 4
|
||||||
* I(int) :3
|
* I(int) : 3
|
||||||
* S(short) :2
|
* S(short) : 2
|
||||||
* B(byte) :1
|
* B(byte) : 1
|
||||||
* 未识别类型 :0
|
* 未识别类型 : 0
|
||||||
*/
|
*/
|
||||||
public class TypePromoteUtils {
|
public class TypePromoteUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回数值类型的宽度优先级,数值越大类型越宽。
|
* 返回数值类型的宽度优先级,数值越大类型越宽。
|
||||||
* 类型及优先级映射如下:
|
* 类型及优先级映射如下:
|
||||||
* D(double): 6
|
* D(double): 6
|
||||||
* F(float) : 5
|
* F(float) : 5
|
||||||
* L(long) : 4
|
* L(long) : 4
|
||||||
@ -69,8 +69,8 @@ public class TypePromoteUtils {
|
|||||||
* 获取类型转换指令名(例如 "I2L", "F2D"),表示从源类型到目标类型的转换操作。
|
* 获取类型转换指令名(例如 "I2L", "F2D"),表示从源类型到目标类型的转换操作。
|
||||||
* 如果源类型和目标类型相同,则返回 null,表示无需转换。
|
* 如果源类型和目标类型相同,则返回 null,表示无需转换。
|
||||||
* <p>
|
* <p>
|
||||||
* 支持的类型标记字符包括:B(byte)、S(short)、I(int)、L(long)、F(float)、D(double)。
|
* 支持的类型标记字符包括: B(byte)、S(short)、I(int)、L(long)、F(float)、D(double)。
|
||||||
* 所有可能的类型转换均已覆盖,如下所示:
|
* 所有可能的类型转换均已覆盖,如下所示:
|
||||||
* B → S/I/L/F/D
|
* B → S/I/L/F/D
|
||||||
* S → B/I/L/F/D
|
* S → B/I/L/F/D
|
||||||
* I → B/S/L/F/D
|
* I → B/S/L/F/D
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import java.util.*;
|
|||||||
* 该类负责将抽象语法树(AST)的表达式节点转换为中间表示(IR)指令和虚拟寄存器,
|
* 该类负责将抽象语法树(AST)的表达式节点转换为中间表示(IR)指令和虚拟寄存器,
|
||||||
* 是编译器IR生成阶段的核心工具。
|
* 是编译器IR生成阶段的核心工具。
|
||||||
* <br/>
|
* <br/>
|
||||||
* 主要职责包括:
|
* 主要职责包括:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>将数字字面量、标识符、二元表达式、函数调用等AST表达式节点,翻译为对应的IR指令序列</li>
|
* <li>将数字字面量、标识符、二元表达式、函数调用等AST表达式节点,翻译为对应的IR指令序列</li>
|
||||||
* <li>管理并分配虚拟寄存器,保证IR操作的数据流正确</li>
|
* <li>管理并分配虚拟寄存器,保证IR操作的数据流正确</li>
|
||||||
@ -31,19 +31,19 @@ public record ExpressionBuilder(IRContext ctx) {
|
|||||||
/**
|
/**
|
||||||
* 构建并返回某个表达式节点对应的虚拟寄存器。
|
* 构建并返回某个表达式节点对应的虚拟寄存器。
|
||||||
*
|
*
|
||||||
* <p>会根据节点的实际类型分别处理:
|
* <p>会根据节点的实际类型分别处理:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>数字字面量:新建常量寄存器</li>
|
* <li>数字字面量: 新建常量寄存器</li>
|
||||||
* <li>布尔字面量:生成值为 0 或 1 的常量寄存器</li>
|
* <li>布尔字面量: 生成值为 0 或 1 的常量寄存器</li>
|
||||||
* <li>标识符:查找当前作用域中的寄存器</li>
|
* <li>标识符: 查找当前作用域中的寄存器</li>
|
||||||
* <li>二元表达式:递归处理子表达式并进行相应运算</li>
|
* <li>二元表达式: 递归处理子表达式并进行相应运算</li>
|
||||||
* <li>一元运算符:
|
* <li>一元运算符:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><code>-x</code>(取负,生成 <code>NEG_I32</code> 指令)与</li>
|
* <li><code>-x</code>(取负,生成 <code>NEG_I32</code> 指令)与</li>
|
||||||
* <li>code>!x</code>(逻辑非,转换为 <code>x == 0</code> 比较指令)</li>
|
* <li>code>!x</code>(逻辑非,转换为 <code>x == 0</code> 比较指令)</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* </li>
|
* </li>
|
||||||
* <li>函数调用:生成对应的Call指令</li>
|
* <li>函数调用: 生成对应的Call指令</li>
|
||||||
* <li>其它类型不支持,抛出异常</li>
|
* <li>其它类型不支持,抛出异常</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
|
|||||||
@ -19,7 +19,7 @@ public class FunctionBuilder {
|
|||||||
/**
|
/**
|
||||||
* 将 AST 中的 FunctionNode 构建为可执行的 IRFunction。
|
* 将 AST 中的 FunctionNode 构建为可执行的 IRFunction。
|
||||||
* <p>
|
* <p>
|
||||||
* 构建过程包括:
|
* 构建过程包括:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>初始化 IRFunction 实例和上下文</li>
|
* <li>初始化 IRFunction 实例和上下文</li>
|
||||||
* <li>根据函数返回类型,设置默认类型后缀,便于表达式推断</li>
|
* <li>根据函数返回类型,设置默认类型后缀,便于表达式推断</li>
|
||||||
@ -37,7 +37,7 @@ public class FunctionBuilder {
|
|||||||
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
|
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
|
||||||
|
|
||||||
|
|
||||||
// 0) 基本初始化:创建 IRFunction 实例与对应上下文
|
// 0) 基本初始化: 创建 IRFunction 实例与对应上下文
|
||||||
IRFunction irFunction = new IRFunction(functionNode.name());
|
IRFunction irFunction = new IRFunction(functionNode.name());
|
||||||
IRContext irContext = new IRContext(irFunction);
|
IRContext irContext = new IRContext(irFunction);
|
||||||
|
|
||||||
@ -53,14 +53,14 @@ public class FunctionBuilder {
|
|||||||
ExpressionUtils.setDefaultSuffix(_returnSuffix);
|
ExpressionUtils.setDefaultSuffix(_returnSuffix);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 2) 声明形参:为每个参数分配虚拟寄存器并声明到作用域
|
// 2) 声明形参: 为每个参数分配虚拟寄存器并声明到作用域
|
||||||
for (ParameterNode p : functionNode.parameters()) {
|
for (ParameterNode p : functionNode.parameters()) {
|
||||||
IRVirtualRegister reg = irFunction.newRegister(); // 新寄存器
|
IRVirtualRegister reg = irFunction.newRegister(); // 新寄存器
|
||||||
irContext.getScope().declare(p.name(), p.type(), reg); // 变量名→寄存器绑定
|
irContext.getScope().declare(p.name(), p.type(), reg); // 变量名→寄存器绑定
|
||||||
irFunction.addParameter(reg); // 添加到函数参数列表
|
irFunction.addParameter(reg); // 添加到函数参数列表
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) 生成函数体 IR:遍历每条语句,逐一转化
|
// 3) 生成函数体 IR: 遍历每条语句,逐一转化
|
||||||
StatementBuilder stmtBuilder = new StatementBuilder(irContext);
|
StatementBuilder stmtBuilder = new StatementBuilder(irContext);
|
||||||
for (StatementNode stmt : functionNode.body()) {
|
for (StatementNode stmt : functionNode.body()) {
|
||||||
stmtBuilder.build(stmt);
|
stmtBuilder.build(stmt);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系。
|
* IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系。
|
||||||
*
|
*
|
||||||
* <p>主要功能包括:
|
* <p>主要功能包括:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>维护在当前作用域中已声明变量的寄存器分配信息;</li>
|
* <li>维护在当前作用域中已声明变量的寄存器分配信息;</li>
|
||||||
* <li>支持将已有虚拟寄存器与变量名重新绑定;</li>
|
* <li>支持将已有虚拟寄存器与变量名重新绑定;</li>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
|||||||
* 以及与之配套的作用域管理(IRBuilderScope),
|
* 以及与之配套的作用域管理(IRBuilderScope),
|
||||||
* 并简化虚拟寄存器分配与 IR 指令添加操作。
|
* 并简化虚拟寄存器分配与 IR 指令添加操作。
|
||||||
*
|
*
|
||||||
* <p>本类提供以下核心功能:
|
* <p>本类提供以下核心功能:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>持有并操作当前 IRFunction 对象;</li>
|
* <li>持有并操作当前 IRFunction 对象;</li>
|
||||||
* <li>管理变量名与虚拟寄存器的映射关系;</li>
|
* <li>管理变量名与虚拟寄存器的映射关系;</li>
|
||||||
@ -62,7 +62,7 @@ public class IRContext {
|
|||||||
/**
|
/**
|
||||||
* 获取当前函数的变量与寄存器映射作用域。
|
* 获取当前函数的变量与寄存器映射作用域。
|
||||||
*
|
*
|
||||||
* <p>包内可见:仅限 builder 包内部使用。
|
* <p>包内可见: 仅限 builder 包内部使用。
|
||||||
*
|
*
|
||||||
* @return IRBuilderScope 实例
|
* @return IRBuilderScope 实例
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* 本类负责将解析生成的 AST 根节点列表转换为可执行的 IRProgram。
|
* 本类负责将解析生成的 AST 根节点列表转换为可执行的 IRProgram。
|
||||||
*
|
*
|
||||||
* <p>主要职责:
|
* <p>主要职责:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>遍历输入的顶层节点,识别 ModuleNode、FunctionNode 及脚本式顶层 StatementNode;</li>
|
* <li>遍历输入的顶层节点,识别 ModuleNode、FunctionNode 及脚本式顶层 StatementNode;</li>
|
||||||
* <li>对 ModuleNode 中的所有函数节点调用 FunctionBuilder 构建 IRFunction 并添加至 IRProgram;</li>
|
* <li>对 ModuleNode 中的所有函数节点调用 FunctionBuilder 构建 IRFunction 并添加至 IRProgram;</li>
|
||||||
@ -36,13 +36,13 @@ public final class IRProgramBuilder {
|
|||||||
for (Node node : roots) {
|
for (Node node : roots) {
|
||||||
switch (node) {
|
switch (node) {
|
||||||
case ModuleNode moduleNode ->
|
case ModuleNode moduleNode ->
|
||||||
// 模块节点:批量构建并添加模块内所有函数
|
// 模块节点: 批量构建并添加模块内所有函数
|
||||||
moduleNode.functions().forEach(f -> irProgram.add(buildFunction(f)));
|
moduleNode.functions().forEach(f -> irProgram.add(buildFunction(f)));
|
||||||
case FunctionNode functionNode ->
|
case FunctionNode functionNode ->
|
||||||
// 顶层函数节点:直接构建并添加
|
// 顶层函数节点: 直接构建并添加
|
||||||
irProgram.add(buildFunction(functionNode));
|
irProgram.add(buildFunction(functionNode));
|
||||||
case StatementNode statementNode ->
|
case StatementNode statementNode ->
|
||||||
// 脚本式顶层语句:封装为“_start”函数后构建并添加
|
// 脚本式顶层语句: 封装为“_start”函数后构建并添加
|
||||||
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
|
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
|
||||||
default ->
|
default ->
|
||||||
// 严格校验节点类型,遇不支持者立即失败
|
// 严格校验节点类型,遇不支持者立即失败
|
||||||
@ -65,7 +65,7 @@ public final class IRProgramBuilder {
|
|||||||
/**
|
/**
|
||||||
* 将单个脚本式顶层 StatementNode 封装为名称固定的“_start”函数节点。
|
* 将单个脚本式顶层 StatementNode 封装为名称固定的“_start”函数节点。
|
||||||
*
|
*
|
||||||
* <p>封装规则:
|
* <p>封装规则:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>函数名固定为“_start”;</li>
|
* <li>函数名固定为“_start”;</li>
|
||||||
* <li>返回类型设为 null,由后续流程处理;</li>
|
* <li>返回类型设为 null,由后续流程处理;</li>
|
||||||
|
|||||||
@ -80,7 +80,7 @@ public class InstructionFactory {
|
|||||||
/**
|
/**
|
||||||
* Move 指令(src → dest)。若寄存器相同也安全。
|
* Move 指令(src → dest)。若寄存器相同也安全。
|
||||||
* <p>
|
* <p>
|
||||||
* 实现方式:dest = src + 0(即加上常量 0)。
|
* 实现方式: dest = src + 0(即加上常量 0)。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param ctx 当前 IR 上下文
|
* @param ctx 当前 IR 上下文
|
||||||
@ -92,7 +92,7 @@ public class InstructionFactory {
|
|||||||
if (src == dest) {
|
if (src == dest) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 回退实现:dest = src + 0
|
// 回退实现: dest = src + 0
|
||||||
IRVirtualRegister zero = loadConst(ctx, 0);
|
IRVirtualRegister zero = loadConst(ctx, 0);
|
||||||
ctx.addInstruction(new BinaryOperationInstruction(IROpCode.ADD_I32, dest, src, zero));
|
ctx.addInstruction(new BinaryOperationInstruction(IROpCode.ADD_I32, dest, src, zero));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -150,7 +150,7 @@ public class StatementBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建循环语句(for/while)。
|
* 构建循环语句(for/while)。
|
||||||
* 处理流程:初始语句 → 条件判断 → 循环体 → 更新语句 → 跳回条件。
|
* 处理流程: 初始语句 → 条件判断 → 循环体 → 更新语句 → 跳回条件。
|
||||||
*
|
*
|
||||||
* @param loop 循环节点
|
* @param loop 循环节点
|
||||||
*/
|
*/
|
||||||
@ -176,7 +176,7 @@ public class StatementBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建分支语句(if/else)。
|
* 构建分支语句(if/else)。
|
||||||
* 处理流程:条件判断 → then 分支 → else 分支(可选)。
|
* 处理流程: 条件判断 → then 分支 → else 分支(可选)。
|
||||||
*
|
*
|
||||||
* @param ifNode if 语句节点
|
* @param ifNode if 语句节点
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -22,8 +22,8 @@ public final class GlobalFunctionTable {
|
|||||||
/**
|
/**
|
||||||
* 存储全局函数返回类型映射表。
|
* 存储全局函数返回类型映射表。
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Key:函数名(不含模块限定)</li>
|
* <li>Key: 函数名(不含模块限定)</li>
|
||||||
* <li>Value:返回类型,统一转换为小写字符串;若无返回值则为 {@code "void"}</li>
|
* <li>Value: 返回类型,统一转换为小写字符串;若无返回值则为 {@code "void"}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
private static final Map<String, String> RETURN_TYPES = new ConcurrentHashMap<>();
|
private static final Map<String, String> RETURN_TYPES = new ConcurrentHashMap<>();
|
||||||
|
|||||||
@ -112,7 +112,7 @@ public class IRFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 以IR代码表示,示例:
|
* 以IR代码表示,示例:
|
||||||
* <pre>
|
* <pre>
|
||||||
* func 名称(%0, %1, ...) {
|
* func 名称(%0, %1, ...) {
|
||||||
* 指令0
|
* 指令0
|
||||||
|
|||||||
@ -24,95 +24,95 @@ public enum IROpCode {
|
|||||||
CONV_D64_TO_F32,
|
CONV_D64_TO_F32,
|
||||||
|
|
||||||
|
|
||||||
/* ───── 算术运算(8位整数:byte)───── */
|
/* ───── 算术运算(8位整数: byte)───── */
|
||||||
ADD_B8, // 8位整型加法:a = b + c
|
ADD_B8, // 8位整型加法: a = b + c
|
||||||
SUB_B8, // 8位整型减法:a = b - c
|
SUB_B8, // 8位整型减法: a = b - c
|
||||||
MUL_B8, // 8位整型乘法:a = b * c
|
MUL_B8, // 8位整型乘法: a = b * c
|
||||||
DIV_B8, // 8位整型除法:a = b / c
|
DIV_B8, // 8位整型除法: a = b / c
|
||||||
NEG_B8, // 8位整型取负:a = -b
|
NEG_B8, // 8位整型取负: a = -b
|
||||||
|
|
||||||
/* ───── 算术运算(16位整数:short)───── */
|
/* ───── 算术运算(16位整数: short)───── */
|
||||||
ADD_S16, // 16位整型加法
|
ADD_S16, // 16位整型加法
|
||||||
SUB_S16, // 16位整型减法
|
SUB_S16, // 16位整型减法
|
||||||
MUL_S16, // 16位整型乘法
|
MUL_S16, // 16位整型乘法
|
||||||
DIV_S16, // 16位整型除法
|
DIV_S16, // 16位整型除法
|
||||||
NEG_S16, // 16位整型取负
|
NEG_S16, // 16位整型取负
|
||||||
|
|
||||||
/* ───── 算术运算(32位整数:int)───── */
|
/* ───── 算术运算(32位整数: int)───── */
|
||||||
ADD_I32, // 32位整型加法
|
ADD_I32, // 32位整型加法
|
||||||
SUB_I32, // 32位整型减法
|
SUB_I32, // 32位整型减法
|
||||||
MUL_I32, // 32位整型乘法
|
MUL_I32, // 32位整型乘法
|
||||||
DIV_I32, // 32位整型除法
|
DIV_I32, // 32位整型除法
|
||||||
NEG_I32, // 32位整型取负
|
NEG_I32, // 32位整型取负
|
||||||
|
|
||||||
/* ───── 算术运算(64位整数:long)───── */
|
/* ───── 算术运算(64位整数: long)───── */
|
||||||
ADD_L64, // 64位整型加法
|
ADD_L64, // 64位整型加法
|
||||||
SUB_L64, // 64位整型减法
|
SUB_L64, // 64位整型减法
|
||||||
MUL_L64, // 64位整型乘法
|
MUL_L64, // 64位整型乘法
|
||||||
DIV_L64, // 64位整型除法
|
DIV_L64, // 64位整型除法
|
||||||
NEG_L64, // 64位整型取负
|
NEG_L64, // 64位整型取负
|
||||||
|
|
||||||
/* ───── 算术运算(32位浮点数:float)───── */
|
/* ───── 算术运算(32位浮点数: float)───── */
|
||||||
ADD_F32, // 32位浮点加法
|
ADD_F32, // 32位浮点加法
|
||||||
SUB_F32, // 32位浮点减法
|
SUB_F32, // 32位浮点减法
|
||||||
MUL_F32, // 32位浮点乘法
|
MUL_F32, // 32位浮点乘法
|
||||||
DIV_F32, // 32位浮点除法
|
DIV_F32, // 32位浮点除法
|
||||||
NEG_F32, // 32位浮点取负
|
NEG_F32, // 32位浮点取负
|
||||||
|
|
||||||
/* ───── 算术运算(64位浮点数:double)───── */
|
/* ───── 算术运算(64位浮点数: double)───── */
|
||||||
ADD_D64, // 64位浮点加法
|
ADD_D64, // 64位浮点加法
|
||||||
SUB_D64, // 64位浮点减法
|
SUB_D64, // 64位浮点减法
|
||||||
MUL_D64, // 64位浮点乘法
|
MUL_D64, // 64位浮点乘法
|
||||||
DIV_D64, // 64位浮点除法
|
DIV_D64, // 64位浮点除法
|
||||||
NEG_D64, // 64位浮点取负
|
NEG_D64, // 64位浮点取负
|
||||||
|
|
||||||
/* ───── 逻辑与比较运算指令(8位整数:byte) ───── */
|
/* ───── 逻辑与比较运算指令(8位整数: byte) ───── */
|
||||||
CMP_BEQ, // 8位整数相等比较:a == b
|
CMP_BEQ, // 8位整数相等比较: a == b
|
||||||
CMP_BNE, // 8位整数不等比较:a != b
|
CMP_BNE, // 8位整数不等比较: a != b
|
||||||
CMP_BLT, // 8位整数小于比较:a < b
|
CMP_BLT, // 8位整数小于比较: a < b
|
||||||
CMP_BGT, // 8位整数大于比较:a > b
|
CMP_BGT, // 8位整数大于比较: a > b
|
||||||
CMP_BLE, // 8位整数小于等于:a <= b
|
CMP_BLE, // 8位整数小于等于: a <= b
|
||||||
CMP_BGE, // 8位整数大于等于:a >= b
|
CMP_BGE, // 8位整数大于等于: a >= b
|
||||||
|
|
||||||
/* ───── 逻辑与比较运算指令(16位整数:int) ───── */
|
/* ───── 逻辑与比较运算指令(16位整数: int) ───── */
|
||||||
CMP_SEQ, // 16位整数相等比较:a == b
|
CMP_SEQ, // 16位整数相等比较: a == b
|
||||||
CMP_SNE, // 16位整数不等比较:a != b
|
CMP_SNE, // 16位整数不等比较: a != b
|
||||||
CMP_SLT, // 16位整数小于比较:a < b
|
CMP_SLT, // 16位整数小于比较: a < b
|
||||||
CMP_SGT, // 16位整数大于比较:a > b
|
CMP_SGT, // 16位整数大于比较: a > b
|
||||||
CMP_SLE, // 16位整数小于等于:a <= b
|
CMP_SLE, // 16位整数小于等于: a <= b
|
||||||
CMP_SGE, // 16位整数大于等于:a >= b
|
CMP_SGE, // 16位整数大于等于: a >= b
|
||||||
|
|
||||||
/* ───── 逻辑与比较运算指令(32位整数:int) ───── */
|
/* ───── 逻辑与比较运算指令(32位整数: int) ───── */
|
||||||
CMP_IEQ, // 32位整数相等比较:a == b
|
CMP_IEQ, // 32位整数相等比较: a == b
|
||||||
CMP_INE, // 32位整数不等比较:a != b
|
CMP_INE, // 32位整数不等比较: a != b
|
||||||
CMP_ILT, // 32位整数小于比较:a < b
|
CMP_ILT, // 32位整数小于比较: a < b
|
||||||
CMP_IGT, // 32位整数大于比较:a > b
|
CMP_IGT, // 32位整数大于比较: a > b
|
||||||
CMP_ILE, // 32位整数小于等于:a <= b
|
CMP_ILE, // 32位整数小于等于: a <= b
|
||||||
CMP_IGE, // 32位整数大于等于:a >= b
|
CMP_IGE, // 32位整数大于等于: a >= b
|
||||||
|
|
||||||
/* ───── 逻辑与比较运算指令(64位整数:long) ───── */
|
/* ───── 逻辑与比较运算指令(64位整数: long) ───── */
|
||||||
CMP_LEQ, // 64位整数相等比较:a == b
|
CMP_LEQ, // 64位整数相等比较: a == b
|
||||||
CMP_LNE, // 64位整数不等比较:a != b
|
CMP_LNE, // 64位整数不等比较: a != b
|
||||||
CMP_LLT, // 64位整数小于比较:a < b
|
CMP_LLT, // 64位整数小于比较: a < b
|
||||||
CMP_LGT, // 64位整数大于比较:a > b
|
CMP_LGT, // 64位整数大于比较: a > b
|
||||||
CMP_LLE, // 64位整数小于等于:a <= b
|
CMP_LLE, // 64位整数小于等于: a <= b
|
||||||
CMP_LGE, // 64位整数大于等于:a >= b
|
CMP_LGE, // 64位整数大于等于: a >= b
|
||||||
|
|
||||||
/* ───── 逻辑与比较运算指令(32位浮点数:float) ───── */
|
/* ───── 逻辑与比较运算指令(32位浮点数: float) ───── */
|
||||||
CMP_FEQ, // 32位浮点相等比较:a == b
|
CMP_FEQ, // 32位浮点相等比较: a == b
|
||||||
CMP_FNE, // 32位浮点不等比较:a != b
|
CMP_FNE, // 32位浮点不等比较: a != b
|
||||||
CMP_FLT, // 32位浮点小于比较:a < b
|
CMP_FLT, // 32位浮点小于比较: a < b
|
||||||
CMP_FGT, // 32位浮点大于比较:a > b
|
CMP_FGT, // 32位浮点大于比较: a > b
|
||||||
CMP_FLE, // 32位浮点小于等于:a <= b
|
CMP_FLE, // 32位浮点小于等于: a <= b
|
||||||
CMP_FGE, // 32位浮点大于等于:a >= b
|
CMP_FGE, // 32位浮点大于等于: a >= b
|
||||||
|
|
||||||
/* ───── 逻辑与比较运算指令(64位浮点数:double) ───── */
|
/* ───── 逻辑与比较运算指令(64位浮点数: double) ───── */
|
||||||
CMP_DEQ, // 64位浮点相等比较:a == b
|
CMP_DEQ, // 64位浮点相等比较: a == b
|
||||||
CMP_DNE, // 64位浮点不等比较:a != b
|
CMP_DNE, // 64位浮点不等比较: a != b
|
||||||
CMP_DLT, // 64位浮点小于比较:a < b
|
CMP_DLT, // 64位浮点小于比较: a < b
|
||||||
CMP_DGT, // 64位浮点大于比较:a > b
|
CMP_DGT, // 64位浮点大于比较: a > b
|
||||||
CMP_DLE, // 64位浮点小于等于:a <= b
|
CMP_DLE, // 64位浮点小于等于: a <= b
|
||||||
CMP_DGE, // 64位浮点大于等于:a >= b
|
CMP_DGE, // 64位浮点大于等于: a >= b
|
||||||
|
|
||||||
/* ───── 数据访问与常量操作 ───── */
|
/* ───── 数据访问与常量操作 ───── */
|
||||||
LOAD, // 从内存加载数据至寄存器
|
LOAD, // 从内存加载数据至寄存器
|
||||||
|
|||||||
@ -11,11 +11,11 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
|||||||
* 实现该接口的类型可以作为 {@link IRInstruction} 中的操作数出现。
|
* 实现该接口的类型可以作为 {@link IRInstruction} 中的操作数出现。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <p>当前支持的 IR 值类型包括:</p>
|
* <p>当前支持的 IR 值类型包括: </p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link IRVirtualRegister}:虚拟寄存器,表示计算结果或中间变量</li>
|
* <li>{@link IRVirtualRegister}: 虚拟寄存器,表示计算结果或中间变量</li>
|
||||||
* <li>{@link IRConstant}:常量值,表示不可变的字面量或数值</li>
|
* <li>{@link IRConstant}: 常量值,表示不可变的字面量或数值</li>
|
||||||
* <li>{@link IRLabel}:标签,表示跳转指令的目标地址</li>
|
* <li>{@link IRLabel}: 标签,表示跳转指令的目标地址</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@ -7,12 +7,12 @@ import org.jcnc.snow.compiler.ir.instruction.*;
|
|||||||
* <p>
|
* <p>
|
||||||
* 它定义了访问者模式的核心机制,通过对每种 {@link IRInstruction} 子类
|
* 它定义了访问者模式的核心机制,通过对每种 {@link IRInstruction} 子类
|
||||||
* 提供独立的 {@code visit} 方法,实现对指令的分发与处理。
|
* 提供独立的 {@code visit} 方法,实现对指令的分发与处理。
|
||||||
* 不同的访问者实现可用于执行不同任务,例如:
|
* 不同的访问者实现可用于执行不同任务,例如:
|
||||||
* </p>
|
* </p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code IRPrinter}:打印指令内容</li>
|
* <li>{@code IRPrinter}: 打印指令内容</li>
|
||||||
* <li>{@code IROptimizer}:分析与重写 IR 以优化性能</li>
|
* <li>{@code IROptimizer}: 分析与重写 IR 以优化性能</li>
|
||||||
* <li>{@code IRCodeGenerator}:生成平台相关的机器码或汇编代码</li>
|
* <li>{@code IRCodeGenerator}: 生成平台相关的机器码或汇编代码</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@ -11,27 +11,27 @@ IR 模块以类 SSA(Static Single Assignment)形式设计,通过统一的
|
|||||||
|
|
||||||
## 核心功能
|
## 核心功能
|
||||||
|
|
||||||
* **统一的中间表示模型**:表达控制流与数据流,支持函数、指令、值等核心结构
|
* **统一的中间表示模型**: 表达控制流与数据流,支持函数、指令、值等核心结构
|
||||||
* **IR 构建器体系**:模块化构建函数、表达式与语句 IR,简化前端对接
|
* **IR 构建器体系**: 模块化构建函数、表达式与语句 IR,简化前端对接
|
||||||
* **灵活的指令层级结构**:支持二元操作、跳转、返回等多种基本指令
|
* **灵活的指令层级结构**: 支持二元操作、跳转、返回等多种基本指令
|
||||||
* **寄存器与常量模型**:统一管理虚拟寄存器、常量、标签等值类型
|
* **寄存器与常量模型**: 统一管理虚拟寄存器、常量、标签等值类型
|
||||||
* **IR 打印与调试支持**:辅助输出 IR 文本格式,支持可视化与调试
|
* **IR 打印与调试支持**: 辅助输出 IR 文本格式,支持可视化与调试
|
||||||
|
|
||||||
## 模块结构
|
## 模块结构
|
||||||
|
|
||||||
```
|
```
|
||||||
ir/
|
ir/
|
||||||
├── builder/ // 构建器模块:负责构造表达式、函数与语句的 IR
|
├── builder/ // 构建器模块: 负责构造表达式、函数与语句的 IR
|
||||||
├── core/ // 核心定义:IR 基础结构,如函数、指令、程序、访问器等
|
├── core/ // 核心定义: IR 基础结构,如函数、指令、程序、访问器等
|
||||||
├── instruction/ // 指令实现:具体的 IR 指令类型(如加法、跳转、返回等)
|
├── instruction/ // 指令实现: 具体的 IR 指令类型(如加法、跳转、返回等)
|
||||||
├── utils/ // 工具模块:提供 IR 操作相关的辅助函数(如表达式工具、操作码工具等)
|
├── utils/ // 工具模块: 提供 IR 操作相关的辅助函数(如表达式工具、操作码工具等)
|
||||||
└── value/ // 值模型:常量、标签、虚拟寄存器等
|
└── value/ // 值模型: 常量、标签、虚拟寄存器等
|
||||||
```
|
```
|
||||||
|
|
||||||
## 开发环境
|
## 开发环境
|
||||||
|
|
||||||
* JDK 24 或更高版本
|
* JDK 24 或更高版本
|
||||||
* Maven 构建管理
|
* Maven 构建管理
|
||||||
* 推荐 IDE:IntelliJ IDEA
|
* 推荐 IDE: IntelliJ IDEA
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BinaryOperationInstruction —— 表示一个二元运算指令,格式为:dest = lhs OP rhs
|
* BinaryOperationInstruction —— 表示一个二元运算指令,格式为: dest = lhs OP rhs
|
||||||
* <p>
|
* <p>
|
||||||
* 该类用于描述形如 a = b + c 或 a = x * y 的二元运算指令。
|
* 该类用于描述形如 a = b + c 或 a = x * y 的二元运算指令。
|
||||||
* 运算类型(OP)由 {@link IROpCode} 指定,包括加法、减法、乘法、除法等。
|
* 运算类型(OP)由 {@link IROpCode} 指定,包括加法、减法、乘法、除法等。
|
||||||
@ -76,7 +76,7 @@ public final class BinaryOperationInstruction extends IRInstruction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为字符串格式,便于调试与打印。
|
* 转换为字符串格式,便于调试与打印。
|
||||||
* 例:v1 = ADD_I32 v2, v3
|
* 例: v1 = ADD_I32 v2, v3
|
||||||
*
|
*
|
||||||
* @return 指令的字符串表示形式
|
* @return 指令的字符串表示形式
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -46,7 +46,7 @@ public class CallInstruction extends IRInstruction {
|
|||||||
return isVoidReturn() ? null : dest;
|
return isVoidReturn() ? null : dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 操作数列表:void 调用不包含 dest */
|
/** 操作数列表: void 调用不包含 dest */
|
||||||
@Override
|
@Override
|
||||||
public List<IRValue> operands() {
|
public List<IRValue> operands() {
|
||||||
List<IRValue> ops = new ArrayList<>();
|
List<IRValue> ops = new ArrayList<>();
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IRAddInstruction —— 表示一个加法指令,形如:dest = lhs + rhs
|
* IRAddInstruction —— 表示一个加法指令,形如: dest = lhs + rhs
|
||||||
* <p>
|
* <p>
|
||||||
* 本类是一个具体的 IRInstruction 子类,表示将两个值相加,并将结果写入目标寄存器的操作。
|
* 本类是一个具体的 IRInstruction 子类,表示将两个值相加,并将结果写入目标寄存器的操作。
|
||||||
* 虽然功能与通用的 {@link BinaryOperationInstruction} 类似,但它作为更简化明确的指令实现,
|
* 虽然功能与通用的 {@link BinaryOperationInstruction} 类似,但它作为更简化明确的指令实现,
|
||||||
@ -40,7 +40,7 @@ public class IRAddInstruction extends IRInstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回该指令的操作码:ADD_I32。
|
* 返回该指令的操作码: ADD_I32。
|
||||||
*
|
*
|
||||||
* @return 加法操作码
|
* @return 加法操作码
|
||||||
*/
|
*/
|
||||||
@ -81,7 +81,7 @@ public class IRAddInstruction extends IRInstruction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回指令的字符串形式,方便调试。
|
* 返回指令的字符串形式,方便调试。
|
||||||
* 例如:v1 = v2 + v3
|
* 例如: v1 = v2 + v3
|
||||||
*
|
*
|
||||||
* @return 字符串表示形式
|
* @return 字符串表示形式
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
|||||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* “比较 + 条件跳转” 复合指令:
|
* “比较 + 条件跳转” 复合指令:
|
||||||
* if ( left <cmpOp> right ) jump targetLabel;
|
* if ( left <cmpOp> right ) jump targetLabel;
|
||||||
* <p>
|
* <p>
|
||||||
* 其中 cmpOp 只能是 IROpCode.CMP_* 六种比较操作码。
|
* 其中 cmpOp 只能是 IROpCode.CMP_* 六种比较操作码。
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public class IRJumpInstruction extends IRInstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取该指令对应的操作码:JUMP。
|
* 获取该指令对应的操作码: JUMP。
|
||||||
*
|
*
|
||||||
* @return IROpCode.JUMP
|
* @return IROpCode.JUMP
|
||||||
*/
|
*/
|
||||||
@ -55,7 +55,7 @@ public class IRJumpInstruction extends IRInstruction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将指令转为字符串形式,便于打印与调试。
|
* 将指令转为字符串形式,便于打印与调试。
|
||||||
* 例如:jump L1
|
* 例如: jump L1
|
||||||
*
|
*
|
||||||
* @return 指令的字符串表示
|
* @return 指令的字符串表示
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -29,7 +29,7 @@ public class IRReturnInstruction extends IRInstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取该指令的操作码:RET。
|
* 获取该指令的操作码: RET。
|
||||||
*
|
*
|
||||||
* @return IROpCode.RET,表示返回操作
|
* @return IROpCode.RET,表示返回操作
|
||||||
*/
|
*/
|
||||||
@ -60,7 +60,7 @@ public class IRReturnInstruction extends IRInstruction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为字符串形式,便于调试与打印。
|
* 转换为字符串形式,便于调试与打印。
|
||||||
* 示例:ret v1
|
* 示例: ret v1
|
||||||
*
|
*
|
||||||
* @return 字符串形式的返回指令
|
* @return 字符串形式的返回指令
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LoadConstInstruction —— 表示一个常量加载指令,格式为:dest = CONST k
|
* LoadConstInstruction —— 表示一个常量加载指令,格式为: dest = CONST k
|
||||||
* <p>
|
* <p>
|
||||||
* 该指令的功能是将一个常量(字面量或编译期已知值)加载到一个虚拟寄存器中,
|
* 该指令的功能是将一个常量(字面量或编译期已知值)加载到一个虚拟寄存器中,
|
||||||
* 供后续指令使用。例如,在表达式计算、参数传递、初始化等场景中常用。
|
* 供后续指令使用。例如,在表达式计算、参数传递、初始化等场景中常用。
|
||||||
@ -66,7 +66,7 @@ public final class LoadConstInstruction extends IRInstruction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回该指令的字符串形式,便于调试或打印。
|
* 返回该指令的字符串形式,便于调试或打印。
|
||||||
* 例如:v1 = CONST 42
|
* 例如: v1 = CONST 42
|
||||||
*
|
*
|
||||||
* @return 指令的字符串表示
|
* @return 指令的字符串表示
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -9,11 +9,11 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ReturnInstruction —— 表示函数返回指令,格式:RET 或 RET <value>
|
* ReturnInstruction —— 表示函数返回指令,格式: RET 或 RET <value>
|
||||||
* <p>
|
* <p>
|
||||||
* 此类用于描述函数执行完毕后的返回操作。支持两种返回形式:
|
* 此类用于描述函数执行完毕后的返回操作。支持两种返回形式:
|
||||||
* - 无返回值(void):生成无参的 RET 指令
|
* - 无返回值(void): 生成无参的 RET 指令
|
||||||
* - 有返回值:将指定虚拟寄存器中的值返回给调用者
|
* - 有返回值: 将指定虚拟寄存器中的值返回给调用者
|
||||||
* <p>
|
* <p>
|
||||||
* 与 {@link IRReturnInstruction} 类似,但更通用,适配多种函数返回风格。
|
* 与 {@link IRReturnInstruction} 类似,但更通用,适配多种函数返回风格。
|
||||||
*/
|
*/
|
||||||
@ -36,7 +36,7 @@ public final class ReturnInstruction extends IRInstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回该指令的操作码类型:RET。
|
* 返回该指令的操作码类型: RET。
|
||||||
*
|
*
|
||||||
* @return IROpCode.RET
|
* @return IROpCode.RET
|
||||||
*/
|
*/
|
||||||
@ -68,8 +68,8 @@ public final class ReturnInstruction extends IRInstruction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为字符串形式,便于调试与输出。
|
* 转换为字符串形式,便于调试与输出。
|
||||||
* - 无返回值:RET
|
* - 无返回值: RET
|
||||||
* - 有返回值:RET v1
|
* - 有返回值: RET v1
|
||||||
*
|
*
|
||||||
* @return 字符串表示的返回指令
|
* @return 字符串表示的返回指令
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -9,14 +9,14 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UnaryOperationInstruction —— 表示一个一元运算指令,格式:dest = OP val
|
* UnaryOperationInstruction —— 表示一个一元运算指令,格式: dest = OP val
|
||||||
* <p>
|
* <p>
|
||||||
* 用于对单个操作数 val 执行指定的一元运算 OP(例如取负 NEG),
|
* 用于对单个操作数 val 执行指定的一元运算 OP(例如取负 NEG),
|
||||||
* 并将结果写入目标虚拟寄存器 dest。
|
* 并将结果写入目标虚拟寄存器 dest。
|
||||||
* <p>
|
* <p>
|
||||||
* 支持的操作由 {@link IROpCode} 定义,目前常见的一元操作包括:
|
* 支持的操作由 {@link IROpCode} 定义,目前常见的一元操作包括:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>NEG_I32 —— 整数取负:dest = -val</li>
|
* <li>NEG_I32 —— 整数取负: dest = -val</li>
|
||||||
* <li>(可扩展)逻辑非、按位非等</li>
|
* <li>(可扩展)逻辑非、按位非等</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@ -76,7 +76,7 @@ public final class UnaryOperationInstruction extends IRInstruction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将该指令格式化为字符串,便于打印与调试。
|
* 将该指令格式化为字符串,便于打印与调试。
|
||||||
* 形式:dest = OP val,例如:v1 = NEG v2
|
* 形式: dest = OP val,例如: v1 = NEG v2
|
||||||
*
|
*
|
||||||
* @return 字符串形式的指令
|
* @return 字符串形式的指令
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -16,11 +16,11 @@ import java.util.Map;
|
|||||||
* 支持自动类型提升,保证 int、long、float、double 等类型的比较均能得到正确的 IR 指令。
|
* 支持自动类型提升,保证 int、long、float、double 等类型的比较均能得到正确的 IR 指令。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* 类型判定支持:
|
* 类型判定支持:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>字面量后缀:支持 B/S/I/L/F/D(大小写均可)</li>
|
* <li>字面量后缀: 支持 B/S/I/L/F/D(大小写均可)</li>
|
||||||
* <li>浮点数支持:如无后缀但有小数点,视为 double</li>
|
* <li>浮点数支持: 如无后缀但有小数点,视为 double</li>
|
||||||
* <li>变量类型:根据传入变量表推断类型,未识别则默认 int</li>
|
* <li>变量类型: 根据传入变量表推断类型,未识别则默认 int</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public final class ComparisonUtils {
|
public final class ComparisonUtils {
|
||||||
@ -39,7 +39,7 @@ public final class ComparisonUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回类型宽度优先级(越大代表类型越宽)。类型对应的优先级:
|
* 返回类型宽度优先级(越大代表类型越宽)。类型对应的优先级:
|
||||||
* - D (double): 6
|
* - D (double): 6
|
||||||
* - F (float): 5
|
* - F (float): 5
|
||||||
* - L (long): 4
|
* - L (long): 4
|
||||||
@ -106,7 +106,7 @@ public final class ComparisonUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内部工具方法:根据表达式节点和变量表推断类型标记字符。
|
* 内部工具方法: 根据表达式节点和变量表推断类型标记字符。
|
||||||
* 字面量支持 B/S/I/L/F/D(大小写均可),浮点数默认 double;
|
* 字面量支持 B/S/I/L/F/D(大小写均可),浮点数默认 double;
|
||||||
* 标识符类型按变量表映射,未知则默认 int。
|
* 标识符类型按变量表映射,未知则默认 int。
|
||||||
*
|
*
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* 表达式分析与操作符选择工具类。
|
* 表达式分析与操作符选择工具类。
|
||||||
* <p>
|
* <p>
|
||||||
* 主要功能:
|
* 主要功能:
|
||||||
* - 解析字面量常量,自动推断类型
|
* - 解析字面量常量,自动推断类型
|
||||||
* - 自动匹配并选择适合的算术/比较操作码
|
* - 自动匹配并选择适合的算术/比较操作码
|
||||||
* - 表达式类型的合并与类型提升
|
* - 表达式类型的合并与类型提升
|
||||||
@ -138,7 +138,7 @@ public final class ExpressionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 兼容旧逻辑:仅凭操作符直接返回 int32 比较指令。
|
* 兼容旧逻辑: 仅凭操作符直接返回 int32 比较指令。
|
||||||
*
|
*
|
||||||
* @param op 比较操作符
|
* @param op 比较操作符
|
||||||
* @return int32 类型的比较操作码
|
* @return int32 类型的比较操作码
|
||||||
@ -148,7 +148,7 @@ public final class ExpressionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 推荐调用:根据左右表达式类型自动选择 int/long/float/double 等合适的比较操作码。
|
* 推荐调用: 根据左右表达式类型自动选择 int/long/float/double 等合适的比较操作码。
|
||||||
*
|
*
|
||||||
* @param variables 变量名到类型的映射
|
* @param variables 变量名到类型的映射
|
||||||
* @param op 比较符号
|
* @param op 比较符号
|
||||||
@ -196,7 +196,7 @@ public final class ExpressionUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回两个类型后缀中的最大类型(宽度优先)。
|
* 返回两个类型后缀中的最大类型(宽度优先)。
|
||||||
* 优先级:d > f > l > i > s > b > '\0'
|
* 优先级: d > f > l > i > s > b > '\0'
|
||||||
*
|
*
|
||||||
* @param l 类型后缀1
|
* @param l 类型后缀1
|
||||||
* @param r 类型后缀2
|
* @param r 类型后缀2
|
||||||
|
|||||||
@ -9,18 +9,18 @@ import org.jcnc.snow.compiler.ir.core.IRValue;
|
|||||||
* 与 {@link IRVirtualRegister} 不同,常量不需要通过寄存器存储,
|
* 与 {@link IRVirtualRegister} 不同,常量不需要通过寄存器存储,
|
||||||
* 可直接作为 IR 指令的操作数使用。
|
* 可直接作为 IR 指令的操作数使用。
|
||||||
* <p>
|
* <p>
|
||||||
* 典型应用:
|
* 典型应用:
|
||||||
* - 加载常量指令:v1 = CONST 42
|
* - 加载常量指令: v1 = CONST 42
|
||||||
* - 计算表达式:v2 = ADD v1, 100
|
* - 计算表达式: v2 = ADD v1, 100
|
||||||
*/
|
*/
|
||||||
public record IRConstant(Object value) implements IRValue {
|
public record IRConstant(Object value) implements IRValue {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将常量值转换为字符串,用于打印 IR 指令或调试输出。
|
* 将常量值转换为字符串,用于打印 IR 指令或调试输出。
|
||||||
* <p>
|
* <p>
|
||||||
* 例如:
|
* 例如:
|
||||||
* - 整数常量:42
|
* - 整数常量: 42
|
||||||
* - 字符串常量:"hello"
|
* - 字符串常量: "hello"
|
||||||
*
|
*
|
||||||
* @return 常量的字符串表示
|
* @return 常量的字符串表示
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -8,11 +8,11 @@ import org.jcnc.snow.compiler.ir.core.IRValue;
|
|||||||
* 在 IR 系统中,虚拟寄存器用于存储每个中间计算结果,是 SSA(Static Single Assignment)形式的核心。
|
* 在 IR 系统中,虚拟寄存器用于存储每个中间计算结果,是 SSA(Static Single Assignment)形式的核心。
|
||||||
* 每个虚拟寄存器在程序中只被赋值一次,其值来源于一条明确的指令输出。
|
* 每个虚拟寄存器在程序中只被赋值一次,其值来源于一条明确的指令输出。
|
||||||
* <p>
|
* <p>
|
||||||
* 特点:
|
* 特点:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>每个寄存器有唯一编号 {@code id},由 {@code IRFunction.newRegister()} 自动生成</li>
|
* <li>每个寄存器有唯一编号 {@code id},由 {@code IRFunction.newRegister()} 自动生成</li>
|
||||||
* <li>实现 {@link IRValue} 接口,可作为 IRInstruction 的操作数</li>
|
* <li>实现 {@link IRValue} 接口,可作为 IRInstruction 的操作数</li>
|
||||||
* <li>具备良好的打印与调试格式:%id</li>
|
* <li>具备良好的打印与调试格式: %id</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* 适用于表达式求值、参数传递、函数返回值、临时变量等所有中间值场景。
|
* 适用于表达式求值、参数传递、函数返回值、临时变量等所有中间值场景。
|
||||||
@ -23,7 +23,7 @@ public record IRVirtualRegister(int id) implements IRValue {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将虚拟寄存器转换为字符串格式,方便输出和调试。
|
* 将虚拟寄存器转换为字符串格式,方便输出和调试。
|
||||||
* 格式为:%<id>,例如 %3 表示编号为 3 的虚拟寄存器。
|
* 格式为: %<id>,例如 %3 表示编号为 3 的虚拟寄存器。
|
||||||
*
|
*
|
||||||
* @return 格式化的字符串表示
|
* @return 格式化的字符串表示
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import org.jcnc.snow.compiler.lexer.base.TokenScanner;
|
|||||||
* 是 {@link TokenScanner} 实现进行词法识别的基础设施。
|
* 是 {@link TokenScanner} 实现进行词法识别的基础设施。
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* 设计要点:
|
* 设计要点:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>构造时统一将 Windows 换行符 (<code>\r\n</code>) 转换为 Unix 风格 (<code>\n</code>)。</li>
|
* <li>构造时统一将 Windows 换行符 (<code>\r\n</code>) 转换为 Unix 风格 (<code>\n</code>)。</li>
|
||||||
* <li>所有坐标均以 <strong>1</strong> 为起始行/列号,更贴合人类直觉。</li>
|
* <li>所有坐标均以 <strong>1</strong> 为起始行/列号,更贴合人类直觉。</li>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Snow 语言词法分析器核心实现。
|
* Snow 语言词法分析器核心实现。
|
||||||
* <p>采用“<b>先扫描 → 后批量校验 → 统一报告</b>”策略:
|
* <p>采用“<b>先扫描 → 后批量校验 → 统一报告</b>”策略:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>{@link #scanAllTokens()}— 用扫描器链把字符流拆成 {@link Token}</li>
|
* <li>{@link #scanAllTokens()}— 用扫描器链把字符流拆成 {@link Token}</li>
|
||||||
* <li>{@link #validateTokens()}— 基于 token 序列做轻量上下文校验</li>
|
* <li>{@link #validateTokens()}— 基于 token 序列做轻量上下文校验</li>
|
||||||
@ -21,20 +21,21 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class LexerEngine {
|
public class LexerEngine {
|
||||||
|
|
||||||
private final List<Token> tokens = new ArrayList<>(); // 扫描结果
|
private final List<Token> tokens = new ArrayList<>(); // 扫描结果
|
||||||
private final List<LexicalError> errors = new ArrayList<>();
|
private final List<LexicalError> errors = new ArrayList<>();
|
||||||
private final String absPath; // 绝对路径
|
private final String absPath; // 绝对路径
|
||||||
private final LexerContext context; // 字符流
|
private final LexerContext context; // 字符流
|
||||||
private final List<TokenScanner> scanners; // 扫描器链
|
private final List<TokenScanner> scanners; // 扫描器链
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建并立即执行扫描-校验-报告流程。
|
* 创建并立即执行扫描-校验-报告流程。
|
||||||
|
*
|
||||||
* @param source 源代码文本
|
* @param source 源代码文本
|
||||||
* @param sourceName 文件名(诊断用)
|
* @param sourceName 文件名(诊断用)
|
||||||
*/
|
*/
|
||||||
public LexerEngine(String source, String sourceName) {
|
public LexerEngine(String source, String sourceName) {
|
||||||
this.absPath = new File(sourceName).getAbsolutePath();
|
this.absPath = new File(sourceName).getAbsolutePath();
|
||||||
this.context = new LexerContext(source);
|
this.context = new LexerContext(source);
|
||||||
this.scanners = List.of(
|
this.scanners = List.of(
|
||||||
new WhitespaceTokenScanner(),
|
new WhitespaceTokenScanner(),
|
||||||
new NewlineTokenScanner(),
|
new NewlineTokenScanner(),
|
||||||
@ -55,12 +56,6 @@ public class LexerEngine {
|
|||||||
TokenPrinter.print(tokens);
|
TokenPrinter.print(tokens);
|
||||||
/* 4. 统一报告错误 */
|
/* 4. 统一报告错误 */
|
||||||
report(errors);
|
report(errors);
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
throw new LexicalException(
|
|
||||||
"Lexing failed with " + errors.size() + " error(s).",
|
|
||||||
context.getLine(), context.getCol()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void report(List<LexicalError> errors) {
|
public static void report(List<LexicalError> errors) {
|
||||||
@ -68,15 +63,20 @@ public class LexerEngine {
|
|||||||
System.out.println("\n## 词法分析通过,没有发现错误\n");
|
System.out.println("\n## 词法分析通过,没有发现错误\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
System.err.println("\n词法分析发现 " + errors.size() + " 个错误:");
|
System.err.println("\n词法分析发现 " + errors.size() + " 个错误: ");
|
||||||
errors.forEach(e -> System.err.println(" " + e));
|
errors.forEach(e -> System.err.println("\t" + e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Token> getAllTokens() { return List.copyOf(tokens); }
|
public List<Token> getAllTokens() {
|
||||||
public List<LexicalError> getErrors() { return List.copyOf(errors); }
|
return List.copyOf(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<LexicalError> getErrors() {
|
||||||
|
return List.copyOf(errors);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 逐字符扫描:依次尝试各扫描器;扫描器抛出的
|
* 逐字符扫描: 依次尝试各扫描器;扫描器抛出的
|
||||||
* {@link LexicalException} 被捕获并转为 {@link LexicalError}。
|
* {@link LexicalException} 被捕获并转为 {@link LexicalError}。
|
||||||
*/
|
*/
|
||||||
private void scanAllTokens() {
|
private void scanAllTokens() {
|
||||||
@ -93,7 +93,7 @@ public class LexerEngine {
|
|||||||
errors.add(new LexicalError(
|
errors.add(new LexicalError(
|
||||||
absPath, le.getLine(), le.getColumn(), le.getReason()
|
absPath, le.getLine(), le.getColumn(), le.getReason()
|
||||||
));
|
));
|
||||||
context.advance(); // 跳过问题字符
|
skipInvalidLexeme();
|
||||||
}
|
}
|
||||||
handled = true;
|
handled = true;
|
||||||
break;
|
break;
|
||||||
@ -105,10 +105,24 @@ public class LexerEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 目前包含三条规则:<br>
|
* 跳过当前位置起连续的“标识符 / 数字 / 下划线 / 点”字符。
|
||||||
* 1. Dot-Prefix'.' 不能作标识符前缀<br>
|
* <p>这样可以把诸如 {@code 1abc} 的残余 {@code abc}、{@code _}、
|
||||||
* 2. Declare-Ident declare 后必须紧跟合法标识符,并且只能一个<br>
|
* {@code .999} 等一次性忽略,避免后续被误识别为新的 token。</p>
|
||||||
* 3. Double-Ident declare 后若出现第二个 IDENTIFIER 视为多余<br>
|
*/
|
||||||
|
private void skipInvalidLexeme() {
|
||||||
|
while (!context.isAtEnd()) {
|
||||||
|
char c = context.peek();
|
||||||
|
if (Character.isWhitespace(c)) break; // 空白 / 换行
|
||||||
|
if (!Character.isLetterOrDigit(c)
|
||||||
|
&& c != '_' && c != '.') break; // 符号分隔
|
||||||
|
context.advance(); // 否则继续吞掉
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目前包含2条规则: <br>
|
||||||
|
* 1. Declare-Ident declare 后必须紧跟合法标识符,并且只能一个<br>
|
||||||
|
* 2. Double-Ident declare 后若出现第二个 IDENTIFIER 视为多余<br>
|
||||||
* <p>发现问题仅写入 {@link #errors},不抛异常。</p>
|
* <p>发现问题仅写入 {@link #errors},不抛异常。</p>
|
||||||
*/
|
*/
|
||||||
private void validateTokens() {
|
private void validateTokens() {
|
||||||
@ -138,7 +152,9 @@ public class LexerEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** index 右侧最近非 NEWLINE token;无则 null */
|
/**
|
||||||
|
* index 右侧最近非 NEWLINE token;无则 null
|
||||||
|
*/
|
||||||
private Token findNextNonNewline(int index) {
|
private Token findNextNonNewline(int index) {
|
||||||
for (int j = index + 1; j < tokens.size(); j++) {
|
for (int j = index + 1; j < tokens.size(); j++) {
|
||||||
Token t = tokens.get(j);
|
Token t = tokens.get(j);
|
||||||
@ -147,8 +163,10 @@ public class LexerEngine {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 构造统一的 LexicalError */
|
/**
|
||||||
|
* 构造统一的 LexicalError
|
||||||
|
*/
|
||||||
private LexicalError err(Token t, String msg) {
|
private LexicalError err(Token t, String msg) {
|
||||||
return new LexicalError(absPath, t.getLine(), t.getCol(), "非法的标记序列:" + msg);
|
return new LexicalError(absPath, t.getLine(), t.getCol(), msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,41 @@
|
|||||||
package org.jcnc.snow.compiler.lexer.core;
|
package org.jcnc.snow.compiler.lexer.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示词法分析过程中发生的错误信息。
|
||||||
|
* <p>
|
||||||
|
* 该类用于封装词法分析(lexical analysis)阶段发现的错误,包括错误位置(文件名、行号、列号)
|
||||||
|
* 以及错误描述信息,便于定位和调试。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
public class LexicalError {
|
public class LexicalError {
|
||||||
|
/**
|
||||||
|
* 出错所在的源文件名。
|
||||||
|
*/
|
||||||
private final String file;
|
private final String file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出错所在的行号(从1开始)。
|
||||||
|
*/
|
||||||
private final int line;
|
private final int line;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出错所在的列号(从1开始)。
|
||||||
|
*/
|
||||||
private final int column;
|
private final int column;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误的详细描述信息。
|
||||||
|
*/
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个新的 {@code LexicalError} 实例。
|
||||||
|
*
|
||||||
|
* @param file 出错的源文件名
|
||||||
|
* @param line 出错所在的行号(从1开始)
|
||||||
|
* @param column 出错所在的列号(从1开始)
|
||||||
|
* @param message 错误的详细描述信息
|
||||||
|
*/
|
||||||
public LexicalError(String file, int line, int column, String message) {
|
public LexicalError(String file, int line, int column, String message) {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
@ -13,6 +43,11 @@ public class LexicalError {
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以易于阅读的字符串形式返回错误信息。
|
||||||
|
*
|
||||||
|
* @return 格式化的错误信息字符串,包含文件名、行号、列号和错误描述
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return file + ": 行 " + line + ", 列 " + column + ": " + message;
|
return file + ": 行 " + line + ", 列 " + column + ": " + message;
|
||||||
|
|||||||
@ -10,8 +10,8 @@ package org.jcnc.snow.compiler.lexer.core;
|
|||||||
* <li>完全禁止 Java 堆栈信息输出,使命令行输出保持整洁。</li>
|
* <li>完全禁止 Java 堆栈信息输出,使命令行输出保持整洁。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <pre>
|
* <pre>
|
||||||
* 例:
|
* 例:
|
||||||
* main.s:2:19: Lexical error: Illegal character sequence '@' at 2:19
|
* Main.snow: 行 7, 列 20: 词法错误: 非法字符序列 '@'
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public class LexicalException extends RuntimeException {
|
public class LexicalException extends RuntimeException {
|
||||||
@ -24,7 +24,7 @@ public class LexicalException extends RuntimeException {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造词法异常
|
* 构造词法异常
|
||||||
* @param reason 错误原因(如:非法字符描述)
|
* @param reason 错误原因(如: 非法字符描述)
|
||||||
* @param line 出错行号
|
* @param line 出错行号
|
||||||
* @param column 出错列号
|
* @param column 出错列号
|
||||||
*/
|
*/
|
||||||
@ -36,7 +36,6 @@ public class LexicalException extends RuntimeException {
|
|||||||
this.column = column;
|
this.column = column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 屏蔽异常堆栈填充(始终不打印堆栈信息)
|
* 屏蔽异常堆栈填充(始终不打印堆栈信息)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -11,11 +11,11 @@
|
|||||||
|
|
||||||
## 核心功能
|
## 核心功能
|
||||||
|
|
||||||
- **词法分析引擎**:从源代码提取标准化 Token 流
|
- **词法分析引擎**: 从源代码提取标准化 Token 流
|
||||||
- **模块化扫描器体系**:按需扩展不同类型的 Token 识别
|
- **模块化扫描器体系**: 按需扩展不同类型的 Token 识别
|
||||||
- **灵活的上下文管理**:跟踪源代码位置,支持错误处理
|
- **灵活的上下文管理**: 跟踪源代码位置,支持错误处理
|
||||||
- **统一的 Token 工厂**:集中创建 Token 实例
|
- **统一的 Token 工厂**: 集中创建 Token 实例
|
||||||
- **工具支持**:Token 打印与调试辅助
|
- **工具支持**: Token 打印与调试辅助
|
||||||
|
|
||||||
## 模块结构
|
## 模块结构
|
||||||
|
|
||||||
@ -33,6 +33,6 @@ lexer/
|
|||||||
|
|
||||||
* JDK 24 或更高版本
|
* JDK 24 或更高版本
|
||||||
* Maven 构建管理
|
* Maven 构建管理
|
||||||
* 推荐 IDE:IntelliJ IDEA
|
* 推荐 IDE: IntelliJ IDEA
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -35,7 +35,7 @@ public abstract class AbstractTokenScanner implements TokenScanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 抽象方法:由子类实现具体的扫描逻辑。
|
* 抽象方法: 由子类实现具体的扫描逻辑。
|
||||||
* <p>
|
* <p>
|
||||||
* 实现应消费一定字符并根据规则构造 Token。
|
* 实现应消费一定字符并根据规则构造 Token。
|
||||||
* 若无需生成 Token,可返回 null。
|
* 若无需生成 Token,可返回 null。
|
||||||
@ -49,7 +49,7 @@ public abstract class AbstractTokenScanner implements TokenScanner {
|
|||||||
protected abstract Token scanToken(LexerContext ctx, int line, int col);
|
protected abstract Token scanToken(LexerContext ctx, int line, int col);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工具方法:连续读取字符直到遇到不满足指定条件的字符。
|
* 工具方法: 连续读取字符直到遇到不满足指定条件的字符。
|
||||||
*
|
*
|
||||||
* @param ctx 当前词法上下文
|
* @param ctx 当前词法上下文
|
||||||
* @param predicate 字符匹配条件
|
* @param predicate 字符匹配条件
|
||||||
|
|||||||
@ -8,17 +8,17 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
|
|||||||
/**
|
/**
|
||||||
* {@code CommentTokenScanner} —— 注释解析器,基于有限状态机(FSM)。
|
* {@code CommentTokenScanner} —— 注释解析器,基于有限状态机(FSM)。
|
||||||
*
|
*
|
||||||
* <p>负责将源码中的两种注释形式切分为 {@link TokenType#COMMENT COMMENT} token:</p>
|
* <p>负责将源码中的两种注释形式切分为 {@link TokenType#COMMENT COMMENT} token: </p>
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>单行注释:以 {@code //} 开头,直至行尾或文件末尾。</li>
|
* <li>单行注释: 以 {@code //} 开头,直至行尾或文件末尾。</li>
|
||||||
* <li>多行注释:以 {@code /*} 开头,以 <code>*/</code> 结束,可跨多行。</li>
|
* <li>多行注释: 以 {@code /*} 开头,以 <code>*/</code> 结束,可跨多行。</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* <p>本扫描器遵循“发现即捕获”原则:注释文本被完整保留在 Token 中,供后续的文档提取、源映射等分析使用。</p>
|
* <p>本扫描器遵循“发现即捕获”原则: 注释文本被完整保留在 Token 中,供后续的文档提取、源映射等分析使用。</p>
|
||||||
*
|
*
|
||||||
* <p>错误处理策略</p>
|
* <p>错误处理策略</p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>未终止的多行注释:若文件结束时仍未遇到 <code>*/</code>,抛出 {@link LexicalException}。</li>
|
* <li>未终止的多行注释: 若文件结束时仍未遇到 <code>*/</code>,抛出 {@link LexicalException}。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class CommentTokenScanner extends AbstractTokenScanner {
|
public class CommentTokenScanner extends AbstractTokenScanner {
|
||||||
@ -61,7 +61,7 @@ public class CommentTokenScanner extends AbstractTokenScanner {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SINGLE_LINE:
|
case SINGLE_LINE:
|
||||||
// 单行注释处理:读取直到行尾
|
// 单行注释处理: 读取直到行尾
|
||||||
if (ctx.isAtEnd() || ctx.peek() == '\n') {
|
if (ctx.isAtEnd() || ctx.peek() == '\n') {
|
||||||
// 如果遇到换行符,停止读取并返回注释内容
|
// 如果遇到换行符,停止读取并返回注释内容
|
||||||
return new Token(TokenType.COMMENT, literal.toString(), line, col);
|
return new Token(TokenType.COMMENT, literal.toString(), line, col);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
|
|||||||
/**
|
/**
|
||||||
* {@code IdentifierTokenScanner} —— 标识符扫描器,负责识别源代码中的标识符(如变量名、函数名等)。
|
* {@code IdentifierTokenScanner} —— 标识符扫描器,负责识别源代码中的标识符(如变量名、函数名等)。
|
||||||
*
|
*
|
||||||
* <p>标识符的识别遵循以下规则:</p>
|
* <p>标识符的识别遵循以下规则: </p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>标识符必须以字母(A-Z,a-z)或下划线(_)开头。</li>
|
* <li>标识符必须以字母(A-Z,a-z)或下划线(_)开头。</li>
|
||||||
* <li>标识符的后续字符可以是字母、数字(0-9)或下划线。</li>
|
* <li>标识符的后续字符可以是字母、数字(0-9)或下划线。</li>
|
||||||
|
|||||||
@ -5,18 +5,12 @@ import org.jcnc.snow.compiler.lexer.token.Token;
|
|||||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 换行符扫描器:将源代码中的换行符(\n)识别为 {@code NEWLINE} 类型的 Token。
|
* 换行符扫描器: 将源代码中的换行符(\n)识别为 {@code NEWLINE} 类型的 Token。
|
||||||
* <p>
|
* <p>
|
||||||
* 用于记录行的分界,辅助语法分析阶段进行行敏感的判断或保持结构清晰。
|
* 用于记录行的分界,辅助语法分析阶段进行行敏感的判断或保持结构清晰。
|
||||||
*/
|
*/
|
||||||
public class NewlineTokenScanner extends AbstractTokenScanner {
|
public class NewlineTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
// 定义状态枚举
|
|
||||||
private enum State {
|
|
||||||
INITIAL,
|
|
||||||
NEWLINE
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前状态
|
// 当前状态
|
||||||
private State currentState = State.INITIAL;
|
private State currentState = State.INITIAL;
|
||||||
|
|
||||||
@ -31,7 +25,7 @@ public class NewlineTokenScanner extends AbstractTokenScanner {
|
|||||||
@Override
|
@Override
|
||||||
public boolean canHandle(char c, LexerContext ctx) {
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
// 只有当处于 INITIAL 状态,并且遇到换行符时,才可以处理
|
// 只有当处于 INITIAL 状态,并且遇到换行符时,才可以处理
|
||||||
return currentState == State.INITIAL && c == '\n';
|
return currentState == State.INITIAL && (c == '\n' || c == '\r');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,16 +39,33 @@ public class NewlineTokenScanner extends AbstractTokenScanner {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Token scanToken(LexerContext ctx, int line, int col) {
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
// 状态转换为 NEWLINE
|
|
||||||
currentState = State.NEWLINE;
|
currentState = State.NEWLINE;
|
||||||
|
|
||||||
// 执行换行符扫描,生成 token
|
char first = ctx.peek();
|
||||||
|
String lexeme;
|
||||||
|
|
||||||
ctx.advance();
|
ctx.advance();
|
||||||
Token newlineToken = new Token(TokenType.NEWLINE, "\n", line, col);
|
if (first == '\r') {
|
||||||
|
// 检查是否是 \r\n
|
||||||
|
if (!ctx.isAtEnd() && ctx.peek() == '\n') {
|
||||||
|
ctx.advance();
|
||||||
|
lexeme = "\r\n";
|
||||||
|
} else {
|
||||||
|
lexeme = "\r";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 一定是 \n
|
||||||
|
lexeme = "\n";
|
||||||
|
}
|
||||||
|
|
||||||
// 扫描完成后,恢复状态为 INITIAL
|
Token newlineToken = new Token(TokenType.NEWLINE, lexeme, line, col);
|
||||||
currentState = State.INITIAL;
|
currentState = State.INITIAL;
|
||||||
|
|
||||||
return newlineToken;
|
return newlineToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 定义状态枚举
|
||||||
|
private enum State {
|
||||||
|
INITIAL,
|
||||||
|
NEWLINE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,17 +8,17 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
|
|||||||
/**
|
/**
|
||||||
* NumberTokenScanner —— 基于有限状态机(FSM)的数字字面量解析器。
|
* NumberTokenScanner —— 基于有限状态机(FSM)的数字字面量解析器。
|
||||||
* <p>
|
* <p>
|
||||||
* 该扫描器负责将源码中的数字字符串切分为 NUMBER_LITERAL token,当前支持:
|
* 该扫描器负责将源码中的数字字符串切分为 NUMBER_LITERAL token,当前支持:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>十进制整数(如 0,42,123456)</li>
|
* <li>十进制整数(如 0,42,123456)</li>
|
||||||
* <li>十进制小数(如 3.14,0.5)</li>
|
* <li>十进制小数(如 3.14,0.5)</li>
|
||||||
* <li>单字符类型后缀(如 2.0f,255B,合法集合见 SUFFIX_CHARS)</li>
|
* <li>单字符类型后缀(如 2.0f,255B,合法集合见 SUFFIX_CHARS)</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
* <p>
|
||||||
* 如果后续需要支持科学计数法、下划线分隔符、不同进制等,只需扩展现有状态机的转移规则。
|
* 如果后续需要支持科学计数法、下划线分隔符、不同进制等,只需扩展现有状态机的转移规则。
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 状态机简述:
|
* 状态机简述:
|
||||||
* INT_PART --'.'--> DEC_POINT
|
* INT_PART --'.'--> DEC_POINT
|
||||||
* | |
|
* | |
|
||||||
* | v
|
* | v
|
||||||
@ -27,31 +27,31 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
|
|||||||
* v
|
* v
|
||||||
* DEC_POINT --digit--> FRAC_PART
|
* DEC_POINT --digit--> FRAC_PART
|
||||||
* </pre>
|
* </pre>
|
||||||
* 状态说明:
|
* 状态说明:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>INT_PART :读取整数部分,遇到 '.' 进入 DEC_POINT,否则结束。</li>
|
* <li>INT_PART : 读取整数部分,遇到 '.' 进入 DEC_POINT,否则结束。</li>
|
||||||
* <li>DEC_POINT :已读到小数点,必须下一个字符是数字,否则报错。</li>
|
* <li>DEC_POINT : 已读到小数点,必须下一个字符是数字,否则报错。</li>
|
||||||
* <li>FRAC_PART :读取小数部分,遇非法字符则结束主体。</li>
|
* <li>FRAC_PART : 读取小数部分,遇非法字符则结束主体。</li>
|
||||||
* <li>END :主体扫描结束,进入后缀/尾随字符判定。</li>
|
* <li>END : 主体扫描结束,进入后缀/尾随字符判定。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
* <p>
|
||||||
* 错误处理策略:
|
* 错误处理策略:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>数字后跟未知字母(如 42X)—— 抛出 LexicalException</li>
|
* <li>数字后跟未知字母(如 42X)—— 抛出 LexicalException</li>
|
||||||
* <li>数字与合法后缀间有空白(如 3 L)—— 抛出 LexicalException</li>
|
* <li>数字与合法后缀间有空白(如 3 L)—— 抛出 LexicalException</li>
|
||||||
* <li>小数点后缺失数字(如 1.)—— 抛出 LexicalException</li>
|
* <li>小数点后缺失数字(如 1.)—— 抛出 LexicalException</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
* <p>
|
||||||
* 支持的单字符类型后缀包括:b, s, l, f, d 及其大写形式。若需支持多字符后缀,可将该集合扩展为 Set<String>。
|
* 支持的单字符类型后缀包括: b, s, l, f 及其大写形式。若需支持多字符后缀,可将该集合扩展为 Set<String>。
|
||||||
*/
|
*/
|
||||||
public class NumberTokenScanner extends AbstractTokenScanner {
|
public class NumberTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支持的单字符类型后缀集合。
|
* 支持的单字符类型后缀集合。
|
||||||
* 包含:b, s, l, f, d 及其大写形式。
|
* 包含: b, s, l, f, d 及其大写形式。
|
||||||
* 对于多字符后缀,可扩展为 Set<String> 并在扫描尾部做贪婪匹配。
|
* 对于多字符后缀,可扩展为 Set<String> 并在扫描尾部做贪婪匹配。
|
||||||
*/
|
*/
|
||||||
private static final String SUFFIX_CHARS = "bslfdBSLFD";
|
private static final String SUFFIX_CHARS = "bslfBSLF";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否由该扫描器处理。
|
* 判断是否由该扫描器处理。
|
||||||
@ -80,6 +80,9 @@ public class NumberTokenScanner extends AbstractTokenScanner {
|
|||||||
StringBuilder literal = new StringBuilder();
|
StringBuilder literal = new StringBuilder();
|
||||||
State state = State.INT_PART;
|
State state = State.INT_PART;
|
||||||
|
|
||||||
|
boolean lastWasUnderscore = false; // 记录前一个是否是下划线
|
||||||
|
boolean sawDigit = false; // 当前段落是否有数字(防止以下划线开头)
|
||||||
|
|
||||||
/* ───── 1. 主体扫描 —— 整数 / 小数 ───── */
|
/* ───── 1. 主体扫描 —— 整数 / 小数 ───── */
|
||||||
mainLoop:
|
mainLoop:
|
||||||
while (!ctx.isAtEnd() && state != State.END) {
|
while (!ctx.isAtEnd() && state != State.END) {
|
||||||
@ -89,10 +92,25 @@ public class NumberTokenScanner extends AbstractTokenScanner {
|
|||||||
case INT_PART:
|
case INT_PART:
|
||||||
if (Character.isDigit(ch)) {
|
if (Character.isDigit(ch)) {
|
||||||
literal.append(ctx.advance());
|
literal.append(ctx.advance());
|
||||||
|
lastWasUnderscore = false;
|
||||||
|
sawDigit = true;
|
||||||
|
} else if (ch == '_') {
|
||||||
|
if (!sawDigit)
|
||||||
|
throw new LexicalException("数字不能以下划线开头", line, col);
|
||||||
|
if (lastWasUnderscore)
|
||||||
|
throw new LexicalException("数字中下划线不能连续出现", line, col);
|
||||||
|
literal.append(ctx.advance());
|
||||||
|
lastWasUnderscore = true;
|
||||||
} else if (ch == '.') {
|
} else if (ch == '.') {
|
||||||
|
if (lastWasUnderscore)
|
||||||
|
throw new LexicalException("下划线不能出现在小数点前", line, col);
|
||||||
state = State.DEC_POINT;
|
state = State.DEC_POINT;
|
||||||
literal.append(ctx.advance());
|
literal.append(ctx.advance());
|
||||||
|
// 不要重置sawDigit!
|
||||||
|
// sawDigit = false; // 移除此句
|
||||||
} else {
|
} else {
|
||||||
|
if (lastWasUnderscore)
|
||||||
|
throw new LexicalException("数字不能以下划线结尾", line, col);
|
||||||
state = State.END;
|
state = State.END;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -100,18 +118,30 @@ public class NumberTokenScanner extends AbstractTokenScanner {
|
|||||||
/* 已读到小数点,下一字符必须是数字 */
|
/* 已读到小数点,下一字符必须是数字 */
|
||||||
case DEC_POINT:
|
case DEC_POINT:
|
||||||
if (Character.isDigit(ch)) {
|
if (Character.isDigit(ch)) {
|
||||||
state = State.FRAC_PART;
|
|
||||||
literal.append(ctx.advance());
|
literal.append(ctx.advance());
|
||||||
|
state = State.FRAC_PART;
|
||||||
|
sawDigit = true;
|
||||||
|
} else if (ch == '_') { // 防止小数点后直接跟下划线
|
||||||
|
throw new LexicalException("小数点后不能直接跟下划线", line, col);
|
||||||
} else {
|
} else {
|
||||||
throw new LexicalException("小数点后必须跟数字", line, col);
|
throw new LexicalException("小数点后必须跟数字", line, col);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* 小数部分 */
|
/* 小数部分 */
|
||||||
|
|
||||||
case FRAC_PART:
|
case FRAC_PART:
|
||||||
if (Character.isDigit(ch)) {
|
if (Character.isDigit(ch)) {
|
||||||
literal.append(ctx.advance());
|
literal.append(ctx.advance());
|
||||||
|
lastWasUnderscore = false;
|
||||||
|
} else if (ch == '_') { // 小数部分下划线检查
|
||||||
|
if (lastWasUnderscore)
|
||||||
|
throw new LexicalException("数字中下划线不能连续出现", line, col);
|
||||||
|
literal.append(ctx.advance());
|
||||||
|
lastWasUnderscore = true;
|
||||||
} else {
|
} else {
|
||||||
|
if (lastWasUnderscore)
|
||||||
|
throw new LexicalException("数字不能以下划线结尾", line, col);
|
||||||
state = State.END;
|
state = State.END;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -121,34 +151,67 @@ public class NumberTokenScanner extends AbstractTokenScanner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 主体结束后,下划线不能在末尾
|
||||||
|
if (lastWasUnderscore)
|
||||||
|
throw new LexicalException("数字不能以下划线结尾", line, col);
|
||||||
|
|
||||||
/* ───── 2. 后缀及非法尾随字符检查 ───── */
|
/* ───── 2. 后缀及非法尾随字符检查 ───── */
|
||||||
if (!ctx.isAtEnd()) {
|
if (!ctx.isAtEnd()) {
|
||||||
char next = ctx.peek();
|
char next = ctx.peek();
|
||||||
|
|
||||||
/* 2-A. 合法单字符后缀(紧邻数字,不允许空格) */
|
/* 2-A. 合法单字符后缀(紧邻数字,无空格) */
|
||||||
if (SUFFIX_CHARS.indexOf(next) >= 0) {
|
if (SUFFIX_CHARS.indexOf(next) >= 0) {
|
||||||
literal.append(ctx.advance());
|
literal.append(ctx.advance());
|
||||||
}
|
|
||||||
/* 2-B. 未知紧邻字母后缀 —— 报错 */
|
/* 后缀只能出现一次:再次出现字母/数字/点即视为非法 */
|
||||||
else if (Character.isLetter(next)) {
|
if (!ctx.isAtEnd()) {
|
||||||
throw new LexicalException("未知的数字类型后缀 '" + next + "'", line, col);
|
char peekAfterSuffix = ctx.peek();
|
||||||
|
if (Character.isLetter(peekAfterSuffix)
|
||||||
|
|| Character.isDigit(peekAfterSuffix)
|
||||||
|
|| peekAfterSuffix == '.') {
|
||||||
|
throw new LexicalException(
|
||||||
|
"数字类型后缀只能是单字符,非法续接 '" + peekAfterSuffix + "'",
|
||||||
|
line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2-B. **非法字母**(既不是后缀,也没有空白隔开) */
|
||||||
|
} else if (Character.isLetter(next)) {
|
||||||
|
var its = new IdentifierTokenScanner();
|
||||||
|
var token = its.scanToken(ctx, line, col);
|
||||||
|
throw new LexicalException(
|
||||||
|
"数字后不能紧跟未知标识符 '" + token.getLexeme() + "'", line, col);
|
||||||
|
/* 2-C. **非法下划线** */
|
||||||
|
} else if (next == '_') {
|
||||||
|
throw new LexicalException(
|
||||||
|
"数字后不能紧跟下划线 '_'", line, col);
|
||||||
}
|
}
|
||||||
/* 其余情况交由外层扫描器处理(包括空白及其它符号) */
|
/* 其余情况交由外层扫描器处理(包括空白及其它符号) */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ───── 3. 生成并返回 Token ───── */
|
/* ───── 3. 生成并返回 Token ───── */
|
||||||
return new Token(TokenType.NUMBER_LITERAL, literal.toString(), line, col);
|
return new Token(TokenType.NUMBER_LITERAL, literal.toString().replace("_", ""), line, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** FSM 内部状态定义 */
|
/**
|
||||||
|
* FSM 内部状态定义
|
||||||
|
*/
|
||||||
private enum State {
|
private enum State {
|
||||||
/** 整数部分 */
|
/**
|
||||||
|
* 整数部分
|
||||||
|
*/
|
||||||
INT_PART,
|
INT_PART,
|
||||||
/** 已读到小数点,但还未读到第一位小数数字 */
|
/**
|
||||||
|
* 已读到小数点,但还未读到第一位小数数字
|
||||||
|
*/
|
||||||
DEC_POINT,
|
DEC_POINT,
|
||||||
/** 小数部分 */
|
/**
|
||||||
|
* 小数部分
|
||||||
|
*/
|
||||||
FRAC_PART,
|
FRAC_PART,
|
||||||
/** 主体结束,准备处理后缀或交还控制权 */
|
/**
|
||||||
|
* 主体结束,准备处理后缀或交还控制权
|
||||||
|
*/
|
||||||
END
|
END
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,14 +8,14 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
|
|||||||
* 运算符扫描器(OperatorTokenScanner)
|
* 运算符扫描器(OperatorTokenScanner)
|
||||||
*
|
*
|
||||||
* <p>负责在词法分析阶段识别由 <b>= ! < > | & %</b> 等字符
|
* <p>负责在词法分析阶段识别由 <b>= ! < > | & %</b> 等字符
|
||||||
* 起始的单字符或双字符运算符,并生成相应 {@link Token}:</p>
|
* 起始的单字符或双字符运算符,并生成相应 {@link Token}: </p>
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>赋值 / 比较:{@code =}, {@code ==}, {@code !=}</li>
|
* <li>赋值 / 比较: {@code =}, {@code ==}, {@code !=}</li>
|
||||||
* <li>关系运算:{@code >}, {@code >=}, {@code <}, {@code <=}</li>
|
* <li>关系运算: {@code >}, {@code >=}, {@code <}, {@code <=}</li>
|
||||||
* <li>逻辑运算:{@code &&}, {@code ||}</li>
|
* <li>逻辑运算: {@code &&}, {@code ||}</li>
|
||||||
* <li>取模运算:{@code %}</li>
|
* <li>取模运算: {@code %}</li>
|
||||||
* <li>逻辑非:{@code !}</li>
|
* <li>逻辑非: {@code !}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>如果无法匹配到合法组合,将返回 {@link TokenType#UNKNOWN}。</p>
|
* <p>如果无法匹配到合法组合,将返回 {@link TokenType#UNKNOWN}。</p>
|
||||||
@ -47,10 +47,6 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
|
|||||||
char c = ctx.advance();
|
char c = ctx.advance();
|
||||||
String lexeme = String.valueOf(c);
|
String lexeme = String.valueOf(c);
|
||||||
TokenType type = TokenType.UNKNOWN;
|
TokenType type = TokenType.UNKNOWN;
|
||||||
|
|
||||||
// 当前状态
|
|
||||||
State currentState = State.OPERATOR;
|
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '=':
|
case '=':
|
||||||
if (ctx.match('=')) {
|
if (ctx.match('=')) {
|
||||||
@ -107,22 +103,10 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
currentState = State.UNKNOWN;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行完扫描后,重置状态为初始状态
|
|
||||||
if (currentState != State.UNKNOWN) {
|
|
||||||
currentState = State.START;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Token(type, lexeme, line, col);
|
return new Token(type, lexeme, line, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义状态枚举
|
|
||||||
private enum State {
|
|
||||||
START, // 初始状态
|
|
||||||
OPERATOR, // 当前字符是运算符的一部分
|
|
||||||
UNKNOWN // 无法识别的状态
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,91 +5,114 @@ import org.jcnc.snow.compiler.lexer.token.Token;
|
|||||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符串扫描器:处理双引号包裹的字符串字面量,支持基本的转义字符。
|
* 字符串扫描器(StringTokenScanner)用于处理由双引号包裹的字符串字面量,
|
||||||
|
* 并支持常见转义字符的解析。该扫描器采用有限状态机(状态机)实现,
|
||||||
|
* 保证对各类字符串格式进行准确的词法分析。
|
||||||
* <p>
|
* <p>
|
||||||
* 支持格式示例:
|
* 主要支持如下字符串样式:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>"hello"</li>
|
* <li>"hello"</li>
|
||||||
* <li>"line\\nbreak"</li>
|
* <li>"line\\nbreak"</li>
|
||||||
* <li>"escaped \\\" quote"</li>
|
* <li>"escaped \\\" quote"</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* 扫描器会保留原始字符串的形式(包含双引号和转义符),
|
* 扫描器会保留字符串的原始形式(含双引号和转义符),
|
||||||
* 并生成 {@code STRING_LITERAL} 类型的 Token。
|
* 并返回{@link TokenType#STRING_LITERAL}类型的Token。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 状态机说明:
|
||||||
|
* <ul>
|
||||||
|
* <li>START:起始状态,准备扫描第一个双引号</li>
|
||||||
|
* <li>STRING:扫描字符串内容</li>
|
||||||
|
* <li>ESCAPE:处理转义字符</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class StringTokenScanner extends AbstractTokenScanner {
|
public class StringTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否可以处理当前位置的字符。
|
* 判断当前位置的字符是否为字符串起始符号。
|
||||||
* <p>当字符为双引号(")时,认为是字符串字面量的开始。</p>
|
* <p>
|
||||||
|
* 只有遇到双引号(")时,才由本扫描器处理。
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param c 当前字符
|
* @param c 当前待扫描字符
|
||||||
* @param ctx 当前词法上下文
|
* @param ctx 当前词法分析上下文
|
||||||
* @return 如果为字符串起始符,则返回 true
|
* @return 如果是字符串起始符号,返回 true,否则返回 false
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandle(char c, LexerContext ctx) {
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
return c == '"'; // 只处理字符串开始符号
|
return c == '"'; // 只处理双引号起始
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行字符串的扫描逻辑。
|
* 执行字符串字面量的具体扫描过程。采用有限状态机处理逻辑,
|
||||||
* <p>从当前位置开始,读取直到匹配结束的双引号。
|
* 从起始的双引号开始,逐字符处理字符串内容,支持基本转义序列(如 \\n、\\t、\\\" 等)。
|
||||||
* 支持转义字符(如 \"、\\n 等),不会中断字符串扫描。</p>
|
* <p>
|
||||||
|
* 扫描遇到未转义的结尾双引号时即结束,并立即返回Token。
|
||||||
|
* 如果遇到换行或文件结束但未遇到结尾引号,视为字符串未闭合,仍返回已扫描内容。
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param ctx 词法上下文
|
* @param ctx 词法分析上下文,支持字符遍历与回退等操作
|
||||||
* @param line 当前行号
|
* @param line 字符串开始行号(用于错误定位)
|
||||||
* @param col 当前列号
|
* @param col 字符串开始列号(用于错误定位)
|
||||||
* @return 字符串字面量类型的 Token
|
* @return 解析得到的字符串字面量类型Token
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Token scanToken(LexerContext ctx, int line, int col) {
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder(); // 用于收集字符串原文
|
||||||
// 当前状态
|
State currentState = State.START; // 初始状态为START
|
||||||
State currentState = State.START; // 初始状态为开始扫描字符串
|
|
||||||
|
|
||||||
// 开始扫描字符串
|
// 主循环,直到文件结束或状态机中止
|
||||||
while (!ctx.isAtEnd()) {
|
while (!ctx.isAtEnd()) {
|
||||||
char c = ctx.advance();
|
char c = ctx.advance(); // 消耗当前字符
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
|
|
||||||
switch (currentState) {
|
switch (currentState) {
|
||||||
case START:
|
case START:
|
||||||
// 开始状态,遇到第一个双引号
|
// 第一个双引号,状态切换到STRING
|
||||||
currentState = State.STRING;
|
currentState = State.STRING;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STRING:
|
case STRING:
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
// 遇到转义字符,进入 ESCAPE 状态
|
// 遇到转义符,切换到ESCAPE状态
|
||||||
currentState = State.ESCAPE;
|
currentState = State.ESCAPE;
|
||||||
} else if (c == '"') {
|
} else if (c == '"') {
|
||||||
// 遇到结束的双引号,结束扫描
|
// 遇到结束双引号,立即返回Token(字符串扫描完毕)
|
||||||
currentState = State.END;
|
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
||||||
|
} else if (c == '\n' || c == '\r') {
|
||||||
|
// 若字符串未闭合且遇到换行,提前返回(可根据需要抛异常或报错)
|
||||||
|
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
||||||
}
|
}
|
||||||
|
// 其他字符,保持在STRING状态继续扫描
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESCAPE:
|
case ESCAPE:
|
||||||
// 在转义状态下,处理转义字符
|
// ESCAPE状态:下一个字符会作为转义内容,无论是"、n、t等
|
||||||
sb.append(ctx.advance()); // 加入转义字符后的字符
|
currentState = State.STRING;
|
||||||
currentState = State.STRING; // 返回字符串状态
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case END:
|
|
||||||
// 结束状态,字符串扫描完成
|
|
||||||
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有结束的双引号,则表示错误,或者未正确处理
|
// 若扫描到文件尾仍未遇到结尾引号,则返回当前内容
|
||||||
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义状态枚举
|
/**
|
||||||
|
* 状态机枚举类型,表示当前字符串解析所处的状态。
|
||||||
|
*/
|
||||||
private enum State {
|
private enum State {
|
||||||
START, // 开始状态,寻找字符串的开始双引号
|
/**
|
||||||
STRING, // 字符串扫描状态,处理字符串中的字符
|
* 起始状态,等待遇到第一个双引号
|
||||||
ESCAPE, // 处理转义字符状态
|
*/
|
||||||
END // 字符串结束状态
|
START,
|
||||||
|
/**
|
||||||
|
* 字符串内容状态,处理实际字符串及普通字符
|
||||||
|
*/
|
||||||
|
STRING,
|
||||||
|
/**
|
||||||
|
* 处理转义序列状态,遇到'\\'后切换到此状态
|
||||||
|
*/
|
||||||
|
ESCAPE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,13 +5,13 @@ import org.jcnc.snow.compiler.lexer.token.Token;
|
|||||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 符号扫描器:识别常见的单字符符号,如冒号、逗号、括号和算术符号。
|
* 符号扫描器: 识别常见的单字符符号,如冒号、逗号、括号和算术符号。
|
||||||
* <p>
|
* <p>
|
||||||
* 支持的符号包括:
|
* 支持的符号包括:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>标点符号:: , .</li>
|
* <li>标点符号: : , .</li>
|
||||||
* <li>括号:( )</li>
|
* <li>括号: ( )</li>
|
||||||
* <li>算术运算符:+ - *</li>
|
* <li>算术运算符: + - *</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* 生成的 Token 类型根据字符分别对应 {@link TokenType} 枚举中的定义。
|
* 生成的 Token 类型根据字符分别对应 {@link TokenType} 枚举中的定义。
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import org.jcnc.snow.compiler.lexer.token.Token;
|
|||||||
* 由本类处理并抛出 {@link LexicalException},终止词法分析流程。
|
* 由本类处理并抛出 {@link LexicalException},终止词法分析流程。
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* 主要作用:保证所有非法、不可识别的字符(如@、$等)不会被静默跳过或误当作合法 Token,
|
* 主要作用: 保证所有非法、不可识别的字符(如@、$等)不会被静默跳过或误当作合法 Token,
|
||||||
* 而是在词法阶段立刻定位并报错,有助于尽早发现源代码问题。
|
* 而是在词法阶段立刻定位并报错,有助于尽早发现源代码问题。
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
@ -50,6 +50,6 @@ public class UnknownTokenScanner extends AbstractTokenScanner {
|
|||||||
if (lexeme.isEmpty())
|
if (lexeme.isEmpty())
|
||||||
lexeme = String.valueOf(ctx.advance());
|
lexeme = String.valueOf(ctx.advance());
|
||||||
// 抛出词法异常,并带上错误片段与具体位置
|
// 抛出词法异常,并带上错误片段与具体位置
|
||||||
throw new LexicalException("Illegal character sequence '" + lexeme + "'", line, col);
|
throw new LexicalException("非法字符序列 '" + lexeme + "'", line, col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
|||||||
import org.jcnc.snow.compiler.lexer.token.Token;
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 空白符扫描器:跳过非换行的空白字符,不生成任何 Token。
|
* 空白符扫描器: 跳过非换行的空白字符,不生成任何 Token。
|
||||||
* <p>
|
* <p>
|
||||||
* 支持的空白字符包括空格、制表符(Tab)等,但不包括换行符(由 {@link NewlineTokenScanner} 处理)。
|
* 支持的空白字符包括空格、制表符(Tab)等,但不包括换行符(由 {@link NewlineTokenScanner} 处理)。
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import java.util.Set;
|
|||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 主要功能与特性:
|
* 主要功能与特性:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>统一管理语言关键字和类型名集合,便于扩展与维护。</li>
|
* <li>统一管理语言关键字和类型名集合,便于扩展与维护。</li>
|
||||||
* <li>自动推断 Token 类型,无需外部干预。</li>
|
* <li>自动推断 Token 类型,无需外部干预。</li>
|
||||||
@ -35,7 +35,7 @@ public class TokenFactory {
|
|||||||
/**
|
/**
|
||||||
* 创建一个根据内容自动推断类型的 {@link Token} 实例。
|
* 创建一个根据内容自动推断类型的 {@link Token} 实例。
|
||||||
* <p>
|
* <p>
|
||||||
* 优先级顺序为:类型(TYPE) > 关键字(KEYWORD) > 标识符(IDENTIFIER) > 未知(UNKNOWN)。
|
* 优先级顺序为: 类型(TYPE) > 关键字(KEYWORD) > 标识符(IDENTIFIER) > 未知(UNKNOWN)。
|
||||||
* 若原始字符串同时属于多类,则按优先级最高者处理。
|
* 若原始字符串同时属于多类,则按优先级最高者处理。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
@ -52,7 +52,7 @@ public class TokenFactory {
|
|||||||
/**
|
/**
|
||||||
* 判断并推断给定字符串的 {@link TokenType} 类型。
|
* 判断并推断给定字符串的 {@link TokenType} 类型。
|
||||||
* <p>
|
* <p>
|
||||||
* 优先级依次为:TYPE > KEYWORD > IDENTIFIER > UNKNOWN。
|
* 优先级依次为: TYPE > KEYWORD > IDENTIFIER > UNKNOWN。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param raw 原始词素字符串
|
* @param raw 原始词素字符串
|
||||||
@ -106,7 +106,7 @@ public class TokenFactory {
|
|||||||
* <p>
|
* <p>
|
||||||
* 合法标识符需以字母(a-z/A-Z)或下划线(_)开头,
|
* 合法标识符需以字母(a-z/A-Z)或下划线(_)开头,
|
||||||
* 后续可包含字母、数字或下划线。
|
* 后续可包含字母、数字或下划线。
|
||||||
* 例如:_abc, a1b2, name_123 均为合法标识符。
|
* 例如: _abc, a1b2, name_123 均为合法标识符。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param raw 输入的字符串
|
* @param raw 输入的字符串
|
||||||
|
|||||||
@ -17,7 +17,7 @@ public class TokenPrinter {
|
|||||||
/**
|
/**
|
||||||
* 将给定的 Token 列表打印到标准输出(控制台)。
|
* 将给定的 Token 列表打印到标准输出(控制台)。
|
||||||
* <p>
|
* <p>
|
||||||
* 输出格式:
|
* 输出格式:
|
||||||
* <pre>
|
* <pre>
|
||||||
* line col type lexeme
|
* line col type lexeme
|
||||||
* ----------------------------------------------------
|
* ----------------------------------------------------
|
||||||
@ -33,7 +33,7 @@ public class TokenPrinter {
|
|||||||
* 都应包含有效的行号、列号、类型和词素信息
|
* 都应包含有效的行号、列号、类型和词素信息
|
||||||
*/
|
*/
|
||||||
public static void print(List<Token> tokens) {
|
public static void print(List<Token> tokens) {
|
||||||
// 打印表头:列名对齐,宽度分别为 6、6、16
|
// 打印表头: 列名对齐,宽度分别为 6、6、16
|
||||||
System.out.printf("%-6s %-6s %-16s %s%n", "line", "col", "type", "lexeme");
|
System.out.printf("%-6s %-6s %-16s %s%n", "line", "col", "type", "lexeme");
|
||||||
System.out.println("----------------------------------------------------");
|
System.out.println("----------------------------------------------------");
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ public class TokenPrinter {
|
|||||||
.replace("\t", "\\t")
|
.replace("\t", "\\t")
|
||||||
.replace("\r", "\\r");
|
.replace("\r", "\\r");
|
||||||
|
|
||||||
// 按照固定格式输出:行号、列号、类型、词素
|
// 按照固定格式输出: 行号、列号、类型、词素
|
||||||
System.out.printf("%-6d %-6d %-16s %s%n",
|
System.out.printf("%-6d %-6d %-16s %s%n",
|
||||||
token.getLine(),
|
token.getLine(),
|
||||||
token.getCol(),
|
token.getCol(),
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public record BinaryExpressionNode(
|
|||||||
/**
|
/**
|
||||||
* 返回该二元运算表达式的字符串表示形式。
|
* 返回该二元运算表达式的字符串表示形式。
|
||||||
* <p>
|
* <p>
|
||||||
* 输出格式为:{@code left + " " + operator + " " + right},
|
* 输出格式为: {@code left + " " + operator + " " + right},
|
||||||
* 适用于调试或打印语法树结构。
|
* 适用于调试或打印语法树结构。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
|||||||
* {@code ExpressionStatementNode} 表示抽象语法树(AST)中的表达式语句节点。
|
* {@code ExpressionStatementNode} 表示抽象语法树(AST)中的表达式语句节点。
|
||||||
* <p>
|
* <p>
|
||||||
* 表达式语句通常由一个单独的表达式组成,并以语句形式出现。
|
* 表达式语句通常由一个单独的表达式组成,并以语句形式出现。
|
||||||
* 例如:{@code foo();}、{@code x = 1;}、{@code print("hello");} 等。
|
* 例如: {@code foo();}、{@code x = 1;}、{@code print("hello");} 等。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param expression 表达式主体,通常为函数调用、赋值、方法链式调用等可求值表达式。
|
* @param expression 表达式主体,通常为函数调用、赋值、方法链式调用等可求值表达式。
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import java.util.List;
|
|||||||
* <p>
|
* <p>
|
||||||
* 函数定义通常包含函数名、形参列表、返回类型以及函数体,
|
* 函数定义通常包含函数名、形参列表、返回类型以及函数体,
|
||||||
* 在语义分析、类型检查与代码生成等阶段具有核心地位。
|
* 在语义分析、类型检查与代码生成等阶段具有核心地位。
|
||||||
* 示例:{@code int add(int a, int b) { return a + b; }}
|
* 示例: {@code int add(int a, int b) { return a + b; }}
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param name 函数名称标识符
|
* @param name 函数名称标识符
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
|||||||
/**
|
/**
|
||||||
* {@code ImportNode} 表示抽象语法树(AST)中的 import 语句节点。
|
* {@code ImportNode} 表示抽象语法树(AST)中的 import 语句节点。
|
||||||
* <p>
|
* <p>
|
||||||
* import 语句用于引入外部模块或库文件,其语法形式一般为:
|
* import 语句用于引入外部模块或库文件,其语法形式一般为:
|
||||||
* {@code import my.module;}
|
* {@code import my.module;}
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import java.util.Optional;
|
|||||||
* return 语句用于从当前函数中返回控制权,并可携带一个可选的返回值表达式。
|
* return 语句用于从当前函数中返回控制权,并可携带一个可选的返回值表达式。
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* 示例:
|
* 示例:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code return;}</li>
|
* <li>{@code return;}</li>
|
||||||
* <li>{@code return x + 1;}</li>
|
* <li>{@code return x + 1;}</li>
|
||||||
|
|||||||
@ -6,10 +6,10 @@ import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
|||||||
/**
|
/**
|
||||||
* {@code UnaryExpressionNode} —— 前缀一元运算 AST 节点。
|
* {@code UnaryExpressionNode} —— 前缀一元运算 AST 节点。
|
||||||
*
|
*
|
||||||
* <p>代表两种受支持的一元前缀表达式:
|
* <p>代表两种受支持的一元前缀表达式:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><b>取负</b>:{@code -x}</li>
|
* <li><b>取负</b>: {@code -x}</li>
|
||||||
* <li><b>逻辑非</b>:{@code !x}</li>
|
* <li><b>逻辑非</b>: {@code !x}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* {@link #equals(Object)}、{@link #hashCode()} 等方法。</p>
|
* {@link #equals(Object)}、{@link #hashCode()} 等方法。</p>
|
||||||
|
|||||||
@ -3,12 +3,12 @@ package org.jcnc.snow.compiler.parser.ast.base;
|
|||||||
/**
|
/**
|
||||||
* {@code Node} 是抽象语法树(AST)中所有语法节点的统一根接口。
|
* {@code Node} 是抽象语法树(AST)中所有语法节点的统一根接口。
|
||||||
* <p>
|
* <p>
|
||||||
* 作为标记接口(Marker Interface),该接口定义 3 个方法:line()、column() 和 file() 用于定位错误,
|
* 作为标记接口(Marker Interface),该接口定义 3 个方法: line()、column() 和 file() 用于定位错误,
|
||||||
* 主要用于统一标识并组织 AST 体系中的各种语法构件节点,包括:
|
* 主要用于统一标识并组织 AST 体系中的各种语法构件节点,包括:
|
||||||
* </p>
|
* </p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link ExpressionNode}:表达式节点,如常量、变量引用、函数调用等</li>
|
* <li>{@link ExpressionNode}: 表达式节点,如常量、变量引用、函数调用等</li>
|
||||||
* <li>{@link StatementNode}:语句节点,如声明、赋值、条件控制、循环、返回语句等</li>
|
* <li>{@link StatementNode}: 语句节点,如声明、赋值、条件控制、循环、返回语句等</li>
|
||||||
* <li>模块、函数、参数等高层结构节点</li>
|
* <li>模块、函数、参数等高层结构节点</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@ -70,7 +70,7 @@ public record ParserEngine(ParserContext ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步:跳过当前行或直到遇到显式注册的顶层关键字。
|
* 同步: 跳过当前行或直到遇到显式注册的顶层关键字。
|
||||||
* <p>
|
* <p>
|
||||||
* 该机制用于语法出错后恢复到下一个可能的有效解析点,防止指针停滞导致死循环或重复抛错。
|
* 该机制用于语法出错后恢复到下一个可能的有效解析点,防止指针停滞导致死循环或重复抛错。
|
||||||
* 同步过程中会优先跳过本行所有未识别 token,并在遇到换行或注册关键字时停止,随后跳过连续空行。
|
* 同步过程中会优先跳过本行所有未识别 token,并在遇到换行或注册关键字时停止,随后跳过连续空行。
|
||||||
|
|||||||
@ -11,13 +11,13 @@
|
|||||||
|
|
||||||
## 核心功能
|
## 核心功能
|
||||||
|
|
||||||
- **抽象语法树(AST)生成**:定义丰富的 AST 节点类型
|
- **抽象语法树(AST)生成**: 定义丰富的 AST 节点类型
|
||||||
- **表达式解析器**:基于 Pratt 解析,支持优先级、调用、成员访问等
|
- **表达式解析器**: 基于 Pratt 解析,支持优先级、调用、成员访问等
|
||||||
- **语句解析器**:支持 Snow 语言中的声明、控制流等
|
- **语句解析器**: 支持 Snow 语言中的声明、控制流等
|
||||||
- **模块解析器**:处理 `import` 导入声明
|
- **模块解析器**: 处理 `import` 导入声明
|
||||||
- **函数解析器**:支持函数定义与调用
|
- **函数解析器**: 支持函数定义与调用
|
||||||
- **灵活的解析上下文管理**:错误处理与流式 Token 管理
|
- **灵活的解析上下文管理**: 错误处理与流式 Token 管理
|
||||||
- **工具支持**:AST JSON 序列化,便于调试与前后端通信
|
- **工具支持**: AST JSON 序列化,便于调试与前后端通信
|
||||||
|
|
||||||
## 模块结构
|
## 模块结构
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ parser/
|
|||||||
|
|
||||||
* JDK 24 或更高版本
|
* JDK 24 或更高版本
|
||||||
* Maven 构建管理
|
* Maven 构建管理
|
||||||
* 推荐 IDE:IntelliJ IDEA
|
* 推荐 IDE: IntelliJ IDEA
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -10,10 +10,10 @@ import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
|
|||||||
/**
|
/**
|
||||||
* {@code UnaryOperatorParselet} —— 前缀一元运算符的 Pratt 解析器。
|
* {@code UnaryOperatorParselet} —— 前缀一元运算符的 Pratt 解析器。
|
||||||
*
|
*
|
||||||
* <p>当前 parselet 负责解析两种前缀运算:
|
* <p>当前 parselet 负责解析两种前缀运算:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><b>取负</b>:{@code -x}</li>
|
* <li><b>取负</b>: {@code -x}</li>
|
||||||
* <li><b>逻辑非</b>:{@code !x}</li>
|
* <li><b>逻辑非</b>: {@code !x}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* 解析过程:
|
* 解析过程:
|
||||||
@ -25,7 +25,7 @@ import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
|
|||||||
* <li>最终生成 {@link UnaryExpressionNode} AST 节点,记录运算符与操作数。</li>
|
* <li>最终生成 {@link UnaryExpressionNode} AST 节点,记录运算符与操作数。</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* <p>此类仅负责<strong>语法结构</strong>的构建:
|
* <p>此类仅负责<strong>语法结构</strong>的构建:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>类型正确性在 {@code UnaryExpressionAnalyzer} 中校验;</li>
|
* <li>类型正确性在 {@code UnaryExpressionAnalyzer} 中校验;</li>
|
||||||
* <li>IR 生成在 {@code ExpressionBuilder.buildUnary} 中完成。</li>
|
* <li>IR 生成在 {@code ExpressionBuilder.buildUnary} 中完成。</li>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
|
|||||||
* 构建一个有效的 {@link ExpressionNode} 抽象语法树结构。
|
* 构建一个有效的 {@link ExpressionNode} 抽象语法树结构。
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* 不同的实现可以采用不同的解析技术:
|
* 不同的实现可以采用不同的解析技术:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>递归下降(Recursive Descent)</li>
|
* <li>递归下降(Recursive Descent)</li>
|
||||||
* <li>Pratt Parser(前缀/中缀优先级驱动)</li>
|
* <li>Pratt Parser(前缀/中缀优先级驱动)</li>
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import org.jcnc.snow.compiler.parser.expression.Precedence;
|
|||||||
* 是 Pratt 解析器架构中处理中缀操作的关键组件。
|
* 是 Pratt 解析器架构中处理中缀操作的关键组件。
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* 每个中缀解析器负责:
|
* 每个中缀解析器负责:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>根据左侧已解析的表达式,结合当前运算符继续解析右侧部分</li>
|
* <li>根据左侧已解析的表达式,结合当前运算符继续解析右侧部分</li>
|
||||||
* <li>提供运算符优先级,用于判断是否继续嵌套解析</li>
|
* <li>提供运算符优先级,用于判断是否继续嵌套解析</li>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import org.jcnc.snow.compiler.lexer.token.Token;
|
|||||||
* {@code PrefixParselet} 是用于解析前缀表达式的通用接口。
|
* {@code PrefixParselet} 是用于解析前缀表达式的通用接口。
|
||||||
* <p>
|
* <p>
|
||||||
* 前缀表达式是以某个词法单元(Token)作为起始的表达式结构,
|
* 前缀表达式是以某个词法单元(Token)作为起始的表达式结构,
|
||||||
* 常见类型包括:
|
* 常见类型包括:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>数字字面量(如 {@code 42})</li>
|
* <li>数字字面量(如 {@code 42})</li>
|
||||||
* <li>标识符(如 {@code foo})</li>
|
* <li>标识符(如 {@code foo})</li>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import java.util.HashMap;
|
|||||||
*/
|
*/
|
||||||
public class StatementParserFactory {
|
public class StatementParserFactory {
|
||||||
|
|
||||||
/** 注册表:语句关键字 -> 对应语句解析器 */
|
/** 注册表: 语句关键字 -> 对应语句解析器 */
|
||||||
private static final Map<String, StatementParser> registry = new HashMap<>();
|
private static final Map<String, StatementParser> registry = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -28,7 +28,7 @@ public class StatementParserFactory {
|
|||||||
registry.put("loop", new LoopStatementParser());
|
registry.put("loop", new LoopStatementParser());
|
||||||
registry.put("return", new ReturnStatementParser());
|
registry.put("return", new ReturnStatementParser());
|
||||||
|
|
||||||
// 默认处理器:表达式语句
|
// 默认处理器: 表达式语句
|
||||||
registry.put("", new ExpressionStatementParser());
|
registry.put("", new ExpressionStatementParser());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ public class TopLevelParserFactory {
|
|||||||
/** 关键字 → 解析器注册表 */
|
/** 关键字 → 解析器注册表 */
|
||||||
private static final Map<String, TopLevelParser> registry = new HashMap<>();
|
private static final Map<String, TopLevelParser> registry = new HashMap<>();
|
||||||
|
|
||||||
/** 缺省解析器:脚本模式(单条语句可执行) */
|
/** 缺省解析器: 脚本模式(单条语句可执行) */
|
||||||
private static final TopLevelParser DEFAULT = new ScriptTopLevelParser();
|
private static final TopLevelParser DEFAULT = new ScriptTopLevelParser();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import java.util.*;
|
|||||||
* 实现 {@link TopLevelParser} 接口,用于将源代码中的函数块解析为抽象语法树(AST)中的 {@link FunctionNode}。
|
* 实现 {@link TopLevelParser} 接口,用于将源代码中的函数块解析为抽象语法树(AST)中的 {@link FunctionNode}。
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 本类使用 {@link FlexibleSectionParser} 机制,按照语义区块结构对函数进行模块化解析,支持以下部分:
|
* 本类使用 {@link FlexibleSectionParser} 机制,按照语义区块结构对函数进行模块化解析,支持以下部分:
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
@ -110,7 +110,7 @@ public class FunctionParser implements TopLevelParser {
|
|||||||
* 构造函数定义中各区块的解析规则(parameter、return_type、body)。
|
* 构造函数定义中各区块的解析规则(parameter、return_type、body)。
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 每个 {@link SectionDefinition} 包含两个部分:区块起始判断器(基于关键字)与具体的解析逻辑。
|
* 每个 {@link SectionDefinition} 包含两个部分: 区块起始判断器(基于关键字)与具体的解析逻辑。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param params 参数节点收集容器,解析结果将存入此列表。
|
* @param params 参数节点收集容器,解析结果将存入此列表。
|
||||||
@ -180,7 +180,7 @@ public class FunctionParser implements TopLevelParser {
|
|||||||
* 解析函数参数列表。
|
* 解析函数参数列表。
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 支持声明后附加注释,格式示例:
|
* 支持声明后附加注释,格式示例:
|
||||||
* <pre>
|
* <pre>
|
||||||
* parameter:
|
* parameter:
|
||||||
* declare x: int // 说明文字
|
* declare x: int // 说明文字
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* {@code ImportParser} 类用于解析源码中的 import 导入语句。
|
* {@code ImportParser} 类用于解析源码中的 import 导入语句。
|
||||||
* <p>
|
* <p>
|
||||||
* 支持以下格式的语法:
|
* 支持以下格式的语法:
|
||||||
* <pre>
|
* <pre>
|
||||||
* import: module1, module2, module3
|
* import: module1, module2, module3
|
||||||
* </pre>
|
* </pre>
|
||||||
@ -23,7 +23,7 @@ public class ImportParser {
|
|||||||
/**
|
/**
|
||||||
* 解析 import 语句,并返回表示被导入模块的语法树节点列表。
|
* 解析 import 语句,并返回表示被导入模块的语法树节点列表。
|
||||||
* <p>
|
* <p>
|
||||||
* 该方法会依次执行以下操作:
|
* 该方法会依次执行以下操作:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>确认当前语句以关键字 {@code import} 开头。</li>
|
* <li>确认当前语句以关键字 {@code import} 开头。</li>
|
||||||
* <li>确认后跟一个冒号 {@code :}。</li>
|
* <li>确认后跟一个冒号 {@code :}。</li>
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import java.util.List;
|
|||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 典型模块语法结构:
|
* 典型模块语法结构:
|
||||||
* <pre>
|
* <pre>
|
||||||
* module: mymod
|
* module: mymod
|
||||||
* import ...
|
* import ...
|
||||||
@ -37,7 +37,7 @@ public class ModuleParser implements TopLevelParser {
|
|||||||
/**
|
/**
|
||||||
* 解析一个模块定义块,返回完整的 {@link ModuleNode} 语法树节点。
|
* 解析一个模块定义块,返回完整的 {@link ModuleNode} 语法树节点。
|
||||||
* <p>
|
* <p>
|
||||||
* 解析过程包括:
|
* 解析过程包括:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>匹配模块声明起始 {@code module: IDENTIFIER}。</li>
|
* <li>匹配模块声明起始 {@code module: IDENTIFIER}。</li>
|
||||||
* <li>收集模块体内所有 import 和 function 语句,允许穿插空行。</li>
|
* <li>收集模块体内所有 import 和 function 语句,允许穿插空行。</li>
|
||||||
|
|||||||
@ -10,12 +10,12 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
|||||||
/**
|
/**
|
||||||
* {@code DeclarationStatementParser} 类负责解析变量声明语句,是语句级解析器的一部分。
|
* {@code DeclarationStatementParser} 类负责解析变量声明语句,是语句级解析器的一部分。
|
||||||
* <p>
|
* <p>
|
||||||
* 本解析器支持以下两种形式的声明语法:
|
* 本解析器支持以下两种形式的声明语法:
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* declare myVar:Integer
|
* declare myVar:Integer
|
||||||
* declare myVar:Integer = 42 + 3
|
* declare myVar:Integer = 42 + 3
|
||||||
* }</pre>
|
* }</pre>
|
||||||
* 其中:
|
* 其中:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code myVar} 为变量名(必须为标识符类型);</li>
|
* <li>{@code myVar} 为变量名(必须为标识符类型);</li>
|
||||||
* <li>{@code Integer} 为类型标注(必须为类型标记);</li>
|
* <li>{@code Integer} 为类型标注(必须为类型标记);</li>
|
||||||
@ -29,7 +29,7 @@ public class DeclarationStatementParser implements StatementParser {
|
|||||||
/**
|
/**
|
||||||
* 解析一条 {@code declare} 声明语句,并返回对应的抽象语法树节点 {@link DeclarationNode}。
|
* 解析一条 {@code declare} 声明语句,并返回对应的抽象语法树节点 {@link DeclarationNode}。
|
||||||
* <p>
|
* <p>
|
||||||
* 解析流程如下:
|
* 解析流程如下:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>匹配关键字 {@code declare};</li>
|
* <li>匹配关键字 {@code declare};</li>
|
||||||
* <li>读取变量名称(标识符类型);</li>
|
* <li>读取变量名称(标识符类型);</li>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
|||||||
/**
|
/**
|
||||||
* {@code ExpressionStatementParser} 用于解析通用表达式语句(赋值或普通表达式)。
|
* {@code ExpressionStatementParser} 用于解析通用表达式语句(赋值或普通表达式)。
|
||||||
* <p>
|
* <p>
|
||||||
* 支持以下两种语法结构:
|
* 支持以下两种语法结构:
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* x = 1 + 2 // 赋值语句
|
* x = 1 + 2 // 赋值语句
|
||||||
* doSomething() // 一般表达式语句
|
* doSomething() // 一般表达式语句
|
||||||
@ -51,7 +51,7 @@ public class ExpressionStatementParser implements StatementParser {
|
|||||||
int column = ts.peek().getCol();
|
int column = ts.peek().getCol();
|
||||||
String file = ctx.getSourceName();
|
String file = ctx.getSourceName();
|
||||||
|
|
||||||
// 赋值语句:IDENTIFIER = expr
|
// 赋值语句: IDENTIFIER = expr
|
||||||
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
|
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
|
||||||
String varName = ts.next().getLexeme();
|
String varName = ts.next().getLexeme();
|
||||||
ts.expect("=");
|
ts.expect("=");
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* {@code IfStatementParser} 类负责解析 if 条件语句,是语句级解析器中的条件分支处理器。
|
* {@code IfStatementParser} 类负责解析 if 条件语句,是语句级解析器中的条件分支处理器。
|
||||||
* <p>
|
* <p>
|
||||||
* 本解析器支持以下结构的条件语法:
|
* 本解析器支持以下结构的条件语法:
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* if <condition> then
|
* if <condition> then
|
||||||
* <then-statements>
|
* <then-statements>
|
||||||
@ -23,7 +23,7 @@ import java.util.List;
|
|||||||
* <else-statements>]
|
* <else-statements>]
|
||||||
* end if
|
* end if
|
||||||
* }</pre>
|
* }</pre>
|
||||||
* 其中:
|
* 其中:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code <condition>} 为任意可解析的布尔或数值表达式,使用 {@link PrattExpressionParser} 解析;</li>
|
* <li>{@code <condition>} 为任意可解析的布尔或数值表达式,使用 {@link PrattExpressionParser} 解析;</li>
|
||||||
* <li>{@code <then-statements>} 与 {@code <else-statements>} 可包含多条语句,自动跳过空行;</li>
|
* <li>{@code <then-statements>} 与 {@code <else-statements>} 可包含多条语句,自动跳过空行;</li>
|
||||||
@ -119,7 +119,7 @@ public class IfStatementParser implements StatementParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// 统一结束处理:end if
|
// 统一结束处理: end if
|
||||||
// -------------------------
|
// -------------------------
|
||||||
ts.expect("end");
|
ts.expect("end");
|
||||||
ts.expect("if");
|
ts.expect("if");
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* {@code LoopStatementParser} 类负责解析自定义结构化的 {@code loop} 语句块。
|
* {@code LoopStatementParser} 类负责解析自定义结构化的 {@code loop} 语句块。
|
||||||
* <p>
|
* <p>
|
||||||
* 该语法结构参考了传统的 for-loop,并将其拆解为命名的语义区块:
|
* 该语法结构参考了传统的 for-loop,并将其拆解为命名的语义区块:
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* loop:
|
* loop:
|
||||||
* init:
|
* init:
|
||||||
@ -36,12 +36,12 @@ import java.util.Map;
|
|||||||
* end loop
|
* end loop
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* 各区块说明:
|
* 各区块说明:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code init}:初始化语句,通常为变量声明。</li>
|
* <li>{@code init}: 初始化语句,通常为变量声明。</li>
|
||||||
* <li>{@code cond}:循环判断条件,必须为布尔或数值表达式。</li>
|
* <li>{@code cond}: 循环判断条件,必须为布尔或数值表达式。</li>
|
||||||
* <li>{@code step}:每轮执行后更新逻辑,通常为赋值语句。</li>
|
* <li>{@code step}: 每轮执行后更新逻辑,通常为赋值语句。</li>
|
||||||
* <li>{@code body}:主执行语句块,支持任意多条语句。</li>
|
* <li>{@code body}: 主执行语句块,支持任意多条语句。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* 本类依赖 {@link FlexibleSectionParser} 实现各区块的统一处理,确保结构明确、可扩展。
|
* 本类依赖 {@link FlexibleSectionParser} 实现各区块的统一处理,确保结构明确、可扩展。
|
||||||
*/
|
*/
|
||||||
@ -50,7 +50,7 @@ public class LoopStatementParser implements StatementParser {
|
|||||||
/**
|
/**
|
||||||
* 解析 {@code loop} 语句块,构建出对应的 {@link LoopNode} 抽象语法树节点。
|
* 解析 {@code loop} 语句块,构建出对应的 {@link LoopNode} 抽象语法树节点。
|
||||||
* <p>
|
* <p>
|
||||||
* 本方法会按顺序检查各个命名区块(可乱序书写),并分别绑定其对应语义解析器:
|
* 本方法会按顺序检查各个命名区块(可乱序书写),并分别绑定其对应语义解析器:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>通过 {@link ParserUtils#matchHeader} 匹配区块开头;</li>
|
* <li>通过 {@link ParserUtils#matchHeader} 匹配区块开头;</li>
|
||||||
* <li>通过 {@link FlexibleSectionParser} 派发区块逻辑;</li>
|
* <li>通过 {@link FlexibleSectionParser} 派发区块逻辑;</li>
|
||||||
@ -83,7 +83,7 @@ public class LoopStatementParser implements StatementParser {
|
|||||||
// 定义各命名区块的识别与处理逻辑
|
// 定义各命名区块的识别与处理逻辑
|
||||||
Map<String, FlexibleSectionParser.SectionDefinition> sections = new HashMap<>();
|
Map<String, FlexibleSectionParser.SectionDefinition> sections = new HashMap<>();
|
||||||
|
|
||||||
// init 区块:仅支持一条语句,通常为 declare
|
// init 区块: 仅支持一条语句,通常为 declare
|
||||||
sections.put("init", new FlexibleSectionParser.SectionDefinition(
|
sections.put("init", new FlexibleSectionParser.SectionDefinition(
|
||||||
ts1 -> ts1.peek().getLexeme().equals("init"),
|
ts1 -> ts1.peek().getLexeme().equals("init"),
|
||||||
(ctx1, ts1) -> {
|
(ctx1, ts1) -> {
|
||||||
@ -93,7 +93,7 @@ public class LoopStatementParser implements StatementParser {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
// cond 区块:支持任意可解析为布尔的表达式
|
// cond 区块: 支持任意可解析为布尔的表达式
|
||||||
sections.put("cond", new FlexibleSectionParser.SectionDefinition(
|
sections.put("cond", new FlexibleSectionParser.SectionDefinition(
|
||||||
ts1 -> ts1.peek().getLexeme().equals("cond"),
|
ts1 -> ts1.peek().getLexeme().equals("cond"),
|
||||||
(ctx1, ts1) -> {
|
(ctx1, ts1) -> {
|
||||||
@ -104,7 +104,7 @@ public class LoopStatementParser implements StatementParser {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
// step 区块:目前仅支持单一变量赋值语句
|
// step 区块: 目前仅支持单一变量赋值语句
|
||||||
sections.put("step", new FlexibleSectionParser.SectionDefinition(
|
sections.put("step", new FlexibleSectionParser.SectionDefinition(
|
||||||
ts1 -> ts1.peek().getLexeme().equals("step"),
|
ts1 -> ts1.peek().getLexeme().equals("step"),
|
||||||
(ctx1, ts1) -> {
|
(ctx1, ts1) -> {
|
||||||
@ -122,7 +122,7 @@ public class LoopStatementParser implements StatementParser {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
// body 区块:支持多条语句,直到遇到 end body
|
// body 区块: 支持多条语句,直到遇到 end body
|
||||||
sections.put("body", new FlexibleSectionParser.SectionDefinition(
|
sections.put("body", new FlexibleSectionParser.SectionDefinition(
|
||||||
ts1 -> ts1.peek().getLexeme().equals("body"),
|
ts1 -> ts1.peek().getLexeme().equals("body"),
|
||||||
(ctx1, ts1) -> {
|
(ctx1, ts1) -> {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user