From 22c7ec46f88180bc9a6d193e4656fd906ebb1ca8 Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 28 Jun 2025 10:20:11 +0800
Subject: [PATCH 01/61] =?UTF-8?q?style:=20TokenStream.isAtEnd=20=E8=BF=94?=
=?UTF-8?q?=E5=9B=9E=E5=80=BC=E7=AC=A6=E5=90=88=E6=96=87=E6=A1=A3=E8=AF=B4?=
=?UTF-8?q?=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/jcnc/snow/compiler/parser/context/TokenStream.java | 2 +-
.../org/jcnc/snow/compiler/parser/core/ParserEngine.java | 6 +++---
.../compiler/parser/expression/PrattExpressionParser.java | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
index 5841b7b..2788300 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
@@ -120,7 +120,7 @@ public class TokenStream {
* @return 若当前位置 Token 为 EOF,则返回 true,否则 false
*/
public boolean isAtEnd() {
- return peek().getType() != TokenType.EOF;
+ return peek().getType() == TokenType.EOF;
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
index 7d0854f..14dc783 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
@@ -17,7 +17,7 @@ public record ParserEngine(ParserContext ctx) {
List errs = new ArrayList<>();
TokenStream ts = ctx.getTokens();
- while (ts.isAtEnd()) {
+ while (!ts.isAtEnd()) {
// 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
@@ -46,7 +46,7 @@ public record ParserEngine(ParserContext ctx) {
* 错误同步:跳到下一行或下一个已注册顶层关键字
*/
private void synchronize(TokenStream ts) {
- while (ts.isAtEnd()) {
+ while (!ts.isAtEnd()) {
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
break;
@@ -57,7 +57,7 @@ public record ParserEngine(ParserContext ctx) {
ts.next();
}
// 连续空行全部吃掉
- while (ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
+ while (!ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
index 4f79274..c5529d4 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
@@ -92,7 +92,7 @@ public class PrattExpressionParser implements ExpressionParser {
ExpressionNode left = prefix.parse(ctx, token);
- while (ctx.getTokens().isAtEnd()
+ while (!ctx.getTokens().isAtEnd()
&& prec.ordinal() < nextPrecedence(ctx)) {
String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex);
From 7a2b65c013051130ea43e68a8cc717e13c5dd1d0 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sat, 28 Jun 2025 16:29:52 +0800
Subject: [PATCH 02/61] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20README.md?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitee/ISSUE_TEMPLATE/bug.yml | 2 +-
README.md | 511 +++++++++++-------
doc/README/IMG/IMG_Run-Profile_1.png | Bin 7455 -> 0 bytes
docs/README/IMG/IMG_Run-Profile_1.png | Bin 0 -> 10203 bytes
.../README/IMG/icon}/IMG_Snow_icon_128.png | Bin
.../README/IMG/icon}/IMG_Snow_icon_128.svg | 0
.../README/IMG/icon}/IMG_Snow_icon_16.png | Bin
.../README/IMG/icon}/IMG_Snow_icon_16.svg | 0
.../README/IMG/icon}/IMG_Snow_icon_256.png | Bin
.../README/IMG/icon}/IMG_Snow_icon_256.svg | 0
.../README/IMG/icon}/IMG_Snow_icon_32.png | Bin
.../README/IMG/icon}/IMG_Snow_icon_32.svg | 0
.../README/IMG/icon}/IMG_Snow_icon_48.png | Bin
.../README/IMG/icon}/IMG_Snow_icon_48.svg | 0
.../README/IMG/icon}/IMG_Snow_icon_64.png | Bin
.../README/IMG/icon}/IMG_Snow_icon_64.svg | 0
.../Snow-Lang-Git-Management.md | 0
...w-Lang-GraalVM-AOT-Native-Image-Package.md | 0
.../img/IMG_Maven_Package_1.png | Bin
.../img/IMG_VS_1.png | Bin
.../img/IMG_VS_2.png | Bin
.../Snow-Lang-Journey/Snow-Lang-Journey.md | 0
.../Snow-Lang-Journey/img/IMG_JNotepad_1.png | Bin
.../Snow-Lang-Journey/img/IMG_JNotepad_2.png | Bin
.../Snow-Lang-Roadmap-2025-06-11.md | 0
25 files changed, 302 insertions(+), 211 deletions(-)
delete mode 100644 doc/README/IMG/IMG_Run-Profile_1.png
create mode 100644 docs/README/IMG/IMG_Run-Profile_1.png
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_128.png (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_128.svg (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_16.png (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_16.svg (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_256.png (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_256.svg (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_32.png (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_32.svg (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_48.png (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_48.svg (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_64.png (100%)
rename {doc/README/IMG => docs/README/IMG/icon}/IMG_Snow_icon_64.svg (100%)
rename {doc => docs}/Snow-Lang-Git-Management/Snow-Lang-Git-Management.md (100%)
rename {doc => docs}/Snow-Lang-GraalVM-AOT-Native-Image-Package/Snow-Lang-GraalVM-AOT-Native-Image-Package.md (100%)
rename {doc => docs}/Snow-Lang-GraalVM-AOT-Native-Image-Package/img/IMG_Maven_Package_1.png (100%)
rename {doc => docs}/Snow-Lang-GraalVM-AOT-Native-Image-Package/img/IMG_VS_1.png (100%)
rename {doc => docs}/Snow-Lang-GraalVM-AOT-Native-Image-Package/img/IMG_VS_2.png (100%)
rename {doc => docs}/Snow-Lang-Journey/Snow-Lang-Journey.md (100%)
rename {doc => docs}/Snow-Lang-Journey/img/IMG_JNotepad_1.png (100%)
rename {doc => docs}/Snow-Lang-Journey/img/IMG_JNotepad_2.png (100%)
rename doc/Snow-Lang-Roadmap/Snow-Lang-Roadmap.md => docs/Snow-Lang-Roadmap/Snow-Lang-Roadmap-2025-06-11.md (100%)
diff --git a/.gitee/ISSUE_TEMPLATE/bug.yml b/.gitee/ISSUE_TEMPLATE/bug.yml
index 6fc2f7a..f65f6a9 100644
--- a/.gitee/ISSUE_TEMPLATE/bug.yml
+++ b/.gitee/ISSUE_TEMPLATE/bug.yml
@@ -69,7 +69,7 @@ body:
attributes:
label: 软件版本/分支
options:
- - v0.3.0
+ - v0.4.0
- main
- dev
- 其他
diff --git a/README.md b/README.md
index 9961193..aa2aab9 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
Snow编程语言
@@ -11,8 +11,8 @@
-
-
+
+
@@ -30,39 +30,53 @@
## 项目简介
-Snow 语言是一个正在发展的编程语言,采用类模块(module)语法风格,支持函数定义和类型注解。它设计目标是让大型语言模型(LLM)更容易生成和理解编程代码。该项目实现了 Snow 语言的完整编译流程,包括词法分析,语法分析,语义分析,中间表示(IR)生成以及最终的虚拟机(VM)指令生成和执行器。通过 Snow 编译器,可以将 `.snow` 源文件编译为中间表示和自定义的虚拟机指令,并在内置的虚拟机上直接运行。
+Snow 是一门受 LLM 时代启发的、面向 AI 友好的编程语言。它设计目标是让 LLM 更容易生成和理解编程代码。
+
+该项目实现了 Snow
+语言的完整编译流程,包括词法分析,语法分析,语义分析,中间表示(IR)生成以及最终的虚拟机(VM)指令生成和执行器,提供从源码到字节码再到自研编程语言虚拟机 (
+SnowVM) 的完整编译-执行链路。
+
+通过 Snow 编译器,可以将 `.snow` 源文件编译为 `.water`虚拟机指令,并在 SnowVM 上直接运行。
+
+从源码编译、构建管理、依赖管理、项目标准化、可视化调试面板到原生镜像发布,全部由 Snow 官方工具完成,降低学习与集成成本。
## 背景理念
-Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的语法和严格的类型系统,以帮助人工智能模型更好地理解程序结构。语言使用显式的 `module` 声明来组织代码,用 `function`,`parameter`,`return_type`,`body` 等关键字分隔不同代码块,语法结构固定且易读。此外,Snow 实现了语义分析来检查变量作用域和类型一致性,在编译阶段捕获错误并确保生成的中间代码正确无误。这种自上而下的编译流程,使得代码设计和生成更加模块化,可解释,也有利于调试和优化。
+Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的语法和严格的类型系统,以帮助 LLM 更好地理解程序。
-相关背景: [心路历程](doc/Snow-Lang-Journey/Snow-Lang-Journey.md)
+语言使用显式的 `module` 声明来组织代码,用 `function`,`parameter`,`return_type`,`body` 等关键字分隔不同代码块,语法结构固定且易读。此外,Snow
+实现了语义分析来检查变量作用域和类型一致性,在编译阶段捕获错误并确保生成的中间代码正确无误。这种自上而下的编译流程,使得代码设计和生成更加模块化,可解释,也有利于调试和优化。
-## 下载Snow发行版
-
-访问: https://gitee.com/jcnc-org/snow/releases
-
-## 相关文档
-
-[Git 管理规范](doc/Snow-Lang-Git-Management/Snow-Lang-Git-Management.md)
-
-[Snow-Lang GraalVM AOT 打包指南](doc/Snow-Lang-GraalVM-AOT-Native-Image-Package/Snow-Lang-GraalVM-AOT-Native-Image-Package.md)
+相关背景: [心路历程](docs/Snow-Lang-Journey/Snow-Lang-Journey.md)
## 功能特性
-* **模块化语法**:支持顶层的 `module:` 声明和 `import:` 导入语句,用于组织代码和依赖管理。
-* **函数与类型**:支持函数定义,函数参数和返回值。在函数体中可以声明变量并进行类型检查。
-* **丰富语句**:支持变量声明(`declare name:Type`,可选初始化),条件语句(`if ... end if`),循环语句(`loop ... end loop`),返回语句等。
-* **表达式解析**:实现了 Pratt 算法的表达式解析器,支持算术,比较和逻辑运算,以及函数调用,成员访问等操作。
-* **完整编译器前端**:包含词法分析器(Lexer),语法分析器(Parser)和语义分析器,对源代码生成抽象语法树(AST)并检查类型/作用域。
-* **中间表示(IR)**:将高层 AST 转换为自定义的三地址式 IR(Intermediate Representation),结构清晰便于后端优化。
-* **后端指令生成**:IR 模块通过线性扫描寄存器分配器映射寄存器,使用指令生成器将 IR 翻译为定制的 VM 指令集。
-* **虚拟机执行**:自带虚拟机引擎(VirtualMachineEngine),能够加载并执行生成的指令序列,支持调试模式输出虚拟机状态。
-* **调试与输出**:编译过程会输出源码,AST(JSON 格式),IR 和最终生成的 VM 代码,方便用户查看编译中间结果和调试。
+| 类别 | 关键特性 |
+|----------|---------------------------------------------------------------------------------------------------|
+| 语言层 | `module` / `import` / `function` / `loop` / `if–else` / Pratt 表达式解析 静态类型检查 & 作用域分析 |
+| 编译器前端 | **Lexer / Parser / Semantic Analyzer** 全栈自研,生成 JSON-AST |
+| IR & 后端 | 三地址式 IR ➜ 线性扫描寄存器分配 ➜ SnowVM 指令 |
+| 虚拟机 | 栈 + 寄存器混合架构、GUI 局部变量监视 |
+| snow pkg | - `.cloud` DSL 描述项目、依赖与构建 - 预设 `clean / compile / run / package / publish` 任务 - 离线缓存与远程仓库解析 |
+| CLI | `snow init`, `snow compile`, `snow run`, `snow clean`, `snow build`, `snow generate` |
+
+## Snow-Lang 官网
+
+[https://snow-lang.com](https://snow-lang.com)
+
+## 下载 Snow 发行版
+
+[https://gitee.com/jcnc-org/snow/releases](https://gitee.com/jcnc-org/snow/releases)
+
+## 相关文档
+
+[Git 管理规范](docs/Snow-Lang-Git-Management/Snow-Lang-Git-Management.md)
+
+[Snow-Lang GraalVM AOT 打包指南](docs/Snow-Lang-GraalVM-AOT-Native-Image-Package/Snow-Lang-GraalVM-AOT-Native-Image-Package.md)
## 开发计划
-[Snow 语言现状和下一阶段开发路线图-2025-06-11](doc/Snow-Lang-Roadmap/Snow-Lang-Roadmap.md)
+~~[Snow 语言现状和下一阶段开发路线图-2025-06-11-已废弃](docs/Snow-Lang-Roadmap/Snow-Lang-Roadmap-2025-06-11.md)~~
## 开发环境安装
@@ -78,199 +92,257 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
3. **运行项目**
- 使用IDEA配置好的运行配置SnowCompiler
+ 使用IDEA配置好的运行配置 `Demo1`
- 
+ 
4. **运行成功**
-``` snow
-## 源代码 (main.snow)
-module: Main
- import:Math
- function: main
- parameter:
- return_type: int
- body:
- Math.factorial(6L,1L)
- return 0
- end body
- end function
-end module
-
-## 源代码 (test.snow)
-module: Math
- function: factorial
- parameter:
- declare n1: long
- declare n2: long
- return_type: long
- body:
- return n1+n2
- end body
- end function
-end module
-
-## 编译器输出
-### AST
-[
- {
- "type": "Module",
- "name": "Main",
- "imports": [
+ ``` snow
+ ## 编译器输出
+ ### Snow 源代码
+ #### Main.snow
+ module: Main
+ import:Math
+ function: main
+ return_type: void
+ body:
+ Math.add(6,1)
+ end body
+ end function
+ end module
+
+ #### Math.snow
+ module: Math
+ function: add
+ parameter:
+ declare n1: int
+ declare n2: int
+ return_type: int
+ body:
+ return n1 + n2
+ end body
+ end function
+ end module
+ ### AST
+ [
{
- "module": "Math",
- "type": "Import"
- }
- ],
- "functions": [
+ "type": "Module",
+ "name": "Main",
+ "imports": [
+ {
+ "module": "Math",
+ "type": "Import"
+ }
+ ],
+ "functions": [
+ {
+ "type": "Function",
+ "name": "main",
+ "parameters": [
+
+ ],
+ "returnType": "void",
+ "body": [
+ {
+ "type": "ExpressionStatement",
+ "expression": {
+ "type": "CallExpression",
+ "callee": {
+ "type": "MemberExpression",
+ "object": {
+ "type": "Identifier",
+ "name": "Math"
+ },
+ "member": "add"
+ },
+ "arguments": [
+ {
+ "type": "NumberLiteral",
+ "value": "6"
+ },
+ {
+ "type": "NumberLiteral",
+ "value": "1"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
{
- "type": "Function",
- "name": "main",
- "parameters": [
+ "type": "Module",
+ "name": "Math",
+ "imports": [
],
- "returnType": "int",
- "body": [
+ "functions": [
{
- "type": "ExpressionStatement",
- "expression": {
- "type": "CallExpression",
- "callee": {
- "type": "MemberExpression",
- "object": {
- "type": "Identifier",
- "name": "Math"
- },
- "member": "factorial"
+ "type": "Function",
+ "name": "add",
+ "parameters": [
+ {
+ "name": "n1",
+ "type": "int"
},
- "arguments": [
- {
- "type": "NumberLiteral",
- "value": "6L"
- },
- {
- "type": "NumberLiteral",
- "value": "1L"
- }
- ]
- }
- },
- {
- "type": "Return",
- "value": {
- "type": "NumberLiteral",
- "value": "0"
- }
- }
- ]
- }
- ]
- },
- {
- "type": "Module",
- "name": "Math",
- "imports": [
-
- ],
- "functions": [
- {
- "type": "Function",
- "name": "factorial",
- "parameters": [
- {
- "name": "n1",
- "type": "long"
- },
- {
- "name": "n2",
- "type": "long"
- }
- ],
- "returnType": "long",
- "body": [
- {
- "type": "Return",
- "value": {
- "type": "BinaryExpression",
- "left": {
- "type": "Identifier",
- "name": "n1"
- },
- "operator": "+",
- "right": {
- "type": "Identifier",
- "name": "n2"
+ {
+ "name": "n2",
+ "type": "int"
}
- }
+ ],
+ "returnType": "int",
+ "body": [
+ {
+ "type": "Return",
+ "value": {
+ "type": "BinaryExpression",
+ "left": {
+ "type": "Identifier",
+ "name": "n1"
+ },
+ "operator": "+",
+ "right": {
+ "type": "Identifier",
+ "name": "n2"
+ }
+ }
+ }
+ ]
}
]
}
]
- }
-]
-### IR
-func main() {
- %0 = CONST 6
- %1 = CONST 1
- %2 = CALL Math.factorial, %0, %1
- %3 = CONST 0
- RET %3
-}
-func factorial(%0, %1) {
- %2 = ADD_L64 %0, %1
- RET %2
-}
-
-### VM code
-112 6
-152 0
-112 1
-152 1
-162 0
-162 1
-201 12 2
-152 2
-111 0
-151 3
-161 3
-255
-162 0
-162 1
-11
-152 2
-162 2
-202
-Calling function at address: 12
-Return 7
-Process has ended
-
-
-### VM Local Variable Table:
-0: 6
-1: 1
-2: 7
-3: 0
-
-```
-
+ ### IR
+ func main() {
+ %0 = CONST 6
+ %1 = CONST 1
+ %2 = CALL Math.add, %0, %1
+ }
+ func add(%0, %1) {
+ %2 = ADD_I32 %0, %1
+ RET %2
+ }
+
+ ### VM code
+ 0000: I_PUSH 6
+ 0001: I_STORE 0
+ 0002: I_PUSH 1
+ 0003: I_STORE 1
+ 0004: I_LOAD 0
+ 0005: I_LOAD 1
+ 0006: CALL 8 2
+ 0007: I_STORE 2
+ 0008: I_LOAD 0
+ 0009: I_LOAD 1
+ 0010: I_ADD
+ 0011: I_STORE 2
+ 0012: I_LOAD 2
+ 0013: RET
+ Written to D:\Devs\IdeaProjects\Snow\target\Demo1.water
+
+ === Launching VM ===
+ Calling function at address: 8
+ Return 7
+ Return 2147483647
+ Operand Stack state:[7]
+
+ --- Call Stack State ---
+
+ Local variable table is empty
+ ```
## 编译Snow源代码
-1. **运行编译器**:
- 运行 Snow 来编译 `.snow` 源文件 (Windows环境) 并且在Snow虚拟机运行,默认以 DEBUG 模式显示执行过程和状态。例如:
+### 1. 独立编译 (Standalone Compilation)
+
+独立编译不依赖 `.cloud` 文件,而是直接使用 `Snow` 编译器进行 `.snow` 文件的编译和执行。
+
+#### 独立编译步骤:
+
+1. **运行编译器:**
+ 你可以通过以下命令来编译单个或多个 `.snow` 文件,或者递归编译一个目录中的所有 `.snow` 源文件为`.water`虚拟机指令。
+
+ * **单个文件编译:**
+
+ ```bash
+ Snow [SnowCode].snow
+ ```
+
+ * **多个文件编译:**
+
+ ```bash
+ Snow [SnowCode1].snow [SnowCode2].snow [SnowCode3].snow
+ ```
+
+ * **目录递归编译:**
+
+ ```bash
+ Snow -d path/to/source_dir
+ ```
+
+2. **查看编译输出:**
+ 编译过程会输出源代码、抽象语法树(AST)、中间表示(IR)以及虚拟机指令等内容。你可以看到如下几个分段输出:
+
+ * **AST**(抽象语法树)部分以 JSON 格式输出。
+ * **IR**(中间表示)部分会列出逐行的中间代码。
+ * **VM code**(虚拟机指令)会展示虚拟机的字节码指令。
+
+3. **默认执行模式:**
+ 编译器会在 **DEBUG 模式** 下运行,显示详细的执行过程和状态,并且在虚拟机中执行编译后的代码,最后会打印出所有局部变量的值。
+
+---
+
+### 2. **集成编译 (Integrated Compilation)**
+
+集成编译需要使用 `.cloud` 文件来指定项目的配置和结构,适用于项目标准化、依赖管理、构建管理和项目分发等场景。
+
+#### 集成编译命令:
+
+1. **基本用法:**
```bash
- # 单个文件编译
- Snow.exe [SnowCode].snow
- # 多个文件编译
- Snow.exe [SnowCode1].snow [SnowCode2].snow [SnowCode3].snow
- # 目录递归编译(-d 参数)
- Snow.exe -d path/to/source_dir
+ snow [OPTIONS]
+ ```
+2. **命令选项:**
- 编译器会输出源代码,AST,IR 和 VM 指令等内容,并自动执行虚拟机引擎,最后打印所有局部变量的值
-2. **查看 AST/IR/VM 输出**:在编译器输出中,可看到 `### AST`,`### IR` 和 `### VM code` 等分段内容。AST 部分为 JSON 格式,IR 和 VM 部分为逐行指令文本。
+ * `-h, --help`:显示帮助信息并退出。
+ * `-v, --version`:打印 Snow 编程语言的版本并退出。
+
+3. **可用命令:**
+
+ * `compile`:将 `.snow` 源文件编译成虚拟机字节码文件(`.water`)。此命令会使用 `.cloud` 文件来指导编译过程。
+ * `clean`:清理构建输出和本地缓存,移除中间产物,释放磁盘空间。
+ * `version`:打印 Snow 的版本。
+ * `run`:运行已编译的虚拟机字节码文件(`.water`)。
+ * `init`:初始化一个新项目,生成 `project.cloud` 文件。
+ * `generate`:根据 `project.cloud` 生成项目目录结构。
+ * `build`:构建当前项目,按顺序解析依赖、编译和打包。
+
+4. **例如,执行集成编译命令:**
+
+ ```bash
+ snow compile [SnowCode].snow
+ ```
+
+ * 此命令会使用 `.cloud` 文件中的配置信息来指导编译过程,并生成 `.water`。
+
+5. **使用帮助:**
+ 如果你需要了解某个命令的详细选项,可以使用:
+
+ ```bash
+ snow --help
+ ```
+
+ 例如,查看 `compile` 命令的具体选项:
+
+ ```bash
+ snow compile --help
+ ```
+
+---
## 示例代码片段
@@ -279,7 +351,6 @@ Process has ended
```snow
module: Math
function: main
- parameter:
return_type: int
body:
Math.factorial(6)
@@ -308,32 +379,52 @@ module: Math
end body
end function
end module
-
```
上述代码定义了一个名为 `Math` 的模块,其中包含两个函数:
* `main`:不接收任何参数,返回类型为 `int`。在函数体内调用了 `Math.factorial(6)`,然后返回 `0`。
-* `factorial`:接收一个 `int` 类型的参数 `n`,返回类型为 `int`。函数体内先声明并初始化局部变量 `num1` 为 `1`,然后通过一个 `loop` 循环(从 `counter = 1` 到 `counter <= n`)依次将 `num1` 乘以 `counter`,循环结束后返回 `num1`,即 `n` 的阶乘值。
+* `factorial`:接收一个 `int` 类型的参数 `n`,返回类型为 `int`。函数体内先声明并初始化局部变量 `num1` 为 `1`,然后通过一个
+ `loop` 循环(从 `counter = 1` 到 `counter <= n`)依次将 `num1` 乘以 `counter`,循环结束后返回 `num1`,即 `n` 的阶乘值。
+> 更多示例代码见 [playground 目录](https://gitee.com/jcnc-org/snow/tree/main/playground)
+
## 项目结构说明
* `compiler/`:Snow 编译器源代码目录
- * `cli/`:命令行接口,包括 `SnowCompiler` 主程序
- * `lexer/`:词法分析模块,负责将源码切分为 Token
- * `parser/`:语法分析模块,将 Token 流解析为 AST;包括模块解析,函数解析,语句解析等子模块
- * `semantic/`:语义分析模块,负责符号表管理,类型检查等
- * `ir/`:中间表示(IR)模块,生成和管理三地址码形式的中间代码
- * `backend/`:编译器后端模块,将 IR 翻译为虚拟机指令,包含寄存器分配和指令生成器
+ * `lexer/`:词法分析模块,负责将源码切分为 Token
+ * `parser/`:语法分析模块,将 Token 流解析为 AST(含模块/函数/语句解析)
+ * `semantic/`:语义分析模块,负责符号表管理、类型检查等
+ * `ir/`:中间表示(IR)模块,生成并管理三地址码形式的中间代码
+ * `backend/`:编译器后端模块,将 IR 翻译为虚拟机指令,包含寄存器分配和指令生成器
+
* `vm/`:虚拟机相关源代码目录
- * `commands/`:定义 VM 指令集的具体实现
- * `engine/`:核心执行引擎,提供指令执行和寄存器栈管理
- * `execution/`:执行流程控制(按指令顺序执行,分支跳转等)
- * `io/`:输入输出辅助类(加载指令,文件解析等)
- * 其他如 `factories/`,`utils/` 等目录包含指令创建和调试工具类
+ * `commands/`:定义 SnowVM 指令集的具体实现
+ * `engine/`:核心执行引擎,提供指令执行和寄存器/栈管理
+ * `execution/`:执行流程控制(按指令顺序执行、分支跳转等)
+ * `io/`:输入输出辅助类(加载指令、文件解析等)
+ * `gui/`:Swing 可视化调试面板,实时展示局部变量表
+ * `factories/`、`utils/`:指令创建、日志调试等公共工具
+
+* `pkg/`:内置构建与包管理器 **snow pkg**
+
+ * `dsl/`:`.cloud` 描述文件解析器
+ * `tasks/`:预设任务实现(`clean · compile · run · package · publish` 等)
+ * `resolver/`:本地/远程仓库解析与缓存
+ * `lifecycle/`:任务生命周期钩子(pre/post 脚本等)
+ * `model/`:项目、依赖、版本等模型
+ * `utils/`:文件、日志、校验和等通用工具
+ * `doc/`:开发者文档与示例 `.cloud` 配置
+
+* `cli/`:独立的命令行前端
+
+ * `commands/`:`compile` / `run` / `pkg` 等子命令实现
+ * `api/`:公共选项解析、终端交互抽象
+ * `utils/`:终端颜色、进度条、异常格式化等
+ * `SnowCLI.java`:CLI 主入口
## 版权声明
diff --git a/doc/README/IMG/IMG_Run-Profile_1.png b/doc/README/IMG/IMG_Run-Profile_1.png
deleted file mode 100644
index b982362966bf71f299104172cb7c3c3ff5b6f3d6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 7455
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
Date: Sat, 28 Jun 2025 16:30:14 +0800
Subject: [PATCH 03/61] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=20Demo1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
playground/Demo1/Main.snow | 6 ++----
playground/Demo1/Math.snow | 10 +++++-----
2 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/playground/Demo1/Main.snow b/playground/Demo1/Main.snow
index 08eeda8..89efc03 100644
--- a/playground/Demo1/Main.snow
+++ b/playground/Demo1/Main.snow
@@ -1,11 +1,9 @@
module: Main
import:Math
function: main
- parameter:
- return_type: int
+ return_type: void
body:
- Math.factorial(6L,1L)
- return 0
+ Math.add(6,1)
end body
end function
end module
diff --git a/playground/Demo1/Math.snow b/playground/Demo1/Math.snow
index bfe8605..2e5963e 100644
--- a/playground/Demo1/Math.snow
+++ b/playground/Demo1/Math.snow
@@ -1,11 +1,11 @@
module: Math
- function: factorial
+ function: add
parameter:
- declare n1: long
- declare n2: long
- return_type: long
+ declare n1: int
+ declare n2: int
+ return_type: int
body:
- return n1+n2
+ return n1 + n2
end body
end function
end module
\ No newline at end of file
From 4f98e4d6a3336d93f7165bf81d33c86f90aa8ca0 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sat, 28 Jun 2025 16:49:12 +0800
Subject: [PATCH 04/61] =?UTF-8?q?docs:=20=E5=A2=9E=E5=8A=A0=20SnowVM-OpCod?=
=?UTF-8?q?e.md?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +
docs/SnowVM-OpCode/SnowVM-OpCode.md | 226 ++++++++++++++++++++++++++++
2 files changed, 228 insertions(+)
create mode 100644 docs/SnowVM-OpCode/SnowVM-OpCode.md
diff --git a/README.md b/README.md
index aa2aab9..6a2dbe8 100644
--- a/README.md
+++ b/README.md
@@ -72,6 +72,8 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
[Git 管理规范](docs/Snow-Lang-Git-Management/Snow-Lang-Git-Management.md)
+[]
+
[Snow-Lang GraalVM AOT 打包指南](docs/Snow-Lang-GraalVM-AOT-Native-Image-Package/Snow-Lang-GraalVM-AOT-Native-Image-Package.md)
## 开发计划
diff --git a/docs/SnowVM-OpCode/SnowVM-OpCode.md b/docs/SnowVM-OpCode/SnowVM-OpCode.md
new file mode 100644
index 0000000..4bd5d46
--- /dev/null
+++ b/docs/SnowVM-OpCode/SnowVM-OpCode.md
@@ -0,0 +1,226 @@
+# SnowVM-OpCode
+
+## 1. Byte8 区域(0x0000-0x001F)
+
+| 指令名 | 十六进制 | 说明 |
+|----------|--------|----------------|
+| B\_ADD | 0x0000 | byte8 加法 |
+| B\_SUB | 0x0001 | byte8 减法 |
+| B\_MUL | 0x0002 | byte8 乘法 |
+| B\_DIV | 0x0003 | byte8 除法 |
+| B\_MOD | 0x0004 | byte8 取余 |
+| B\_NEG | 0x0005 | byte8 取负 |
+| B\_INC | 0x0006 | byte8 自增 |
+| B\_AND | 0x0007 | byte8 按位与 |
+| B\_OR | 0x0008 | byte8 按位或 |
+| B\_XOR | 0x0009 | byte8 按位异或 |
+| B\_PUSH | 0x000A | byte8 入栈 |
+| B\_LOAD | 0x000B | byte8 本地变量加载 |
+| B\_STORE | 0x000C | byte8 本地变量存储 |
+| B\_CE | 0x000D | byte8 等于条件判断 |
+| B\_CNE | 0x000E | byte8 不等于条件判断 |
+| B\_CG | 0x000F | byte8 大于条件判断 |
+| B\_CGE | 0x0010 | byte8 大于等于条件判断 |
+| B\_CL | 0x0011 | byte8 小于条件判断 |
+| B\_CLE | 0x0012 | byte8 小于等于条件判断 |
+
+---
+
+## 2. Short16 区域(0x0020-0x003F)
+
+| 指令名 | 十六进制 | 说明 |
+|----------|--------|------------------|
+| S\_ADD | 0x0020 | short16 加法 |
+| S\_SUB | 0x0021 | short16 减法 |
+| S\_MUL | 0x0022 | short16 乘法 |
+| S\_DIV | 0x0023 | short16 除法 |
+| S\_MOD | 0x0024 | short16 取余 |
+| S\_NEG | 0x0025 | short16 取负 |
+| S\_INC | 0x0026 | short16 自增 |
+| S\_AND | 0x0027 | short16 按位与 |
+| S\_OR | 0x0028 | short16 按位或 |
+| S\_XOR | 0x0029 | short16 按位异或 |
+| S\_PUSH | 0x002A | short16 入栈 |
+| S\_LOAD | 0x002B | short16 本地变量加载 |
+| S\_STORE | 0x002C | short16 本地变量存储 |
+| S\_CE | 0x002D | short16 等于条件判断 |
+| S\_CNE | 0x002E | short16 不等于条件判断 |
+| S\_CG | 0x002F | short16 大于条件判断 |
+| S\_CGE | 0x0030 | short16 大于等于条件判断 |
+| S\_CL | 0x0031 | short16 小于条件判断 |
+| S\_CLE | 0x0032 | short16 小于等于条件判断 |
+
+---
+
+## 3. Int32 区域(0x0040-0x005F)
+
+| 指令名 | 十六进制 | 说明 |
+|----------|--------|----------------|
+| I\_ADD | 0x0040 | int32 加法 |
+| I\_SUB | 0x0041 | int32 减法 |
+| I\_MUL | 0x0042 | int32 乘法 |
+| I\_DIV | 0x0043 | int32 除法 |
+| I\_MOD | 0x0044 | int32 取余 |
+| I\_NEG | 0x0045 | int32 取负 |
+| I\_INC | 0x0046 | int32 自增 |
+| I\_AND | 0x0047 | int32 按位与 |
+| I\_OR | 0x0048 | int32 按位或 |
+| I\_XOR | 0x0049 | int32 按位异或 |
+| I\_PUSH | 0x004A | int32 入栈 |
+| I\_LOAD | 0x004B | int32 本地变量加载 |
+| I\_STORE | 0x004C | int32 本地变量存储 |
+| I\_CE | 0x004D | int32 等于条件判断 |
+| I\_CNE | 0x004E | int32 不等于条件判断 |
+| I\_CG | 0x004F | int32 大于条件判断 |
+| I\_CGE | 0x0050 | int32 大于等于条件判断 |
+| I\_CL | 0x0051 | int32 小于条件判断 |
+| I\_CLE | 0x0052 | int32 小于等于条件判断 |
+
+---
+
+## 4. Long64 区域(0x0060-0x007F)
+
+| 指令名 | 十六进制 | 说明 |
+|----------|--------|-----------------|
+| L\_ADD | 0x0060 | long64 加法 |
+| L\_SUB | 0x0061 | long64 减法 |
+| L\_MUL | 0x0062 | long64 乘法 |
+| L\_DIV | 0x0063 | long64 除法 |
+| L\_MOD | 0x0064 | long64 取余 |
+| L\_NEG | 0x0065 | long64 取负 |
+| L\_INC | 0x0066 | long64 自增 |
+| L\_AND | 0x0067 | long64 按位与 |
+| L\_OR | 0x0068 | long64 按位或 |
+| L\_XOR | 0x0069 | long64 按位异或 |
+| L\_PUSH | 0x006A | long64 入栈 |
+| L\_LOAD | 0x006B | long64 本地变量加载 |
+| L\_STORE | 0x006C | long64 本地变量存储 |
+| L\_CE | 0x006D | long64 等于条件判断 |
+| L\_CNE | 0x006E | long64 不等于条件判断 |
+| L\_CG | 0x006F | long64 大于条件判断 |
+| L\_CGE | 0x0070 | long64 大于等于条件判断 |
+| L\_CL | 0x0071 | long64 小于条件判断 |
+| L\_CLE | 0x0072 | long64 小于等于条件判断 |
+
+---
+
+## 5. Float32 区域(0x0080-0x009F)
+
+| 指令名 | 十六进制 | 说明 |
+|----------|--------|------------------|
+| F\_ADD | 0x0080 | float32 加法 |
+| F\_SUB | 0x0081 | float32 减法 |
+| F\_MUL | 0x0082 | float32 乘法 |
+| F\_DIV | 0x0083 | float32 除法 |
+| F\_MOD | 0x0084 | float32 取余 |
+| F\_NEG | 0x0085 | float32 取负 |
+| F\_INC | 0x0086 | float32 自增 |
+| F\_PUSH | 0x0087 | float32 入栈 |
+| F\_LOAD | 0x0088 | float32 本地变量加载 |
+| F\_STORE | 0x0089 | float32 本地变量存储 |
+| F\_CE | 0x008A | float32 等于条件判断 |
+| F\_CNE | 0x008B | float32 不等于条件判断 |
+| F\_CG | 0x008C | float32 大于条件判断 |
+| F\_CGE | 0x008D | float32 大于等于条件判断 |
+| F\_CL | 0x008E | float32 小于条件判断 |
+| F\_CLE | 0x008F | float32 小于等于条件判断 |
+
+---
+
+## 6. Double64 区域(0x00A0-0x00BF)
+
+| 指令名 | 十六进制 | 说明 |
+|----------|--------|-------------------|
+| D\_ADD | 0x00A0 | double64 加法 |
+| D\_SUB | 0x00A1 | double64 减法 |
+| D\_MUL | 0x00A2 | double64 乘法 |
+| D\_DIV | 0x00A3 | double64 除法 |
+| D\_MOD | 0x00A4 | double64 取余 |
+| D\_NEG | 0x00A5 | double64 取负 |
+| D\_INC | 0x00A6 | double64 自增 |
+| D\_PUSH | 0x00A7 | double64 入栈 |
+| D\_LOAD | 0x00A8 | double64 本地变量加载 |
+| D\_STORE | 0x00A9 | double64 本地变量存储 |
+| D\_CE | 0x00AA | double64 等于条件判断 |
+| D\_CNE | 0x00AB | double64 不等于条件判断 |
+| D\_CG | 0x00AC | double64 大于条件判断 |
+| D\_CGE | 0x00AD | double64 大于等于条件判断 |
+| D\_CL | 0x00AE | double64 小于条件判断 |
+| D\_CLE | 0x00AF | double64 小于等于条件判断 |
+
+---
+
+## 7. 类型转换(0x00C0-0x00DF)
+
+| 指令名 | 十六进制 | 说明 |
+|-----|--------|--------------------|
+| B2S | 0x00C0 | byte8 转 short16 |
+| B2I | 0x00C1 | byte8 转 int32 |
+| B2L | 0x00C2 | byte8 转 long64 |
+| B2F | 0x00C3 | byte8 转 float32 |
+| B2D | 0x00C4 | byte8 转 double64 |
+| S2B | 0x00C5 | short16 转 byte8 |
+| S2I | 0x00C6 | short16 转 int32 |
+| S2L | 0x00C7 | short16 转 long64 |
+| S2F | 0x00C8 | short16 转 float32 |
+| S2D | 0x00C9 | short16 转 double64 |
+| I2B | 0x00CA | int32 转 byte8 |
+| I2S | 0x00CB | int32 转 short16 |
+| I2L | 0x00CC | int32 转 long64 |
+| I2F | 0x00CD | int32 转 float32 |
+| I2D | 0x00CE | int32 转 double64 |
+| L2B | 0x00CF | long64 转 byte8 |
+| L2S | 0x00D0 | long64 转 short16 |
+| L2I | 0x00D1 | long64 转 int32 |
+| L2F | 0x00D2 | long64 转 float32 |
+| L2D | 0x00D3 | long64 转 double64 |
+| F2B | 0x00D4 | float32 转 byte8 |
+| F2S | 0x00D5 | float32 转 short16 |
+| F2I | 0x00D6 | float32 转 int32 |
+| F2L | 0x00D7 | float32 转 long64 |
+| F2D | 0x00D8 | float32 转 double64 |
+| D2B | 0x00D9 | double64 转 byte8 |
+| D2S | 0x00DA | double64 转 short16 |
+| D2I | 0x00DB | double64 转 int32 |
+| D2L | 0x00DC | double64 转 long64 |
+| D2F | 0x00DD | double64 转 float32 |
+
+
+
+---
+
+## 8. 栈控制(0x0100-0x01FF)
+
+| 指令名 | 十六进制 | 说明 |
+|------|--------|----------|
+| POP | 0x0100 | 弹出栈顶元素 |
+| DUP | 0x0101 | 复制栈顶元素 |
+| SWAP | 0x0102 | 交换栈顶前两元素 |
+
+---
+
+## 9. 流程控制(0x0200-0x02FF)
+
+| 指令名 | 十六进制/十进制 | 说明 |
+|------|----------|-------|
+| JUMP | 0x0200 | 无条件跳转 |
+| CALL | 0x0201 | 子程序调用 |
+| RET | 0x0202 | 子程序返回 |
+
+---
+
+## 10. 寄存器控制(0x0300-0x03FF)
+
+| 指令名 | 十六进制 | 说明 |
+|-----|--------|---------|
+| MOV | 0x0300 | 局部变量间赋值 |
+
+---
+
+## 11. 系统控制(0x0400-0x04FF)
+
+| 指令名 | 十六进制 | 说明 |
+|-------------|--------|------|
+| HALT | 0x0400 | 程序终止 |
+| SYSCALL | 0x0401 | 系统调用 |
+| DEBUG\_TRAP | 0x0402 | 调试断点 |
\ No newline at end of file
From 5ea546e04ee8a9e1ce883a1461c8718e4c681a5d Mon Sep 17 00:00:00 2001
From: Luke
Date: Sat, 28 Jun 2025 16:58:33 +0800
Subject: [PATCH 05/61] =?UTF-8?q?docs:=20README.md=20=E5=A2=9E=E5=8A=A0=20?=
=?UTF-8?q?SnowVM-OpCode?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 6a2dbe8..301fb98 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
[Git 管理规范](docs/Snow-Lang-Git-Management/Snow-Lang-Git-Management.md)
-[]
+[SnowVM OpCode 指令表](docs/SnowVM-OpCode/SnowVM-OpCode.md)
[Snow-Lang GraalVM AOT 打包指南](docs/Snow-Lang-GraalVM-AOT-Native-Image-Package/Snow-Lang-GraalVM-AOT-Native-Image-Package.md)
From 26a533adaf5209a8ff2743f09883369e27b77405 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sat, 28 Jun 2025 17:19:08 +0800
Subject: [PATCH 06/61] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=20Demo1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
playground/Demo1/Main.snow | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/playground/Demo1/Main.snow b/playground/Demo1/Main.snow
index 89efc03..9058208 100644
--- a/playground/Demo1/Main.snow
+++ b/playground/Demo1/Main.snow
@@ -1,9 +1,10 @@
module: Main
import:Math
function: main
- return_type: void
+ return_type: int
body:
Math.add(6,1)
+ return 0
end body
end function
-end module
+end module
\ No newline at end of file
From a52a94179913e38eaa00c1881c3e61952af04aae Mon Sep 17 00:00:00 2001
From: Luke
Date: Sat, 28 Jun 2025 17:19:22 +0800
Subject: [PATCH 07/61] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20README.md?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 212 +++++++++++++++++++++++++++++-------------------------
1 file changed, 116 insertions(+), 96 deletions(-)
diff --git a/README.md b/README.md
index 301fb98..a6cda8c 100644
--- a/README.md
+++ b/README.md
@@ -99,21 +99,21 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的

4. **运行成功**
-
- ``` snow
+
+ `````snow
## 编译器输出
### Snow 源代码
#### Main.snow
module: Main
import:Math
function: main
- return_type: void
+ return_type: int
body:
Math.add(6,1)
+ return 0
end body
end function
end module
-
#### Math.snow
module: Math
function: add
@@ -122,111 +122,121 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
declare n2: int
return_type: int
body:
- return n1 + n2
+ return n1 + n2
end body
end function
end module
### AST
[
- {
+ {
"type": "Module",
"name": "Main",
"imports": [
- {
+ {
"module": "Math",
"type": "Import"
- }
+ }
],
"functions": [
- {
+ {
"type": "Function",
"name": "main",
"parameters": [
-
- ],
- "returnType": "void",
- "body": [
- {
- "type": "ExpressionStatement",
- "expression": {
- "type": "CallExpression",
- "callee": {
- "type": "MemberExpression",
- "object": {
- "type": "Identifier",
- "name": "Math"
- },
- "member": "add"
- },
- "arguments": [
- {
- "type": "NumberLiteral",
- "value": "6"
- },
- {
- "type": "NumberLiteral",
- "value": "1"
- }
- ]
- }
- }
- ]
- }
- ]
- },
- {
- "type": "Module",
- "name": "Math",
- "imports": [
-
- ],
- "functions": [
- {
- "type": "Function",
- "name": "add",
- "parameters": [
- {
- "name": "n1",
- "type": "int"
- },
- {
- "name": "n2",
- "type": "int"
- }
+
],
"returnType": "int",
"body": [
- {
+ {
+ "type": "ExpressionStatement",
+ "expression": {
+ "type": "CallExpression",
+ "callee": {
+ "type": "MemberExpression",
+ "object": {
+ "type": "Identifier",
+ "name": "Math"
+ },
+ "member": "add"
+ },
+ "arguments": [
+ {
+ "type": "NumberLiteral",
+ "value": "6"
+ },
+ {
+ "type": "NumberLiteral",
+ "value": "1"
+ }
+ ]
+ }
+ },
+ {
"type": "Return",
"value": {
- "type": "BinaryExpression",
- "left": {
+ "type": "NumberLiteral",
+ "value": "0"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "Module",
+ "name": "Math",
+ "imports": [
+
+ ],
+ "functions": [
+ {
+ "type": "Function",
+ "name": "add",
+ "parameters": [
+ {
+ "name": "n1",
+ "type": "int"
+ },
+ {
+ "name": "n2",
+ "type": "int"
+ }
+ ],
+ "returnType": "int",
+ "body": [
+ {
+ "type": "Return",
+ "value": {
+ "type": "BinaryExpression",
+ "left": {
"type": "Identifier",
"name": "n1"
- },
- "operator": "+",
- "right": {
+ },
+ "operator": "+",
+ "right": {
"type": "Identifier",
"name": "n2"
- }
}
- }
+ }
+ }
]
- }
+ }
]
- }
+ }
]
### IR
func main() {
- %0 = CONST 6
- %1 = CONST 1
- %2 = CALL Math.add, %0, %1
+ %0 = CONST 6
+ %1 = CONST 1
+ %2 = CALL Math.add, %0, %1
+ %3 = CONST 0
+ RET %3
}
func add(%0, %1) {
- %2 = ADD_I32 %0, %1
- RET %2
+ %2 = ADD_I32 %0, %1
+ RET %2
}
-
+
+
### VM code
0000: I_PUSH 6
0001: I_STORE 0
@@ -234,27 +244,37 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
0003: I_STORE 1
0004: I_LOAD 0
0005: I_LOAD 1
- 0006: CALL 8 2
+ 0006: CALL 12 2
0007: I_STORE 2
- 0008: I_LOAD 0
- 0009: I_LOAD 1
- 0010: I_ADD
- 0011: I_STORE 2
- 0012: I_LOAD 2
- 0013: RET
+ 0008: I_PUSH 0
+ 0009: I_STORE 3
+ 0010: I_LOAD 3
+ 0011: HALT
+ 0012: I_LOAD 0
+ 0013: I_LOAD 1
+ 0014: I_ADD
+ 0015: I_STORE 2
+ 0016: I_LOAD 2
+ 0017: RET
Written to D:\Devs\IdeaProjects\Snow\target\Demo1.water
-
- === Launching VM ===
- Calling function at address: 8
- Return 7
- Return 2147483647
- Operand Stack state:[7]
-
- --- Call Stack State ---
-
- Local variable table is empty
- ```
+ === Launching VM ===
+ Calling function at address: 12
+ Return 7
+ Process has ended
+
+ Operand Stack state:[0]
+
+ --- Call Stack State ---
+
+
+ ### VM Local Variable Table:
+ 0: 6
+ 1: 1
+ 2: 7
+ 3: 0
+ `````
+
## 编译Snow源代码
### 1. 独立编译 (Standalone Compilation)
@@ -269,13 +289,13 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
* **单个文件编译:**
```bash
- Snow [SnowCode].snow
+ Snow complete [SnowCode].snow
```
* **多个文件编译:**
```bash
- Snow [SnowCode1].snow [SnowCode2].snow [SnowCode3].snow
+ Snow complete [SnowCode1].snow [SnowCode2].snow [SnowCode3].snow -o [Name]
```
* **目录递归编译:**
From dcb09c955bfe6940a83d91416c15a6e623bdc0d6 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sat, 28 Jun 2025 17:21:45 +0800
Subject: [PATCH 08/61] =?UTF-8?q?docs:=20=E4=BC=98=E5=8C=96=E6=A0=87?=
=?UTF-8?q?=E9=A2=98=E7=A9=BA=E6=A0=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index a6cda8c..74438c3 100644
--- a/README.md
+++ b/README.md
@@ -275,7 +275,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
3: 0
`````
-## 编译Snow源代码
+## 编译 Snow 源代码
### 1. 独立编译 (Standalone Compilation)
From ed99460850c3db712587804698d8df6c964dfd42 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sat, 28 Jun 2025 17:32:04 +0800
Subject: [PATCH 09/61] =?UTF-8?q?style:=20=E4=BC=98=E5=8C=96=E7=A4=BA?=
=?UTF-8?q?=E4=BE=8B=E7=9A=84=E6=A0=BC=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java b/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java
index 6aea927..ffd18d1 100644
--- a/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java
+++ b/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java
@@ -34,10 +34,10 @@ public final class SnowExample {
function: factorial
parameter:
- declare n:int
+ declare n: int
return_type: int
body:
- declare num1:int = 1
+ declare num1: int = 1
loop:
initializer:
declare counter:int = 1
From cf333d95d94ceec345d6f9d941a50ed75befa046 Mon Sep 17 00:00:00 2001
From: luke
Date: Sat, 28 Jun 2025 18:09:33 +0800
Subject: [PATCH 10/61] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=20maven=20?=
=?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/pom.xml b/pom.xml
index 87b4f35..285cc54 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,14 +38,9 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.12.1
+ 3.14.0
-
- true
-
+ 24
From 0d8e26d2cceb284cf99fd169238f595bcc1d7c55 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sun, 29 Jun 2025 18:23:58 +0800
Subject: [PATCH 11/61] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=20Demo11=20?=
=?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.run/Demo11.run.xml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 .run/Demo11.run.xml
diff --git a/.run/Demo11.run.xml b/.run/Demo11.run.xml
new file mode 100644
index 0000000..e6748fb
--- /dev/null
+++ b/.run/Demo11.run.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 5e0d31f3adc325e701664e28d10ee3a3d998d138 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sun, 29 Jun 2025 18:24:34 +0800
Subject: [PATCH 12/61] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=20Bug1=20?=
=?UTF-8?q?=E8=87=B3=20BugFarm?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
playground/BugFarm/Bug1/Main.snow | 7 +++++++
playground/BugFarm/Bug1/README.md | 12 ++++++++++++
playground/Demo11/Main.snow | 7 +++++++
3 files changed, 26 insertions(+)
create mode 100644 playground/BugFarm/Bug1/Main.snow
create mode 100644 playground/BugFarm/Bug1/README.md
create mode 100644 playground/Demo11/Main.snow
diff --git a/playground/BugFarm/Bug1/Main.snow b/playground/BugFarm/Bug1/Main.snow
new file mode 100644
index 0000000..d9a3e6e
--- /dev/null
+++ b/playground/BugFarm/Bug1/Main.snow
@@ -0,0 +1,7 @@
+function: main
+ return_type: int
+ body:
+ 3 L
+ return 65537
+ end body
+end function
\ No newline at end of file
diff --git a/playground/BugFarm/Bug1/README.md b/playground/BugFarm/Bug1/README.md
new file mode 100644
index 0000000..b34d14d
--- /dev/null
+++ b/playground/BugFarm/Bug1/README.md
@@ -0,0 +1,12 @@
+## 编译器输出
+### Snow 源代码
+#### Main.snow
+```snow
+function: main
+ return_type: int
+ body:
+ 3 L
+ return 65537
+ end body
+end function
+```
\ No newline at end of file
diff --git a/playground/Demo11/Main.snow b/playground/Demo11/Main.snow
new file mode 100644
index 0000000..d9a3e6e
--- /dev/null
+++ b/playground/Demo11/Main.snow
@@ -0,0 +1,7 @@
+function: main
+ return_type: int
+ body:
+ 3 L
+ return 65537
+ end body
+end function
\ No newline at end of file
From cb4faf0632fb351d4b8dca7fd29fdaa204462cf6 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sun, 29 Jun 2025 18:28:43 +0800
Subject: [PATCH 13/61] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=20Bug1=20?=
=?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.run/Bug1.run.xml | 17 +++++++++++++++++
.run/Demo1.run.xml | 2 +-
2 files changed, 18 insertions(+), 1 deletion(-)
create mode 100644 .run/Bug1.run.xml
diff --git a/.run/Bug1.run.xml b/.run/Bug1.run.xml
new file mode 100644
index 0000000..46f5bc6
--- /dev/null
+++ b/.run/Bug1.run.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo1.run.xml b/.run/Demo1.run.xml
index 867e2f7..55005c8 100644
--- a/.run/Demo1.run.xml
+++ b/.run/Demo1.run.xml
@@ -3,7 +3,7 @@
-
+
From 6a247f456c603d11367b42119272c22f77baa62a Mon Sep 17 00:00:00 2001
From: Luke
Date: Sun, 29 Jun 2025 23:56:21 +0800
Subject: [PATCH 14/61] =?UTF-8?q?feat:=20NumberTokenScanner=20=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E8=A7=84=E5=88=99=E7=BA=A6=E6=9D=9F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../compiler/lexer/core/LexerContext.java | 107 ++++++++-------
.../lexer/scanners/NumberTokenScanner.java | 124 ++++++++++--------
2 files changed, 130 insertions(+), 101 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerContext.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerContext.java
index dbfe62f..4462fca 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerContext.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerContext.java
@@ -3,60 +3,72 @@ package org.jcnc.snow.compiler.lexer.core;
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
/**
- * {@code LexerContext} 是词法分析阶段的上下文状态管理器。
+ * {@code LexerContext} —— 词法分析阶段的上下文状态管理器。
*
- * 该类提供对源代码字符流的读取访问,追踪当前行号与列号,
- * 并支持字符匹配、回看与指针推进等操作,是 {@link TokenScanner} 实现进行词法识别的重要支撑工具。
+ * 提供对源代码字符流的读取访问、行列号追踪、指针推进与字符匹配等操作,
+ * 是 {@link TokenScanner} 实现进行词法识别的基础设施。
*
*
- * 所有源代码输入在构造时统一将 Windows 风格的换行符(\r\n)转换为 Unix 风格(\n),
- * 保证换行行为一致性。
+ * 设计要点:
+ *
+ * 构造时统一将 Windows 换行符 (\r\n) 转换为 Unix 风格 (\n)。
+ * 所有坐标均以 1 为起始行/列号,更贴合人类直觉。
+ * 提供 {@link #peekAhead(int)} 方法以支持“向前多字符查看”而不移动游标。
+ *
*
*/
public class LexerContext {
- /** 源代码字符串,换行符已标准化为 \n */
+ /* ───────────────────────────────── 私有字段 ───────────────────────────────── */
+
+ /** 源代码字符串(换行符已标准化为 \n) */
private final String source;
- /** 当前扫描位置(从 0 开始的偏移) */
+ /** 当前扫描位置(自 0 起算的全局偏移量) */
private int pos = 0;
- /** 当前行号,从 1 开始 */
+ /** 当前行号(从 1 开始) */
private int line = 1;
- /** 当前列号,从 1 开始 */
+ /** 当前列号(从 1 开始) */
private int col = 1;
- /** 上一个字符对应的列号(用于位置精确记录) */
+ /** 上一个字符对应的列号(用于异常定位) */
private int lastCol = 1;
+ /* ──────────────────────────────── 构造 & 基本信息 ─────────────────────────────── */
+
/**
- * 构造一个新的 {@code LexerContext} 实例,并标准化换行符。
+ * 创建新的 {@code LexerContext},并完成换行符标准化。
*
- * @param source 原始源代码字符串
+ * @param rawSource 原始源代码文本
*/
- public LexerContext(String source) {
- this.source = source.replace("\r\n", "\n");
+ public LexerContext(String rawSource) {
+ this.source = rawSource.replace("\r\n", "\n");
}
/**
- * 判断是否已读取到源代码末尾。
+ * 判断是否已到达源代码结尾。
*
- * @return 若已结束,返回 {@code true};否则返回 {@code false}
+ * @return 若游标位于终点之后返回 {@code true}
*/
public boolean isAtEnd() {
return pos >= source.length();
}
+ /* ──────────────────────────────── 指针推进与查看 ─────────────────────────────── */
+
/**
- * 消费当前字符并前进一个位置,自动更新行列信息。
+ * 消费 当前 字符并前进一个位置,同时更新行列号。
*
- * @return 当前字符,若已结束则返回空字符('\0')
+ * @return 被消费的字符;若已结束则返回空字符 {@code '\0'}
*/
public char advance() {
if (isAtEnd()) return '\0';
+
char c = source.charAt(pos++);
lastCol = col;
+
if (c == '\n') {
line++;
col = 1;
@@ -67,9 +79,9 @@ public class LexerContext {
}
/**
- * 查看当前位置的字符,但不前进。
+ * 查看当前位置字符,但不移动游标。
*
- * @return 当前字符,若结束则返回空字符
+ * @return 当前字符;若越界则返回 {@code '\0'}
*/
public char peek() {
return isAtEnd() ? '\0' : source.charAt(pos);
@@ -78,17 +90,29 @@ public class LexerContext {
/**
* 查看下一个字符,但不改变位置。
*
- * @return 下一个字符,若结束则返回空字符
+ * @return 下一字符;若越界则返回 {@code '\0'}
*/
public char peekNext() {
return pos + 1 >= source.length() ? '\0' : source.charAt(pos + 1);
}
/**
- * 若当前字符与期望字符相同,则前进并返回 {@code true},否则不动并返回 {@code false}。
+ * 向前查看 offset 个字符(不移动游标)。offset=1 等价于 {@link #peekNext()}。
*
- * @param expected 期待匹配的字符
- * @return 是否匹配成功并消费
+ * @param offset 偏移量 (≥ 1)
+ * @return 指定偏移处的字符;若越界返回 {@code '\0'}
+ */
+ public char peekAhead(int offset) {
+ if (offset <= 0) return peek();
+ int idx = pos + offset;
+ return idx >= source.length() ? '\0' : source.charAt(idx);
+ }
+
+ /**
+ * 若当前位置字符等于 {@code expected},则消费并返回 {@code true};否则保持原位返回 {@code false}。
+ *
+ * @param expected 期望匹配的字符
+ * @return 是否匹配并消费
*/
public boolean match(char expected) {
if (isAtEnd() || source.charAt(pos) != expected) return false;
@@ -96,30 +120,17 @@ public class LexerContext {
return true;
}
- /**
- * 获取当前位置的行号。
- *
- * @return 当前行号(从 1 开始)
- */
- public int getLine() {
- return line;
- }
+ /* ──────────────────────────────── 坐标查询 ─────────────────────────────── */
- /**
- * 获取当前位置的列号。
- *
- * @return 当前列号(从 1 开始)
- */
- public int getCol() {
- return col;
- }
+ /** @return 当前行号 (1-based) */
+ public int getLine() { return line; }
- /**
- * 获取上一个字符所在的列号。
- *
- * @return 上一个字符对应的列位置
- */
- public int getLastCol() {
- return lastCol;
- }
+ /** @return 当前列号 (1-based) */
+ public int getCol() { return col; }
+
+ /** @return 上一个字符的列号 */
+ public int getLastCol() { return lastCol; }
+
+ /** @return 当前指针在源文件中的全局偏移 (0-based) */
+ public int getPos() { return pos; }
}
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
index 517509f..ac598de 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
@@ -1,92 +1,110 @@
package org.jcnc.snow.compiler.lexer.scanners;
import org.jcnc.snow.compiler.lexer.core.LexerContext;
+import org.jcnc.snow.compiler.lexer.core.LexicalException;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
/**
- * 数字扫描器:识别整数、小数以及带有类型后缀 的数字字面量。
+ * 数字扫描器:识别整数、小数以及带有 类型后缀 的数字字面量。
*
- * 支持的格式示例:
+ * 支持格式示例:
*
- * 整数:123、0、45678
- * 小数:3.14、0.5、12.0
- * 带类型后缀:2.0f、42L、7s、255B
+ * 整数:123、0、45678
+ * 小数:3.14、0.5、12.0
+ * 带后缀:2.0f、42L、7s、255B
*
+ *
*
- * 语法允许在数字 (整数或小数) 末尾添加以下单字符后缀 来显式指定常量类型:
- *
b | s | l | f | d // 分别对应 byte、short、long、float、double
- * B | S | L | F | D // 同上,大小写皆可
- * 生成的 Token 类型始终为 {@code NUMBER_LITERAL},词法单元将携带完整的文本(含后缀,若存在)。
+ * 单字符类型后缀:
+ *
+ * b | s | l | f | d // byte, short, long, float, double
+ * B | S | L | F | D // 同上(大小写均可)
+ *
+ *
+ *
+ * 规则约束:
+ * 若数字主体之后出现以下情况,将在词法阶段抛出 {@link LexicalException}:
+ *
+ * 空白 + 字母(如 3 L)
+ * 未知字母紧邻(如 3E)
+ * '/' 紧邻(如 3/、3/*)
+ *
+ * 以避免编译器陷入死循环。
+ *
*/
public class NumberTokenScanner extends AbstractTokenScanner {
- /**
- * 可选类型后缀字符集合 (大小写均可)。
- * 与 {@code ExpressionBuilder} 内的后缀解析逻辑保持一致。
- */
+ /** 合法类型后缀字符集合 */
private static final String SUFFIX_CHARS = "bslfdBSLFD";
- /**
- * 判断是否可以处理当前位置的字符。
- * 当字符为数字时,表示可能是数字字面量的起始。
- *
- * @param c 当前字符
- * @param ctx 当前词法上下文
- * @return 如果为数字字符,则返回 true
- */
@Override
public boolean canHandle(char c, LexerContext ctx) {
return Character.isDigit(c);
}
- /**
- * 执行数字扫描逻辑。
- *
- * 连续读取数字字符,允许出现一个 小数点,用于识别整数或小数。
- * 读取完主体后,一次性 检查下一个字符,若属于合法类型后缀则吸收。
- *
- * 这样可以保证诸如 {@code 2.0f} 被视为一个整体的 {@code NUMBER_LITERAL},
- * 而不是拆分成 "2.0" 与 "f" 两个 Token。
- *
- * @param ctx 词法上下文
- * @param line 当前行号
- * @param col 当前列号
- * @return 表示数字字面量的 Token
- */
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
- StringBuilder sb = new StringBuilder();
- boolean hasDot = false; // 标识是否已经遇到过小数点
+ StringBuilder literal = new StringBuilder();
+ boolean hasDot = false; // 是否已遇到小数点
- /*
- * 1️⃣ 扫描整数或小数主体
- * 允许出现一个小数点,其余必须是数字。
- */
+ /* 1. 读取数字主体(整数 / 小数) */
while (!ctx.isAtEnd()) {
char c = ctx.peek();
if (c == '.' && !hasDot) {
hasDot = true;
- sb.append(ctx.advance());
+ literal.append(ctx.advance());
} else if (Character.isDigit(c)) {
- sb.append(ctx.advance());
+ literal.append(ctx.advance());
} else {
- break; // 遇到非数字/第二个点 => 主体结束
+ break;
}
}
- /*
- * 2️⃣ 可选类型后缀
- * 如果下一字符是合法后缀字母,则一起纳入当前 Token。
- */
+ /* 2. 处理后缀或非法跟随字符 */
if (!ctx.isAtEnd()) {
- char suffix = ctx.peek();
- if (SUFFIX_CHARS.indexOf(suffix) >= 0) {
- sb.append(ctx.advance());
+ char next = ctx.peek();
+
+ /* 2-A: 合法类型后缀,直接吸收 */
+ if (SUFFIX_CHARS.indexOf(next) >= 0) {
+ literal.append(ctx.advance());
}
+ /* 2-B: 未知字母紧邻 → 抛异常 */
+ else if (Character.isLetter(next)) {
+ throw new LexicalException(
+ "Unknown numeric suffix '" + next + "'",
+ line, col
+ );
+ }
+ /* 2-C: 数字后空白(非换行)→ 若空白后跟字母,抛异常 */
+ else if (Character.isWhitespace(next) && next != '\n') {
+ int off = 1;
+ char look;
+ do {
+ look = ctx.peekAhead(off);
+ if (look == '\n' || look == '\0') break;
+ if (!Character.isWhitespace(look)) break;
+ off++;
+ } while (true);
+
+ if (Character.isLetter(look)) {
+ throw new LexicalException(
+ "Whitespace between numeric literal and an alphabetic character is not allowed",
+ line, col
+ );
+ }
+ }
+ /* 2-D: 紧邻字符为 '/' → 抛异常以避免死循环 */
+ else if (next == '/') {
+ throw new LexicalException(
+ "Unexpected '/' after numeric literal",
+ line, col
+ );
+ }
+ /* 其余字符(运算符、分隔符等)留给后续扫描器处理 */
}
- // 构造并返回 NUMBER_LITERAL Token,文本内容形如 "123", "3.14f" 等。
- return new Token(TokenType.NUMBER_LITERAL, sb.toString(), line, col);
+ /* 3. 返回 NUMBER_LITERAL Token */
+ return new Token(TokenType.NUMBER_LITERAL, literal.toString(), line, col);
}
}
From 3eacdf6d392c10c4a01d3d70375bb7c8fb93a2c4 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 30 Jun 2025 16:21:01 +0800
Subject: [PATCH 15/61] =?UTF-8?q?fix:=20=E6=95=B0=E5=AD=97=E5=AD=97?=
=?UTF-8?q?=E9=9D=A2=E9=87=8F=E4=B8=8E=E4=BD=8D=E5=AE=BD=E7=AC=A6=E5=8F=B7?=
=?UTF-8?q?=E4=B9=8B=E9=97=B4=E4=B8=8D=E5=85=81=E8=AE=B8=E6=9C=89=E7=A9=BA?=
=?UTF-8?q?=E7=99=BD=E7=AC=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
playground/BugFarm/Bug1/Main.snow | 4 +-
playground/Demo1/Math.snow | 4 +-
src/main/java/org/jcnc/snow/cli/SnowCLI.java | 2 +-
.../snow/compiler/lexer/core/LexerEngine.java | 61 +++++++++++++++----
.../compiler/lexer/core/LexicalError.java | 20 ++++++
.../compiler/lexer/core/LexicalException.java | 15 ++++-
.../lexer/scanners/NumberTokenScanner.java | 30 +++++----
.../utils/SemanticAnalysisReporter.java | 2 +-
8 files changed, 106 insertions(+), 32 deletions(-)
create mode 100644 src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalError.java
diff --git a/playground/BugFarm/Bug1/Main.snow b/playground/BugFarm/Bug1/Main.snow
index d9a3e6e..3dae6e6 100644
--- a/playground/BugFarm/Bug1/Main.snow
+++ b/playground/BugFarm/Bug1/Main.snow
@@ -1,7 +1,7 @@
function: main
return_type: int
body:
- 3 L
- return 65537
+ declare num1 :int = 3.1 G
+ return 65537
end body
end function
\ No newline at end of file
diff --git a/playground/Demo1/Math.snow b/playground/Demo1/Math.snow
index bfe8605..973f7c9 100644
--- a/playground/Demo1/Math.snow
+++ b/playground/Demo1/Math.snow
@@ -1,8 +1,8 @@
module: Math
function: factorial
parameter:
- declare n1: long
- declare n2: long
+ declare n1: int
+ declare n2: int
return_type: long
body:
return n1+n2
diff --git a/src/main/java/org/jcnc/snow/cli/SnowCLI.java b/src/main/java/org/jcnc/snow/cli/SnowCLI.java
index 03a4391..0afa4e4 100644
--- a/src/main/java/org/jcnc/snow/cli/SnowCLI.java
+++ b/src/main/java/org/jcnc/snow/cli/SnowCLI.java
@@ -91,7 +91,7 @@ public class SnowCLI {
System.exit(exitCode);
} catch (Exception e) {
// 捕获命令执行过程中的异常并打印错误消息
- System.err.println("Error: " + e.getMessage());
+// System.err.println("Error: " + e.getMessage());
System.exit(1);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
index 4d06999..060d3c3 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
@@ -4,6 +4,7 @@ import org.jcnc.snow.compiler.lexer.base.TokenScanner;
import org.jcnc.snow.compiler.lexer.scanners.*;
import org.jcnc.snow.compiler.lexer.token.Token;
+import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -24,6 +25,7 @@ public class LexerEngine {
*/
private final List tokens = new ArrayList<>();
+ private final String absPath;
/**
* 词法上下文,提供字符流读取与位置信息
*/
@@ -34,6 +36,8 @@ public class LexerEngine {
*/
private final List scanners;
+ private final List errors = new ArrayList<>();
+
/**
* 构造词法分析器(假定输入源自标准输入,文件名默认为 )
*
@@ -43,6 +47,7 @@ public class LexerEngine {
this(source, "");
}
+
/**
* 构造词法分析器,并指定源文件名(用于诊断信息)。
* 构造时立即进行全量扫描。
@@ -51,6 +56,7 @@ public class LexerEngine {
* @param sourceName 文件名或来源描述(如"Main.snow")
*/
public LexerEngine(String source, String sourceName) {
+ this.absPath = new File(sourceName).getAbsolutePath();
this.context = new LexerContext(source);
this.scanners = List.of(
new WhitespaceTokenScanner(), // 跳过空格、制表符等
@@ -68,15 +74,28 @@ public class LexerEngine {
try {
scanAllTokens();
} catch (LexicalException le) {
- // 输出:文件名:行:列: 错误信息,简洁明了
+ // 输出:绝对路径: 行 x, 列 y: 错误信息
System.err.printf(
- "%s:%d:%d: %s%n",
- sourceName,
- le.getLine(), // 获取出错行号
- le.getColumn(), // 获取出错列号
- le.getMessage() // 错误描述
+ "%s: 行 %d, 列 %d: %s%n",
+ absPath,
+ le.getLine(),
+ le.getColumn(),
+ le.getReason()
);
- System.exit(65); // 65 = EX_DATAERR,标准数据错误退出码
+ System.exit(65); // 65 = EX_DATAERR
+ }
+ LexerEngine.report(this.getErrors());
+ }
+
+ /**
+ * 静态报告方法
+ */
+ public static void report(List errors) {
+ if (errors != null && !errors.isEmpty()) {
+ System.err.println("\n词法分析发现 " + errors.size() + " 个错误:");
+ errors.forEach(err -> System.err.println(" " + err));
+ } else {
+ System.out.println("## 词法分析通过,没有发现错误\n");
}
}
@@ -88,15 +107,28 @@ public class LexerEngine {
private void scanAllTokens() {
while (!context.isAtEnd()) {
char currentChar = context.peek();
- // 依次查找能处理当前字符的扫描器
+ boolean handled = false;
for (TokenScanner scanner : scanners) {
if (scanner.canHandle(currentChar, context)) {
- scanner.handle(context, tokens);
- break; // 已处理,跳到下一个字符
+ try {
+ scanner.handle(context, tokens);
+ } catch (LexicalException le) {
+ // 收集词法错误,不直接退出
+ errors.add(new LexicalError(
+ absPath, le.getLine(), le.getColumn(), le.getReason()
+ ));
+ // 跳过当前字符,防止死循环
+ context.advance();
+ }
+ handled = true;
+ break;
}
}
+ if (!handled) {
+ // 万一没有任何扫描器能处理,跳过一个字符防止死循环
+ context.advance();
+ }
}
- // 末尾补一个 EOF 标记
tokens.add(Token.eof(context.getLine()));
}
@@ -108,4 +140,11 @@ public class LexerEngine {
public List getAllTokens() {
return List.copyOf(tokens);
}
+
+ /**
+ * 返回全部词法错误
+ */
+ public List getErrors() {
+ return List.copyOf(errors);
+ }
}
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalError.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalError.java
new file mode 100644
index 0000000..81ac9ee
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalError.java
@@ -0,0 +1,20 @@
+package org.jcnc.snow.compiler.lexer.core;
+
+public class LexicalError {
+ private final String file;
+ private final int line;
+ private final int column;
+ private final String message;
+
+ public LexicalError(String file, int line, int column, String message) {
+ this.file = file;
+ this.line = line;
+ this.column = column;
+ this.message = message;
+ }
+
+ @Override
+ public String toString() {
+ return file + ": 行 " + line + ", 列 " + column + ": " + message;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalException.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalException.java
index 96cfc0c..cdd01cb 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalException.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexicalException.java
@@ -19,6 +19,8 @@ public class LexicalException extends RuntimeException {
private final int line;
/** 错误发生的列号(从1开始) */
private final int column;
+ /** 错误原因 */
+ private final String reason;
/**
* 构造词法异常
@@ -27,13 +29,14 @@ public class LexicalException extends RuntimeException {
* @param column 出错列号
*/
public LexicalException(String reason, int line, int column) {
- // 构造出错消息,并禁止异常堆栈打印
- super(String.format("Lexical error: %s at %d:%d", reason, line, column),
- null, false, false);
+ // 错误描述直接为 reason,禁止异常堆栈打印
+ super(reason, null, false, false);
+ this.reason = reason;
this.line = line;
this.column = column;
}
+
/**
* 屏蔽异常堆栈填充(始终不打印堆栈信息)
*/
@@ -51,4 +54,10 @@ public class LexicalException extends RuntimeException {
* @return 列号
*/
public int getColumn() { return column; }
+
+ /**
+ * 获取出错的描述
+ * @return 出错描述
+ */
+ public String getReason() { return reason; }
}
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
index ac598de..084c8f7 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
@@ -35,51 +35,56 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
*/
public class NumberTokenScanner extends AbstractTokenScanner {
- /** 合法类型后缀字符集合 */
+ /** 合法类型后缀字符集合(单字符,大小写均可) */
private static final String SUFFIX_CHARS = "bslfdBSLFD";
@Override
public boolean canHandle(char c, LexerContext ctx) {
+ // 仅当遇到数字时,本扫描器才处理
return Character.isDigit(c);
}
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
StringBuilder literal = new StringBuilder();
- boolean hasDot = false; // 是否已遇到小数点
+ boolean hasDot = false; // 标记是否已出现过小数点
- /* 1. 读取数字主体(整数 / 小数) */
+ /* 1. 读取数字主体部分(包括整数、小数) */
while (!ctx.isAtEnd()) {
char c = ctx.peek();
if (c == '.' && !hasDot) {
+ // 遇到第一个小数点
hasDot = true;
literal.append(ctx.advance());
} else if (Character.isDigit(c)) {
+ // 吸收数字字符
literal.append(ctx.advance());
} else {
+ // 非数字/非小数点,终止主体读取
break;
}
}
- /* 2. 处理后缀或非法跟随字符 */
+ /* 2. 检查数字字面量后的字符,决定是否继续吸收或抛出异常 */
if (!ctx.isAtEnd()) {
char next = ctx.peek();
- /* 2-A: 合法类型后缀,直接吸收 */
+ /* 2-A: 合法类型后缀,直接吸收(如 42L、3.0F) */
if (SUFFIX_CHARS.indexOf(next) >= 0) {
literal.append(ctx.advance());
}
- /* 2-B: 未知字母紧邻 → 抛异常 */
+ /* 2-B: 若紧跟未知字母(如 42X),抛出词法异常 */
else if (Character.isLetter(next)) {
throw new LexicalException(
- "Unknown numeric suffix '" + next + "'",
+ "未知的数字类型后缀 '" + next + "'",
line, col
);
}
- /* 2-C: 数字后空白(非换行)→ 若空白后跟字母,抛异常 */
+ /* 2-C: 若数字后有空白,且空白后紧跟字母(如 3 L),也为非法 */
else if (Character.isWhitespace(next) && next != '\n') {
int off = 1;
char look;
+ // 跳过所有空白字符,找到第一个非空白字符
do {
look = ctx.peekAhead(off);
if (look == '\n' || look == '\0') break;
@@ -88,20 +93,21 @@ public class NumberTokenScanner extends AbstractTokenScanner {
} while (true);
if (Character.isLetter(look)) {
+ // 抛出:数字字面量与位宽符号之间不允许有空白符
throw new LexicalException(
- "Whitespace between numeric literal and an alphabetic character is not allowed",
+ "数字字面量与位宽符号之间不允许有空白符",
line, col
);
}
}
- /* 2-D: 紧邻字符为 '/' → 抛异常以避免死循环 */
+ /* 2-D: 若紧跟 '/',抛出异常防止死循环 */
else if (next == '/') {
throw new LexicalException(
- "Unexpected '/' after numeric literal",
+ "数字字面量后不允许直接出现 '/'",
line, col
);
}
- /* 其余字符(运算符、分隔符等)留给后续扫描器处理 */
+ // 其余情况(如分号、括号、运算符),交由其他扫描器处理
}
/* 3. 返回 NUMBER_LITERAL Token */
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java b/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java
index dc2b336..91b4487 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/utils/SemanticAnalysisReporter.java
@@ -28,7 +28,7 @@ public final class SemanticAnalysisReporter {
System.err.println("语义分析发现 " + errors.size() + " 个错误:");
errors.forEach(err -> System.err.println(" " + err));
} else {
-// System.out.println("## 语义分析通过,没有发现错误\n");
+ System.out.println("\n## 语义分析通过,没有发现错误\n");
}
}
From dc32f00ead1139eae9d2d6a3a21c1b7b3ae463a7 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 30 Jun 2025 16:21:48 +0800
Subject: [PATCH 16/61] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=20demo1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
playground/Demo1/Math.snow | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/playground/Demo1/Math.snow b/playground/Demo1/Math.snow
index 973f7c9..bfe8605 100644
--- a/playground/Demo1/Math.snow
+++ b/playground/Demo1/Math.snow
@@ -1,8 +1,8 @@
module: Math
function: factorial
parameter:
- declare n1: int
- declare n2: int
+ declare n1: long
+ declare n2: long
return_type: long
body:
return n1+n2
From 67d2b582315e27ab79ca5f555b67bef63cfd55dd Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 30 Jun 2025 16:52:53 +0800
Subject: [PATCH 17/61] =?UTF-8?q?feat:=20=E9=BB=98=E8=AE=A4=E5=90=AF?=
=?UTF-8?q?=E5=8A=A8=E6=8A=A5=E5=91=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
index 060d3c3..09daf38 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
@@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.lexer.core;
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
import org.jcnc.snow.compiler.lexer.scanners.*;
import org.jcnc.snow.compiler.lexer.token.Token;
+import org.jcnc.snow.compiler.lexer.utils.TokenPrinter;
import java.io.File;
import java.util.ArrayList;
@@ -84,6 +85,7 @@ public class LexerEngine {
);
System.exit(65); // 65 = EX_DATAERR
}
+ TokenPrinter.print(this.tokens);
LexerEngine.report(this.getErrors());
}
@@ -95,7 +97,7 @@ public class LexerEngine {
System.err.println("\n词法分析发现 " + errors.size() + " 个错误:");
errors.forEach(err -> System.err.println(" " + err));
} else {
- System.out.println("## 词法分析通过,没有发现错误\n");
+ System.out.println("\n## 词法分析通过,没有发现错误\n");
}
}
From ce4106743aa4667b66c59e776da476a71325af86 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 30 Jun 2025 17:51:01 +0800
Subject: [PATCH 18/61] =?UTF-8?q?docs:=20=E5=AE=8C=E5=96=84=20LexerEngine.?=
=?UTF-8?q?java=20=E7=9A=84=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../snow/compiler/lexer/core/LexerEngine.java | 43 +++++++++++++------
1 file changed, 30 insertions(+), 13 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
index 09daf38..351f94b 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
@@ -22,25 +22,34 @@ import java.util.List;
*/
public class LexerEngine {
/**
- * 扫描生成的 Token 序列(包含文件结束符 EOF)
+ * 扫描生成的 Token 序列(包含文件结束符 EOF)。
+ * 每个 Token 表示源代码中的一个词法单元。
*/
private final List tokens = new ArrayList<>();
- private final String absPath;
/**
- * 词法上下文,提供字符流读取与位置信息
+ * 当前源文件的绝对路径,用于错误信息定位。
+ */
+ private final String absPath;
+
+ /**
+ * 词法上下文,负责字符流读取与位置信息维护。
*/
private final LexerContext context;
/**
- * Token 扫描器集合,按优先级顺序组织,用于识别不同类别的 Token
+ * Token 扫描器集合,按优先级顺序排列,
+ * 用于识别不同类别的 Token(如空白、注释、数字、标识符等)。
*/
private final List scanners;
+ /**
+ * 词法分析过程中收集到的全部词法错误。
+ */
private final List errors = new ArrayList<>();
/**
- * 构造词法分析器(假定输入源自标准输入,文件名默认为 )
+ * 构造词法分析器(假定输入源自标准输入,文件名默认为 <stdin>)。
*
* @param source 源代码文本
*/
@@ -48,10 +57,9 @@ public class LexerEngine {
this(source, "");
}
-
/**
* 构造词法分析器,并指定源文件名(用于诊断信息)。
- * 构造时立即进行全量扫描。
+ * 构造时立即进行全量扫描,扫描结束后打印所有 Token 并报告词法错误。
*
* @param source 源代码文本
* @param sourceName 文件名或来源描述(如"Main.snow")
@@ -90,7 +98,12 @@ public class LexerEngine {
}
/**
- * 静态报告方法
+ * 静态报告方法。
+ *
+ * 打印所有词法分析过程中收集到的错误信息。
+ * 如果无错误,输出词法分析通过的提示。
+ *
+ * @param errors 词法错误列表
*/
public static void report(List errors) {
if (errors != null && !errors.isEmpty()) {
@@ -102,9 +115,11 @@ public class LexerEngine {
}
/**
- * 主扫描循环,将源代码转为 Token 序列
- * 依次尝试每个扫描器,直到找到可处理当前字符的扫描器为止
- * 扫描到结尾后补充 EOF Token
+ * 主扫描循环,将源代码转为 Token 序列。
+ *
+ * 依次尝试每个扫描器,直到找到可处理当前字符的扫描器为止。
+ * 扫描到结尾后补充 EOF Token。
+ * 若遇到词法异常则收集错误并跳过当前字符,避免死循环。
*/
private void scanAllTokens() {
while (!context.isAtEnd()) {
@@ -135,7 +150,7 @@ public class LexerEngine {
}
/**
- * 获取全部 Token(包含 EOF),返回只读列表
+ * 获取全部 Token(包含 EOF),返回只读列表。
*
* @return 词法分析结果 Token 列表
*/
@@ -144,7 +159,9 @@ public class LexerEngine {
}
/**
- * 返回全部词法错误
+ * 返回全部词法错误(返回只读列表)。
+ *
+ * @return 词法错误列表
*/
public List getErrors() {
return List.copyOf(errors);
From f9a65531c19b899d328d3b18fd995a4ccdd865d1 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 30 Jun 2025 17:52:37 +0800
Subject: [PATCH 19/61] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E4=B8=BA?=
=?UTF-8?q?=E5=9F=BA=E4=BA=8E=E6=9C=89=E9=99=90=E7=8A=B6=E6=80=81=E6=9C=BA?=
=?UTF-8?q?=EF=BC=88FSM=EF=BC=89=E7=9A=84=E6=95=B0=E5=AD=97=E5=AD=97?=
=?UTF-8?q?=E9=9D=A2=E9=87=8F=E8=A7=A3=E6=9E=90=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../lexer/scanners/NumberTokenScanner.java | 194 ++++++++++++------
1 file changed, 133 insertions(+), 61 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
index 084c8f7..5e1bd7d 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
@@ -6,111 +6,183 @@ import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
/**
- * 数字扫描器:识别整数、小数以及带有 类型后缀 的数字字面量。
+ * NumberTokenScanner —— 基于有限状态机(FSM)的数字字面量解析器。
*
- * 支持格式示例:
- *
- * 整数:123、0、45678
- * 小数:3.14、0.5、12.0
- * 带后缀:2.0f、42L、7s、255B
- *
- *
+ * 该扫描器负责将源码中的数字字符串切分为 NUMBER_LITERAL token,当前支持:
+ *
+ * 十进制整数(如 0,42,123456)
+ * 十进制小数(如 3.14,0.5)
+ * 单字符类型后缀(如 2.0f,255B,合法集合见 SUFFIX_CHARS)
+ *
*
- * 单字符类型后缀:
+ * 如果后续需要支持科学计数法、下划线分隔符、不同进制等,只需扩展现有状态机的转移规则。
*
- * b | s | l | f | d // byte, short, long, float, double
- * B | S | L | F | D // 同上(大小写均可)
+ * 状态机简述:
+ * INT_PART --'.'--> DEC_POINT
+ * | |
+ * | v
+ * else------------> END
+ * |
+ * v
+ * DEC_POINT --digit--> FRAC_PART
+ * |
+ * v
+ * else--> END
*
- *
- *
- * 规则约束:
- * 若数字主体之后出现以下情况,将在词法阶段抛出 {@link LexicalException}:
+ * 状态说明:
*
- * 空白 + 字母(如 3 L)
- * 未知字母紧邻(如 3E)
- * '/' 紧邻(如 3/、3/*)
+ * INT_PART :读取整数部分,遇到 '.' 进入 DEC_POINT,否则结束。
+ * DEC_POINT :已读到小数点,必须下一个字符是数字,否则报错。
+ * FRAC_PART :读取小数部分,遇非法字符则结束主体。
+ * END :主体扫描结束,进入后缀/尾随字符判定。
*
- * 以避免编译器陷入死循环。
- *
+ * 错误处理策略:
+ *
+ * 数字后跟未知字母(如 42X)—— 抛出 LexicalException
+ * 数字与合法后缀间有空白(如 3 L)—— 抛出 LexicalException
+ * 数字后直接出现 '/'(如 3/ 或 3/*)—— 抛出 LexicalException,避免死循环
+ * 小数点后缺失数字(如 1.)—— 抛出 LexicalException
+ *
+ * 支持的单字符类型后缀包括:b, s, l, f, d 及其大写形式。若需支持多字符后缀,可将该集合扩展为 Set。
*/
public class NumberTokenScanner extends AbstractTokenScanner {
- /** 合法类型后缀字符集合(单字符,大小写均可) */
+ /**
+ * 支持的单字符类型后缀集合。
+ * 包含:b, s, l, f, d 及其大写形式。
+ * 对于多字符后缀,可扩展为 Set 并在扫描尾部做贪婪匹配。
+ */
private static final String SUFFIX_CHARS = "bslfdBSLFD";
+ /**
+ * 判断是否由该扫描器处理。
+ * 仅当首字符为数字时,NumberTokenScanner 介入处理。
+ *
+ * @param c 当前待判断字符
+ * @param ctx 当前 LexerContext(可用于进一步判断)
+ * @return 如果为数字返回 true,否则返回 false
+ */
@Override
public boolean canHandle(char c, LexerContext ctx) {
- // 仅当遇到数字时,本扫描器才处理
return Character.isDigit(c);
}
+ /**
+ * 按照有限状态机读取完整数字字面量,并对尾随字符进行合法性校验。
+ *
+ * 主体流程:
+ * 1. 整数部分、可选小数点和小数部分扫描。
+ * 2. 检查合法的类型后缀。
+ * 3. 检查非法尾随字符,如未知字母、空白后缀或非法 '/'。
+ * 4. 生成并返回 NUMBER_LITERAL Token。
+ *
+ * @param ctx 当前 LexerContext(提供游标、前瞻等功能)
+ * @param line 源码起始行号(1 基)
+ * @param col 源码起始列号(1 基)
+ * @return NUMBER_LITERAL 类型的 Token
+ * @throws LexicalException 如果遇到非法格式或未受支持的尾随字符
+ */
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
StringBuilder literal = new StringBuilder();
- boolean hasDot = false; // 标记是否已出现过小数点
+ State state = State.INT_PART;
- /* 1. 读取数字主体部分(包括整数、小数) */
- while (!ctx.isAtEnd()) {
- char c = ctx.peek();
- if (c == '.' && !hasDot) {
- // 遇到第一个小数点
- hasDot = true;
- literal.append(ctx.advance());
- } else if (Character.isDigit(c)) {
- // 吸收数字字符
- literal.append(ctx.advance());
- } else {
- // 非数字/非小数点,终止主体读取
- break;
+ // 1. 主体扫描 —— 整数 / 小数
+ mainLoop:
+ while (!ctx.isAtEnd() && state != State.END) {
+ char ch = ctx.peek();
+ switch (state) {
+ case INT_PART:
+ if (Character.isDigit(ch)) {
+ literal.append(ctx.advance());
+ } else if (ch == '.') {
+ state = State.DEC_POINT;
+ literal.append(ctx.advance());
+ } else {
+ state = State.END; // 整数已结束
+ }
+ break;
+
+ case DEC_POINT:
+ if (Character.isDigit(ch)) {
+ state = State.FRAC_PART;
+ literal.append(ctx.advance());
+ } else {
+ // 如 "1." —— 语言规范不允许尾点数字
+ throw new LexicalException("小数点后必须跟数字", line, col);
+ }
+ break;
+
+ case FRAC_PART:
+ if (Character.isDigit(ch)) {
+ literal.append(ctx.advance());
+ } else {
+ state = State.END; // 小数字符串结束
+ }
+ break;
+
+ default:
+ break mainLoop; // 理论不会到达
}
}
- /* 2. 检查数字字面量后的字符,决定是否继续吸收或抛出异常 */
+ // 2. 后缀及非法尾随字符检查
if (!ctx.isAtEnd()) {
char next = ctx.peek();
- /* 2-A: 合法类型后缀,直接吸收(如 42L、3.0F) */
+ // 2‑A. 合法单字符后缀
if (SUFFIX_CHARS.indexOf(next) >= 0) {
literal.append(ctx.advance());
}
- /* 2-B: 若紧跟未知字母(如 42X),抛出词法异常 */
+ // 2‑B. 紧跟未知字母(如 42X)
else if (Character.isLetter(next)) {
- throw new LexicalException(
- "未知的数字类型后缀 '" + next + "'",
- line, col
- );
+ throw new LexicalException("未知的数字类型后缀 '" + next + "'", line, col);
}
- /* 2-C: 若数字后有空白,且空白后紧跟字母(如 3 L),也为非法 */
+ // 2‑C. 数字后出现空白 + 字母(如 3 L)
else if (Character.isWhitespace(next) && next != '\n') {
int off = 1;
char look;
- // 跳过所有空白字符,找到第一个非空白字符
- do {
+ while (true) {
look = ctx.peekAhead(off);
- if (look == '\n' || look == '\0') break;
+ if (look == '\n' || look == '\0') break; // 行尾或 EOF
if (!Character.isWhitespace(look)) break;
off++;
- } while (true);
-
+ }
if (Character.isLetter(look)) {
- // 抛出:数字字面量与位宽符号之间不允许有空白符
- throw new LexicalException(
- "数字字面量与位宽符号之间不允许有空白符",
- line, col
- );
+ throw new LexicalException("数字字面量与类型后缀之间不允许有空白符", line, col);
}
}
- /* 2-D: 若紧跟 '/',抛出异常防止死循环 */
+ // 2‑D. 紧跟 '/'(如 3/ 或 3/*)
else if (next == '/') {
- throw new LexicalException(
- "数字字面量后不允许直接出现 '/'",
- line, col
- );
+ throw new LexicalException("数字字面量后不允许直接出现 '/'", line, col);
}
- // 其余情况(如分号、括号、运算符),交由其他扫描器处理
+ // 其他字符(分号、运算符、括号等)留给外层扫描流程处理
}
- /* 3. 返回 NUMBER_LITERAL Token */
+ // 3. 生成并返回 Token
return new Token(TokenType.NUMBER_LITERAL, literal.toString(), line, col);
}
+
+ /**
+ * FSM 内部状态。
+ * 每次读取一个字符后,根据“当前状态 + 当前字符”决定转移。
+ */
+ private enum State {
+ /**
+ * 整数部分(尚未读到小数点)
+ */
+ INT_PART,
+ /**
+ * 已读到小数点,但还未读到第一位小数数字
+ */
+ DEC_POINT,
+ /**
+ * 小数部分(小数点右侧)
+ */
+ FRAC_PART,
+ /**
+ * 主体结束,准备处理后缀或交还控制权
+ */
+ END
+ }
}
From e9939e73c1c1f2d9b0ae958d32bd3ac166f61be1 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 30 Jun 2025 17:54:30 +0800
Subject: [PATCH 20/61] =?UTF-8?q?fix:=20=E5=88=A0=E9=99=A4=20LexerEngine.j?=
=?UTF-8?q?ava=20=E7=9A=84=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/jcnc/snow/compiler/lexer/core/LexerEngine.java | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
index 351f94b..6ce7b77 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
@@ -48,15 +48,6 @@ public class LexerEngine {
*/
private final List errors = new ArrayList<>();
- /**
- * 构造词法分析器(假定输入源自标准输入,文件名默认为 <stdin>)。
- *
- * @param source 源代码文本
- */
- public LexerEngine(String source) {
- this(source, "");
- }
-
/**
* 构造词法分析器,并指定源文件名(用于诊断信息)。
* 构造时立即进行全量扫描,扫描结束后打印所有 Token 并报告词法错误。
From f4568f82a4fd30ce4741ad0b593400f6c0faa8dc Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 30 Jun 2025 23:42:14 +0800
Subject: [PATCH 21/61] =?UTF-8?q?fix:=20=E7=BC=96=E8=AF=91=E9=81=87?=
=?UTF-8?q?=E5=88=B0=E8=AF=8D=E6=B3=95=E9=94=99=E8=AF=AF=E7=AB=8B=E5=8D=B3?=
=?UTF-8?q?=E7=BB=88=E6=AD=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
index 6ce7b77..970ec59 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
@@ -86,6 +86,9 @@ public class LexerEngine {
}
TokenPrinter.print(this.tokens);
LexerEngine.report(this.getErrors());
+ if (!errors.isEmpty()) {
+ throw new LexicalException("Lexing failed with " + errors.size() + " error(s).", this.context.getLine(), this.context.getCol());
+ }
}
/**
From 2a085f6906ca8840610c87e879a3f114f36c07a1 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 30 Jun 2025 23:42:34 +0800
Subject: [PATCH 22/61] =?UTF-8?q?docs:=20=E4=BF=AE=E5=A4=8D=E6=B3=A8?=
=?UTF-8?q?=E9=87=8A=E6=A0=BC=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../compiler/lexer/scanners/NumberTokenScanner.java | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
index 5e1bd7d..eea6458 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
@@ -71,10 +71,12 @@ public class NumberTokenScanner extends AbstractTokenScanner {
* 按照有限状态机读取完整数字字面量,并对尾随字符进行合法性校验。
*
* 主体流程:
- * 1. 整数部分、可选小数点和小数部分扫描。
- * 2. 检查合法的类型后缀。
- * 3. 检查非法尾随字符,如未知字母、空白后缀或非法 '/'。
- * 4. 生成并返回 NUMBER_LITERAL Token。
+ *
+ * 整数部分、可选小数点和小数部分扫描。
+ * 检查合法的类型后缀。
+ * 检查非法尾随字符,如未知字母、空白后缀或非法 '/'。
+ * 生成并返回 NUMBER_LITERAL Token。
+ *
*
* @param ctx 当前 LexerContext(提供游标、前瞻等功能)
* @param line 源码起始行号(1 基)
From 30b89c0f3d031934018218bc132ba9f4bdbc7469 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 30 Jun 2025 23:42:52 +0800
Subject: [PATCH 23/61] =?UTF-8?q?fix:=20=E7=BB=9F=E4=B8=80=E5=93=A8?=
=?UTF-8?q?=E5=85=B5=E5=80=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/jcnc/snow/vm/commands/function/RetCommand.java | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/vm/commands/function/RetCommand.java b/src/main/java/org/jcnc/snow/vm/commands/function/RetCommand.java
index af37032..1299bf5 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/function/RetCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/function/RetCommand.java
@@ -14,7 +14,9 @@ import org.jcnc.snow.vm.module.*;
*/
public class RetCommand implements Command {
- /** Sentinel value that tells the VM loop to terminate gracefully. */
+ /**
+ * Sentinel value that tells the VM loop to terminate gracefully.
+ */
private static final int PROGRAM_END = Integer.MAX_VALUE;
@Override
@@ -32,8 +34,8 @@ public class RetCommand implements Command {
StackFrame topFrame = callStack.peekFrame();
/* ----- Root frame: do NOT pop, just end program ----- */
- if (topFrame.getReturnAddress() == 0) {
- System.out.println("Return 0");
+ if (topFrame.getReturnAddress() == PROGRAM_END) {
+ System.out.println("Return ");
return PROGRAM_END; // VM main loop should break
}
From c88404fada081eddc9c9184764383e1a6e9f1cd8 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 00:15:14 +0800
Subject: [PATCH 24/61] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E4=B8=BA?=
=?UTF-8?q?=E5=9F=BA=E4=BA=8E=E6=9C=89=E9=99=90=E7=8A=B6=E6=80=81=E6=9C=BA?=
=?UTF-8?q?=EF=BC=88FSM=EF=BC=89=E7=9A=84=E6=B3=A8=E9=87=8A=E8=A7=A3?=
=?UTF-8?q?=E6=9E=90=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../lexer/scanners/CommentTokenScanner.java | 78 +++++++++++--------
1 file changed, 45 insertions(+), 33 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java
index a8a3313..328dee7 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java
@@ -1,29 +1,31 @@
package org.jcnc.snow.compiler.lexer.scanners;
import org.jcnc.snow.compiler.lexer.core.LexerContext;
+import org.jcnc.snow.compiler.lexer.core.LexicalException;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
/**
- * 注释扫描器:处理源代码中的注释部分,包括:
+ * {@code CommentTokenScanner} —— 注释解析器,基于有限状态机(FSM)。
+ *
+ * 负责将源码中的两种注释形式切分为 {@link TokenType#COMMENT COMMENT} token:
+ *
+ * 单行注释:以 {@code //} 开头,直至行尾或文件末尾。
+ * 多行注释:以 {@code /*} 开头,以 */ 结束,可跨多行。
+ *
+ *
+ * 本扫描器遵循“发现即捕获”原则:
+ * 注释文本被完整保留在 Token 中,供后续的文档提取、源映射等分析使用。
+ *
+ * 错误处理策略
*
- * 单行注释(以 "//" 开头,直到行尾)
- * 多行注释(以 "/*" 开头,以 "*/" 结尾)
+ * 未终止的多行注释:若文件结束时仍未遇到 */,抛出 {@link LexicalException}。
*
- *
- * 本扫描器会识别注释并生成 {@code TokenType.COMMENT} 类型的 Token,
- * 不会丢弃注释内容,而是将完整注释文本保留在 Token 中,便于后续分析(如文档提取、保留注释等场景)。
- *
*/
public class CommentTokenScanner extends AbstractTokenScanner {
/**
- * 判断是否可以处理当前位置的字符。
- * 当当前位置字符为 '/' 且下一个字符为 '/' 或 '*' 时,表示可能是注释的起始。
- *
- * @param c 当前字符
- * @param ctx 当前词法上下文
- * @return 如果是注释的起始符,则返回 true
+ * 仅当当前字符为 {@code '/'} 且下一个字符为 {@code '/'} 或 {@code '*'} 时,由本扫描器处理。
*/
@Override
public boolean canHandle(char c, LexerContext ctx) {
@@ -31,44 +33,54 @@ public class CommentTokenScanner extends AbstractTokenScanner {
}
/**
- * 实现注释的扫描逻辑。
- * 支持两种注释格式:
- *
- * 单行注释: 以 "//" 开头,直到遇到换行符
- * 多行注释: 以 "/*" 开头,直到遇到 "*/" 结束
- *
+ * 执行注释扫描,生成 {@code COMMENT} Token。
*
* @param ctx 词法上下文
- * @param line 当前行号(用于 Token 位置信息)
- * @param col 当前列号(用于 Token 位置信息)
- * @return 包含完整注释内容的 COMMENT 类型 Token
+ * @param line 起始行号(1 基)
+ * @param col 起始列号(1 基)
+ * @return 包含完整注释文本的 Token
+ * @throws LexicalException 若遇到未终止的多行注释
*/
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
- // 消费第一个 '/' 字符
- ctx.advance();
- StringBuilder sb = new StringBuilder("/");
+ StringBuilder literal = new StringBuilder();
- // 处理单行注释 //
+ /*
+ * 1. 读取注释起始符
+ * - 已由 canHandle 保证当前位置一定是 '/'
+ */
+ literal.append(ctx.advance()); // 消费首个 '/'
+
+ // -------- 单行注释 (//) --------
if (ctx.match('/')) {
- sb.append('/');
+ literal.append('/');
while (!ctx.isAtEnd() && ctx.peek() != '\n') {
- sb.append(ctx.advance());
+ literal.append(ctx.advance());
}
+ // 行尾或文件尾时退出,换行符留给上层扫描器处理。
}
- // 处理多行注释 /* ... */
+ // -------- 多行注释 (/* ... */) --------
else if (ctx.match('*')) {
- sb.append('*');
+ literal.append('*');
+ boolean terminated = false;
while (!ctx.isAtEnd()) {
char ch = ctx.advance();
- sb.append(ch);
+ literal.append(ch);
if (ch == '*' && ctx.peek() == '/') {
- sb.append(ctx.advance()); // 消费 '/'
+ literal.append(ctx.advance()); // 追加 '/'
+ terminated = true;
break;
}
}
+ if (!terminated) {
+ // 文件结束仍未闭合,抛 LexicalException
+ throw new LexicalException("未终止的多行注释", line, col);
+ }
}
- return new Token(TokenType.COMMENT, sb.toString(), line, col);
+ /*
+ * 2. 生成并返回 Token
+ */
+ return new Token(TokenType.COMMENT, literal.toString(), line, col);
}
}
From 6ae6d6e893ca39d12002748896732373fd2a3951 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 09:34:38 +0800
Subject: [PATCH 25/61] =?UTF-8?q?chore:=20IDEA=20=E7=82=B9=E5=87=BB?=
=?UTF-8?q?=E8=BF=90=E8=A1=8C=E5=90=8E"Run=20tool=20=E7=AA=97=E5=8F=A3"?=
=?UTF-8?q?=E4=BC=9A=E5=87=BA=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.run/Bug1.run.xml | 2 +-
.run/Demo1.run.xml | 2 +-
.run/Demo10.run.xml | 2 +-
.run/Demo11.run.xml | 2 +-
.run/Demo2.run.xml | 2 +-
.run/Demo3.run.xml | 2 +-
.run/Demo4.run.xml | 2 +-
.run/Demo5.run.xml | 2 +-
.run/Demo6.run.xml | 2 +-
.run/Demo7.run.xml | 2 +-
.run/Demo8.run.xml | 2 +-
.run/Demo9.run.xml | 2 +-
12 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/.run/Bug1.run.xml b/.run/Bug1.run.xml
index 46f5bc6..f9c6da7 100644
--- a/.run/Bug1.run.xml
+++ b/.run/Bug1.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo1.run.xml b/.run/Demo1.run.xml
index 55005c8..653910c 100644
--- a/.run/Demo1.run.xml
+++ b/.run/Demo1.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo10.run.xml b/.run/Demo10.run.xml
index 19c1c9c..3d2b22a 100644
--- a/.run/Demo10.run.xml
+++ b/.run/Demo10.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo11.run.xml b/.run/Demo11.run.xml
index e6748fb..07d914d 100644
--- a/.run/Demo11.run.xml
+++ b/.run/Demo11.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo2.run.xml b/.run/Demo2.run.xml
index 2661fde..0711740 100644
--- a/.run/Demo2.run.xml
+++ b/.run/Demo2.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo3.run.xml b/.run/Demo3.run.xml
index 57994d1..a2b72da 100644
--- a/.run/Demo3.run.xml
+++ b/.run/Demo3.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo4.run.xml b/.run/Demo4.run.xml
index 083127e..491197e 100644
--- a/.run/Demo4.run.xml
+++ b/.run/Demo4.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo5.run.xml b/.run/Demo5.run.xml
index 2c2bbf8..d3f69a3 100644
--- a/.run/Demo5.run.xml
+++ b/.run/Demo5.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo6.run.xml b/.run/Demo6.run.xml
index 9c76927..b7eb743 100644
--- a/.run/Demo6.run.xml
+++ b/.run/Demo6.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo7.run.xml b/.run/Demo7.run.xml
index d78ab8c..afa8cd9 100644
--- a/.run/Demo7.run.xml
+++ b/.run/Demo7.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo8.run.xml b/.run/Demo8.run.xml
index 0857919..8bff503 100644
--- a/.run/Demo8.run.xml
+++ b/.run/Demo8.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo9.run.xml b/.run/Demo9.run.xml
index 97cff0d..8e63237 100644
--- a/.run/Demo9.run.xml
+++ b/.run/Demo9.run.xml
@@ -1,5 +1,5 @@
-
+
From 4507e3589fcaa909e4e139044dcd696c084a31a9 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 09:55:21 +0800
Subject: [PATCH 26/61] =?UTF-8?q?fix:=20NumberTokenScanner=20=E7=A7=BB?=
=?UTF-8?q?=E9=99=A4=E6=95=B0=E5=AD=97=E5=AD=97=E9=9D=A2=E9=87=8F=E5=90=8E?=
=?UTF-8?q?=E7=9B=B4=E6=8E=A5=E8=B7=9F=20/=20=E7=9A=84=E8=A7=84=E5=88=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java | 2 +-
.../snow/compiler/lexer/scanners/NumberTokenScanner.java | 5 -----
2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
index 970ec59..5df39fa 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
@@ -136,7 +136,7 @@ public class LexerEngine {
}
}
if (!handled) {
- // 万一没有任何扫描器能处理,跳过一个字符防止死循环
+ // 没有任何扫描器能处理,跳过一个字符防止死循环
context.advance();
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
index eea6458..11eefa5 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
@@ -40,7 +40,6 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
*
* 数字后跟未知字母(如 42X)—— 抛出 LexicalException
* 数字与合法后缀间有空白(如 3 L)—— 抛出 LexicalException
- * 数字后直接出现 '/'(如 3/ 或 3/*)—— 抛出 LexicalException,避免死循环
* 小数点后缺失数字(如 1.)—— 抛出 LexicalException
*
* 支持的单字符类型后缀包括:b, s, l, f, d 及其大写形式。若需支持多字符后缀,可将该集合扩展为 Set。
@@ -154,10 +153,6 @@ public class NumberTokenScanner extends AbstractTokenScanner {
throw new LexicalException("数字字面量与类型后缀之间不允许有空白符", line, col);
}
}
- // 2‑D. 紧跟 '/'(如 3/ 或 3/*)
- else if (next == '/') {
- throw new LexicalException("数字字面量后不允许直接出现 '/'", line, col);
- }
// 其他字符(分号、运算符、括号等)留给外层扫描流程处理
}
From 7b9bd3790043282697666a346db6b85a8e9fd74d Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 10:45:56 +0800
Subject: [PATCH 27/61] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=A1=8C?=
=?UTF-8?q?=E5=86=85=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../compiler/parser/context/TokenStream.java | 82 ++++++++++++++-----
.../compiler/parser/core/ParserEngine.java | 6 +-
.../expression/PrattExpressionParser.java | 2 +-
3 files changed, 65 insertions(+), 25 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
index 2788300..c064476 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
@@ -6,37 +6,49 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
import java.util.List;
/**
- * {@code TokenStream} 封装了一个 Token 列表并维护当前解析位置,
- * 是语法分析器读取词法单元的核心工具类。
- *
- * 提供前瞻(peek)、消费(next)、匹配(match)、断言(expect)等常用操作,
- * 支持前向查看和异常处理,适用于递归下降解析等常见语法构建策略。
- *
+ * {@code TokenStream} 封装了一个 Token 列表并维护当前解析位置,是语法分析器读取词法单元的核心工具类。
+ *
+ * 提供前瞻(peek)、消费(next)、匹配(match)、断言(expect)等常用操作,
+ * 支持前向查看和异常处理,适用于递归下降解析等常见语法构建策略。
+ *
*/
public class TokenStream {
- /** 源 Token 列表 */
+ /**
+ * 源 Token 列表。
+ */
private final List tokens;
- /** 当前解析位置索引 */
+ /**
+ * 当前解析位置索引。
+ */
private int pos = 0;
/**
* 使用 Token 列表构造 TokenStream。
*
* @param tokens 由词法分析器产生的 Token 集合
+ * @throws NullPointerException 如果 tokens 为 null
*/
public TokenStream(List tokens) {
+ if (tokens == null) {
+ throw new NullPointerException("Token list cannot be null.");
+ }
this.tokens = tokens;
}
/**
* 向前查看指定偏移量处的 Token(不移动位置)。
+ * 会在 offset==0 时自动跳过当前位置的所有注释(COMMENT)token。
*
- * @param offset 相对当前位置的偏移量(0 表示当前)
+ * @param offset 相对当前位置的偏移量(0 表示当前 token)
* @return 指定位置的 Token;若越界则返回自动构造的 EOF Token
*/
public Token peek(int offset) {
+ // 只在 offset==0 时跳注释,向前多步 peek 由调用方控制
+ if (offset == 0) {
+ skipTrivia();
+ }
int idx = pos + offset;
if (idx >= tokens.size()) {
return Token.eof(tokens.size() + 1);
@@ -47,28 +59,30 @@ public class TokenStream {
/**
* 查看当前位置的 Token,等效于 {@code peek(0)}。
*
- * @return 当前 Token
+ * @return 当前有效 Token(已跳过注释)
*/
public Token peek() {
+ skipTrivia();
return peek(0);
}
/**
- * 消费当前位置的 Token 并返回,位置前移。
+ * 消费当前位置的 Token 并返回,位置前移。注释 token 会被自动跳过。
*
- * @return 当前 Token
+ * @return 被消费的有效 Token(已跳过注释)
*/
public Token next() {
- Token t = peek();
- pos++;
+ Token t = peek(); // peek() 已跳过注释
+ pos++; // 指针指向下一个 raw token
+ skipTrivia(); // 立即吞掉紧随其后的注释(若有)
return t;
}
/**
- * 匹配当前 Token 的词素与指定字符串,若匹配则消费。
+ * 匹配当前 Token 的词素与指定字符串,若匹配则消费该 token 并前移指针。
*
- * @param lexeme 待匹配词素
- * @return 若成功匹配则返回 true
+ * @param lexeme 待匹配的词素字符串
+ * @return 匹配成功返回 true,否则返回 false
*/
public boolean match(String lexeme) {
if (peek().getLexeme().equals(lexeme)) {
@@ -80,6 +94,7 @@ public class TokenStream {
/**
* 断言当前 Token 的词素与指定值相符,否则抛出 {@link ParseException}。
+ * 匹配成功会消费该 token 并前移指针。
*
* @param lexeme 期望的词素值
* @return 匹配成功的 Token
@@ -98,6 +113,7 @@ public class TokenStream {
/**
* 断言当前 Token 类型为指定类型,否则抛出 {@link ParseException}。
+ * 匹配成功会消费该 token 并前移指针。
*
* @param type 期望的 Token 类型
* @return 匹配成功的 Token
@@ -115,13 +131,37 @@ public class TokenStream {
}
/**
- * 判断是否“已经”到达 EOF。
+ * 判断是否“已经”到达文件末尾(EOF)。
*
- * @return 若当前位置 Token 为 EOF,则返回 true,否则 false
+ * @return 若当前位置 Token 为 EOF,则返回 true,否则返回 false
*/
public boolean isAtEnd() {
return peek().getType() == TokenType.EOF;
}
-
-}
\ No newline at end of file
+ /**
+ * 跳过所有连续的注释(COMMENT)token。
+ *
+ *
+ * 此方法会检查当前指针 pos 所指向的 token,
+ * 如果其类型为 TokenType.COMMENT,则直接将指针递增,
+ * 直到遇到非 COMMENT 类型或到达 token 列表末尾。
+ *
+ *
+ *
+ * 注意:此方法只会跳过注释 ,不会递归或调用任何
+ * 会产生递归的方法(如 peek()/next()),以避免堆栈溢出。
+ *
+ *
+ *
+ * 使用场景:词法分析产物中允许出现注释 token,语法分析时需要自动跳过它们,
+ * 保证 parser 只处理有效语法 token。
+ *
+ */
+ private void skipTrivia() {
+ while (pos < tokens.size()
+ && tokens.get(pos).getType() == TokenType.COMMENT) {
+ pos++; // 直接跳过 COMMENT 类型
+ }
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
index 14dc783..7d0854f 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
@@ -17,7 +17,7 @@ public record ParserEngine(ParserContext ctx) {
List errs = new ArrayList<>();
TokenStream ts = ctx.getTokens();
- while (!ts.isAtEnd()) {
+ while (ts.isAtEnd()) {
// 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
@@ -46,7 +46,7 @@ public record ParserEngine(ParserContext ctx) {
* 错误同步:跳到下一行或下一个已注册顶层关键字
*/
private void synchronize(TokenStream ts) {
- while (!ts.isAtEnd()) {
+ while (ts.isAtEnd()) {
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
break;
@@ -57,7 +57,7 @@ public record ParserEngine(ParserContext ctx) {
ts.next();
}
// 连续空行全部吃掉
- while (!ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
+ while (ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
index c5529d4..4f79274 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
@@ -92,7 +92,7 @@ public class PrattExpressionParser implements ExpressionParser {
ExpressionNode left = prefix.parse(ctx, token);
- while (!ctx.getTokens().isAtEnd()
+ while (ctx.getTokens().isAtEnd()
&& prec.ordinal() < nextPrecedence(ctx)) {
String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex);
From 0a218f995e3f94a63a34eefbaf5867eff5aa0a2d Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 11:07:30 +0800
Subject: [PATCH 28/61] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=BE=AA?=
=?UTF-8?q?=E7=8E=AF=E6=9D=A1=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/jcnc/snow/compiler/parser/context/TokenStream.java | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
index c064476..0f28737 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
@@ -10,7 +10,6 @@ import java.util.List;
*
* 提供前瞻(peek)、消费(next)、匹配(match)、断言(expect)等常用操作,
* 支持前向查看和异常处理,适用于递归下降解析等常见语法构建策略。
- *
*/
public class TokenStream {
@@ -136,7 +135,7 @@ public class TokenStream {
* @return 若当前位置 Token 为 EOF,则返回 true,否则返回 false
*/
public boolean isAtEnd() {
- return peek().getType() == TokenType.EOF;
+ return peek().getType() != TokenType.EOF;
}
/**
From 3bff750fc9dcbe0a3380415a8fd98fae9d8494c0 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 11:17:49 +0800
Subject: [PATCH 29/61] =?UTF-8?q?fix:=20=20NumberTokenScanner.java=20?=
=?UTF-8?q?=E4=B8=AD=E4=BF=AE=E5=A4=8D=E4=BA=86=20=E2=80=9C=E6=95=B0?=
=?UTF-8?q?=E5=AD=97=E5=AD=97=E9=9D=A2=E9=87=8F=E4=B8=8E=E7=B1=BB=E5=9E=8B?=
=?UTF-8?q?=E5=90=8E=E7=BC=80=E4=B9=8B=E9=97=B4=E4=B8=8D=E5=85=81=E8=AE=B8?=
=?UTF-8?q?=E6=9C=89=E7=A9=BA=E7=99=BD=E7=AC=A6=E2=80=9D=20=E7=9A=84?=
=?UTF-8?q?=E8=AF=AF=E5=88=A4=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../lexer/scanners/NumberTokenScanner.java | 28 +++++++------------
1 file changed, 10 insertions(+), 18 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
index 11eefa5..1a9b8bb 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
@@ -25,9 +25,6 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
* |
* v
* DEC_POINT --digit--> FRAC_PART
- * |
- * v
- * else--> END
*
* 状态说明:
*
@@ -139,17 +136,21 @@ public class NumberTokenScanner extends AbstractTokenScanner {
else if (Character.isLetter(next)) {
throw new LexicalException("未知的数字类型后缀 '" + next + "'", line, col);
}
- // 2‑C. 数字后出现空白 + 字母(如 3 L)
+ // 2‑C. 数字后出现空白 + 类型后缀(如 3 f) —— 不允许
else if (Character.isWhitespace(next) && next != '\n') {
+ // 允许数字后与普通标识符/关键字间存在空白;
+ // 仅当空白后的首个非空字符是合法的类型后缀时才报错。
int off = 1;
char look;
+ // 跳过任意空白(不含换行)
while (true) {
look = ctx.peekAhead(off);
if (look == '\n' || look == '\0') break; // 行尾或 EOF
if (!Character.isWhitespace(look)) break;
off++;
}
- if (Character.isLetter(look)) {
+ // 如果紧跟类型后缀字符,中间存在空白则视为非法
+ if (SUFFIX_CHARS.indexOf(look) >= 0) {
throw new LexicalException("数字字面量与类型后缀之间不允许有空白符", line, col);
}
}
@@ -162,24 +163,15 @@ public class NumberTokenScanner extends AbstractTokenScanner {
/**
* FSM 内部状态。
- * 每次读取一个字符后,根据“当前状态 + 当前字符”决定转移。
*/
private enum State {
- /**
- * 整数部分(尚未读到小数点)
- */
+ /** 整数部分(小数点左侧) */
INT_PART,
- /**
- * 已读到小数点,但还未读到第一位小数数字
- */
+ /** 已读到小数点,但还未读到第一位小数数字 */
DEC_POINT,
- /**
- * 小数部分(小数点右侧)
- */
+ /** 小数部分(小数点右侧) */
FRAC_PART,
- /**
- * 主体结束,准备处理后缀或交还控制权
- */
+ /** 主体结束,准备处理后缀或交还控制权 */
END
}
}
From 114958d99237b364eeeb94e919e6978c03a33ab5 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 11:28:42 +0800
Subject: [PATCH 30/61] =?UTF-8?q?chore:=20IDEA=20=E9=85=8D=E7=BD=AE?=
=?UTF-8?q?=E6=96=87=E4=BB=B6=E4=BF=AE=E6=94=B9,=E8=AE=A9.water=E6=96=87?=
=?UTF-8?q?=E4=BB=B6=E8=BF=9B=E5=85=A5target=E6=96=87=E4=BB=B6=E5=A4=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.run/Demo10.run.xml | 2 +-
.run/Demo2.run.xml | 2 +-
.run/Demo3.run.xml | 2 +-
.run/Demo4.run.xml | 2 +-
.run/Demo5.run.xml | 2 +-
.run/Demo6.run.xml | 2 +-
.run/Demo7.run.xml | 2 +-
.run/Demo8.run.xml | 2 +-
.run/Demo9.run.xml | 2 +-
9 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/.run/Demo10.run.xml b/.run/Demo10.run.xml
index 3d2b22a..46332d1 100644
--- a/.run/Demo10.run.xml
+++ b/.run/Demo10.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo2.run.xml b/.run/Demo2.run.xml
index 0711740..464e671 100644
--- a/.run/Demo2.run.xml
+++ b/.run/Demo2.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo3.run.xml b/.run/Demo3.run.xml
index a2b72da..846b27a 100644
--- a/.run/Demo3.run.xml
+++ b/.run/Demo3.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo4.run.xml b/.run/Demo4.run.xml
index 491197e..8f5ba76 100644
--- a/.run/Demo4.run.xml
+++ b/.run/Demo4.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo5.run.xml b/.run/Demo5.run.xml
index d3f69a3..50e1218 100644
--- a/.run/Demo5.run.xml
+++ b/.run/Demo5.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo6.run.xml b/.run/Demo6.run.xml
index b7eb743..8d0f410 100644
--- a/.run/Demo6.run.xml
+++ b/.run/Demo6.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo7.run.xml b/.run/Demo7.run.xml
index afa8cd9..2a2132e 100644
--- a/.run/Demo7.run.xml
+++ b/.run/Demo7.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo8.run.xml b/.run/Demo8.run.xml
index 8bff503..877a106 100644
--- a/.run/Demo8.run.xml
+++ b/.run/Demo8.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo9.run.xml b/.run/Demo9.run.xml
index 8e63237..764e4f2 100644
--- a/.run/Demo9.run.xml
+++ b/.run/Demo9.run.xml
@@ -3,7 +3,7 @@
-
+
From ded31578d7df8ad468c213d60e64d08bf7fc743c Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 14:52:33 +0800
Subject: [PATCH 31/61] =?UTF-8?q?refactor:=20CommentTokenScanner=20?=
=?UTF-8?q?=E9=87=8D=E6=9E=84=E4=B8=BA=E7=8A=B6=E6=80=81=E6=9C=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../lexer/scanners/CommentTokenScanner.java | 86 ++++++++++++-------
1 file changed, 53 insertions(+), 33 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java
index 328dee7..90204ce 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java
@@ -11,11 +11,10 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
* 负责将源码中的两种注释形式切分为 {@link TokenType#COMMENT COMMENT} token:
*
* 单行注释:以 {@code //} 开头,直至行尾或文件末尾。
- * 多行注释:以 {@code /*} 开头,以 */ 结束,可跨多行。
+ * 多行注释:以 {@code /*} 开头,以 */ 结束,可跨多行。
*
*
- * 本扫描器遵循“发现即捕获”原则:
- * 注释文本被完整保留在 Token 中,供后续的文档提取、源映射等分析使用。
+ * 本扫描器遵循“发现即捕获”原则:注释文本被完整保留在 Token 中,供后续的文档提取、源映射等分析使用。
*
* 错误处理策略
*
@@ -44,43 +43,64 @@ public class CommentTokenScanner extends AbstractTokenScanner {
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
StringBuilder literal = new StringBuilder();
+ State currentState = State.INITIAL;
- /*
- * 1. 读取注释起始符
- * - 已由 canHandle 保证当前位置一定是 '/'
- */
+ // 读取注释起始符
literal.append(ctx.advance()); // 消费首个 '/'
- // -------- 单行注释 (//) --------
- if (ctx.match('/')) {
- literal.append('/');
- while (!ctx.isAtEnd() && ctx.peek() != '\n') {
- literal.append(ctx.advance());
- }
- // 行尾或文件尾时退出,换行符留给上层扫描器处理。
- }
- // -------- 多行注释 (/* ... */) --------
- else if (ctx.match('*')) {
- literal.append('*');
- boolean terminated = false;
- while (!ctx.isAtEnd()) {
- char ch = ctx.advance();
- literal.append(ch);
- if (ch == '*' && ctx.peek() == '/') {
- literal.append(ctx.advance()); // 追加 '/'
- terminated = true;
+ while (!ctx.isAtEnd()) {
+ switch (currentState) {
+ case INITIAL:
+ if (ctx.match('/')) {
+ literal.append('/');
+ currentState = State.SINGLE_LINE;
+ } else if (ctx.match('*')) {
+ literal.append('*');
+ currentState = State.MULTI_LINE;
+ }
break;
- }
- }
- if (!terminated) {
- // 文件结束仍未闭合,抛 LexicalException
- throw new LexicalException("未终止的多行注释", line, col);
+
+ case SINGLE_LINE:
+ // 单行注释处理:读取直到行尾
+ if (ctx.isAtEnd() || ctx.peek() == '\n') {
+ // 如果遇到换行符,停止读取并返回注释内容
+ return new Token(TokenType.COMMENT, literal.toString(), line, col);
+ } else {
+ literal.append(ctx.advance()); // 继续读取注释内容
+ }
+ break;
+
+
+ case MULTI_LINE:
+ // 多行注释处理
+ char ch = ctx.advance();
+ literal.append(ch);
+ if (ch == '*' && ctx.peek() == '/') {
+ literal.append(ctx.advance()); // 追加 '/'
+ currentState = State.MULTI_LINE_END;
+ }
+ break;
+
+ case MULTI_LINE_END:
+ // 已经读取了闭合的 "*/"
+ return new Token(TokenType.COMMENT, literal.toString(), line, col);
}
}
- /*
- * 2. 生成并返回 Token
- */
+ // 如果未终止的多行注释,抛出异常
+ if (currentState == State.MULTI_LINE) {
+ throw new LexicalException("未终止的多行注释", line, col);
+ }
+
+ // 在正常情况下返回生成的注释 Token
return new Token(TokenType.COMMENT, literal.toString(), line, col);
}
+
+ // 定义状态
+ private enum State {
+ INITIAL, // 初始状态
+ SINGLE_LINE, // 单行注释状态
+ MULTI_LINE, // 多行注释状态
+ MULTI_LINE_END // 多行注释结束状态
+ }
}
From b43245b1f58814937abac26a9ee1a31bf38f8485 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 17:02:01 +0800
Subject: [PATCH 32/61] =?UTF-8?q?refactor:=20IdentifierTokenScanner=20?=
=?UTF-8?q?=E9=87=8D=E6=9E=84=E4=B8=BA=E7=8A=B6=E6=80=81=E6=9C=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../scanners/IdentifierTokenScanner.java | 82 ++++++++++++++-----
1 file changed, 63 insertions(+), 19 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java
index 633d834..1e18cbb 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java
@@ -1,30 +1,34 @@
package org.jcnc.snow.compiler.lexer.scanners;
import org.jcnc.snow.compiler.lexer.core.LexerContext;
+import org.jcnc.snow.compiler.lexer.core.LexicalException;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenFactory;
+import org.jcnc.snow.compiler.lexer.token.TokenType;
/**
- * 标识符扫描器:处理标识符的识别,如变量名、函数名等。
- *
- * 识别规则如下:
+ * {@code IdentifierTokenScanner} —— 标识符扫描器,负责识别源代码中的标识符(如变量名、函数名等)。
+ *
+ *
标识符的识别遵循以下规则:
*
- * 必须以字母或下划线(_)开头
- * 后续字符可以是字母、数字或下划线
+ * 标识符必须以字母(A-Z,a-z)或下划线(_)开头。
+ * 标识符的后续字符可以是字母、数字(0-9)或下划线。
*
- *
- * 扫描完成后会调用 {@link TokenFactory} 自动判断是否为关键字,
- * 并返回对应类型的 {@link Token}。
+ *
+ *
在扫描过程中,标识符会被处理为一个 {@link Token} 对象。如果该标识符是一个关键字,
+ * 扫描器会通过 {@link TokenFactory} 自动识别并返回相应的 {@link TokenType}。
+ *
+ * 本扫描器实现了一个有限状态机(FSM),它能够在不同状态之间转换,确保标识符的正确识别。
*/
public class IdentifierTokenScanner extends AbstractTokenScanner {
/**
- * 判断是否可以处理当前位置的字符。
- * 如果字符为字母或下划线,则认为是标识符的起始。
+ * 判断当前字符是否可以作为标识符的起始字符。
+ * 如果字符为字母或下划线,则认为是标识符的开始。
*
* @param c 当前字符
* @param ctx 当前词法上下文
- * @return 如果是标识符起始字符,则返回 true
+ * @return 如果字符是标识符的起始字符,则返回 {@code true};否则返回 {@code false}。
*/
@Override
public boolean canHandle(char c, LexerContext ctx) {
@@ -32,17 +36,57 @@ public class IdentifierTokenScanner extends AbstractTokenScanner {
}
/**
- * 执行标识符的扫描逻辑。
- * 连续读取满足标识符规则的字符序列,交由 {@code TokenFactory} 创建对应的 Token。
+ * 执行标识符扫描。
+ * 使用状态机模式扫描标识符。首先从初始状态开始,读取标识符的起始字符(字母或下划线)。
+ * 然后,进入标识符状态,继续读取标识符字符(字母、数字或下划线)。一旦遇到不符合标识符规则的字符,
+ * 标识符扫描结束,返回一个 {@link Token}。
*
- * @param ctx 词法上下文
- * @param line 当前行号
- * @param col 当前列号
- * @return 标识符或关键字类型的 Token
+ * @param ctx 词法上下文,用于获取字符流
+ * @param line 当前行号(1 基)
+ * @param col 当前列号(1 基)
+ * @return 返回一个包含标识符或关键字的 {@link Token} 对象。
+ * @throws LexicalException 如果标识符以非法字符(如点号)开头,则抛出异常
*/
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
- String lexeme = readWhile(ctx, ch -> Character.isLetterOrDigit(ch) || ch == '_');
- return TokenFactory.create(lexeme, line, col);
+ StringBuilder lexeme = new StringBuilder(); // 用于构建标识符的字符串
+ State currentState = State.INITIAL; // 初始状态
+
+ // 遍历字符流,直到遇到不合法的字符或流结束
+ while (!ctx.isAtEnd()) {
+ char currentChar = ctx.peek(); // 获取当前字符
+ switch (currentState) {
+ case INITIAL:
+ // 初始状态,标识符开始
+ if (Character.isLetter(currentChar) || currentChar == '_') {
+ lexeme.append(ctx.advance()); // 接受当前字符
+ currentState = State.IDENTIFIER; // 进入标识符状态
+ } else {
+ return null; // 当前字符不符合标识符的规则,返回 null
+ }
+ break;
+
+ case IDENTIFIER:
+ // 标识符状态,继续读取合法标识符字符
+ if (Character.isLetterOrDigit(currentChar) || currentChar == '_') {
+ lexeme.append(ctx.advance()); // 继续接受合法字符
+ } else {
+ // 当前字符不符合标识符的规则,标识符结束,返回 token
+ return TokenFactory.create(lexeme.toString(), line, col);
+ }
+ break;
+ }
+ }
+
+ // 如果字符流结束,返回标识符 token
+ return TokenFactory.create(lexeme.toString(), line, col);
+ }
+
+ /**
+ * 枚举类型表示标识符扫描的状态。
+ */
+ private enum State {
+ INITIAL, // 初始状态,等待标识符的开始
+ IDENTIFIER // 标识符状态,继续读取标识符字符
}
}
From e83244df61f3791da4e24b866e5f40b7d496acc1 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 17:02:35 +0800
Subject: [PATCH 33/61] =?UTF-8?q?feat:=20LexerEngine=20=E5=A2=9E=E5=8A=A0?=
=?UTF-8?q?=E5=90=8E=E7=BD=AE=E6=95=B4=E4=BD=93=E6=A0=A1=E9=AA=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../snow/compiler/lexer/core/LexerEngine.java | 221 +++++++++---------
1 file changed, 106 insertions(+), 115 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
index 5df39fa..6d6bb2f 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
@@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.lexer.core;
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
import org.jcnc.snow.compiler.lexer.scanners.*;
import org.jcnc.snow.compiler.lexer.token.Token;
+import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.lexer.utils.TokenPrinter;
import java.io.File;
@@ -10,154 +11,144 @@ import java.util.ArrayList;
import java.util.List;
/**
- * {@code LexerEngine} 是编译器前端的词法分析器核心实现。
- *
- * 负责将源代码字符串按顺序扫描并转换为一系列 {@link Token} 实例,
- * 每个 Token 表示语法上可识别的最小单位(如标识符、关键字、常量、运算符等)。
- *
- * 分析流程通过注册多个 {@link TokenScanner} 扫描器实现类型识别,
- * 并由 {@link LexerContext} 提供字符流与位置信息支持。
- * 支持文件名传递,遇到非法字符时会以“文件名:行:列:错误信息”输出简洁诊断。
- *
+ * Snow 语言词法分析器核心实现。
+ * 采用“先扫描 → 后批量校验 → 统一报告 ”策略:
+ *
+ * {@link #scanAllTokens()}— 用扫描器链把字符流拆成 {@link Token}
+ * {@link #validateTokens()}— 基于 token 序列做轻量上下文校验
+ * {@link #report(List)}— 一次性输出所有词法错误
+ *
*/
public class LexerEngine {
- /**
- * 扫描生成的 Token 序列(包含文件结束符 EOF)。
- * 每个 Token 表示源代码中的一个词法单元。
- */
- private final List tokens = new ArrayList<>();
+
+ private final List tokens = new ArrayList<>(); // 扫描结果
+ private final List errors = new ArrayList<>();
+ private final String absPath; // 绝对路径
+ private final LexerContext context; // 字符流
+ private final List scanners; // 扫描器链
/**
- * 当前源文件的绝对路径,用于错误信息定位。
- */
- private final String absPath;
-
- /**
- * 词法上下文,负责字符流读取与位置信息维护。
- */
- private final LexerContext context;
-
- /**
- * Token 扫描器集合,按优先级顺序排列,
- * 用于识别不同类别的 Token(如空白、注释、数字、标识符等)。
- */
- private final List scanners;
-
- /**
- * 词法分析过程中收集到的全部词法错误。
- */
- private final List errors = new ArrayList<>();
-
- /**
- * 构造词法分析器,并指定源文件名(用于诊断信息)。
- * 构造时立即进行全量扫描,扫描结束后打印所有 Token 并报告词法错误。
- *
+ * 创建并立即执行扫描-校验-报告流程。
* @param source 源代码文本
- * @param sourceName 文件名或来源描述(如"Main.snow")
+ * @param sourceName 文件名(诊断用)
*/
public LexerEngine(String source, String sourceName) {
- this.absPath = new File(sourceName).getAbsolutePath();
- this.context = new LexerContext(source);
+ this.absPath = new File(sourceName).getAbsolutePath();
+ this.context = new LexerContext(source);
this.scanners = List.of(
- new WhitespaceTokenScanner(), // 跳过空格、制表符等
- new NewlineTokenScanner(), // 处理换行符,生成 NEWLINE Token
- new CommentTokenScanner(), // 处理单行/多行注释
- new NumberTokenScanner(), // 识别整数与浮点数字面量
- new IdentifierTokenScanner(), // 识别标识符和关键字
- new StringTokenScanner(), // 处理字符串常量
- new OperatorTokenScanner(), // 识别运算符
- new SymbolTokenScanner(), // 识别括号、分号等符号
- new UnknownTokenScanner() // 捕捉无法识别的字符,最后兜底
+ new WhitespaceTokenScanner(),
+ new NewlineTokenScanner(),
+ new CommentTokenScanner(),
+ new NumberTokenScanner(),
+ new IdentifierTokenScanner(),
+ new StringTokenScanner(),
+ new OperatorTokenScanner(),
+ new SymbolTokenScanner(),
+ new UnknownTokenScanner()
);
- // 主扫描流程,遇到非法字符立即输出错误并终止进程
- try {
- scanAllTokens();
- } catch (LexicalException le) {
- // 输出:绝对路径: 行 x, 列 y: 错误信息
- System.err.printf(
- "%s: 行 %d, 列 %d: %s%n",
- absPath,
- le.getLine(),
- le.getColumn(),
- le.getReason()
- );
- System.exit(65); // 65 = EX_DATAERR
- }
- TokenPrinter.print(this.tokens);
- LexerEngine.report(this.getErrors());
+ /* 1. 扫描 */
+ scanAllTokens();
+ /* 2. 后置整体校验 */
+ validateTokens();
+ /* 3. 打印 token */
+ TokenPrinter.print(tokens);
+ /* 4. 统一报告错误 */
+ report(errors);
if (!errors.isEmpty()) {
- throw new LexicalException("Lexing failed with " + errors.size() + " error(s).", this.context.getLine(), this.context.getCol());
+ throw new LexicalException(
+ "Lexing failed with " + errors.size() + " error(s).",
+ context.getLine(), context.getCol()
+ );
}
}
- /**
- * 静态报告方法。
- *
- * 打印所有词法分析过程中收集到的错误信息。
- * 如果无错误,输出词法分析通过的提示。
- *
- * @param errors 词法错误列表
- */
public static void report(List errors) {
- if (errors != null && !errors.isEmpty()) {
- System.err.println("\n词法分析发现 " + errors.size() + " 个错误:");
- errors.forEach(err -> System.err.println(" " + err));
- } else {
+ if (errors == null || errors.isEmpty()) {
System.out.println("\n## 词法分析通过,没有发现错误\n");
+ return;
}
+ System.err.println("\n词法分析发现 " + errors.size() + " 个错误:");
+ errors.forEach(e -> System.err.println(" " + e));
}
+ public List getAllTokens() { return List.copyOf(tokens); }
+ public List getErrors() { return List.copyOf(errors); }
+
/**
- * 主扫描循环,将源代码转为 Token 序列。
- *
- * 依次尝试每个扫描器,直到找到可处理当前字符的扫描器为止。
- * 扫描到结尾后补充 EOF Token。
- * 若遇到词法异常则收集错误并跳过当前字符,避免死循环。
+ * 逐字符扫描:依次尝试各扫描器;扫描器抛出的
+ * {@link LexicalException} 被捕获并转为 {@link LexicalError}。
*/
private void scanAllTokens() {
while (!context.isAtEnd()) {
- char currentChar = context.peek();
+ char ch = context.peek();
boolean handled = false;
- for (TokenScanner scanner : scanners) {
- if (scanner.canHandle(currentChar, context)) {
- try {
- scanner.handle(context, tokens);
- } catch (LexicalException le) {
- // 收集词法错误,不直接退出
- errors.add(new LexicalError(
- absPath, le.getLine(), le.getColumn(), le.getReason()
- ));
- // 跳过当前字符,防止死循环
- context.advance();
- }
- handled = true;
- break;
+
+ for (TokenScanner s : scanners) {
+ if (!s.canHandle(ch, context)) continue;
+
+ try {
+ s.handle(context, tokens);
+ } catch (LexicalException le) {
+ errors.add(new LexicalError(
+ absPath, le.getLine(), le.getColumn(), le.getReason()
+ ));
+ context.advance(); // 跳过问题字符
}
+ handled = true;
+ break;
}
- if (!handled) {
- // 没有任何扫描器能处理,跳过一个字符防止死循环
- context.advance();
- }
+
+ if (!handled) context.advance(); // 理论不会走到,保险
}
tokens.add(Token.eof(context.getLine()));
}
/**
- * 获取全部 Token(包含 EOF),返回只读列表。
- *
- * @return 词法分析结果 Token 列表
+ * 目前包含三条规则:
+ * 1. Dot-Prefix'.' 不能作标识符前缀
+ * 2. Declare-Ident declare 后必须紧跟合法标识符,并且只能一个
+ * 3. Double-Ident declare 后若出现第二个 IDENTIFIER 视为多余
+ *
发现问题仅写入 {@link #errors},不抛异常。
*/
- public List getAllTokens() {
- return List.copyOf(tokens);
+ private void validateTokens() {
+ for (int i = 0; i < tokens.size(); i++) {
+ Token tok = tokens.get(i);
+
+ /* ---------- declare 规则 ---------- */
+ if (tok.getType() == TokenType.KEYWORD
+ && "declare".equalsIgnoreCase(tok.getLexeme())) {
+
+ // 第一个非 NEWLINE token
+ Token id1 = findNextNonNewline(i);
+ if (id1 == null || id1.getType() != TokenType.IDENTIFIER) {
+ errors.add(err(
+ (id1 == null ? tok : id1),
+ "declare 后必须跟合法标识符 (以字母或 '_' 开头)"
+ ));
+ continue; // 若首标识符就错,后续检查可略
+ }
+
+ // 检查是否有第二个 IDENTIFIER
+ Token id2 = findNextNonNewline(tokens.indexOf(id1));
+ if (id2 != null && id2.getType() == TokenType.IDENTIFIER) {
+ errors.add(err(id2, "declare 声明中出现多余的标识符"));
+ }
+ }
+ }
}
- /**
- * 返回全部词法错误(返回只读列表)。
- *
- * @return 词法错误列表
- */
- public List getErrors() {
- return List.copyOf(errors);
+ /** index 右侧最近非 NEWLINE token;无则 null */
+ private Token findNextNonNewline(int index) {
+ for (int j = index + 1; j < tokens.size(); j++) {
+ Token t = tokens.get(j);
+ if (t.getType() != TokenType.NEWLINE) return t;
+ }
+ return null;
+ }
+
+ /** 构造统一的 LexicalError */
+ private LexicalError err(Token t, String msg) {
+ return new LexicalError(absPath, t.getLine(), t.getCol(), "非法的标记序列:" + msg);
}
}
From 51f5ba9884f3c0365c1967840c4a79ec8eb0b4ea Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 17:09:52 +0800
Subject: [PATCH 34/61] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0demo11?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
playground/Demo11/Main.snow | 1 -
1 file changed, 1 deletion(-)
diff --git a/playground/Demo11/Main.snow b/playground/Demo11/Main.snow
index d9a3e6e..3dd121d 100644
--- a/playground/Demo11/Main.snow
+++ b/playground/Demo11/Main.snow
@@ -1,7 +1,6 @@
function: main
return_type: int
body:
- 3 L
return 65537
end body
end function
\ No newline at end of file
From dbc3ea0a33dd6aecca66d2d8083b516f366d1452 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 17:10:11 +0800
Subject: [PATCH 35/61] =?UTF-8?q?feat:=20NewlineTokenScanner=20=E9=87=8D?=
=?UTF-8?q?=E6=9E=84=E4=B8=BA=E7=8A=B6=E6=80=81=E6=9C=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../lexer/scanners/NewlineTokenScanner.java | 27 ++++++++++++++++---
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NewlineTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NewlineTokenScanner.java
index daea57c..0f63e70 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NewlineTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NewlineTokenScanner.java
@@ -7,10 +7,19 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
/**
* 换行符扫描器:将源代码中的换行符(\n)识别为 {@code NEWLINE} 类型的 Token。
*
- * 通常用于记录行的分界,辅助语法分析阶段进行行敏感的判断或保持结构清晰。
+ * 用于记录行的分界,辅助语法分析阶段进行行敏感的判断或保持结构清晰。
*/
public class NewlineTokenScanner extends AbstractTokenScanner {
+ // 定义状态枚举
+ private enum State {
+ INITIAL,
+ NEWLINE
+ }
+
+ // 当前状态
+ private State currentState = State.INITIAL;
+
/**
* 判断是否可以处理当前位置的字符。
*
当字符为换行符(\n)时返回 true。
@@ -21,7 +30,8 @@ public class NewlineTokenScanner extends AbstractTokenScanner {
*/
@Override
public boolean canHandle(char c, LexerContext ctx) {
- return c == '\n';
+ // 只有当处于 INITIAL 状态,并且遇到换行符时,才可以处理
+ return currentState == State.INITIAL && c == '\n';
}
/**
@@ -35,7 +45,16 @@ public class NewlineTokenScanner extends AbstractTokenScanner {
*/
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
+ // 状态转换为 NEWLINE
+ currentState = State.NEWLINE;
+
+ // 执行换行符扫描,生成 token
ctx.advance();
- return new Token(TokenType.NEWLINE, "\n", line, col);
+ Token newlineToken = new Token(TokenType.NEWLINE, "\n", line, col);
+
+ // 扫描完成后,恢复状态为 INITIAL
+ currentState = State.INITIAL;
+
+ return newlineToken;
}
-}
\ No newline at end of file
+}
From 367ae8653ed43af506b22ca5ba9efad165529abf Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 17:14:40 +0800
Subject: [PATCH 36/61] =?UTF-8?q?feat:=20OperatorTokenScanner=20=E9=87=8D?=
=?UTF-8?q?=E6=9E=84=E4=B8=BA=E7=8A=B6=E6=80=81=E6=9C=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../lexer/scanners/OperatorTokenScanner.java | 56 ++++++++++---------
1 file changed, 30 insertions(+), 26 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java
index ec2f2bf..951b1c5 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java
@@ -45,80 +45,84 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
char c = ctx.advance();
- String lexeme;
- TokenType type;
+ String lexeme = String.valueOf(c);
+ TokenType type = TokenType.UNKNOWN;
+
+ // 当前状态
+ State currentState = State.OPERATOR;
switch (c) {
case '=':
if (ctx.match('=')) {
lexeme = "==";
- type = TokenType.DOUBLE_EQUALS;
+ type = TokenType.DOUBLE_EQUALS;
} else {
- lexeme = "=";
- type = TokenType.EQUALS;
+ type = TokenType.EQUALS;
}
break;
case '!':
if (ctx.match('=')) {
lexeme = "!=";
- type = TokenType.NOT_EQUALS;
+ type = TokenType.NOT_EQUALS;
} else {
- lexeme = "!";
- type = TokenType.NOT;
+ type = TokenType.NOT;
}
break;
case '>':
if (ctx.match('=')) {
lexeme = ">=";
- type = TokenType.GREATER_EQUAL;
+ type = TokenType.GREATER_EQUAL;
} else {
- lexeme = ">";
- type = TokenType.GREATER_THAN;
+ type = TokenType.GREATER_THAN;
}
break;
case '<':
if (ctx.match('=')) {
lexeme = "<=";
- type = TokenType.LESS_EQUAL;
+ type = TokenType.LESS_EQUAL;
} else {
- lexeme = "<";
- type = TokenType.LESS_THAN;
+ type = TokenType.LESS_THAN;
}
break;
case '%':
- lexeme = "%";
- type = TokenType.MODULO;
+ type = TokenType.MODULO;
break;
case '&':
if (ctx.match('&')) {
lexeme = "&&";
- type = TokenType.AND;
- } else {
- lexeme = "&";
- type = TokenType.UNKNOWN;
+ type = TokenType.AND;
}
break;
case '|':
if (ctx.match('|')) {
lexeme = "||";
- type = TokenType.OR;
- } else {
- lexeme = "|";
- type = TokenType.UNKNOWN;
+ type = TokenType.OR;
}
break;
default:
- lexeme = String.valueOf(c);
- type = TokenType.UNKNOWN;
+ currentState = State.UNKNOWN;
+ break;
+ }
+
+ // 执行完扫描后,重置状态为初始状态
+ if (currentState != State.UNKNOWN) {
+ currentState = State.START;
}
return new Token(type, lexeme, line, col);
}
+
+ // 定义状态枚举
+ private enum State {
+ START, // 初始状态
+ OPERATOR, // 当前字符是运算符的一部分
+ UNKNOWN // 无法识别的状态
+ }
}
From bb4c4f6ce4b49661c9621ca3605b7cf73052ebff Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 1 Jul 2025 17:17:23 +0800
Subject: [PATCH 37/61] =?UTF-8?q?feat:=20StringTokenScanner=20=E9=87=8D?=
=?UTF-8?q?=E6=9E=84=E4=B8=BA=E7=8A=B6=E6=80=81=E6=9C=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../lexer/scanners/StringTokenScanner.java | 44 ++++++++++++++++---
1 file changed, 38 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/StringTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/StringTokenScanner.java
index a8643e4..a610d06 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/StringTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/StringTokenScanner.java
@@ -29,7 +29,7 @@ public class StringTokenScanner extends AbstractTokenScanner {
*/
@Override
public boolean canHandle(char c, LexerContext ctx) {
- return c == '"';
+ return c == '"'; // 只处理字符串开始符号
}
/**
@@ -45,19 +45,51 @@ public class StringTokenScanner extends AbstractTokenScanner {
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
StringBuilder sb = new StringBuilder();
- sb.append(ctx.advance()); // 起始双引号
+ // 当前状态
+ State currentState = State.START; // 初始状态为开始扫描字符串
+ // 开始扫描字符串
while (!ctx.isAtEnd()) {
char c = ctx.advance();
sb.append(c);
- if (c == '\\') {
- sb.append(ctx.advance()); // 添加转义字符后的实际字符
- } else if (c == '"') {
- break;
+ switch (currentState) {
+ case START:
+ // 开始状态,遇到第一个双引号
+ currentState = State.STRING;
+ break;
+
+ case STRING:
+ if (c == '\\') {
+ // 遇到转义字符,进入 ESCAPE 状态
+ currentState = State.ESCAPE;
+ } else if (c == '"') {
+ // 遇到结束的双引号,结束扫描
+ currentState = State.END;
+ }
+ break;
+
+ case ESCAPE:
+ // 在转义状态下,处理转义字符
+ sb.append(ctx.advance()); // 加入转义字符后的字符
+ currentState = State.STRING; // 返回字符串状态
+ break;
+
+ case END:
+ // 结束状态,字符串扫描完成
+ return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
}
}
+ // 如果没有结束的双引号,则表示错误,或者未正确处理
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
}
+
+ // 定义状态枚举
+ private enum State {
+ START, // 开始状态,寻找字符串的开始双引号
+ STRING, // 字符串扫描状态,处理字符串中的字符
+ ESCAPE, // 处理转义字符状态
+ END // 字符串结束状态
+ }
}
From 55ab421d88c4b308b600b6cb8717be813a082832 Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Wed, 2 Jul 2025 22:10:16 +0800
Subject: [PATCH 38/61] =?UTF-8?q?fix:=20AOT=20=E7=BC=96=E8=AF=91=E5=90=8E?=
=?UTF-8?q?=E6=97=A0=E6=B3=95=E5=AE=9A=E4=BD=8D=E8=AF=AD=E4=B9=89=E9=94=99?=
=?UTF-8?q?=E8=AF=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../compiler/ir/builder/StatementBuilder.java | 4 +--
.../compiler/ir/utils/ComparisonUtils.java | 2 +-
.../compiler/ir/utils/ExpressionUtils.java | 2 +-
.../compiler/parser/ast/BoolLiteralNode.java | 16 +++++++++---
.../parser/ast/NumberLiteralNode.java | 12 +++++++--
.../parser/ast/StringLiteralNode.java | 12 +++++++--
.../snow/compiler/parser/ast/base/Node.java | 25 +++++++++++++++++--
.../expression/BoolLiteralParselet.java | 2 +-
.../expression/NumberLiteralParselet.java | 2 +-
.../expression/StringLiteralParselet.java | 2 +-
.../compiler/parser/function/ASTPrinter.java | 4 +--
.../parser/utils/ASTJsonSerializer.java | 8 +++---
.../expression/CallExpressionAnalyzer.java | 6 ++---
.../semantic/error/SemanticError.java | 15 +++--------
14 files changed, 74 insertions(+), 38 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
index f731c57..557634b 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
@@ -66,12 +66,12 @@ public class StatementBuilder {
buildIf(ifNode);
return;
}
- if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, _, _, _)) {
+ if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, int _, int _, String _)) {
// 纯表达式语句,如 foo();
expr.build(exp);
return;
}
- if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, _, _, _)) {
+ if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, int _, int _, String _)) {
// 赋值语句,如 a = b + 1;
final String type = ctx.getScope().lookupType(var);
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/utils/ComparisonUtils.java b/src/main/java/org/jcnc/snow/compiler/ir/utils/ComparisonUtils.java
index 59042e9..08b42b8 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/utils/ComparisonUtils.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/utils/ComparisonUtils.java
@@ -41,7 +41,7 @@ public final class ComparisonUtils {
/* ------------ 内部工具 ------------ */
private static boolean isLongLiteral(ExpressionNode node) {
- if (node instanceof NumberLiteralNode(String value)) {
+ if (node instanceof NumberLiteralNode(String value, int _, int _, String _)) {
return value.endsWith("L") || value.endsWith("l");
}
return false; // 变量暂不处理(后续可扩展符号表查询)
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java b/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java
index 7c93c9a..291b861 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java
@@ -127,7 +127,7 @@ public final class ExpressionUtils {
/** 递归推断单个表达式节点的类型后缀(b/s/i/l/f/d)。 */
private static char typeChar(ExpressionNode node) {
- if (node instanceof NumberLiteralNode(String value)) {
+ if (node instanceof NumberLiteralNode(String value, int _, int _, String _)) {
char last = Character.toLowerCase(value.charAt(value.length() - 1));
return switch (last) {
case 'b','s','i','l','f','d' -> last;
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/BoolLiteralNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/BoolLiteralNode.java
index 15c7088..bb46a30 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/ast/BoolLiteralNode.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/BoolLiteralNode.java
@@ -9,9 +9,17 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* 表达布尔类型的字面量常量(如 "true" 或 "false")。
*
*
- * @param value 字面量的布尔值
+ * @param value 字面量的布尔值
+ * @param line 当前节点所在的行号
+ * @param column 当前节点所在的列号
+ * @param file 当前节点所在的文件
*/
-public record BoolLiteralNode(boolean value) implements ExpressionNode {
+public record BoolLiteralNode(
+ boolean value,
+ int line,
+ int column,
+ String file
+) implements ExpressionNode {
/**
* 使用布尔字面量字符串构造一个 {@code BoolLiteralNode} 实例。
@@ -22,8 +30,8 @@ public record BoolLiteralNode(boolean value) implements ExpressionNode {
*
* @param lexeme 布尔字面量的字符串表示
*/
- public BoolLiteralNode(String lexeme) {
- this(Boolean.parseBoolean(lexeme));
+ public BoolLiteralNode(String lexeme, int line, int column, String file) {
+ this(Boolean.parseBoolean(lexeme), line, column, file);
}
/**
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/NumberLiteralNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/NumberLiteralNode.java
index f30f526..9f253e2 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/ast/NumberLiteralNode.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/NumberLiteralNode.java
@@ -10,9 +10,17 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* 在语义分析或类型推导阶段再行解析为具体数值类型。
*
*
- * @param value 数字字面量的原始字符串表示
+ * @param value 数字字面量的原始字符串表示
+ * @param line 当前节点所在的行号
+ * @param column 当前节点所在的列号
+ * @param file 当前节点所在的文件
*/
-public record NumberLiteralNode(String value) implements ExpressionNode {
+public record NumberLiteralNode(
+ String value,
+ int line,
+ int column,
+ String file
+) implements ExpressionNode {
/**
* 返回数字字面量的字符串形式。
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/StringLiteralNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/StringLiteralNode.java
index b04d561..daa9f42 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/ast/StringLiteralNode.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/StringLiteralNode.java
@@ -9,9 +9,17 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* 节点内部仅保存不带引号的字符串内容,便于后续语义处理或编码。
*
*
- * @param value 字符串常量的内容,原始值中不包含双引号
+ * @param value 字符串常量的内容,原始值中不包含双引号
+ * @param line 当前节点所在的行号
+ * @param column 当前节点所在的列号
+ * @param file 当前节点所在的文件
*/
-public record StringLiteralNode(String value) implements ExpressionNode {
+public record StringLiteralNode(
+ String value,
+ int line,
+ int column,
+ String file
+) implements ExpressionNode {
/**
* 返回字符串字面量的带引号表示,适用于语法树调试或文本输出。
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/base/Node.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/Node.java
index 8c7a8b9..5cba805 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/ast/base/Node.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/Node.java
@@ -3,7 +3,7 @@ package org.jcnc.snow.compiler.parser.ast.base;
/**
* {@code Node} 是抽象语法树(AST)中所有语法节点的统一根接口。
*
- * 作为标记接口(Marker Interface),该接口不定义任何方法,
+ * 作为标记接口(Marker Interface),该接口定义 3 个方法:line()、column() 和 file() 用于定位错误,
* 主要用于统一标识并组织 AST 体系中的各种语法构件节点,包括:
*
*
@@ -15,4 +15,25 @@ package org.jcnc.snow.compiler.parser.ast.base;
* 所有 AST 处理逻辑(如遍历、分析、代码生成)均可基于该接口实现统一调度和类型判定。
*
*/
-public interface Node {}
+public interface Node {
+ /**
+ * 获取当前表达式所在的行号。
+ *
+ * @return 当前表达式的行号。
+ */
+ int line();
+
+ /**
+ * 获取当前表达式所在的列号。
+ *
+ * @return 当前表达式的列号。
+ */
+ int column();
+
+ /**
+ * 获取当前表达式所在的文件名。
+ *
+ * @return 当前表达式所在的文件名。
+ */
+ String file();
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/BoolLiteralParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/BoolLiteralParselet.java
index cb2ede5..cfd49db 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/BoolLiteralParselet.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/BoolLiteralParselet.java
@@ -28,6 +28,6 @@ public class BoolLiteralParselet implements PrefixParselet {
*/
@Override
public ExpressionNode parse(ParserContext ctx, Token token) {
- return new BoolLiteralNode(token.getLexeme());
+ return new BoolLiteralNode(token.getLexeme(), token.getLine(), token.getCol(), ctx.getSourceName());
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/NumberLiteralParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/NumberLiteralParselet.java
index 6ea186f..4b75174 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/NumberLiteralParselet.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/NumberLiteralParselet.java
@@ -24,6 +24,6 @@ public class NumberLiteralParselet implements PrefixParselet {
*/
@Override
public ExpressionNode parse(ParserContext ctx, Token token) {
- return new NumberLiteralNode(token.getLexeme());
+ return new NumberLiteralNode(token.getLexeme(), token.getLine(), token.getCol(), ctx.getSourceName());
}
}
\ No newline at end of file
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/StringLiteralParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/StringLiteralParselet.java
index b73603d..b4eb3b3 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/StringLiteralParselet.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/StringLiteralParselet.java
@@ -27,6 +27,6 @@ public class StringLiteralParselet implements PrefixParselet {
public ExpressionNode parse(ParserContext ctx, Token token) {
String raw = token.getRaw();
String content = raw.substring(1, raw.length() - 1);
- return new StringLiteralNode(content);
+ return new StringLiteralNode(content, token.getLine(), token.getCol(), ctx.getSourceName());
}
}
\ No newline at end of file
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java b/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java
index a45ea72..75ffbca 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java
@@ -67,7 +67,7 @@ public class ASTPrinter {
}
case FunctionNode(
String name, List parameters, String returnType, List body
- , _, _, _
+ , int _, int _, String _
) -> {
System.out.println(pad + "function " + name
+ "(params=" + parameters + ", return=" + returnType + ")");
@@ -82,7 +82,7 @@ public class ASTPrinter {
.orElse("");
System.out.println(pad + "declare " + d.getName() + ":" + d.getType() + init);
}
- case AssignmentNode(String variable, ExpressionNode value, _, int _, String _) ->
+ case AssignmentNode(String variable, ExpressionNode value, int _, int _, String _) ->
System.out.println(pad + variable + " = " + value);
case IfNode(
ExpressionNode condition, List thenBranch, List elseBranch, int _,
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
index db65294..231d9af 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
@@ -194,15 +194,15 @@ public class ASTJsonSerializer {
"operand", exprToMap(operand)
);
// 布尔字面量
- case BoolLiteralNode(boolean value) -> exprMap("BoolLiteral", "value", value);
+ case BoolLiteralNode(boolean value, int _, int _, String _) -> exprMap("BoolLiteral", "value", value);
// 标识符
case IdentifierNode(String name, int _, int _, String _) -> exprMap("Identifier", "name", name);
// 数字字面量
- case NumberLiteralNode(String value) -> exprMap("NumberLiteral", "value", value);
+ case NumberLiteralNode(String value, int _, int _, String _) -> exprMap("NumberLiteral", "value", value);
// 字符串字面量
- case StringLiteralNode(String value) -> exprMap("StringLiteral", "value", value);
+ case StringLiteralNode(String value, int _, int _, String _) -> exprMap("StringLiteral", "value", value);
// 调用表达式
- case CallExpressionNode(ExpressionNode callee, List arguments, _, _, _) -> {
+ case CallExpressionNode(ExpressionNode callee, List arguments, int _, int _, String _) -> {
List args = new ArrayList<>(arguments.size());
for (ExpressionNode arg : arguments) args.add(exprToMap(arg));
yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args);
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java
index 9a6f923..83c331c 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java
@@ -51,8 +51,8 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer
Date: Wed, 2 Jul 2025 22:43:09 +0800
Subject: [PATCH 39/61] =?UTF-8?q?fix:=20CleanTask=20=E5=B0=86=20build=20?=
=?UTF-8?q?=E7=9B=AE=E5=BD=95=E5=88=A0=E9=99=A4=E5=AF=BC=E8=87=B4=E6=97=A0?=
=?UTF-8?q?=E6=B3=95=E5=B0=86=E5=AD=97=E8=8A=82=E7=A0=81=E5=86=99=E5=85=A5?=
=?UTF-8?q?=E7=9B=AE=E6=A0=87=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
index 80700d6..2b6da14 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
@@ -30,8 +30,13 @@ public final class CleanTask implements Task {
*/
@Override
public void run() throws IOException {
- deleteDir(Path.of("build"));
- deleteDir(Path.of("dist"));
+ Path build = Path.of("build");
+ Path dist = Path.of("dist");
+ deleteDir(build);
+ deleteDir(dist);
+ Files.createDirectories(build);
+ Files.createDirectories(dist);
+
System.out.println("[clean] done.");
}
From b730b53f7b643b56853748f5db46ce35091c88c9 Mon Sep 17 00:00:00 2001
From: Luke
Date: Thu, 3 Jul 2025 21:56:33 +0800
Subject: [PATCH 40/61] =?UTF-8?q?style:=20=E4=BF=AE=E5=A4=8D=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E7=BC=A9=E8=BF=9B=E5=BC=82=E5=B8=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/jcnc/snow/compiler/parser/ast/CallExpressionNode.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/CallExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/CallExpressionNode.java
index 22f47bc..d7dbce9 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/ast/CallExpressionNode.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/CallExpressionNode.java
@@ -67,5 +67,7 @@ public record CallExpressionNode(
*
* @return 当前表达式所在的文件名。
*/
- public String file() { return file; }
+ public String file() {
+ return file;
+ }
}
From f1086a1ef925b898bf413fff1403adff881e3859 Mon Sep 17 00:00:00 2001
From: Luke
Date: Thu, 3 Jul 2025 23:49:26 +0800
Subject: [PATCH 41/61] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=20parser=20?=
=?UTF-8?q?=E7=9A=84=E5=BC=82=E5=B8=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../compiler/parser/context/MissingToken.java | 11 ++
.../parser/context/ParseException.java | 21 ++-
.../compiler/parser/context/TokenStream.java | 10 +-
.../parser/context/UnexpectedToken.java | 11 ++
.../parser/context/UnsupportedFeature.java | 11 ++
.../compiler/parser/core/ParserEngine.java | 59 ++++++--
.../expression/PrattExpressionParser.java | 8 +-
.../compiler/parser/module/ModuleParser.java | 5 +-
.../statement/ExpressionStatementParser.java | 5 +-
.../parser/top/ScriptTopLevelParser.java | 3 +-
.../parser/utils/FlexibleSectionParser.java | 5 +-
.../compiler/parser/utils/JSONParser.java | 131 ++++++++++++------
12 files changed, 205 insertions(+), 75 deletions(-)
create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/context/MissingToken.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/context/UnexpectedToken.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/context/UnsupportedFeature.java
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/MissingToken.java b/src/main/java/org/jcnc/snow/compiler/parser/context/MissingToken.java
new file mode 100644
index 0000000..b56ae02
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/MissingToken.java
@@ -0,0 +1,11 @@
+package org.jcnc.snow.compiler.parser.context;
+
+/**
+ * 当语法结构缺失必须出现的 Token 时抛出。
+ */
+public final class MissingToken extends ParseException {
+
+ public MissingToken(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java b/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java
index 0313d7a..0262868 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java
@@ -1,22 +1,19 @@
package org.jcnc.snow.compiler.parser.context;
/**
- * {@code ParseException} 表示语法分析阶段发生的错误。
- *
- * 当语法分析器遇到非法的语法结构或无法继续处理的标记序列时,
- * 应抛出该异常以中断当前解析流程,并向调用方报告错误信息。
- *
- *
- * 该异常通常由 {@code ParserContext} 或各类语法规则处理器主动抛出,
- * 用于提示编译器前端或 IDE 系统进行错误提示与恢复。
- *
+ * {@code ParseException}——语法分析阶段所有错误的基类。
+ *
+ * 声明为 sealed ,仅允许 {@link UnexpectedToken}、
+ * {@link MissingToken}、{@link UnsupportedFeature} 三个受信子类继承,
+ * 以便调用方根据异常类型进行精确处理。
*/
-public class ParseException extends RuntimeException {
+public sealed class ParseException extends RuntimeException
+ permits UnexpectedToken, MissingToken, UnsupportedFeature {
/**
- * 构造一个带有错误描述信息的解析异常实例。
+ * 构造解析异常并附带错误消息。
*
- * @param message 错误描述文本,用于指明具体的语法错误原因
+ * @param message 错误描述
*/
public ParseException(String message) {
super(message);
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
index 0f28737..9169318 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
@@ -31,7 +31,7 @@ public class TokenStream {
*/
public TokenStream(List tokens) {
if (tokens == null) {
- throw new NullPointerException("Token list cannot be null.");
+ throw new NullPointerException("Token 列表不能为空");
}
this.tokens = tokens;
}
@@ -103,8 +103,8 @@ public class TokenStream {
Token t = peek();
if (!t.getLexeme().equals(lexeme)) {
throw new ParseException(
- "Expected lexeme '" + lexeme + "' but got '" + t.getLexeme() +
- "' at " + t.getLine() + ":" + t.getCol()
+ "期望的词素是'" + lexeme + "',但得到的是'" + t.getLexeme() +
+ "在" + t.getLine() + ":" + t.getCol()
);
}
return next();
@@ -122,8 +122,8 @@ public class TokenStream {
Token t = peek();
if (t.getType() != type) {
throw new ParseException(
- "Expected token type " + type + " but got " + t.getType() +
- " ('" + t.getLexeme() + "') at " + t.getLine() + ":" + t.getCol()
+ "期望的标记类型为 " + type + " 但实际得到的是 " + t.getType() +
+ " ('" + t.getLexeme() + "') 在 " + t.getLine() + ":" + t.getCol()
);
}
return next();
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/UnexpectedToken.java b/src/main/java/org/jcnc/snow/compiler/parser/context/UnexpectedToken.java
new file mode 100644
index 0000000..bfa5ecd
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/UnexpectedToken.java
@@ -0,0 +1,11 @@
+package org.jcnc.snow.compiler.parser.context;
+
+/**
+ * 当解析过程中遇到意料之外或无法识别的 Token 时抛出。
+ */
+public final class UnexpectedToken extends ParseException {
+
+ public UnexpectedToken(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/UnsupportedFeature.java b/src/main/java/org/jcnc/snow/compiler/parser/context/UnsupportedFeature.java
new file mode 100644
index 0000000..558f32a
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/UnsupportedFeature.java
@@ -0,0 +1,11 @@
+package org.jcnc.snow.compiler.parser.context;
+
+/**
+ * 当源码使用了当前编译器尚未支持的语言特性或语法时抛出。
+ */
+public final class UnsupportedFeature extends ParseException {
+
+ public UnsupportedFeature(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
index 7d0854f..c5bfb36 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
@@ -1,24 +1,58 @@
package org.jcnc.snow.compiler.parser.core;
import org.jcnc.snow.compiler.lexer.token.TokenType;
+import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
+import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.factory.TopLevelParserFactory;
-import org.jcnc.snow.compiler.parser.ast.base.Node;
import java.util.ArrayList;
import java.util.List;
+import java.util.StringJoiner;
+/**
+ * 语法解析引擎(ParserEngine)。
+ *
+ * 负责驱动 Snow 源码的顶层语法结构解析,将源码 TokenStream
+ * 递交给各类 TopLevelParser,并收集语法树节点与异常。
+ * 支持容错解析,能够批量报告所有语法错误,并提供同步恢复功能。
+ *
+ *
+ *
+ * 典型用法:
+ *
+ * ParserEngine engine = new ParserEngine(context);
+ * List<Node> ast = engine.parse();
+ *
+ *
+ *
+ * @param ctx 解析器上下文,负责持有 TokenStream 及所有全局状态。
+ */
public record ParserEngine(ParserContext ctx) {
+ /**
+ * 解析输入 TokenStream,生成语法树节点列表。
+ *
+ *
+ * 调用各类顶级语句解析器(如 module, func, import),
+ * 遇到错误时会自动跳过到下一行或已知结构关键字,继续后续分析,
+ * 最终汇总所有错误。如果解析出现错误,将以
+ * {@link UnexpectedToken} 抛出所有语法错误信息。
+ *
+ *
+ * @return AST 节点列表,每个节点对应一个顶层语法结构
+ * @throws UnexpectedToken 如果解析期间发现语法错误
+ */
public List parse() {
List nodes = new ArrayList<>();
List errs = new ArrayList<>();
TokenStream ts = ctx.getTokens();
+ // 主循环:直到全部 token 处理完毕
while (ts.isAtEnd()) {
- // 跳过空行
+ // 跳过所有空行
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
continue;
@@ -30,22 +64,31 @@ public record ParserEngine(ParserContext ctx) {
nodes.add(parser.parse(ctx));
} catch (Exception ex) {
errs.add(ex.getMessage());
- synchronize(ts); // 错误恢复
+ synchronize(ts); // 错误恢复:同步到下一个语句
}
}
+ // 批量报告所有解析错误
if (!errs.isEmpty()) {
- throw new IllegalStateException("解析过程中检测到 "
- + errs.size() + " 处错误:\n - "
- + String.join("\n - ", errs));
+ StringJoiner sj = new StringJoiner("\n - ", "", "");
+ errs.forEach(sj::add);
+ throw new UnexpectedToken("解析过程中检测到 "
+ + errs.size() + " 处错误:\n - " + sj);
}
return nodes;
}
/**
- * 错误同步:跳到下一行或下一个已注册顶层关键字
+ * 错误同步机制:跳过当前 TokenStream,直到遇到下一行
+ * 或下一个可识别的顶级结构关键字,以保证后续解析不会被卡住。
+ *
+ * 同时会跳过连续空行。
+ *
+ *
+ * @param ts 当前 TokenStream
*/
private void synchronize(TokenStream ts) {
+ // 跳到下一行或下一个顶层结构关键字
while (ts.isAtEnd()) {
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
@@ -56,7 +99,7 @@ public record ParserEngine(ParserContext ctx) {
}
ts.next();
}
- // 连续空行全部吃掉
+ // 吃掉后续所有空行
while (ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
index 4f79274..c0a869e 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
@@ -4,6 +4,7 @@ import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.context.ParserContext;
+import org.jcnc.snow.compiler.parser.context.UnsupportedFeature;
import org.jcnc.snow.compiler.parser.expression.base.ExpressionParser;
import org.jcnc.snow.compiler.parser.expression.base.InfixParselet;
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
@@ -87,7 +88,7 @@ public class PrattExpressionParser implements ExpressionParser {
Token token = ctx.getTokens().next();
PrefixParselet prefix = prefixes.get(token.getType().name());
if (prefix == null) {
- throw new IllegalStateException("没有为该 Token 类型注册前缀解析器: " + token.getType());
+ throw new UnsupportedFeature("没有为该 Token 类型注册前缀解析器: " + token.getType());
}
ExpressionNode left = prefix.parse(ctx, token);
@@ -96,7 +97,10 @@ public class PrattExpressionParser implements ExpressionParser {
&& prec.ordinal() < nextPrecedence(ctx)) {
String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex);
- if (infix == null) break;
+ if (infix == null) {
+ throw new UnsupportedFeature(
+ "没有为该 Token 类型注册中缀解析器: " + token.getType());
+ }
left = infix.parse(ctx, left);
}
return left;
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
index ac9e05d..bcf2555 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
@@ -7,6 +7,7 @@ import org.jcnc.snow.compiler.parser.context.TokenStream;
import org.jcnc.snow.compiler.parser.ast.ImportNode;
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.function.FunctionParser;
import java.util.ArrayList;
@@ -33,7 +34,7 @@ public class ModuleParser implements TopLevelParser {
*
* @param ctx 当前解析器上下文,包含词法流、状态信息等。
* @return 返回一个 {@link ModuleNode} 实例,表示完整模块的语法结构。
- * @throws IllegalStateException 当模块体中出现未识别的语句时抛出。
+ * @throws UnexpectedToken 当模块体中出现未识别的语句时抛出。
*/
@Override
public ModuleNode parse(ParserContext ctx) {
@@ -86,7 +87,7 @@ public class ModuleParser implements TopLevelParser {
functions.add(funcParser.parse(ctx));
} else {
// 遇到无法识别的语句开头,抛出异常并提供详细提示
- throw new IllegalStateException("Unexpected token in module: " + lex);
+ throw new UnexpectedToken("Unexpected token in module: " + lex);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
index 526dbf0..3c14a90 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
@@ -7,6 +7,7 @@ import org.jcnc.snow.compiler.parser.ast.ExpressionStatementNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
+import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/**
@@ -39,7 +40,7 @@ public class ExpressionStatementParser implements StatementParser {
*
* @param ctx 当前解析上下文,提供词法流与状态信息。
* @return 返回 {@link AssignmentNode} 或 {@link ExpressionStatementNode} 表示的语法节点。
- * @throws IllegalStateException 若表达式起始为关键字或语法非法。
+ * @throws UnexpectedToken 若表达式起始为关键字或语法非法。
*/
@Override
public StatementNode parse(ParserContext ctx) {
@@ -47,7 +48,7 @@ public class ExpressionStatementParser implements StatementParser {
// 快速检查:若遇空行或关键字开头,不可作为表达式语句
if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) {
- throw new IllegalStateException("Cannot parse expression starting with keyword: " + ts.peek().getLexeme());
+ throw new UnexpectedToken("无法解析以关键字开头的表达式: " + ts.peek().getLexeme());
}
// 获取当前 token 的行号、列号和文件名
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java b/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java
index f669c8e..3403dcf 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java
@@ -19,7 +19,6 @@ public class ScriptTopLevelParser implements TopLevelParser {
public Node parse(ParserContext ctx) {
String first = ctx.getTokens().peek().getLexeme();
StatementParser sp = StatementParserFactory.get(first);
- StatementNode stmt = sp.parse(ctx);
- return stmt; // StatementNode 亦是 Node
+ return sp.parse(ctx);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java
index c319d4f..90cbc2c 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java
@@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.utils;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
+import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import java.util.Map;
import java.util.function.BiConsumer;
@@ -45,7 +46,7 @@ public class FlexibleSectionParser {
* @param ctx 当前解析上下文,提供语法环境与作用域信息
* @param tokens 当前 token 流
* @param sectionDefinitions 各个区块的定义映射(key 为关键字,value 为判断 + 解析逻辑组合)
- * @throws RuntimeException 若出现无法识别的关键字或未满足的匹配条件
+ * @throws UnexpectedToken 若出现无法识别的关键字或未满足的匹配条件
*/
public static void parse(ParserContext ctx,
TokenStream tokens,
@@ -70,7 +71,7 @@ public class FlexibleSectionParser {
if (definition != null && definition.condition().test(tokens)) {
definition.parser().accept(ctx, tokens); // 执行解析逻辑
} else {
- throw new RuntimeException("未识别的关键字或条件不满足: " + keyword);
+ throw new UnexpectedToken("未识别的关键字或条件不满足: " + keyword);
}
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java
index ce30193..6d77478 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java
@@ -1,5 +1,7 @@
package org.jcnc.snow.compiler.parser.utils;
+import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
+
import java.util.*;
import java.util.Map.Entry;
@@ -10,26 +12,28 @@ import java.util.Map.Entry;
* - 序列化:将 Java 原生对象转换为符合 JSON 标准的字符串
*
* 设计要点:
- * 1. 使用静态方法作为唯一入口,避免状态共享导致的线程安全问题
- * 2. 解析器内部使用 char[] 缓冲区,提高访问性能
- * 3. 维护行列号信息,抛出异常时能精确定位错误位置
- * 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建
+ * 1. 使用静态方法作为唯一入口,避免状态共享导致的线程安全问题
+ * 2. 解析器内部使用 char[] 缓冲区,提高访问性能
+ * 3. 维护行列号信息,抛出异常时能精确定位错误位置
+ * 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建
*/
public class JSONParser {
- private JSONParser() {}
+ private JSONParser() {
+ }
/**
* 将 JSON 文本解析为对应的 Java 对象
+ *
* @param input JSON 格式字符串
* @return 对应的 Java 原生对象:
- * - JSON 对象 -> Map
- * - JSON 数组 -> List
- * - JSON 字符串 -> String
- * - JSON 数值 -> Long 或 Double
- * - JSON 布尔 -> Boolean
- * - JSON null -> null
- * @throws RuntimeException 如果遇到语法错误或多余字符,异常消息中包含行列信息
+ * - JSON 对象 -> Map
+ * - JSON 数组 -> List
+ * - JSON 字符串 -> String
+ * - JSON 数值 -> Long 或 Double
+ * - JSON 布尔 -> Boolean
+ * - JSON null -> null
+ * @throws UnexpectedToken 如果遇到语法错误或多余字符,异常消息中包含行列信息
*/
public static Object parse(String input) {
return new Parser(input).parseInternal();
@@ -37,6 +41,7 @@ public class JSONParser {
/**
* 将 Java 原生对象序列化为 JSON 字符串
+ *
* @param obj 支持的类型:Map、Collection、String、Number、Boolean 或 null
* @return 符合 JSON 规范的字符串
*/
@@ -45,21 +50,31 @@ public class JSONParser {
}
// ======= 内部解析器 =======
+
/**
* 负责将 char[] 缓冲区中的 JSON 文本解析为 Java 对象
*/
private static class Parser {
- /** 输入缓冲区 */
+ /**
+ * 输入缓冲区
+ */
private final char[] buf;
- /** 当前解析到的位置索引 */
+ /**
+ * 当前解析到的位置索引
+ */
private int pos;
- /** 当前字符所在行号,从 1 开始 */
+ /**
+ * 当前字符所在行号,从 1 开始
+ */
private int line;
- /** 当前字符所在列号,从 1 开始 */
+ /**
+ * 当前字符所在列号,从 1 开始
+ */
private int col;
/**
* 构造解析器,初始化缓冲区和行列信息
+ *
* @param input 待解析的 JSON 文本
*/
Parser(String input) {
@@ -115,7 +130,9 @@ public class JSONParser {
while (true) {
skipWhitespace();
String key = parseString(); // 解析键
- skipWhitespace(); expect(':'); skipWhitespace();
+ skipWhitespace();
+ expect(':');
+ skipWhitespace();
Object val = parseValue(); // 解析值
map.put(key, val);
skipWhitespace();
@@ -123,7 +140,8 @@ public class JSONParser {
advance(); // 跳过 '}'
break;
}
- expect(','); skipWhitespace();
+ expect(',');
+ skipWhitespace();
}
return map;
}
@@ -149,7 +167,8 @@ public class JSONParser {
advance();
break;
}
- expect(','); skipWhitespace();
+ expect(',');
+ skipWhitespace();
}
return list;
}
@@ -170,18 +189,35 @@ public class JSONParser {
advance(); // 跳过 '\'
c = currentChar();
switch (c) {
- case '"': sb.append('"'); break;
- case '\\': sb.append('\\'); break;
- case '/': sb.append('/'); break;
- case 'b': sb.append('\b'); break;
- case 'f': sb.append('\f'); break;
- case 'n': sb.append('\n'); break;
- case 'r': sb.append('\r'); break;
- case 't': sb.append('\t'); break;
+ case '"':
+ sb.append('"');
+ break;
+ case '\\':
+ sb.append('\\');
+ break;
+ case '/':
+ sb.append('/');
+ break;
+ case 'b':
+ sb.append('\b');
+ break;
+ case 'f':
+ sb.append('\f');
+ break;
+ case 'n':
+ sb.append('\n');
+ break;
+ case 'r':
+ sb.append('\r');
+ break;
+ case 't':
+ sb.append('\t');
+ break;
case 'u': // 解析 Unicode 转义
- String hex = new String(buf, pos+1, 4);
+ String hex = new String(buf, pos + 1, 4);
sb.append((char) Integer.parseInt(hex, 16));
- pos += 4; col += 4;
+ pos += 4;
+ col += 4;
break;
default:
error("无效转义字符 '\\" + c + "'");
@@ -250,7 +286,8 @@ public class JSONParser {
private void advance() {
if (pos < buf.length) {
if (buf[pos] == '\n') {
- line++; col = 1;
+ line++;
+ col = 1;
} else {
col++;
}
@@ -292,16 +329,19 @@ public class JSONParser {
* 抛出带行列定位的解析错误
*/
private void error(String msg) {
- throw new RuntimeException("Error at line " + line + ", column " + col + ": " + msg);
+ throw new UnexpectedToken("在第 " + line + " 行,第 " + col + " 列出现错误: " + msg);
}
}
// ======= 内部序列化器 =======
+
/**
* 负责高效地将 Java 对象写为 JSON 文本
*/
private static class Writer {
- /** 默认 StringBuilder 初始容量,避免频繁扩容 */
+ /**
+ * 默认 StringBuilder 初始容量,避免频繁扩容
+ */
private static final int DEFAULT_CAPACITY = 1024;
/**
@@ -344,8 +384,8 @@ public class JSONParser {
}
sb.append(']');
} else {
- // 其他类型,使用 toString 并加引号
- quote(obj.toString(), sb);
+ throw new UnsupportedOperationException(
+ "不支持的 JSON 字符串化类型: " + obj.getClass());
}
}
@@ -356,12 +396,23 @@ public class JSONParser {
sb.append('"');
for (char c : s.toCharArray()) {
switch (c) {
- case '\\': sb.append("\\\\"); break;
- case '"': sb.append("\\\""); break;
- case '\n': sb.append("\\n"); break;
- case '\r': sb.append("\\r"); break;
- case '\t': sb.append("\\t"); break;
- default: sb.append(c);
+ case '\\':
+ sb.append("\\\\");
+ break;
+ case '"':
+ sb.append("\\\"");
+ break;
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ default:
+ sb.append(c);
}
}
sb.append('"');
From b6262087715c3c149e6a8aab93c821bb94757278 Mon Sep 17 00:00:00 2001
From: Luke
Date: Thu, 3 Jul 2025 23:51:00 +0800
Subject: [PATCH 42/61] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=E6=B5=8B?=
=?UTF-8?q?=E8=AF=95=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.run/测试.run.xml | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 .run/测试.run.xml
diff --git a/.run/测试.run.xml b/.run/测试.run.xml
new file mode 100644
index 0000000..43d3751
--- /dev/null
+++ b/.run/测试.run.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 169523bc3370f30b931dc57395d69a6067154257 Mon Sep 17 00:00:00 2001
From: Luke
Date: Fri, 4 Jul 2025 23:58:58 +0800
Subject: [PATCH 43/61] =?UTF-8?q?style:=20=E4=BF=AE=E6=94=B9=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E7=BC=A9=E8=BF=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../jcnc/snow/compiler/parser/context/ParserContext.java | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/ParserContext.java b/src/main/java/org/jcnc/snow/compiler/parser/context/ParserContext.java
index 5ff228a..b9c9852 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/ParserContext.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/ParserContext.java
@@ -15,10 +15,14 @@ import java.util.List;
*/
public class ParserContext {
- /** 当前语法分析所使用的 Token 流 */
+ /**
+ * 当前语法分析所使用的 Token 流
+ */
private final TokenStream tokens;
- /** 当前语法分析所使用的资源文件名 */
+ /**
+ * 当前语法分析所使用的资源文件名
+ */
private final String sourceName;
From 3c43e31afb202a93c133a534fa8d1bee3b43c862 Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 5 Jul 2025 10:35:33 +0800
Subject: [PATCH 44/61] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=20clean=20?=
=?UTF-8?q?=E5=91=BD=E4=BB=A4=E7=9A=84=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BD=BF?=
=?UTF-8?q?=E5=85=B6=E7=AC=A6=E5=90=88=E7=9B=B4=E8=A7=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/org/jcnc/snow/pkg/tasks/CleanTask.java | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
index 2b6da14..3022a35 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
@@ -32,16 +32,15 @@ public final class CleanTask implements Task {
public void run() throws IOException {
Path build = Path.of("build");
Path dist = Path.of("dist");
- deleteDir(build);
- deleteDir(dist);
- Files.createDirectories(build);
- Files.createDirectories(dist);
+ deleteDir(build, false);
+ deleteDir(dist, false);
System.out.println("[clean] done.");
}
/**
- * 递归删除指定目录及其所有子文件和子目录。
+ * 递归删除指定目录下的所有子文件和子目录。
+ * 如需删除指定目录本身可将第二个参数 containSelf 设置为 true
*
* 若目录不存在,则直接返回。
*
@@ -50,14 +49,18 @@ public final class CleanTask implements Task {
*
*
* @param dir 需要删除的目录路径
+ * @param containSelf 是否删除指定目录本身
* @throws IOException 删除目录或文件过程中发生 IO 错误时抛出
*/
- private void deleteDir(Path dir) throws IOException {
+ private void deleteDir(Path dir, boolean containSelf) throws IOException {
if (Files.notExists(dir)) return;
try (var stream = Files.walk(dir)) {
stream.sorted(Comparator.reverseOrder()) // 先删子文件,后删父目录
.forEach(p -> {
try {
+ if (!containSelf && p == dir) {
+ return;
+ }
Files.delete(p);
} catch (IOException e) {
throw new RuntimeException(e);
From a1b92f0cc81e1ab87cd93740c94d01bbc9ea17a5 Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 5 Jul 2025 10:37:28 +0800
Subject: [PATCH 45/61] =?UTF-8?q?style:=20=E7=A7=BB=E9=99=A4=E6=B2=A1?=
=?UTF-8?q?=E5=BF=85=E8=A6=81=E7=9A=84=E5=B1=80=E9=83=A8=E5=8F=98=E9=87=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
index 3022a35..021c44b 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
@@ -30,10 +30,8 @@ public final class CleanTask implements Task {
*/
@Override
public void run() throws IOException {
- Path build = Path.of("build");
- Path dist = Path.of("dist");
- deleteDir(build, false);
- deleteDir(dist, false);
+ deleteDir(Path.of("build"), false);
+ deleteDir(Path.of("dist"), false);
System.out.println("[clean] done.");
}
From 699bc2721e1bcd8707abbb4da34ef7bd86657329 Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 5 Jul 2025 10:41:21 +0800
Subject: [PATCH 46/61] =?UTF-8?q?docs:=20=E4=BC=98=E5=8C=96=E6=96=B9?=
=?UTF-8?q?=E6=B3=95=E6=96=87=E6=A1=A3=E7=9A=84=E4=B8=BB=E6=8F=8F=E8=BF=B0?=
=?UTF-8?q?=E9=83=A8=E5=88=86=EF=BC=8C=E4=BB=A5=E6=9B=B4=E5=A5=BD=E5=9C=B0?=
=?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=8F=82=E6=95=B0=E5=90=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
index 021c44b..c7431d2 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/CleanTask.java
@@ -38,7 +38,7 @@ public final class CleanTask implements Task {
/**
* 递归删除指定目录下的所有子文件和子目录。
- * 如需删除指定目录本身可将第二个参数 containSelf 设置为 true
+ * 如需删除指定目录本身可将第二个参数 {@code containSelf} 设置为 true
*
* 若目录不存在,则直接返回。
*
From e33f6b0ce292bdca99086e453ec20b968cdd9a07 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sat, 5 Jul 2025 14:20:43 +0800
Subject: [PATCH 47/61] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=95=B0?=
=?UTF-8?q?=E5=AD=97=E5=90=8E=E7=A9=BA=E6=A0=BC=E5=90=8E=E6=8E=A5=E4=B8=8A?=
=?UTF-8?q?=E9=9D=9E=E6=B3=95=E5=90=8E=E7=BC=80=E8=BF=9B=E5=85=A5=E6=AD=BB?=
=?UTF-8?q?=E5=BE=AA=E7=8E=AF=E7=9A=84=E9=94=99=E8=AF=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../lexer/scanners/NumberTokenScanner.java | 35 ++++++-----
.../compiler/parser/core/ParserEngine.java | 61 +++++--------------
.../parser/factory/TopLevelParserFactory.java | 24 ++++++--
3 files changed, 54 insertions(+), 66 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
index 1a9b8bb..eef90ae 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
@@ -128,33 +128,30 @@ public class NumberTokenScanner extends AbstractTokenScanner {
if (!ctx.isAtEnd()) {
char next = ctx.peek();
- // 2‑A. 合法单字符后缀
+ // 2-A. 合法单字符后缀(紧邻,不允许空格)
if (SUFFIX_CHARS.indexOf(next) >= 0) {
literal.append(ctx.advance());
}
- // 2‑B. 紧跟未知字母(如 42X)
+ // 未知单字符后缀 —— 直接报错
else if (Character.isLetter(next)) {
throw new LexicalException("未知的数字类型后缀 '" + next + "'", line, col);
}
- // 2‑C. 数字后出现空白 + 类型后缀(如 3 f) —— 不允许
+ // “数字 + 空格 + 字母” —— 一律非法
else if (Character.isWhitespace(next) && next != '\n') {
- // 允许数字后与普通标识符/关键字间存在空白;
- // 仅当空白后的首个非空字符是合法的类型后缀时才报错。
int off = 1;
char look;
- // 跳过任意空白(不含换行)
+ // 跳过空白(不含换行)
while (true) {
look = ctx.peekAhead(off);
- if (look == '\n' || look == '\0') break; // 行尾或 EOF
+ if (look == '\n' || look == '\0') break;
if (!Character.isWhitespace(look)) break;
off++;
}
- // 如果紧跟类型后缀字符,中间存在空白则视为非法
- if (SUFFIX_CHARS.indexOf(look) >= 0) {
- throw new LexicalException("数字字面量与类型后缀之间不允许有空白符", line, col);
+ if (Character.isLetter(look)) {
+ throw new LexicalException("数字字面量后不允许出现空格再跟标识符/后缀", line, col);
}
}
- // 其他字符(分号、运算符、括号等)留给外层扫描流程处理
+ // 其他符号由外层扫描器处理
}
// 3. 生成并返回 Token
@@ -165,13 +162,21 @@ public class NumberTokenScanner extends AbstractTokenScanner {
* FSM 内部状态。
*/
private enum State {
- /** 整数部分(小数点左侧) */
+ /**
+ * 整数部分(小数点左侧)
+ */
INT_PART,
- /** 已读到小数点,但还未读到第一位小数数字 */
+ /**
+ * 已读到小数点,但还未读到第一位小数数字
+ */
DEC_POINT,
- /** 小数部分(小数点右侧) */
+ /**
+ * 小数部分(小数点右侧)
+ */
FRAC_PART,
- /** 主体结束,准备处理后缀或交还控制权 */
+ /**
+ * 主体结束,准备处理后缀或交还控制权
+ */
END
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
index c5bfb36..f278b41 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
@@ -14,61 +14,34 @@ import java.util.StringJoiner;
/**
* 语法解析引擎(ParserEngine)。
- *
- * 负责驱动 Snow 源码的顶层语法结构解析,将源码 TokenStream
- * 递交给各类 TopLevelParser,并收集语法树节点与异常。
- * 支持容错解析,能够批量报告所有语法错误,并提供同步恢复功能。
- *
- *
- *
- * 典型用法:
- *
- * ParserEngine engine = new ParserEngine(context);
- * List<Node> ast = engine.parse();
- *
- *
- *
- * @param ctx 解析器上下文,负责持有 TokenStream 及所有全局状态。
+ * 驱动顶层解析,并在捕获异常后通过同步机制恢复,防止死循环。
*/
public record ParserEngine(ParserContext ctx) {
- /**
- * 解析输入 TokenStream,生成语法树节点列表。
- *
- *
- * 调用各类顶级语句解析器(如 module, func, import),
- * 遇到错误时会自动跳过到下一行或已知结构关键字,继续后续分析,
- * 最终汇总所有错误。如果解析出现错误,将以
- * {@link UnexpectedToken} 抛出所有语法错误信息。
- *
- *
- * @return AST 节点列表,每个节点对应一个顶层语法结构
- * @throws UnexpectedToken 如果解析期间发现语法错误
- */
+ /** 解析整份 TokenStream,返回顶层 AST 节点列表。 */
public List parse() {
List nodes = new ArrayList<>();
- List errs = new ArrayList<>();
- TokenStream ts = ctx.getTokens();
+ List errs = new ArrayList<>();
+ TokenStream ts = ctx.getTokens();
- // 主循环:直到全部 token 处理完毕
+ // 主循环至 EOF
while (ts.isAtEnd()) {
- // 跳过所有空行
+ // 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
continue;
}
TopLevelParser parser = TopLevelParserFactory.get(ts.peek().getLexeme());
-
try {
nodes.add(parser.parse(ctx));
} catch (Exception ex) {
errs.add(ex.getMessage());
- synchronize(ts); // 错误恢复:同步到下一个语句
+ synchronize(ts); // 出错后同步恢复
}
}
- // 批量报告所有解析错误
+ // 聚合并抛出全部语法错误
if (!errs.isEmpty()) {
StringJoiner sj = new StringJoiner("\n - ", "", "");
errs.forEach(sj::add);
@@ -79,27 +52,21 @@ public record ParserEngine(ParserContext ctx) {
}
/**
- * 错误同步机制:跳过当前 TokenStream,直到遇到下一行
- * 或下一个可识别的顶级结构关键字,以保证后续解析不会被卡住。
- *
- * 同时会跳过连续空行。
- *
- *
- * @param ts 当前 TokenStream
+ * 同步:跳过当前行或直到遇到 **显式注册** 的顶层关键字。
+ * 这样可避免因默认脚本解析器导致指针停滞而进入死循环。
*/
private void synchronize(TokenStream ts) {
- // 跳到下一行或下一个顶层结构关键字
while (ts.isAtEnd()) {
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
break;
}
- if (TopLevelParserFactory.get(ts.peek().getLexeme()) != null) {
- break;
+ if (TopLevelParserFactory.isRegistered(ts.peek().getLexeme())) {
+ break; // 仅在已注册关键字处停下
}
- ts.next();
+ ts.next(); // 继续丢弃 token
}
- // 吃掉后续所有空行
+ // 清掉后续连续空行
while (ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java b/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java
index 7fa779c..82c486f 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java
@@ -8,22 +8,38 @@ import org.jcnc.snow.compiler.parser.top.ScriptTopLevelParser;
import java.util.Map;
import java.util.HashMap;
+/**
+ * {@code TopLevelParserFactory} 用于根据源码中顶层关键字取得对应的解析器。
+ *
+ * 若关键字未注册,则回退到脚本模式解析器 {@link ScriptTopLevelParser}。
+ */
public class TopLevelParserFactory {
+ /** 关键字 → 解析器注册表 */
private static final Map registry = new HashMap<>();
- private static final TopLevelParser DEFAULT = new ScriptTopLevelParser(); // ← 默认解析器
+
+ /** 缺省解析器:脚本模式(单条语句可执行) */
+ private static final TopLevelParser DEFAULT = new ScriptTopLevelParser();
static {
- // 顶层结构解析器
+ // 在此注册所有受支持的顶层结构关键字
registry.put("module", new ModuleParser());
registry.put("function", new FunctionParser());
- // 也可按需继续注册其它关键字
+ // 若未来新增顶层结构,可继续在此处注册
}
/**
- * 根据关键字获取解析器;若未注册,回退到脚本语句解析。
+ * 依据关键字返回解析器;若未注册则返回脚本解析器。
*/
public static TopLevelParser get(String keyword) {
return registry.getOrDefault(keyword, DEFAULT);
}
+
+ /**
+ * 判断某关键字是否已显式注册为顶层结构,
+ * 供同步恢复逻辑使用,避免死循环。
+ */
+ public static boolean isRegistered(String keyword) {
+ return registry.containsKey(keyword);
+ }
}
From e11d519627aa5b4ce6bab8873992cddba35c7b2f Mon Sep 17 00:00:00 2001
From: Luke
Date: Sat, 5 Jul 2025 17:02:45 +0800
Subject: [PATCH 48/61] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E8=AF=AD?=
=?UTF-8?q?=E6=B3=95=E5=88=86=E6=9E=90=E6=A8=A1=E5=9D=97=E5=B9=B6=E4=BC=98?=
=?UTF-8?q?=E5=8C=96=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E6=9C=BA=E5=88=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 优化了 ExpressionStatementParser、FlexibleSectionParser 和 JSONParser 的代码结构
- 改进了模块解析器 (ModuleParser) 的实现
- 重构了语法异常 (ParseException) 类,增加了错误位置信息
- 新增 ParseError 类用于收集和展示语法错误信息
- 改进了同步机制以更好地恢复解析过程
---
playground/BugFarm/Bug1/Main.snow | 4 +-
src/main/java/org/jcnc/snow/cli/SnowCLI.java | 2 +-
.../compiler/parser/context/MissingToken.java | 17 +-
.../compiler/parser/context/ParseError.java | 45 +++++
.../parser/context/ParseException.java | 72 ++++++-
.../compiler/parser/context/TokenStream.java | 92 ++++-----
.../parser/context/UnexpectedToken.java | 16 +-
.../parser/context/UnsupportedFeature.java | 16 +-
.../compiler/parser/core/ParserEngine.java | 67 ++++---
.../expression/PrattExpressionParser.java | 108 +++++-----
.../compiler/parser/module/ModuleParser.java | 75 ++++---
.../statement/ExpressionStatementParser.java | 65 +++---
.../parser/utils/FlexibleSectionParser.java | 73 +++----
.../compiler/parser/utils/JSONParser.java | 186 +++++++-----------
14 files changed, 457 insertions(+), 381 deletions(-)
create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/context/ParseError.java
diff --git a/playground/BugFarm/Bug1/Main.snow b/playground/BugFarm/Bug1/Main.snow
index 3dae6e6..0065669 100644
--- a/playground/BugFarm/Bug1/Main.snow
+++ b/playground/BugFarm/Bug1/Main.snow
@@ -1,7 +1,7 @@
function: main
- return_type: int
+ return_type: int 111
body:
- declare num1 :int = 3.1 G
+
return 65537
end body
end function
\ No newline at end of file
diff --git a/src/main/java/org/jcnc/snow/cli/SnowCLI.java b/src/main/java/org/jcnc/snow/cli/SnowCLI.java
index 0afa4e4..0ce6ba0 100644
--- a/src/main/java/org/jcnc/snow/cli/SnowCLI.java
+++ b/src/main/java/org/jcnc/snow/cli/SnowCLI.java
@@ -91,7 +91,7 @@ public class SnowCLI {
System.exit(exitCode);
} catch (Exception e) {
// 捕获命令执行过程中的异常并打印错误消息
-// System.err.println("Error: " + e.getMessage());
+ System.err.println(e.getMessage());
System.exit(1);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/MissingToken.java b/src/main/java/org/jcnc/snow/compiler/parser/context/MissingToken.java
index b56ae02..3610426 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/MissingToken.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/MissingToken.java
@@ -1,11 +1,22 @@
package org.jcnc.snow.compiler.parser.context;
/**
- * 当语法结构缺失必须出现的 Token 时抛出。
+ * 表示在语法分析过程中,必须出现的 Token 缺失时抛出的异常。
+ *
+ * 当分析器检测到输入流中缺少某个预期 Token 时,会抛出此异常,以便准确地指明语法错误位置。
+ * 该异常包含了缺失 Token 的名称以及发生缺失的位置(行号和列号),便于错误定位和后续处理。
+ *
*/
public final class MissingToken extends ParseException {
- public MissingToken(String message) {
- super(message);
+ /**
+ * 构造一个表示缺失 Token 的异常。
+ *
+ * @param expected 预期但未出现的 Token 名称
+ * @param line 发生异常的行号
+ * @param column 发生异常的列号
+ */
+ public MissingToken(String expected, int line, int column) {
+ super("缺失 Token: " + expected, line, column);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/ParseError.java b/src/main/java/org/jcnc/snow/compiler/parser/context/ParseError.java
new file mode 100644
index 0000000..24c1d27
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/ParseError.java
@@ -0,0 +1,45 @@
+package org.jcnc.snow.compiler.parser.context;
+
+/**
+ * 语法错误的数据传输对象(DTO)。
+ *
+ * 用于收集和展示语法分析过程中检测到的错误信息,便于错误定位和报告。
+ * 包含出错文件、行号、列号和具体错误信息等字段。
+ *
+ */
+public class ParseError {
+
+ /** 出错的文件名 */
+ private final String file;
+ /** 出错的行号 */
+ private final int line;
+ /** 出错的列号 */
+ private final int column;
+ /** 错误信息描述 */
+ private final String message;
+
+ /**
+ * 构造一个语法错误数据对象。
+ *
+ * @param file 出错文件名
+ * @param line 出错行号
+ * @param column 出错列号
+ * @param message 错误信息描述
+ */
+ public ParseError(String file, int line, int column, String message) {
+ this.file = file;
+ this.line = line;
+ this.column = column;
+ this.message = message;
+ }
+
+ /**
+ * 返回该错误对象的字符串表示。
+ *
+ * @return 格式化后的错误描述字符串
+ */
+ @Override
+ public String toString() {
+ return file + ": 行 " + line + ", 列 " + column + ": " + message;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java b/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java
index 0262868..97ceb33 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/ParseException.java
@@ -1,21 +1,75 @@
package org.jcnc.snow.compiler.parser.context;
/**
- * {@code ParseException}——语法分析阶段所有错误的基类。
+ * 语法分析阶段所有错误的基类。
+ *
+ * 本异常作为语法分析相关错误的统一父类,屏蔽了堆栈信息,确保在命令行界面(CLI)输出时只占用一行,方便用户快速定位问题。
+ * 通过 {@code permits} 关键字,限定了可被继承的异常类型,增强类型安全性。
+ *
*
- * 声明为 sealed ,仅允许 {@link UnexpectedToken}、
- * {@link MissingToken}、{@link UnsupportedFeature} 三个受信子类继承,
- * 以便调用方根据异常类型进行精确处理。
+ *
+ * 该异常携带错误发生的行号、列号和具体原因信息,用于语法错误的精确报告和输出展示。
+ *
*/
public sealed class ParseException extends RuntimeException
- permits UnexpectedToken, MissingToken, UnsupportedFeature {
+ permits MissingToken, UnexpectedToken, UnsupportedFeature {
+
+ /** 出错行号(从 1 开始) */
+ private final int line;
+ /** 出错列号(从 1 开始) */
+ private final int column;
+ /** 错误原因描述 */
+ private final String reason;
/**
- * 构造解析异常并附带错误消息。
+ * 构造语法分析异常。
*
- * @param message 错误描述
+ * @param reason 错误原因描述
+ * @param line 出错行号(从 1 开始)
+ * @param column 出错列号(从 1 开始)
*/
- public ParseException(String message) {
- super(message);
+ public ParseException(String reason, int line, int column) {
+ // 禁用 cause / suppression / stackTrace,确保 CLI 输出简洁
+ super(reason, null, false, false);
+ this.reason = reason;
+ this.line = line;
+ this.column = column;
+ }
+
+ /**
+ * 禁用堆栈信息的生成,保证异常始终为单行输出。
+ *
+ * @return 当前异常对象自身
+ */
+ @Override
+ public synchronized Throwable fillInStackTrace() {
+ return this;
+ }
+
+ /**
+ * 获取出错行号(从 1 开始)。
+ *
+ * @return 行号
+ */
+ public int getLine() {
+ return line;
+ }
+
+ /**
+ * 获取出错列号(从 1 开始)。
+ *
+ * @return 列号
+ */
+ public int getColumn() {
+ return column;
+ }
+
+ /**
+ * 获取错误原因描述。
+ *
+ * @return 错误原因
+ */
+ public String getReason() {
+ return reason;
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
index 9169318..07a4ffb 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
@@ -6,27 +6,29 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
import java.util.List;
/**
- * {@code TokenStream} 封装了一个 Token 列表并维护当前解析位置,是语法分析器读取词法单元的核心工具类。
- *
- * 提供前瞻(peek)、消费(next)、匹配(match)、断言(expect)等常用操作,
- * 支持前向查看和异常处理,适用于递归下降解析等常见语法构建策略。
+ * {@code TokenStream} 封装了 Token 序列并维护当前解析位置,是语法分析器读取词法单元的核心工具类。
+ *
+ * 该类提供前瞻(peek)、消费(next)、匹配(match)、断言(expect)等常用操作,
+ * 支持前向查看和异常处理,适用于递归下降等常见语法解析策略。
+ * 设计上自动跳过注释(COMMENT)token,并对越界情况提供自动构造的 EOF(文件结束)token,
+ * 有效提升语法处理的健壮性与易用性。
+ *
*/
public class TokenStream {
/**
- * 源 Token 列表。
+ * 源 Token 列表
*/
private final List tokens;
-
/**
- * 当前解析位置索引。
+ * 当前解析位置索引
*/
private int pos = 0;
/**
* 使用 Token 列表构造 TokenStream。
*
- * @param tokens 由词法分析器产生的 Token 集合
+ * @param tokens 词法分析器输出的 Token 集合
* @throws NullPointerException 如果 tokens 为 null
*/
public TokenStream(List tokens) {
@@ -37,14 +39,13 @@ public class TokenStream {
}
/**
- * 向前查看指定偏移量处的 Token(不移动位置)。
- * 会在 offset==0 时自动跳过当前位置的所有注释(COMMENT)token。
+ * 向前查看指定偏移量处的 Token(不移动当前位置)。
+ * 在 {@code offset == 0} 时自动跳过所有连续的注释(COMMENT)token。
*
- * @param offset 相对当前位置的偏移量(0 表示当前 token)
+ * @param offset 相对当前位置的偏移量(0 表示当前位置 token)
* @return 指定位置的 Token;若越界则返回自动构造的 EOF Token
*/
public Token peek(int offset) {
- // 只在 offset==0 时跳注释,向前多步 peek 由调用方控制
if (offset == 0) {
skipTrivia();
}
@@ -56,9 +57,9 @@ public class TokenStream {
}
/**
- * 查看当前位置的 Token,等效于 {@code peek(0)}。
+ * 查看当前位置的有效 Token(已跳过注释)。
*
- * @return 当前有效 Token(已跳过注释)
+ * @return 当前 Token,等效于 {@code peek(0)}
*/
public Token peek() {
skipTrivia();
@@ -66,21 +67,21 @@ public class TokenStream {
}
/**
- * 消费当前位置的 Token 并返回,位置前移。注释 token 会被自动跳过。
+ * 消费当前位置的有效 Token 并前移指针,自动跳过注释 token。
*
- * @return 被消费的有效 Token(已跳过注释)
+ * @return 被消费的有效 Token
*/
public Token next() {
- Token t = peek(); // peek() 已跳过注释
- pos++; // 指针指向下一个 raw token
- skipTrivia(); // 立即吞掉紧随其后的注释(若有)
+ Token t = peek();
+ pos++;
+ skipTrivia();
return t;
}
/**
- * 匹配当前 Token 的词素与指定字符串,若匹配则消费该 token 并前移指针。
+ * 若当前 Token 的词素等于指定字符串,则消费该 Token 并前移,否则不变。
*
- * @param lexeme 待匹配的词素字符串
+ * @param lexeme 目标词素字符串
* @return 匹配成功返回 true,否则返回 false
*/
public boolean match(String lexeme) {
@@ -92,75 +93,60 @@ public class TokenStream {
}
/**
- * 断言当前 Token 的词素与指定值相符,否则抛出 {@link ParseException}。
- * 匹配成功会消费该 token 并前移指针。
+ * 断言当前位置 Token 的词素等于指定值,否则抛出 {@link ParseException}。
+ * 匹配成功时消费该 Token 并前移。
*
- * @param lexeme 期望的词素值
+ * @param lexeme 期望的词素字符串
* @return 匹配成功的 Token
- * @throws ParseException 若词素不符
+ * @throws ParseException 若词素不匹配
*/
public Token expect(String lexeme) {
Token t = peek();
if (!t.getLexeme().equals(lexeme)) {
throw new ParseException(
- "期望的词素是'" + lexeme + "',但得到的是'" + t.getLexeme() +
- "在" + t.getLine() + ":" + t.getCol()
+ "期望的词素是 '" + lexeme + "',但得到的是 '" + t.getLexeme() + "'",
+ t.getLine(), t.getCol()
);
}
return next();
}
/**
- * 断言当前 Token 类型为指定类型,否则抛出 {@link ParseException}。
- * 匹配成功会消费该 token 并前移指针。
+ * 断言当前位置 Token 类型为指定类型,否则抛出 {@link ParseException}。
+ * 匹配成功时消费该 Token 并前移。
*
* @param type 期望的 Token 类型
* @return 匹配成功的 Token
- * @throws ParseException 若类型不匹配
+ * @throws ParseException 若类型不符
*/
public Token expectType(TokenType type) {
Token t = peek();
if (t.getType() != type) {
throw new ParseException(
- "期望的标记类型为 " + type + " 但实际得到的是 " + t.getType() +
- " ('" + t.getLexeme() + "') 在 " + t.getLine() + ":" + t.getCol()
+ "期望的标记类型为 " + type + ",但实际得到的是 " + t.getType() +
+ " ('" + t.getLexeme() + "')",
+ t.getLine(), t.getCol()
);
}
return next();
}
/**
- * 判断是否“已经”到达文件末尾(EOF)。
+ * 判断是否已到达文件末尾(EOF)。
*
- * @return 若当前位置 Token 为 EOF,则返回 true,否则返回 false
+ * @return 若当前位置 Token 为 EOF,则返回 true;否则返回 false
*/
public boolean isAtEnd() {
- return peek().getType() != TokenType.EOF;
+ return peek().getType() == TokenType.EOF;
}
/**
- * 跳过所有连续的注释(COMMENT)token。
- *
- *
- * 此方法会检查当前指针 pos 所指向的 token,
- * 如果其类型为 TokenType.COMMENT,则直接将指针递增,
- * 直到遇到非 COMMENT 类型或到达 token 列表末尾。
- *
- *
- *
- * 注意:此方法只会跳过注释 ,不会递归或调用任何
- * 会产生递归的方法(如 peek()/next()),以避免堆栈溢出。
- *
- *
- *
- * 使用场景:词法分析产物中允许出现注释 token,语法分析时需要自动跳过它们,
- * 保证 parser 只处理有效语法 token。
- *
+ * 跳过所有连续的注释(COMMENT)token,使解析器总是定位在第一个有效 Token 上。
*/
private void skipTrivia() {
while (pos < tokens.size()
&& tokens.get(pos).getType() == TokenType.COMMENT) {
- pos++; // 直接跳过 COMMENT 类型
+ pos++;
}
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/UnexpectedToken.java b/src/main/java/org/jcnc/snow/compiler/parser/context/UnexpectedToken.java
index bfa5ecd..ebaaef7 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/UnexpectedToken.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/UnexpectedToken.java
@@ -1,11 +1,21 @@
package org.jcnc.snow.compiler.parser.context;
/**
- * 当解析过程中遇到意料之外或无法识别的 Token 时抛出。
+ * 表示在语法分析过程中遇到意料之外或无法识别的 Token 时抛出的异常。
+ *
+ * 当分析器检测到实际遇到的 Token 不符合语法规则,或与预期类型不符时会抛出本异常,便于错误定位和报告。
+ *
*/
public final class UnexpectedToken extends ParseException {
- public UnexpectedToken(String message) {
- super(message);
+ /**
+ * 构造一个“意外的 Token”异常。
+ *
+ * @param actual 实际遇到的 Token 描述
+ * @param line 发生异常的行号
+ * @param column 发生异常的列号
+ */
+ public UnexpectedToken(String actual, int line, int column) {
+ super("意外的 Token: " + actual, line, column);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/UnsupportedFeature.java b/src/main/java/org/jcnc/snow/compiler/parser/context/UnsupportedFeature.java
index 558f32a..a1288bb 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/UnsupportedFeature.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/UnsupportedFeature.java
@@ -1,11 +1,21 @@
package org.jcnc.snow.compiler.parser.context;
/**
- * 当源码使用了当前编译器尚未支持的语言特性或语法时抛出。
+ * 表示在语法分析过程中使用了尚未支持的语法或语言特性时抛出的异常。
+ *
+ * 当用户使用了当前编译器实现尚不支持的语法、关键字或特性时,语法分析器将抛出此异常,用于清晰提示和错误报告。
+ *
*/
public final class UnsupportedFeature extends ParseException {
- public UnsupportedFeature(String message) {
- super(message);
+ /**
+ * 构造一个“暂未支持的语法/特性”异常。
+ *
+ * @param feature 未被支持的语法或特性描述
+ * @param line 发生异常的行号
+ * @param column 发生异常的列号
+ */
+ public UnsupportedFeature(String feature, int line, int column) {
+ super("暂未支持的语法/特性: " + feature, line, column);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
index f278b41..93f3547 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
@@ -3,9 +3,7 @@ package org.jcnc.snow.compiler.parser.core;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
-import org.jcnc.snow.compiler.parser.context.ParserContext;
-import org.jcnc.snow.compiler.parser.context.TokenStream;
-import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
+import org.jcnc.snow.compiler.parser.context.*;
import org.jcnc.snow.compiler.parser.factory.TopLevelParserFactory;
import java.util.ArrayList;
@@ -14,18 +12,36 @@ import java.util.StringJoiner;
/**
* 语法解析引擎(ParserEngine)。
- * 驱动顶层解析,并在捕获异常后通过同步机制恢复,防止死循环。
+ *
+ * 负责驱动顶层语法解析,并统一处理、收集所有语法异常,防止死循环,确保整体解析流程的健壮性与鲁棒性。
+ * 支持基于同步点的错误恢复,适用于命令式和脚本式语法环境。
+ *
+ *
+ *
+ * 本引擎以异常收集为核心设计,所有捕获到的 {@link ParseException} 会被聚合,在分析结束后一次性统一抛出。
+ * 同时,在解析出错时会通过同步(synchronize)机制,跳过错误片段以恢复到有效解析点,避免因指针停滞导致的死循环。
+ *
*/
public record ParserEngine(ParserContext ctx) {
- /** 解析整份 TokenStream,返回顶层 AST 节点列表。 */
+ /**
+ * 解析整个 TokenStream,返回顶层 AST 节点列表。
+ *
+ * 过程中如遇语法异常,均会被收集并在最后聚合抛出,避免单点失败导致整个解析中断。
+ *
+ *
+ * @return 解析所得的顶层 AST 节点列表
+ * @throws UnexpectedToken 当存在语法错误时,统一抛出聚合异常
+ */
public List parse() {
List nodes = new ArrayList<>();
- List errs = new ArrayList<>();
- TokenStream ts = ctx.getTokens();
+ List errs = new ArrayList<>();
+
+ TokenStream ts = ctx.getTokens();
+ String file = ctx.getSourceName();
// 主循环至 EOF
- while (ts.isAtEnd()) {
+ while (!ts.isAtEnd()) {
// 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
@@ -35,39 +51,46 @@ public record ParserEngine(ParserContext ctx) {
TopLevelParser parser = TopLevelParserFactory.get(ts.peek().getLexeme());
try {
nodes.add(parser.parse(ctx));
- } catch (Exception ex) {
- errs.add(ex.getMessage());
- synchronize(ts); // 出错后同步恢复
+ } catch (ParseException ex) {
+ // 收集错误并尝试同步
+ errs.add(new ParseError(file, ex.getLine(), ex.getColumn(), ex.getReason()));
+ synchronize(ts);
}
}
- // 聚合并抛出全部语法错误
+ /* ───── 统一抛出聚合异常 ───── */
if (!errs.isEmpty()) {
StringJoiner sj = new StringJoiner("\n - ", "", "");
- errs.forEach(sj::add);
- throw new UnexpectedToken("解析过程中检测到 "
- + errs.size() + " 处错误:\n - " + sj);
+ errs.forEach(e -> sj.add(e.toString()));
+
+ String msg = "解析过程中检测到 " + errs.size() + " 处错误:\n - " + sj;
+ throw new UnexpectedToken(msg, 0, 0);
}
return nodes;
}
/**
- * 同步:跳过当前行或直到遇到 **显式注册** 的顶层关键字。
- * 这样可避免因默认脚本解析器导致指针停滞而进入死循环。
+ * 同步:跳过当前行或直到遇到显式注册的顶层关键字。
+ *
+ * 该机制用于语法出错后恢复到下一个可能的有效解析点,防止指针停滞导致死循环或重复抛错。
+ * 同步过程中会优先跳过本行所有未识别 token,并在遇到换行或注册关键字时停止,随后跳过连续空行。
+ *
+ *
+ * @param ts 词法 token 流
*/
private void synchronize(TokenStream ts) {
- while (ts.isAtEnd()) {
+ while (!ts.isAtEnd()) {
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
break;
}
if (TopLevelParserFactory.isRegistered(ts.peek().getLexeme())) {
- break; // 仅在已注册关键字处停下
+ break; // 仅在已注册关键字处停下
}
- ts.next(); // 继续丢弃 token
+ ts.next(); // 继续丢弃 token
}
- // 清掉后续连续空行
- while (ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
+ // 清理后续连续空行
+ while (!ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
index c0a869e..df2ee36 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
@@ -13,64 +13,57 @@ import java.util.HashMap;
import java.util.Map;
/**
- * {@code PrattExpressionParser} 是基于 Pratt 算法实现的表达式解析器。
+ * {@code PrattExpressionParser} 基于 Pratt 算法的表达式解析器实现。
*
- * 它支持灵活的运算符优先级控制,结合前缀(PrefixParselet)和中缀(InfixParselet)解析器,
- * 可高效解析复杂表达式结构,包括:
- *
- * 字面量(数字、字符串)
- * 标识符
- * 函数调用、成员访问
- * 带括号的表达式、二元运算符
- *
- * 本类提供统一注册机制和递归表达式解析入口。
+ * 该类通过前缀(PrefixParselet)和中缀(InfixParselet)解析器注册表,
+ * 支持灵活扩展的表达式语法,包括字面量、变量、函数调用、成员访问和各种运算符表达式。
+ *
+ *
+ * 运算符优先级通过枚举控制,结合递归解析实现高效的优先级处理和语法结构解析。
+ * 未注册的语法类型或运算符会统一抛出 {@link UnsupportedFeature} 异常。
*
*/
public class PrattExpressionParser implements ExpressionParser {
- /**
- * 前缀解析器注册表:按 Token 类型映射
- */
+ /** 前缀解析器注册表(按 Token 类型名索引) */
private static final Map prefixes = new HashMap<>();
-
- /**
- * 中缀解析器注册表:按运算符词素映射
- */
+ /** 中缀解析器注册表(按运算符词素索引) */
private static final Map infixes = new HashMap<>();
static {
- // 注册前缀解析器
- prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
- prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
- prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
- prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
- prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
+ // 前缀解析器注册
+ prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
+ prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
+ prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
+ prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
+ prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
- // 注册一元前缀运算
+ // 一元前缀运算符
prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet());
- prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
+ prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
- // 注册中缀解析器
- infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
- infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true));
- infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true));
- infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("!=", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put(">=", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("<=", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("(", new CallParselet());
- infixes.put(".", new MemberParselet());
+ // 中缀解析器注册
+ infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
+ infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true));
+ infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true));
+ infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("!=", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put(">=", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("<=", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("(", new CallParselet());
+ infixes.put(".", new MemberParselet());
}
/**
- * 表达式解析入口,使用最低优先级启动递归解析。
+ * 表达式解析统一入口。
+ * 以最低优先级启动递归下降,适配任意表达式复杂度。
*
- * @param ctx 当前语法解析上下文
- * @return 表达式抽象语法树节点
+ * @param ctx 当前解析上下文
+ * @return 解析后的表达式 AST 节点
*/
@Override
public ExpressionNode parse(ParserContext ctx) {
@@ -78,28 +71,41 @@ public class PrattExpressionParser implements ExpressionParser {
}
/**
- * 根据指定优先级解析表达式。
+ * 按指定优先级解析表达式。Pratt 算法主循环。
+ *
+ * 先根据当前 Token 类型查找前缀解析器进行初始解析,
+ * 然后根据优先级不断递归处理中缀运算符和右侧表达式。
+ *
*
- * @param ctx 当前上下文
- * @param prec 当前优先级阈值
+ * @param ctx 解析上下文
+ * @param prec 当前运算符优先级阈值
* @return 构建完成的表达式节点
+ * @throws UnsupportedFeature 若遇到未注册的前缀或中缀解析器
*/
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
Token token = ctx.getTokens().next();
PrefixParselet prefix = prefixes.get(token.getType().name());
if (prefix == null) {
- throw new UnsupportedFeature("没有为该 Token 类型注册前缀解析器: " + token.getType());
+ throw new UnsupportedFeature(
+ "没有为该 Token 类型注册前缀解析器: " + token.getType(),
+ token.getLine(),
+ token.getCol()
+ );
}
ExpressionNode left = prefix.parse(ctx, token);
- while (ctx.getTokens().isAtEnd()
+ while (!ctx.getTokens().isAtEnd()
&& prec.ordinal() < nextPrecedence(ctx)) {
String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex);
if (infix == null) {
+ Token t = ctx.getTokens().peek();
throw new UnsupportedFeature(
- "没有为该 Token 类型注册中缀解析器: " + token.getType());
+ "没有为该运算符注册中缀解析器: '" + lex + "'",
+ t.getLine(),
+ t.getCol()
+ );
}
left = infix.parse(ctx, left);
}
@@ -107,10 +113,10 @@ public class PrattExpressionParser implements ExpressionParser {
}
/**
- * 获取下一个中缀解析器的优先级,用于判断是否继续解析。
+ * 获取下一个中缀解析器的优先级(Pratt 算法核心)。
*
- * @param ctx 当前上下文
- * @return 优先级枚举 ordinal 值;若无解析器则为 -1
+ * @param ctx 当前解析上下文
+ * @return 下一个中缀运算符的优先级序号;若无解析器则为 -1
*/
private int nextPrecedence(ParserContext ctx) {
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
index bcf2555..8a21e70 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
@@ -1,12 +1,12 @@
package org.jcnc.snow.compiler.parser.module;
import org.jcnc.snow.compiler.lexer.token.TokenType;
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.parser.ast.ImportNode;
+import org.jcnc.snow.compiler.parser.ast.ModuleNode;
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
-import org.jcnc.snow.compiler.parser.ast.ImportNode;
-import org.jcnc.snow.compiler.parser.ast.ModuleNode;
-import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.function.FunctionParser;
@@ -14,88 +14,85 @@ import java.util.ArrayList;
import java.util.List;
/**
- * {@code ModuleParser} 类负责解析源码中的模块定义结构,属于顶层结构解析器的一种。
+ * {@code ModuleParser} 负责解析源码中的模块结构,是顶层结构解析器实现之一。
*
- * 模块中可包含多个导入语句和函数定义,导入语句可在模块中任意位置出现,
- * 同时支持空行,空行将被自动忽略,不影响语法结构的正确性。
+ * 模块定义可包含多个导入(import)语句和函数定义(function),
+ * 导入语句可在模块中任意位置出现,且允许模块体中穿插任意数量的空行(空行会被自动忽略,不影响语法结构)。
+ *
+ *
+ *
+ * 典型模块语法结构:
+ *
+ * module: mymod
+ * import ...
+ * function ...
+ * ...
+ * end module
+ *
+ *
*/
public class ModuleParser implements TopLevelParser {
/**
- * 解析一个模块定义块,返回构建好的 {@link ModuleNode} 对象。
+ * 解析一个模块定义块,返回完整的 {@link ModuleNode} 语法树节点。
*
- * 本方法的语法流程包括:
+ * 解析过程包括:
*
- * 匹配模块声明开头 {@code module: IDENTIFIER}。
- * 收集模块体中的 import 语句与 function 定义,允许穿插空行。
- * 模块结尾必须为 {@code end module},且后接换行符。
+ * 匹配模块声明起始 {@code module: IDENTIFIER}。
+ * 收集模块体内所有 import 和 function 语句,允许穿插空行。
+ * 匹配模块结束 {@code end module}。
*
- * 所有语法错误将在解析过程中抛出异常,以便准确反馈问题位置和原因。
+ * 若遇到未识别的语句,将抛出 {@link UnexpectedToken} 异常,定位错误位置和原因。
+ *
*
- * @param ctx 当前解析器上下文,包含词法流、状态信息等。
- * @return 返回一个 {@link ModuleNode} 实例,表示完整模块的语法结构。
- * @throws UnexpectedToken 当模块体中出现未识别的语句时抛出。
+ * @param ctx 当前解析上下文(包含词法流等状态)
+ * @return 解析得到的 {@link ModuleNode} 实例
+ * @throws UnexpectedToken 当模块体中出现未识别的顶层语句时抛出
*/
@Override
public ModuleNode parse(ParserContext ctx) {
- // 获取当前上下文中提供的词法流
TokenStream ts = ctx.getTokens();
- // 获取当前 token 的行号、列号和文件名
- int line = ctx.getTokens().peek().getLine();
- int column = ctx.getTokens().peek().getCol();
+ int line = ts.peek().getLine();
+ int column = ts.peek().getCol();
String file = ctx.getSourceName();
- // 期望模块声明以关键字 "module:" 开始
ts.expect("module");
ts.expect(":");
-
- // 读取模块名称(要求为标识符类型的词法单元)
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
-
- // 模块声明必须以换行符结束
ts.expectType(TokenType.NEWLINE);
- // 初始化模块的导入节点列表与函数节点列表
List imports = new ArrayList<>();
List functions = new ArrayList<>();
- // 创建 import 与 function 的子解析器
ImportParser importParser = new ImportParser();
FunctionParser funcParser = new FunctionParser();
- // 进入模块主体内容解析循环
while (true) {
- // 跳过所有空行(即连续的 NEWLINE)
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
continue;
}
-
- // 若遇到 "end",则表明模块定义结束
if ("end".equals(ts.peek().getLexeme())) {
break;
}
-
- // 根据当前行首关键字决定解析器的选择
String lex = ts.peek().getLexeme();
if ("import".equals(lex)) {
- // 调用导入语句解析器,解析多个模块导入节点
imports.addAll(importParser.parse(ctx));
} else if ("function".equals(lex)) {
- // 调用函数定义解析器,解析单个函数结构
functions.add(funcParser.parse(ctx));
} else {
- // 遇到无法识别的语句开头,抛出异常并提供详细提示
- throw new UnexpectedToken("Unexpected token in module: " + lex);
+ throw new UnexpectedToken(
+ "Unexpected token in module: " + lex,
+ ts.peek().getLine(),
+ ts.peek().getCol()
+ );
}
}
- // 确保模块体以 "end module" 结束
ts.expect("end");
ts.expect("module");
- // 构建并返回完整的模块语法树节点
return new ModuleNode(name, imports, functions, line, column, file);
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
index 3c14a90..f824b6b 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
@@ -2,8 +2,8 @@ package org.jcnc.snow.compiler.parser.statement;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.AssignmentNode;
-import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.ExpressionStatementNode;
+import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
@@ -11,66 +11,57 @@ import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/**
- * {@code ExpressionStatementParser} 负责解析通用表达式语句,包括赋值语句和单一表达式语句。
+ * {@code ExpressionStatementParser} 用于解析通用表达式语句(赋值或普通表达式)。
*
- * 支持的语法结构如下:
+ * 支持以下两种语法结构:
*
{@code
* x = 1 + 2 // 赋值语句
- * doSomething() // 函数调用等普通表达式语句
+ * doSomething() // 一般表达式语句
* }
*
- * 若以标识符开头,且后接等号 {@code =},则视为赋值语句,解析为 {@link AssignmentNode}。
+ * 以标识符开头且后接 {@code =} 时,解析为 {@link AssignmentNode}。
* 否则视为普通表达式,解析为 {@link ExpressionStatementNode}。
- * 所有表达式语句必须以换行符 {@code NEWLINE} 结束。
+ * 所有表达式语句必须以换行符({@code NEWLINE})结尾。
*
- * 不允许以关键字或空行作为表达式的起始,若遇到非法开头,将抛出解析异常。
+ * 若语句起始为关键字或空行,将直接抛出异常,防止非法语法进入表达式解析流程。
*/
public class ExpressionStatementParser implements StatementParser {
/**
- * 解析一个表达式语句,根据上下文决定其为赋值或一般表达式。
- *
- * 具体逻辑如下:
- *
- * 若当前行为标识符后接等号,则作为赋值处理。
- * 否则解析整个表达式作为单独语句。
- * 所有语句都必须以换行符结束。
- * 若表达式以关键字或空行开头,将立即抛出异常,避免非法解析。
- *
+ * 解析单行表达式语句,根据上下文判断其为赋值语句或普通表达式语句。
*
- * @param ctx 当前解析上下文,提供词法流与状态信息。
- * @return 返回 {@link AssignmentNode} 或 {@link ExpressionStatementNode} 表示的语法节点。
- * @throws UnexpectedToken 若表达式起始为关键字或语法非法。
+ * @param ctx 当前解析上下文,提供词法流与环境信息
+ * @return {@link AssignmentNode} 或 {@link ExpressionStatementNode} 语法节点
+ * @throws UnexpectedToken 若遇到非法起始(关键字、空行等)
*/
@Override
public StatementNode parse(ParserContext ctx) {
TokenStream ts = ctx.getTokens();
- // 快速检查:若遇空行或关键字开头,不可作为表达式语句
if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) {
- throw new UnexpectedToken("无法解析以关键字开头的表达式: " + ts.peek().getLexeme());
+ throw new UnexpectedToken(
+ "无法解析以关键字开头的表达式: " + ts.peek().getLexeme(),
+ ts.peek().getLine(),
+ ts.peek().getCol()
+ );
}
- // 获取当前 token 的行号、列号和文件名
- int line = ctx.getTokens().peek().getLine();
- int column = ctx.getTokens().peek().getCol();
+ int line = ts.peek().getLine();
+ int column = ts.peek().getCol();
String file = ctx.getSourceName();
- // 处理赋值语句:格式为 identifier = expression
- if (ts.peek().getType() == TokenType.IDENTIFIER
- && ts.peek(1).getLexeme().equals("=")) {
-
- String varName = ts.next().getLexeme(); // 消耗标识符
- ts.expect("="); // 消耗等号
- ExpressionNode value = new PrattExpressionParser().parse(ctx); // 解析表达式
- ts.expectType(TokenType.NEWLINE); // 语句必须以换行符结束
- return new AssignmentNode(varName, value, line, column, file); // 返回赋值节点
+ // 赋值语句:IDENTIFIER = expr
+ if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
+ String varName = ts.next().getLexeme();
+ ts.expect("=");
+ ExpressionNode value = new PrattExpressionParser().parse(ctx);
+ ts.expectType(TokenType.NEWLINE);
+ return new AssignmentNode(varName, value, line, column, file);
}
- // 处理普通表达式语句,如函数调用、字面量、运算表达式等
+ // 普通表达式语句
ExpressionNode expr = new PrattExpressionParser().parse(ctx);
- ts.expectType(TokenType.NEWLINE); // 语句必须以换行符结束
- return new ExpressionStatementNode(expr, line, column, file); // 返回表达式语句节点
+ ts.expectType(TokenType.NEWLINE);
+ return new ExpressionStatementNode(expr, line, column, file);
}
-
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java
index 90cbc2c..692e5a4 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java
@@ -10,84 +10,78 @@ import java.util.function.BiConsumer;
import java.util.function.Predicate;
/**
- * {@code FlexibleSectionParser} 是一个通用的语法块解析工具。
+ * {@code FlexibleSectionParser} 是一个通用的区块(Section)解析工具。
*
- * 该工具支持解析由关键字标识的多段结构化区块内容,常用于解析函数、类、模块、循环等语法单元中的命名子结构。
- * 相比传统硬编码方式,提供更灵活、可组合的解析能力,允许解析器模块动态注册处理逻辑,而非将所有逻辑写死在主流程中。
+ * 支持通过注册表驱动的方式解析具有区块关键字标识的多段结构内容,
+ * 常用于函数、类、模块、循环等语法单元中的命名子结构。
+ * 通过外部注册解析逻辑,支持高度可扩展与复用。
+ *
*
- * 典型应用包括:
+ *
+ * 典型用途包括:
*
* 函数体解析中的 {@code params}、{@code returns}、{@code body} 等部分
* 模块定义中的 {@code imports}、{@code functions} 等部分
- * 用户自定义 DSL 的可扩展语法结构
+ * 可扩展 DSL 的结构化语法区块
*
+ *
*
- * 该工具具备以下能力:
+ *
主要特性:
*
* 自动跳过注释与空行
- * 根据区块名称调用外部提供的解析器
- * 支持终止标志(如 {@code end})来退出解析流程
+ * 区块入口通过关键字匹配和可选条件判断
+ * 解析逻辑由外部以函数式接口方式注册
+ * 支持遇到终止关键字(如 {@code end})时自动停止
*
*/
public class FlexibleSectionParser {
/**
- * 启动结构化区块的统一解析流程。
- *
- * 每次调用会:
- *
- * 从 token 流中跳过空行与注释
- * 依照当前 token 判断是否匹配某个区块
- * 调用对应 {@link SectionDefinition} 执行区块解析逻辑
- * 若遇到 {@code end} 关键字,则终止解析过程
- * 若当前 token 不匹配任何已注册区块,抛出异常
- *
+ * 解析并分派处理多区块结构。
*
- * @param ctx 当前解析上下文,提供语法环境与作用域信息
- * @param tokens 当前 token 流
- * @param sectionDefinitions 各个区块的定义映射(key 为关键字,value 为判断 + 解析逻辑组合)
- * @throws UnexpectedToken 若出现无法识别的关键字或未满足的匹配条件
+ * @param ctx 解析上下文
+ * @param tokens 词法流
+ * @param sectionDefinitions 区块处理注册表,key 为区块关键字,value 为对应的处理定义
+ * @throws UnexpectedToken 遇到未注册或条件不符的关键字时抛出
*/
public static void parse(ParserContext ctx,
TokenStream tokens,
Map sectionDefinitions) {
- // 跳过开头的注释或空行
skipCommentsAndNewlines(tokens);
while (true) {
- // 跳过当前区块之间的空白与注释
skipCommentsAndNewlines(tokens);
String keyword = tokens.peek().getLexeme();
- // 结束关键字表示解析流程终止
if ("end".equals(keyword)) {
break;
}
- // 查找匹配的区块定义
SectionDefinition definition = sectionDefinitions.get(keyword);
if (definition != null && definition.condition().test(tokens)) {
- definition.parser().accept(ctx, tokens); // 执行解析逻辑
+ definition.parser().accept(ctx, tokens);
} else {
- throw new UnexpectedToken("未识别的关键字或条件不满足: " + keyword);
+ throw new UnexpectedToken(
+ "未识别的关键字或条件不满足: " + keyword,
+ tokens.peek().getLine(),
+ tokens.peek().getCol()
+ );
}
}
}
/**
- * 跳过连续出现的注释行或空行(NEWLINE)。
- *
- * 该方法用于在区块之间清理无效 token,避免影响结构判断。
+ * 跳过所有连续的注释(COMMENT)和空行(NEWLINE)token。
*
- * @param tokens 当前 token 流
+ * @param tokens 当前词法流
*/
private static void skipCommentsAndNewlines(TokenStream tokens) {
while (true) {
TokenType type = tokens.peek().getType();
if (type == TokenType.COMMENT || type == TokenType.NEWLINE) {
- tokens.next(); // 跳过注释或换行
+ tokens.next();
continue;
}
break;
@@ -95,17 +89,10 @@ public class FlexibleSectionParser {
}
/**
- * 表示一个结构区块的定义,包含匹配条件与解析器。
- *
- * 每个区块由两部分组成:
- *
- * {@code condition}:用于判断当前 token 是否应进入该区块
- * {@code parser}:该区块对应的实际解析逻辑
- *
- * 可实现懒加载、多语言支持或 DSL 的结构化扩展。
+ * 区块定义,包含进入区块的判断条件与具体解析逻辑。
*
- * @param condition 判断是否触发该区块的谓词函数
- * @param parser 区块解析逻辑(消费语法上下文与 token 流)
+ * @param condition 匹配区块的前置条件
+ * @param parser 区块内容的具体解析操作
*/
public record SectionDefinition(Predicate condition,
BiConsumer parser) {
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java
index 6d77478..41eb1fa 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java
@@ -6,77 +6,61 @@ import java.util.*;
import java.util.Map.Entry;
/**
- * JSON 工具类,提供线程安全、可重用的解析与序列化功能
+ * JSON 工具类,提供线程安全、可重用的 JSON 解析与序列化能力。
*
- * - 解析:将合法的 JSON 文本转换为 Java 原生对象(Map、List、String、Number、Boolean 或 null)
- * - 序列化:将 Java 原生对象转换为符合 JSON 标准的字符串
- *
- * 设计要点:
- * 1. 使用静态方法作为唯一入口,避免状态共享导致的线程安全问题
- * 2. 解析器内部使用 char[] 缓冲区,提高访问性能
- * 3. 维护行列号信息,抛出异常时能精确定位错误位置
- * 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建
+ * 主要功能:
+ *
+ * 解析:将合法的 JSON 文本转换为 Java 原生对象(Map、List、String、Number、Boolean 或 null)
+ * 序列化:将 Java 原生对象转换为符合 JSON 标准的字符串
+ *
+ *
+ *
+ * 设计要点:
+ *
+ * 仅提供静态方法入口,无状态,线程安全
+ * 解析器内部采用 char[] 缓冲区,支持高性能处理
+ * 精确维护行列号信息,异常可定位错误文本位置
+ * 序列化器使用 StringBuilder,默认预分配容量
+ *
*/
public class JSONParser {
- private JSONParser() {
- }
+ private JSONParser() {}
/**
- * 将 JSON 文本解析为对应的 Java 对象
+ * 解析 JSON 格式字符串为对应的 Java 对象。
*
* @param input JSON 格式字符串
- * @return 对应的 Java 原生对象:
- * - JSON 对象 -> Map
- * - JSON 数组 -> List
- * - JSON 字符串 -> String
- * - JSON 数值 -> Long 或 Double
- * - JSON 布尔 -> Boolean
- * - JSON null -> null
- * @throws UnexpectedToken 如果遇到语法错误或多余字符,异常消息中包含行列信息
+ * @return 解析得到的 Java 对象(Map、List、String、Number、Boolean 或 null)
+ * @throws UnexpectedToken 语法错误或多余字符,异常消息带行列定位
*/
public static Object parse(String input) {
return new Parser(input).parseInternal();
}
/**
- * 将 Java 原生对象序列化为 JSON 字符串
+ * 将 Java 原生对象序列化为 JSON 字符串。
*
- * @param obj 支持的类型:Map、Collection、String、Number、Boolean 或 null
+ * @param obj 支持 Map、Collection、String、Number、Boolean 或 null
* @return 符合 JSON 规范的字符串
+ * @throws UnsupportedOperationException 遇到不支持的类型时抛出
*/
public static String toJson(Object obj) {
return Writer.write(obj);
}
- // ======= 内部解析器 =======
+ // ======= 内部解析器实现 =======
/**
- * 负责将 char[] 缓冲区中的 JSON 文本解析为 Java 对象
+ * 负责将 char[] 缓冲区中的 JSON 文本解析为 Java 对象。
+ * 维护行列号,所有异常均带精确位置。
*/
private static class Parser {
- /**
- * 输入缓冲区
- */
private final char[] buf;
- /**
- * 当前解析到的位置索引
- */
private int pos;
- /**
- * 当前字符所在行号,从 1 开始
- */
private int line;
- /**
- * 当前字符所在列号,从 1 开始
- */
private int col;
- /**
- * 构造解析器,初始化缓冲区和行列信息
- *
- * @param input 待解析的 JSON 文本
- */
Parser(String input) {
this.buf = input.toCharArray();
this.pos = 0;
@@ -85,7 +69,7 @@ public class JSONParser {
}
/**
- * 入口方法,跳过空白后调用 parseValue,再校验尾部无多余字符
+ * 解析主入口,校验无多余字符。
*/
Object parseInternal() {
skipWhitespace();
@@ -98,7 +82,7 @@ public class JSONParser {
}
/**
- * 根据下一个字符决定解析哪种 JSON 值
+ * 解析 JSON 值(null, true, false, string, number, object, array)
*/
private Object parseValue() {
skipWhitespace();
@@ -111,33 +95,31 @@ public class JSONParser {
if (c == '[') return parseArray();
if (c == '-' || isDigit(c)) return parseNumber();
error("遇到意外字符 '" + c + "'");
- return null; // 永不到达
+ return null;
}
/**
- * 解析 JSON 对象,返回 Map
+ * 解析对象类型 { ... }
*/
private Map parseObject() {
- expect('{'); // 跳过 '{'
+ expect('{');
skipWhitespace();
Map map = new LinkedHashMap<>();
- // 空对象 {}
if (currentChar() == '}') {
- advance(); // 跳过 '}'
+ advance();
return map;
}
- // 多成员对象解析
while (true) {
skipWhitespace();
- String key = parseString(); // 解析键
+ String key = parseString();
skipWhitespace();
expect(':');
skipWhitespace();
- Object val = parseValue(); // 解析值
+ Object val = parseValue();
map.put(key, val);
skipWhitespace();
if (currentChar() == '}') {
- advance(); // 跳过 '}'
+ advance();
break;
}
expect(',');
@@ -147,18 +129,16 @@ public class JSONParser {
}
/**
- * 解析 JSON 数组,返回 List
+ * 解析数组类型 [ ... ]
*/
private List parseArray() {
expect('[');
skipWhitespace();
List list = new ArrayList<>();
- // 空数组 []
if (currentChar() == ']') {
- advance(); // 跳过 ']'
+ advance();
return list;
}
- // 多元素数组解析
while (true) {
skipWhitespace();
list.add(parseValue());
@@ -174,46 +154,38 @@ public class JSONParser {
}
/**
- * 解析 JSON 字符串文字,处理转义字符
+ * 解析字符串类型,支持标准 JSON 转义。
*/
private String parseString() {
- expect('"'); // 跳过开头 '"'
+ expect('"');
StringBuilder sb = new StringBuilder();
while (true) {
char c = currentChar();
if (c == '"') {
- advance(); // 跳过结束 '"'
+ advance();
break;
}
if (c == '\\') {
- advance(); // 跳过 '\'
+ advance();
c = currentChar();
switch (c) {
case '"':
- sb.append('"');
- break;
+ sb.append('"'); break;
case '\\':
- sb.append('\\');
- break;
+ sb.append('\\'); break;
case '/':
- sb.append('/');
- break;
+ sb.append('/'); break;
case 'b':
- sb.append('\b');
- break;
+ sb.append('\b'); break;
case 'f':
- sb.append('\f');
- break;
+ sb.append('\f'); break;
case 'n':
- sb.append('\n');
- break;
+ sb.append('\n'); break;
case 'r':
- sb.append('\r');
- break;
+ sb.append('\r'); break;
case 't':
- sb.append('\t');
- break;
- case 'u': // 解析 Unicode 转义
+ sb.append('\t'); break;
+ case 'u':
String hex = new String(buf, pos + 1, 4);
sb.append((char) Integer.parseInt(hex, 16));
pos += 4;
@@ -232,15 +204,14 @@ public class JSONParser {
}
/**
- * 解析 JSON 数值,支持整数、浮点及科学计数法
+ * 解析数字类型(支持整数、小数、科学计数法)。
*/
private Number parseNumber() {
int start = pos;
if (currentChar() == '-') advance();
while (isDigit(currentChar())) advance();
if (currentChar() == '.') {
- do advance();
- while (isDigit(currentChar()));
+ do { advance(); } while (isDigit(currentChar()));
}
if (currentChar() == 'e' || currentChar() == 'E') {
advance();
@@ -248,7 +219,6 @@ public class JSONParser {
while (isDigit(currentChar())) advance();
}
String num = new String(buf, start, pos - start);
- // 判断返回 Long 还是 Double
if (num.indexOf('.') >= 0 || num.indexOf('e') >= 0 || num.indexOf('E') >= 0) {
return Double.parseDouble(num);
}
@@ -260,7 +230,7 @@ public class JSONParser {
}
/**
- * 跳过所有空白字符,支持空格、制表符、回车、换行
+ * 跳过所有空白符(含换行),同时维护行/列号。
*/
private void skipWhitespace() {
while (pos < buf.length) {
@@ -273,21 +243,17 @@ public class JSONParser {
}
}
- /**
- * 获取当前位置字符,超出范围返回 '\0'
- */
private char currentChar() {
return pos < buf.length ? buf[pos] : '\0';
}
/**
- * 推进到下一个字符,并更新行列信息
+ * 指针前移并更新行/列号。
*/
private void advance() {
if (pos < buf.length) {
if (buf[pos] == '\n') {
- line++;
- col = 1;
+ line++; col = 1;
} else {
col++;
}
@@ -296,7 +262,7 @@ public class JSONParser {
}
/**
- * 验证当前位置字符等于预期字符,否则抛出错误
+ * 匹配下一个字符(或字符串),并前移指针。
*/
private void expect(char c) {
if (currentChar() != c) {
@@ -306,7 +272,7 @@ public class JSONParser {
}
/**
- * 尝试匹配给定字符串,匹配成功则移动位置并返回 true
+ * 判断当前位置是否能完整匹配目标字符串,若能则移动指针。
*/
private boolean match(String s) {
int len = s.length();
@@ -318,35 +284,30 @@ public class JSONParser {
return true;
}
- /**
- * 判断字符是否为数字
- */
private boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
/**
- * 抛出带行列定位的解析错误
+ * 抛出带行列号的解析异常。
*/
private void error(String msg) {
- throw new UnexpectedToken("在第 " + line + " 行,第 " + col + " 列出现错误: " + msg);
+ throw new UnexpectedToken(
+ "在第 " + line + " 行,第 " + col + " 列出现错误: " + msg,
+ line,
+ col
+ );
}
}
- // ======= 内部序列化器 =======
+ // ======= 内部序列化器实现 =======
/**
- * 负责高效地将 Java 对象写为 JSON 文本
+ * 负责将 Java 对象序列化为 JSON 字符串。
*/
private static class Writer {
- /**
- * 默认 StringBuilder 初始容量,避免频繁扩容
- */
private static final int DEFAULT_CAPACITY = 1024;
- /**
- * 入口方法,根据 obj 类型分派写入逻辑
- */
static String write(Object obj) {
StringBuilder sb = new StringBuilder(DEFAULT_CAPACITY);
writeValue(obj, sb);
@@ -354,7 +315,7 @@ public class JSONParser {
}
/**
- * 根据对象类型选择合适的写入方式
+ * 递归输出任意支持的 JSON 类型对象。
*/
private static void writeValue(Object obj, StringBuilder sb) {
if (obj == null) {
@@ -390,27 +351,22 @@ public class JSONParser {
}
/**
- * 为字符串添加双引号并转义必须的字符
+ * JSON 字符串输出,处理所有必要的转义字符。
*/
private static void quote(String s, StringBuilder sb) {
sb.append('"');
for (char c : s.toCharArray()) {
switch (c) {
case '\\':
- sb.append("\\\\");
- break;
+ sb.append("\\\\"); break;
case '"':
- sb.append("\\\"");
- break;
+ sb.append("\\\""); break;
case '\n':
- sb.append("\\n");
- break;
+ sb.append("\\n"); break;
case '\r':
- sb.append("\\r");
- break;
+ sb.append("\\r"); break;
case '\t':
- sb.append("\\t");
- break;
+ sb.append("\\t"); break;
default:
sb.append(c);
}
From cb208bb8af610b20133d536bfac606d3e7db91c1 Mon Sep 17 00:00:00 2001
From: Luke
Date: Sun, 6 Jul 2025 23:57:58 +0800
Subject: [PATCH 49/61] =?UTF-8?q?test:=20=E4=BF=AE=E6=94=B9=E4=B8=BA?=
=?UTF-8?q?=E6=9C=80=E6=96=B0=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
playground/BugFarm/Bug1/Main.snow | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/playground/BugFarm/Bug1/Main.snow b/playground/BugFarm/Bug1/Main.snow
index 0065669..4a13b7d 100644
--- a/playground/BugFarm/Bug1/Main.snow
+++ b/playground/BugFarm/Bug1/Main.snow
@@ -1,7 +1,17 @@
-function: main
- return_type: int 111
- body:
-
- return 65537
- end body
-end function
\ No newline at end of file
+module: Main
+ function: main
+ parameter:
+ return_type: int
+ body:
+ declare n1: int =1
+ declare n2: int =2
+ declare n3: int =1
+ if n1 ==1 then
+ if n2 ==2 then
+ n3 =3
+ end if
+ end if
+ return n3
+ end body
+ end function
+end module
\ No newline at end of file
From 0ea8cb352dc16b5da33e2a07ce3d4b56f080485f Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 7 Jul 2025 00:02:31 +0800
Subject: [PATCH 50/61] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=95=B0?=
=?UTF-8?q?=E5=AD=97=E5=AD=97=E9=9D=A2=E9=87=8F=E6=89=AB=E6=8F=8F=E9=80=BB?=
=?UTF-8?q?=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 优化了 NumberTokenScanner 类的代码结构
- 改进了数字字面量的扫描逻辑,包括整数和小数部分
- 调整了后缀和非法字符的检查策略
- 优化了错误处理和异常抛出的逻辑
- 移除了未使用的代码和不必要的注释
---
.../lexer/scanners/NumberTokenScanner.java | 76 ++++++-------------
1 file changed, 24 insertions(+), 52 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
index eef90ae..88592b9 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java
@@ -14,8 +14,9 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
* 十进制小数(如 3.14,0.5)
* 单字符类型后缀(如 2.0f,255B,合法集合见 SUFFIX_CHARS)
*
- *
+ *
* 如果后续需要支持科学计数法、下划线分隔符、不同进制等,只需扩展现有状态机的转移规则。
+ *
*
* 状态机简述:
* INT_PART --'.'--> DEC_POINT
@@ -33,12 +34,14 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
* FRAC_PART :读取小数部分,遇非法字符则结束主体。
* END :主体扫描结束,进入后缀/尾随字符判定。
*
+ *
* 错误处理策略:
*
* 数字后跟未知字母(如 42X)—— 抛出 LexicalException
* 数字与合法后缀间有空白(如 3 L)—— 抛出 LexicalException
* 小数点后缺失数字(如 1.)—— 抛出 LexicalException
*
+ *
* 支持的单字符类型后缀包括:b, s, l, f, d 及其大写形式。若需支持多字符后缀,可将该集合扩展为 Set。
*/
public class NumberTokenScanner extends AbstractTokenScanner {
@@ -55,7 +58,7 @@ public class NumberTokenScanner extends AbstractTokenScanner {
* 仅当首字符为数字时,NumberTokenScanner 介入处理。
*
* @param c 当前待判断字符
- * @param ctx 当前 LexerContext(可用于进一步判断)
+ * @param ctx 当前 LexerContext
* @return 如果为数字返回 true,否则返回 false
*/
@Override
@@ -65,31 +68,24 @@ public class NumberTokenScanner extends AbstractTokenScanner {
/**
* 按照有限状态机读取完整数字字面量,并对尾随字符进行合法性校验。
- *
- * 主体流程:
- *
- * 整数部分、可选小数点和小数部分扫描。
- * 检查合法的类型后缀。
- * 检查非法尾随字符,如未知字母、空白后缀或非法 '/'。
- * 生成并返回 NUMBER_LITERAL Token。
- *
*
- * @param ctx 当前 LexerContext(提供游标、前瞻等功能)
+ * @param ctx 当前 LexerContext
* @param line 源码起始行号(1 基)
* @param col 源码起始列号(1 基)
* @return NUMBER_LITERAL 类型的 Token
* @throws LexicalException 如果遇到非法格式或未受支持的尾随字符
*/
@Override
- protected Token scanToken(LexerContext ctx, int line, int col) {
+ protected Token scanToken(LexerContext ctx, int line, int col) throws LexicalException {
StringBuilder literal = new StringBuilder();
State state = State.INT_PART;
- // 1. 主体扫描 —— 整数 / 小数
+ /* ───── 1. 主体扫描 —— 整数 / 小数 ───── */
mainLoop:
while (!ctx.isAtEnd() && state != State.END) {
char ch = ctx.peek();
switch (state) {
+ /* 整数部分 */
case INT_PART:
if (Character.isDigit(ch)) {
literal.append(ctx.advance());
@@ -97,86 +93,62 @@ public class NumberTokenScanner extends AbstractTokenScanner {
state = State.DEC_POINT;
literal.append(ctx.advance());
} else {
- state = State.END; // 整数已结束
+ state = State.END;
}
break;
+ /* 已读到小数点,下一字符必须是数字 */
case DEC_POINT:
if (Character.isDigit(ch)) {
state = State.FRAC_PART;
literal.append(ctx.advance());
} else {
- // 如 "1." —— 语言规范不允许尾点数字
throw new LexicalException("小数点后必须跟数字", line, col);
}
break;
+ /* 小数部分 */
case FRAC_PART:
if (Character.isDigit(ch)) {
literal.append(ctx.advance());
} else {
- state = State.END; // 小数字符串结束
+ state = State.END;
}
break;
default:
- break mainLoop; // 理论不会到达
+ break mainLoop;
}
}
- // 2. 后缀及非法尾随字符检查
+ /* ───── 2. 后缀及非法尾随字符检查 ───── */
if (!ctx.isAtEnd()) {
char next = ctx.peek();
- // 2-A. 合法单字符后缀(紧邻,不允许空格)
+ /* 2-A. 合法单字符后缀(紧邻数字,不允许空格) */
if (SUFFIX_CHARS.indexOf(next) >= 0) {
literal.append(ctx.advance());
}
- // 未知单字符后缀 —— 直接报错
+ /* 2-B. 未知紧邻字母后缀 —— 报错 */
else if (Character.isLetter(next)) {
throw new LexicalException("未知的数字类型后缀 '" + next + "'", line, col);
}
- // “数字 + 空格 + 字母” —— 一律非法
- else if (Character.isWhitespace(next) && next != '\n') {
- int off = 1;
- char look;
- // 跳过空白(不含换行)
- while (true) {
- look = ctx.peekAhead(off);
- if (look == '\n' || look == '\0') break;
- if (!Character.isWhitespace(look)) break;
- off++;
- }
- if (Character.isLetter(look)) {
- throw new LexicalException("数字字面量后不允许出现空格再跟标识符/后缀", line, col);
- }
- }
- // 其他符号由外层扫描器处理
+ /* 其余情况交由外层扫描器处理(包括空白及其它符号) */
}
- // 3. 生成并返回 Token
+ /* ───── 3. 生成并返回 Token ───── */
return new Token(TokenType.NUMBER_LITERAL, literal.toString(), line, col);
}
- /**
- * FSM 内部状态。
- */
+ /** FSM 内部状态定义 */
private enum State {
- /**
- * 整数部分(小数点左侧)
- */
+ /** 整数部分 */
INT_PART,
- /**
- * 已读到小数点,但还未读到第一位小数数字
- */
+ /** 已读到小数点,但还未读到第一位小数数字 */
DEC_POINT,
- /**
- * 小数部分(小数点右侧)
- */
+ /** 小数部分 */
FRAC_PART,
- /**
- * 主体结束,准备处理后缀或交还控制权
- */
+ /** 主体结束,准备处理后缀或交还控制权 */
END
}
}
From 506bf193d542982eaece196ec9ab2f84a3d5d0c0 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 7 Jul 2025 00:26:36 +0800
Subject: [PATCH 51/61] =?UTF-8?q?chore:=20=E4=BF=AE=E6=94=B9IDEA=E9=85=8D?=
=?UTF-8?q?=E7=BD=AE=E8=AE=A9.water=E8=BE=93=E5=87=BA=E5=88=B0target?=
=?UTF-8?q?=E7=9B=AE=E5=BD=95=E5=90=8E=E4=B8=8D=E6=B1=A1=E6=9F=93=E6=A0=B9?=
=?UTF-8?q?=E7=9B=AE=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.run/Demo10.run.xml | 2 +-
.run/Demo2.run.xml | 2 +-
.run/Demo3.run.xml | 2 +-
.run/Demo4.run.xml | 2 +-
.run/Demo5.run.xml | 2 +-
.run/Demo6.run.xml | 2 +-
.run/Demo7.run.xml | 2 +-
.run/Demo8.run.xml | 2 +-
.run/Demo9.run.xml | 2 +-
9 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/.run/Demo10.run.xml b/.run/Demo10.run.xml
index 46332d1..94bfc43 100644
--- a/.run/Demo10.run.xml
+++ b/.run/Demo10.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo2.run.xml b/.run/Demo2.run.xml
index 464e671..882e77a 100644
--- a/.run/Demo2.run.xml
+++ b/.run/Demo2.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo3.run.xml b/.run/Demo3.run.xml
index 846b27a..a949e95 100644
--- a/.run/Demo3.run.xml
+++ b/.run/Demo3.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo4.run.xml b/.run/Demo4.run.xml
index 8f5ba76..57afa15 100644
--- a/.run/Demo4.run.xml
+++ b/.run/Demo4.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo5.run.xml b/.run/Demo5.run.xml
index 50e1218..cb543d1 100644
--- a/.run/Demo5.run.xml
+++ b/.run/Demo5.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo6.run.xml b/.run/Demo6.run.xml
index 8d0f410..b6c3fdc 100644
--- a/.run/Demo6.run.xml
+++ b/.run/Demo6.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo7.run.xml b/.run/Demo7.run.xml
index 2a2132e..6f25770 100644
--- a/.run/Demo7.run.xml
+++ b/.run/Demo7.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo8.run.xml b/.run/Demo8.run.xml
index 877a106..4bb76fe 100644
--- a/.run/Demo8.run.xml
+++ b/.run/Demo8.run.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.run/Demo9.run.xml b/.run/Demo9.run.xml
index 764e4f2..a6bf203 100644
--- a/.run/Demo9.run.xml
+++ b/.run/Demo9.run.xml
@@ -1,5 +1,5 @@
-
+
From 439b2fbc1ec4210286dd4d7306ea6454b0e3f2c0 Mon Sep 17 00:00:00 2001
From: Luke
Date: Thu, 10 Jul 2025 02:47:46 +0000
Subject: [PATCH 52/61] update LICENSE.
Signed-off-by: Luke
---
LICENSE | 1 +
1 file changed, 1 insertion(+)
diff --git a/LICENSE b/LICENSE
index 094dd59..8940ae8 100644
--- a/LICENSE
+++ b/LICENSE
@@ -187,6 +187,7 @@
identification within third-party archives.
Copyright © 2025 Ke Xu (Luke), on behalf of the SnowLang Project
+ Repository: https://gitee.com/jcnc-org/snow
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
From ae0baf3e50e496ee19fdc365fdf7221d03566776 Mon Sep 17 00:00:00 2001
From: Luke
Date: Thu, 10 Jul 2025 10:55:46 +0800
Subject: [PATCH 53/61] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E9=A1=B9?=
=?UTF-8?q?=E7=9B=AE=E5=A3=B0=E6=98=8E=E6=96=87=E4=BB=B6=E5=B9=B6=E6=9B=B4?=
=?UTF-8?q?=E6=96=B0=20README?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 NOTICE 文件,包含项目版权、许可信息和贡献者致谢
- 在 README.md 中添加项目仓库地址
---
NOTICE | 15 +++++++++++++++
README.md | 1 +
2 files changed, 16 insertions(+)
create mode 100644 NOTICE
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..b289ed9
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,15 @@
+==========================================
+SnowLang Project – NOTICE
+==========================================
+
+Copyright © 2025 Ke Xu (Luke), on behalf of the SnowLang Project
+Repository: https://gitee.com/jcnc-org/snow
+
+Licensed under the Apache License, Version 2.0 (the "License")
+You may obtain a copy of the License at:
+ http://www.apache.org/licenses/LICENSE-2.0
+
+===============================================================================
+This product includes software developed by the SnowLang Project and
+its contributors.
+===============================================================================
diff --git a/README.md b/README.md
index 74438c3..af43071 100644
--- a/README.md
+++ b/README.md
@@ -452,6 +452,7 @@ end module
## 版权声明
版权所有 © 2025 许轲(Luke),代表 SnowLang 项目。
+仓库地址:
本项目依据 [Apache 2.0 许可证](LICENSE) 进行许可和发布。
“SnowLang 项目”为由许轲(Luke)发起的独立开源项目。
From 2289cf3ee4b0c6a9fbcd88dbd733ff7092a5577d Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 28 Jun 2025 20:28:12 +0800
Subject: [PATCH 54/61] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20byte8=20?=
=?UTF-8?q?=E8=BD=AC=20short16=E3=80=81long64=E3=80=81float32=E3=80=81doub?=
=?UTF-8?q?le64?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../commands/type/conversion/B2DCommand.java | 47 +++++++++++++++++++
.../commands/type/conversion/B2FCommand.java | 47 +++++++++++++++++++
.../commands/type/conversion/B2LCommand.java | 47 +++++++++++++++++++
.../commands/type/conversion/B2SCommand.java | 47 +++++++++++++++++++
4 files changed, 188 insertions(+)
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2DCommand.java
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2FCommand.java
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2LCommand.java
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2SCommand.java
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2DCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2DCommand.java
new file mode 100644
index 0000000..1ca0655
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2DCommand.java
@@ -0,0 +1,47 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * B2DCommand Opcode: Represents the type conversion operation from byte8 to double64 in the virtual machine.
+ * This opcode is implemented by the {@link B2DCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top byte8 value from the operand stack.
+ * Convert the byte8 value to a double64 value.
+ * Push the converted double64 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a byte8 value to a double64 type to ensure compatibility with integer-based operations.
+ */
+public class B2DCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of B2DCommand.
+ */
+ public B2DCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the byte8 to double64 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ double convertedValue = (byte) operandStack.pop();
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2FCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2FCommand.java
new file mode 100644
index 0000000..c61891b
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2FCommand.java
@@ -0,0 +1,47 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * B2FCommand Opcode: Represents the type conversion operation from byte8 to float32 in the virtual machine.
+ * This opcode is implemented by the {@link B2FCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top byte8 value from the operand stack.
+ * Convert the byte8 value to a float32 value.
+ * Push the converted float32 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a byte8 value to a float32 type to ensure compatibility with integer-based operations.
+ */
+public class B2FCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of B2FCommand.
+ */
+ public B2FCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the byte8 to float32 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ float convertedValue = (byte) operandStack.pop();
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2LCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2LCommand.java
new file mode 100644
index 0000000..344b49d
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2LCommand.java
@@ -0,0 +1,47 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * B2LCommand Opcode: Represents the type conversion operation from byte8 to long64 in the virtual machine.
+ * This opcode is implemented by the {@link B2LCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top byte8 value from the operand stack.
+ * Convert the byte8 value to a long64 value.
+ * Push the converted long64 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a byte8 value to a long64 type to ensure compatibility with integer-based operations.
+ */
+public class B2LCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of B2LCommand.
+ */
+ public B2LCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the byte8 to long64 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ long convertedValue = (byte) operandStack.pop();
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2SCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2SCommand.java
new file mode 100644
index 0000000..1f09ff9
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/B2SCommand.java
@@ -0,0 +1,47 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * B2SCommand Opcode: Represents the type conversion operation from byte8 to short16 in the virtual machine.
+ * This opcode is implemented by the {@link B2SCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top byte8 value from the operand stack.
+ * Convert the byte8 value to a short16 value.
+ * Push the converted short16 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a byte8 value to a short16 type to ensure compatibility with integer-based operations.
+ */
+public class B2SCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of B2SCommand.
+ */
+ public B2SCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the byte8 to short16 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ short convertedValue = (byte) operandStack.pop();
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
From 9e2eb6731f1029bbe1e14f4aa26b31038395aca0 Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 28 Jun 2025 20:31:16 +0800
Subject: [PATCH 55/61] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20short16=20?=
=?UTF-8?q?=E8=BD=AC=20byte8=E3=80=81long64=E3=80=81float32=E3=80=81double?=
=?UTF-8?q?64?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../commands/type/conversion/S2BCommand.java | 47 +++++++++++++++++++
.../commands/type/conversion/S2DCommand.java | 47 +++++++++++++++++++
.../commands/type/conversion/S2FCommand.java | 47 +++++++++++++++++++
.../commands/type/conversion/S2LCommand.java | 47 +++++++++++++++++++
4 files changed, 188 insertions(+)
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2BCommand.java
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2DCommand.java
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2FCommand.java
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2LCommand.java
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2BCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2BCommand.java
new file mode 100644
index 0000000..fa86e5f
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2BCommand.java
@@ -0,0 +1,47 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * S2BCommand Opcode: Represents the type conversion operation from short16 to byte8 in the virtual machine.
+ * This opcode is implemented by the {@link S2BCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top short16 value from the operand stack.
+ * Convert the short16 value to a byte8 value.
+ * Push the converted byte8 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a short16 value to a byte8 type, facilitating subsequent integer arithmetic or comparison operations.
+ */
+public class S2BCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of S2BCommand.
+ */
+ public S2BCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the short16 to byte8 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ byte convertedValue = (byte) ((short) operandStack.pop());
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2DCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2DCommand.java
new file mode 100644
index 0000000..2f3381f
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2DCommand.java
@@ -0,0 +1,47 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * S2DCommand Opcode: Represents the type conversion operation from short16 to double64 in the virtual machine.
+ * This opcode is implemented by the {@link S2DCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top short16 value from the operand stack.
+ * Convert the short16 value to an double64 value.
+ * Push the converted double64 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a short16 value to an double64 type, facilitating subsequent integer arithmetic or comparison operations.
+ */
+public class S2DCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of S2DCommand.
+ */
+ public S2DCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the short16 to double64 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ double convertedValue = (short) operandStack.pop();
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2FCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2FCommand.java
new file mode 100644
index 0000000..78fbb4a
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2FCommand.java
@@ -0,0 +1,47 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * S2FCommand Opcode: Represents the type conversion operation from short16 to float32 in the virtual machine.
+ * This opcode is implemented by the {@link S2FCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top short16 value from the operand stack.
+ * Convert the short16 value to an float32 value.
+ * Push the converted float32 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a short16 value to an float32 type, facilitating subsequent integer arithmetic or comparison operations.
+ */
+public class S2FCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of S2FCommand.
+ */
+ public S2FCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the short16 to float32 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ float convertedValue = (short) operandStack.pop();
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2LCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2LCommand.java
new file mode 100644
index 0000000..cec58e6
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2LCommand.java
@@ -0,0 +1,47 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * S2LCommand Opcode: Represents the type conversion operation from short16 to long64 in the virtual machine.
+ * This opcode is implemented by the {@link S2LCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top short16 value from the operand stack.
+ * Convert the short16 value to a long64 value.
+ * Push the converted long64 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a short16 value to a long64 type, facilitating subsequent integer arithmetic or comparison operations.
+ */
+public class S2LCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of S2LCommand.
+ */
+ public S2LCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the short16 to long64 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ long convertedValue = (short) operandStack.pop();
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
From 4595583ca4bba4e318124536891702cfe81347ed Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 28 Jun 2025 21:00:09 +0800
Subject: [PATCH 56/61] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20double64=20?=
=?UTF-8?q?=E8=BD=AC=20byte8=E3=80=81short16?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../commands/type/conversion/D2BCommand.java | 48 +++++++++++++++++++
.../commands/type/conversion/D2SCommand.java | 48 +++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2BCommand.java
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2SCommand.java
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2BCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2BCommand.java
new file mode 100644
index 0000000..7180d2e
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2BCommand.java
@@ -0,0 +1,48 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * D2BCommand Opcode: Represents the type conversion operation from double64 to byte8 in the virtual machine.
+ * This opcode is implemented by the {@link D2BCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top double64 value from the operand stack.
+ * Convert the double64 value to a byte8 value (this may involve truncation).
+ * Push the converted byte8 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to narrow a double64 value to a byte8 type for further integer-based operations.
+ */
+public class D2BCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of D2BCommand.
+ */
+ public D2BCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the double64 to byte8 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ double value = (double) operandStack.pop();
+ byte convertedValue = (byte) value;
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2SCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2SCommand.java
new file mode 100644
index 0000000..ab11cf0
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2SCommand.java
@@ -0,0 +1,48 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * D2SCommand Opcode: Represents the type conversion operation from double64 to short16 in the virtual machine.
+ * This opcode is implemented by the {@link D2SCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top double64 value from the operand stack.
+ * Convert the double64 value to an short16 value (this may involve truncation).
+ * Push the converted short16 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to narrow a double64 value to an short16 type for further integer-based operations.
+ */
+public class D2SCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of D2SCommand.
+ */
+ public D2SCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the double64 to short16 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ double value = (double) operandStack.pop();
+ short convertedValue = (short) value;
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
From 85fae69c971dabe06a3cebdd7276496d9b115252 Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 28 Jun 2025 21:02:30 +0800
Subject: [PATCH 57/61] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20float32=20?=
=?UTF-8?q?=E8=BD=AC=20byte8=E3=80=81short16?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../commands/type/conversion/F2BCommand.java | 48 +++++++++++++++++++
.../commands/type/conversion/F2SCommand.java | 48 +++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/F2BCommand.java
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/F2SCommand.java
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/F2BCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/F2BCommand.java
new file mode 100644
index 0000000..5240f86
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/F2BCommand.java
@@ -0,0 +1,48 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * F2BCommand Opcode: Represents the type conversion operation from float32 to byte8 in the virtual machine.
+ * This opcode is implemented by the {@link F2BCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top float32 value from the operand stack.
+ * Convert the float32 value to a byte8 value (this may involve truncation).
+ * Push the converted byte8 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to convert a float32 value to a byte8 type for further integer operations or comparisons.
+ */
+public class F2BCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of F2BCommand.
+ */
+ public F2BCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the float32 to byte8 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ float value = (float) operandStack.pop();
+ byte convertedValue = (byte) value;
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/F2SCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/F2SCommand.java
new file mode 100644
index 0000000..83a79b5
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/F2SCommand.java
@@ -0,0 +1,48 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * F2SCommand Opcode: Represents the type conversion operation from float32 to short16 in the virtual machine.
+ * This opcode is implemented by the {@link F2SCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top float32 value from the operand stack.
+ * Convert the float32 value to a short16 value (this may involve truncation).
+ * Push the converted short16 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to convert a float32 value to a short16 type for further integer operations or comparisons.
+ */
+public class F2SCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of F2SCommand.
+ */
+ public F2SCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the float32 to short16 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ float value = (float) operandStack.pop();
+ short convertedValue = (short) value;
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
From 9c69c1f37ba6cd5f718ec1a639e0d6781bc1f0a0 Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 28 Jun 2025 21:04:23 +0800
Subject: [PATCH 58/61] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20long64=20?=
=?UTF-8?q?=E8=BD=AC=20byte8=E3=80=81short16?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../commands/type/conversion/L2BCommand.java | 48 +++++++++++++++++++
.../commands/type/conversion/L2SCommand.java | 48 +++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/L2BCommand.java
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/type/conversion/L2SCommand.java
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/L2BCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/L2BCommand.java
new file mode 100644
index 0000000..cde429c
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/L2BCommand.java
@@ -0,0 +1,48 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * L2BCommand Opcode: Represents the type conversion operation from long64 to byte8 in the virtual machine.
+ * This opcode is implemented by the {@link L2BCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top long64 value from the operand stack.
+ * Convert the long64 value to a byte8 value.
+ * Push the converted byte8 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a long64 value to a byte8 type for high-precision floating-point computations.
+ */
+public class L2BCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of L2BCommand.
+ */
+ public L2BCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the long64 to byte8 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ long value = (long) operandStack.pop();
+ byte convertedValue = (byte) value;
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/L2SCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/L2SCommand.java
new file mode 100644
index 0000000..54652d6
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/L2SCommand.java
@@ -0,0 +1,48 @@
+package org.jcnc.snow.vm.commands.type.conversion;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * L2SCommand Opcode: Represents the type conversion operation from long64 to short16 in the virtual machine.
+ * This opcode is implemented by the {@link L2SCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top long64 value from the operand stack.
+ * Convert the long64 value to a short16 value.
+ * Push the converted short16 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a long64 value to a short16 type for high-precision floating-point computations.
+ */
+public class L2SCommand implements Command {
+
+ /**
+ * Default constructor for creating an instance of L2SCommand.
+ */
+ public L2SCommand() {
+ // Empty constructor
+ }
+
+ /**
+ * Executes the long64 to short16 conversion operation.
+ *
+ * @param parts The array of instruction parameters, which is not used in this operation.
+ * @param currentPC The current program counter, representing the instruction address.
+ * @param operandStack The operand stack of the virtual machine.
+ * @param localVariableStore The local variable store for managing method-local variables.
+ * @param callStack The call stack of the virtual machine.
+ * @return The updated program counter after execution.
+ */
+ @Override
+ public int execute(String[] parts, int currentPC, OperandStack operandStack,
+ LocalVariableStore localVariableStore, CallStack callStack) {
+ long value = (long) operandStack.pop();
+ short convertedValue = (short) value;
+ operandStack.push(convertedValue);
+ return currentPC + 1;
+ }
+}
From 872865268b2db68422c0bfc3d8ccb91ae4ca4d65 Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 28 Jun 2025 21:07:07 +0800
Subject: [PATCH 59/61] =?UTF-8?q?docs:=20=E4=BF=AE=E6=94=B9=E6=8B=BC?=
=?UTF-8?q?=E5=86=99=E9=94=99=E8=AF=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/jcnc/snow/vm/commands/type/conversion/D2SCommand.java | 4 ++--
.../org/jcnc/snow/vm/commands/type/conversion/S2DCommand.java | 4 ++--
.../org/jcnc/snow/vm/commands/type/conversion/S2FCommand.java | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2SCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2SCommand.java
index ab11cf0..41a8cec 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2SCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/D2SCommand.java
@@ -12,11 +12,11 @@ import org.jcnc.snow.vm.module.OperandStack;
* Execution Steps:
*
* Pop the top double64 value from the operand stack.
- * Convert the double64 value to an short16 value (this may involve truncation).
+ * Convert the double64 value to a short16 value (this may involve truncation).
* Push the converted short16 value back onto the operand stack for subsequent operations.
*
*
- * This opcode is used to narrow a double64 value to an short16 type for further integer-based operations.
+ * This opcode is used to narrow a double64 value to a short16 type for further integer-based operations.
*/
public class D2SCommand implements Command {
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2DCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2DCommand.java
index 2f3381f..7703f26 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2DCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2DCommand.java
@@ -12,11 +12,11 @@ import org.jcnc.snow.vm.module.OperandStack;
* Execution Steps:
*
* Pop the top short16 value from the operand stack.
- * Convert the short16 value to an double64 value.
+ * Convert the short16 value to a double64 value.
* Push the converted double64 value back onto the operand stack for subsequent operations.
*
*
- * This opcode is used to widen a short16 value to an double64 type, facilitating subsequent integer arithmetic or comparison operations.
+ * This opcode is used to widen a short16 value to a double64 type, facilitating subsequent integer arithmetic or comparison operations.
*/
public class S2DCommand implements Command {
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2FCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2FCommand.java
index 78fbb4a..478e397 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2FCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2FCommand.java
@@ -12,11 +12,11 @@ import org.jcnc.snow.vm.module.OperandStack;
* Execution Steps:
*
* Pop the top short16 value from the operand stack.
- * Convert the short16 value to an float32 value.
+ * Convert the short16 value to a float32 value.
* Push the converted float32 value back onto the operand stack for subsequent operations.
*
*
- * This opcode is used to widen a short16 value to an float32 type, facilitating subsequent integer arithmetic or comparison operations.
+ * This opcode is used to widen a short16 value to a float32 type, facilitating subsequent integer arithmetic or comparison operations.
*/
public class S2FCommand implements Command {
From f382b2bc5455f680867e49235793e1bb8fce755c Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Sat, 28 Jun 2025 21:08:49 +0800
Subject: [PATCH 60/61] =?UTF-8?q?style:=20=E7=BB=9F=E4=B8=80=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E5=AE=9E=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/jcnc/snow/vm/commands/type/conversion/S2BCommand.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2BCommand.java b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2BCommand.java
index fa86e5f..d94eb8a 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2BCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/type/conversion/S2BCommand.java
@@ -40,7 +40,8 @@ public class S2BCommand implements Command {
@Override
public int execute(String[] parts, int currentPC, OperandStack operandStack,
LocalVariableStore localVariableStore, CallStack callStack) {
- byte convertedValue = (byte) ((short) operandStack.pop());
+ short value = (short) operandStack.pop();
+ byte convertedValue = (byte) value;
operandStack.push(convertedValue);
return currentPC + 1;
}
From 4f63f88b400de03baf0e47f09e5fb608960068dc Mon Sep 17 00:00:00 2001
From: zhangxun <1958638841@qq.com>
Date: Tue, 8 Jul 2025 20:19:45 +0800
Subject: [PATCH 61/61] =?UTF-8?q?feat:=20=E8=A1=A5=E5=85=85=E5=9F=BA?=
=?UTF-8?q?=E6=9C=AC=E6=95=B0=E5=80=BC=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2?=
=?UTF-8?q?=E6=8C=87=E4=BB=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../org/jcnc/snow/vm/engine/VMOpCode.java | 200 ++++++++++--------
.../snow/vm/factories/CommandFactory.java | 29 ++-
2 files changed, 135 insertions(+), 94 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java
index c53f511..8b634ca 100644
--- a/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java
+++ b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java
@@ -2043,34 +2043,33 @@ public class VMOpCode {
// endregion
// region Type Conversion (0x00C0-0x00DF)
- /**
- * I2L Opcode: Represents the type conversion operation from int32 to long64 in the virtual machine.
- * This opcode is implemented by the {@link I2LCommand} class, which defines its specific execution logic.
- *
- * Execution Steps:
- *
- * Pop the top int32 value from the operand stack.
- * Convert the int32 value to a long64 value.
- * Push the converted long64 value back onto the operand stack for subsequent operations.
- *
- *
- * This opcode is commonly used to widen an int32 value to a long64 type to accommodate larger numeric ranges.
- */
- public static final int I2L = 0x00C0;
- /**
- * I2S Opcode: Represents the type conversion operation from int32 to short16 in the virtual machine.
- * This opcode is implemented by the {@link I2SCommand} class, which defines its specific execution logic.
- *
- * Execution Steps:
- *
- * Pop the top int32 value from the operand stack.
- * Convert the int32 value to a short16 value (this may involve truncation).
- * Push the converted short16 value back onto the operand stack for subsequent operations.
- *
- *
- * This opcode is typically used to narrow an int32 value to a short16 type when a smaller data representation is needed.
- */
- public static final int I2S = 0x00C1;
+ // region Byte8 (0x00C0-0xC4)
+
+ public static final int B2S = 0x00C0;
+
+ public static final int B2I = 0x00C1;
+
+ public static final int B2L = 0x00C2;
+
+ public static final int B2F = 0x00C3;
+
+ public static final int B2D = 0x00C4;
+ // endregion Byte8
+
+ // region Short16 (0x00C5-0xC9)
+
+ public static final int S2B = 0x00C5;
+
+ public static final int S2I = 0x00C6;
+
+ public static final int S2L = 0x00C7;
+
+ public static final int S2F = 0x00C8;
+
+ public static final int S2D = 0x00C9;
+ // endregion Short16
+
+ // region Int32 (0x00CA-0xCE)
/**
* I2B Opcode: Represents the type conversion operation from int32 to byte8 in the virtual machine.
* This opcode is implemented by the {@link I2BCommand} class, which defines its specific execution logic.
@@ -2084,21 +2083,37 @@ public class VMOpCode {
*
* This opcode is used to narrow an int32 value to a byte8 type, suitable when a smaller numeric type is required.
*/
- public static final int I2B = 0x00C2;
+ public static final int I2B = 0x00CA;
+
/**
- * I2D Opcode: Represents the type conversion operation from int32 to double64 in the virtual machine.
- * This opcode is implemented by the {@link I2DCommand} class, which defines its specific execution logic.
+ * I2S Opcode: Represents the type conversion operation from int32 to short16 in the virtual machine.
+ * This opcode is implemented by the {@link I2SCommand} class, which defines its specific execution logic.
*
* Execution Steps:
*
* Pop the top int32 value from the operand stack.
- * Convert the int32 value to a double64 value.
- * Push the converted double64 value back onto the operand stack for subsequent operations.
+ * Convert the int32 value to a short16 value (this may involve truncation).
+ * Push the converted short16 value back onto the operand stack for subsequent operations.
*
*
- * This opcode is used to widen an int32 value to a double64 type, providing high-precision floating-point calculations.
+ * This opcode is typically used to narrow an int32 value to a short16 type when a smaller data representation is needed.
*/
- public static final int I2D = 0x00C3;
+ public static final int I2S = 0x00CB;
+
+ /**
+ * I2L Opcode: Represents the type conversion operation from int32 to long64 in the virtual machine.
+ * This opcode is implemented by the {@link I2LCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top int32 value from the operand stack.
+ * Convert the int32 value to a long64 value.
+ * Push the converted long64 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is commonly used to widen an int32 value to a long64 type to accommodate larger numeric ranges.
+ */
+ public static final int I2L = 0x00CC;
/**
* I2F Opcode: Represents the type conversion operation from int32 to float32 in the virtual machine.
* This opcode is implemented by the {@link I2FCommand} class, which defines its specific execution logic.
@@ -2112,7 +2127,29 @@ public class VMOpCode {
*
* This opcode is used to convert an int32 value to a float32 type when floating-point arithmetic is required.
*/
- public static final int I2F = 0x00C4;
+ public static final int I2F = 0x00CD;
+ /**
+ * I2D Opcode: Represents the type conversion operation from int32 to double64 in the virtual machine.
+ * This opcode is implemented by the {@link I2DCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top int32 value from the operand stack.
+ * Convert the int32 value to a double64 value.
+ * Push the converted double64 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen an int32 value to a double64 type, providing high-precision floating-point calculations.
+ */
+ public static final int I2D = 0x00CE;
+ // endregion Int32
+
+ // region Long64 (0x00CF-0xD3)
+
+ public static final int L2B = 0x00CF;
+
+ public static final int L2S = 0x00D0;
+
/**
* L2I Opcode: Represents the type conversion operation from long64 to int32 in the virtual machine.
* This opcode is implemented by the {@link L2ICommand} class, which defines its specific execution logic.
@@ -2126,21 +2163,7 @@ public class VMOpCode {
*
* This opcode is typically used to narrow a long64 value to an int32 type for further integer operations.
*/
- public static final int L2I = 0x00C5;
- /**
- * L2D Opcode: Represents the type conversion operation from long64 to double64 in the virtual machine.
- * This opcode is implemented by the {@link L2DCommand} class, which defines its specific execution logic.
- *
- * Execution Steps:
- *
- * Pop the top long64 value from the operand stack.
- * Convert the long64 value to a double64 value.
- * Push the converted double64 value back onto the operand stack for subsequent operations.
- *
- *
- * This opcode is used to widen a long64 value to a double64 type for high-precision floating-point computations.
- */
- public static final int L2D = 0x00C6;
+ public static final int L2I = 0x00D1;
/**
* L2F Opcode: Represents the type conversion operation from long64 to float32 in the virtual machine.
* This opcode is implemented by the {@link L2FCommand} class, which defines its specific execution logic.
@@ -2154,7 +2177,29 @@ public class VMOpCode {
*
* This opcode is used to convert a long64 value to a float32 type, typically for floating-point arithmetic involving long values.
*/
- public static final int L2F = 0x00C7;
+ public static final int L2F = 0x00D2;
+ /**
+ * L2D Opcode: Represents the type conversion operation from long64 to double64 in the virtual machine.
+ * This opcode is implemented by the {@link L2DCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * Pop the top long64 value from the operand stack.
+ * Convert the long64 value to a double64 value.
+ * Push the converted double64 value back onto the operand stack for subsequent operations.
+ *
+ *
+ * This opcode is used to widen a long64 value to a double64 type for high-precision floating-point computations.
+ */
+ public static final int L2D = 0x00D3;
+ // endregion Long64
+
+ // region Float32 (0x00D4-0xD8)
+
+ public static final int F2B = 0x00D4;
+
+ public static final int F2S = 0x00D5;
+
/**
* F2I Opcode: Represents the type conversion operation from float32 to int32 in the virtual machine.
* This opcode is implemented by the {@link F2ICommand} class, which defines its specific execution logic.
@@ -2168,7 +2213,7 @@ public class VMOpCode {
*
* This opcode is used to convert a float32 value to an int32 type for further integer-based operations or comparisons.
*/
- public static final int F2I = 0x00C8;
+ public static final int F2I = 0x00D6;
/**
* F2L Opcode: Represents the type conversion operation from float32 to long64 in the virtual machine.
* This opcode is implemented by the {@link F2LCommand} class, which defines its specific execution logic.
@@ -2182,7 +2227,7 @@ public class VMOpCode {
*
* This opcode is used to widen a float32 value to a long64 type, which is useful when operations require a larger numeric range.
*/
- public static final int F2L = 0x00C9;
+ public static final int F2L = 0x00D7;
/**
* F2D Opcode: Represents the type conversion operation from float32 to double64 in the virtual machine.
* This opcode is implemented by the {@link F2DCommand} class, which defines its specific execution logic.
@@ -2196,7 +2241,15 @@ public class VMOpCode {
*
* This opcode is used to promote a float32 value to a double64 type, thereby increasing precision for floating-point computations.
*/
- public static final int F2D = 0x00CA;
+ public static final int F2D = 0x00D8;
+ // endregion Float32
+
+ // region Double64 (0x00D9-0xDD)
+
+ public static final int D2B = 0x00D9;
+
+ public static final int D2S = 0x00DA;
+
/**
* D2I Opcode: Represents the type conversion operation from double64 to int32 in the virtual machine.
* This opcode is implemented by the {@link D2ICommand} class, which defines its specific execution logic.
@@ -2210,7 +2263,7 @@ public class VMOpCode {
*
* This opcode is used to narrow a double64 value to an int32 type for further integer-based processing.
*/
- public static final int D2I = 0x00CB;
+ public static final int D2I = 0x00DB;
/**
* D2L Opcode: Represents the type conversion operation from double64 to long64 in the virtual machine.
* This opcode is implemented by the {@link D2LCommand} class, which defines its specific execution logic.
@@ -2224,7 +2277,7 @@ public class VMOpCode {
*
* This opcode is used to narrow a double64 value to a long64 type, which can then be used for integer operations.
*/
- public static final int D2L = 0x00CC;
+ public static final int D2L = 0x00DC;
/**
* D2F Opcode: Represents the type conversion operation from double64 to float32 in the virtual machine.
* This opcode is implemented by the {@link D2FCommand} class, which defines its specific execution logic.
@@ -2238,36 +2291,9 @@ public class VMOpCode {
*
* This opcode is used to narrow a double64 value to a float32 type when lower precision floating-point arithmetic is acceptable.
*/
- public static final int D2F = 0x00CD;
- /**
- * S2I Opcode: Represents the type conversion operation from short16 to int32 in the virtual machine.
- * This opcode is implemented by the {@link S2ICommand} class, which defines its specific execution logic.
- *
- * Execution Steps:
- *
- * Pop the top short16 value from the operand stack.
- * Convert the short16 value to an int32 value.
- * Push the converted int32 value back onto the operand stack for subsequent operations.
- *
- *
- * This opcode is used to widen a short16 value to an int32 type, facilitating subsequent integer arithmetic or comparison operations.
- */
- public static final int S2I = 0x00CE;
- /**
- * B2I Opcode: Represents the type conversion operation from byte8 to int32 in the virtual machine.
- * This opcode is implemented by the {@link B2ICommand} class, which defines its specific execution logic.
- *
- * Execution Steps:
- *
- * Pop the top byte8 value from the operand stack.
- * Convert the byte8 value to an int32 value.
- * Push the converted int32 value back onto the operand stack for subsequent operations.
- *
- *
- * This opcode is used to widen a byte8 value to an int32 type to ensure compatibility with integer-based operations.
- */
- public static final int B2I = 0x00CF;
- // endregion
+ public static final int D2F = 0x00DD;
+ // endregion Double64
+ // endregion Conversion
// region Stack Control (0x0100-0x01FF)
/**
diff --git a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java
index a5af00b..4054a11 100644
--- a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java
+++ b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java
@@ -207,26 +207,41 @@ public class CommandFactory {
// endregion
// region Type Conversion (0x00C0-0x00DF)
- COMMANDS[VMOpCode.I2L] = new I2LCommand();
- COMMANDS[VMOpCode.I2S] = new I2SCommand();
+ COMMANDS[VMOpCode.B2S] = new B2SCommand();
+ COMMANDS[VMOpCode.B2I] = new B2ICommand();
+ COMMANDS[VMOpCode.B2L] = new B2LCommand();
+ COMMANDS[VMOpCode.B2F] = new B2FCommand();
+ COMMANDS[VMOpCode.B2D] = new B2DCommand();
+
+ COMMANDS[VMOpCode.S2B] = new S2BCommand();
+ COMMANDS[VMOpCode.S2I] = new S2ICommand();
+ COMMANDS[VMOpCode.S2L] = new S2LCommand();
+ COMMANDS[VMOpCode.S2F] = new S2FCommand();
+ COMMANDS[VMOpCode.S2D] = new S2DCommand();
+
COMMANDS[VMOpCode.I2B] = new I2BCommand();
- COMMANDS[VMOpCode.I2D] = new I2DCommand();
+ COMMANDS[VMOpCode.I2S] = new I2SCommand();
+ COMMANDS[VMOpCode.I2L] = new I2LCommand();
COMMANDS[VMOpCode.I2F] = new I2FCommand();
+ COMMANDS[VMOpCode.I2D] = new I2DCommand();
+ COMMANDS[VMOpCode.L2B] = new L2BCommand();
+ COMMANDS[VMOpCode.L2S] = new L2SCommand();
COMMANDS[VMOpCode.L2I] = new L2ICommand();
- COMMANDS[VMOpCode.L2D] = new L2DCommand();
COMMANDS[VMOpCode.L2F] = new L2FCommand();
+ COMMANDS[VMOpCode.L2D] = new L2DCommand();
+ COMMANDS[VMOpCode.F2B] = new F2BCommand();
+ COMMANDS[VMOpCode.F2S] = new F2SCommand();
COMMANDS[VMOpCode.F2I] = new F2ICommand();
COMMANDS[VMOpCode.F2L] = new F2LCommand();
COMMANDS[VMOpCode.F2D] = new F2DCommand();
+ COMMANDS[VMOpCode.D2B] = new D2BCommand();
+ COMMANDS[VMOpCode.D2S] = new D2SCommand();
COMMANDS[VMOpCode.D2I] = new D2ICommand();
COMMANDS[VMOpCode.D2L] = new D2LCommand();
COMMANDS[VMOpCode.D2F] = new D2FCommand();
-
- COMMANDS[VMOpCode.S2I] = new S2ICommand();
- COMMANDS[VMOpCode.B2I] = new B2ICommand();
// endregion
// region Stack Control (0x0100-0x01FF)