!38 release: 合并 v0.4.2 版本至 main 分支

Merge pull request !38 from Luke/release/v0.4.2
This commit is contained in:
Luke 2025-07-11 09:30:48 +00:00 committed by Gitee
commit 3e59529666
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
124 changed files with 2536 additions and 941 deletions

View File

@ -69,7 +69,7 @@ body:
attributes:
label: 软件版本/分支
options:
- v0.4.0
- v0.4.2
- main
- dev
- 其他

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo1 -o target/Demo1" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo1 -o target/Demo1" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo10" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
<configuration default="false" name="Demo10" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo10 -o target/Demo10" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo10 -o target/Demo10" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo11 -o target/Demo11" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo11 -o target/Demo11" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

17
.run/Demo12.run.xml Normal file
View File

@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo12" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo12 -o target/Demo12" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

10
.run/Demo13.run.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo13" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo13 -o target/Demo13" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo2" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
<configuration default="false" name="Demo2" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo2 -o target/Demo2" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo2 -o target/Demo2" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo3" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
<configuration default="false" name="Demo3" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo3 -o target/Demo3" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo3 -o target/Demo3" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo4" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
<configuration default="false" name="Demo4" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo4 -o target/Demo4" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo4 -o target/Demo4" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo5" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
<configuration default="false" name="Demo5" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo5 -o target/Demo5" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo5 -o target/Demo5" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo6" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
<configuration default="false" name="Demo6" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo6 -o target/Demo6" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo6 -o target/Demo6" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo7" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
<configuration default="false" name="Demo7" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo7 -o target/Demo7" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo7 -o target/Demo7" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo8" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
<configuration default="false" name="Demo8" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo8 -o target/Demo8" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo8 -o target/Demo8" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -1,9 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo9" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
<configuration default="false" name="Demo9" type="Application" factoryName="Application" folderName="Demo">
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo9 -o target/Demo9" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo9 -o target/Demo9" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />

View File

@ -3,6 +3,9 @@
<toRun name="Demo1" type="Application" />
<toRun name="Demo10" type="Application" />
<toRun name="Demo11" type="Application" />
<toRun name="Demo11" type="Application" />
<toRun name="Demo12" type="Application" />
<toRun name="Demo13" type="Application" />
<toRun name="Demo2" type="Application" />
<toRun name="Demo3" type="Application" />
<toRun name="Demo4" type="Application" />

View File

@ -11,8 +11,8 @@
<a href="https://gitee.com/jcnc-org/snow/blob/main/LICENSE">
<img src="https://img.shields.io/badge/%20license-Apache--2.0%20-blue" alt="">
</a>
<a href="https://gitee.com/jcnc-org/snow/tree/v0.4.0/">
<img src="https://img.shields.io/badge/version-v0.4.0-blue" alt="">
<a href="https://gitee.com/jcnc-org/snow/tree/v0.4.2/">
<img src="https://img.shields.io/badge/version-v0.4.2-blue" alt="">
</a>
</p>
@ -69,6 +69,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
[https://gitee.com/jcnc-org/snow/releases](https://gitee.com/jcnc-org/snow/releases)
## 相关文档
[Snow-Lang 语法](docs/Snow-Lang-Syntax/Snow-Lang-Syntax.md)
[Git 管理规范](docs/Snow-Lang-Git-Management/Snow-Lang-Git-Management.md)

View File

@ -0,0 +1,260 @@
# Snow-Lang 语法
## 快速入门
一个简单的 snow 程序
```snow
module: Main
function: main
return_type: int
body:
return 1 + 1
end body
end function
end module
```
## 基础
### 注释
```snow
// 单行注释
/*
多行注释
多行注释
多行注释
*/
```
### 数据类型
bool 类型:
两种值:`true``false`
数值类型:
| Number | keyword |
|----------|---------|
| byte8 | byte |
| short16 | short |
| int32 | int |
| long64 | long |
| float32 | float |
| double64 | double |
默认整数的类型为 int浮点数的类型为 double。
数值字面量后缀:
| 数值字面量后缀 | 例子 |
|---------|----|
| b、B | 7b |
| s、S | 7s |
| i、I | 7i |
| l、L | 7l |
| f、F | 7f |
| d、D | 7d |
### 变量
定义变量的形式如下,中括号表示可选:
```snow
declare name: type [= initial_value]
```
其中 `name` 是变量名,`type` 是变量类型,`initial_value` 是初始值
例:
```snow
declare x: int
declare y: long = 123456789
```
读取变量值的方法,直接写变量的名字即可:
```snow
x
```
设置变量值的方法,先写变量名,后面接 `=`,最后写一个表达式即可:
```snow
x = 10
```
于是可以通过这种方式让变量参与计算并保存结果:
```snow
x = y + 1
```
读取 `y` 的值加 1 并保存到变量 `x`
变量只能定义在函数体中、函数参数列表、loop 初始化器中。
## 流程控制
### if
if 语句的形式如下else 是可选的:
```snow
if condition then
// 条件成立执行的代码
else
// 以上条件不成立执行的代码
end if
```
condition 可以是表达式(结果为 bool 类型)或者 bool 字面量
例:
```snow
module: Main
function: main
return_type: int
body:
if 5 > 7 then
return 5
else
return 7
end if
return 0
end body
end function
end module
```
### loop
loop 语句的形式如下:
```snow
loop:
initializer:
// 循环开始前可声明循环变量,有且只能声明一个
declare i: int = 1
condition:
// 循环条件,成立则执行 body 的代码,
// 不成立则退出循环,有且只能写一条
i <= 100
update:
// 循环体执行完后执行的代码,有且只能写一条
i = i + 1
body:
// 每次执行的代码写这里
end body
end loop
```
例子(求 1 ~ 100 的和):
```snow
module: Main
function: main
return_type: int
body:
declare sum: int = 0
loop:
initializer:
declare i: int = 1
condition:
i <= 100
update:
i = i + 1
body:
sum = sum + i
end body
end loop
return sum
end body
end function
end module
```
## 函数
函数的形式如下:
```snow
function: add
parameter:
declare a: int
declare b: int
return_type: int
body:
return a + b
end body
end function
```
其中 add 是函数名parameter 下面是参数列表(可省略),与变量的定义类似,但是不允许赋初值,
接着 return_type 设置返回值类型,最后的 body 为函数体。
## 模块
一个模块可以包含多个函数,
通过 import 语句导入模块,
snow 会自动将同名模块的函数合并。
在我们最初的例子中,就用了 module 这个关键字。让我们回忆一下:
```snow
module: Main
function: main
return_type: int
body:
return 1 + 1
end body
end function
end module
```
可以看到模块名是 Main里面有函数 main。
假如现在有一个模块 Math代码如下
```snow
// Math.snow
module: Math
function: add
parameter:
declare a: int
declare b: int
return_type: int
body:
return a + b
end body
end function
end module
```
可以使用 import 来导入 Math 模块:
```snow
// main.snow
module: Main
import: Math
function: main
return_type: int
body:
return Math.add(5, 7)
end body
end function
end module
```
可以同时导入多个模块,用逗号(半角)分隔模块名即可:
```snow
// main.snow
module: Main
import: Math, Time
function: main
return_type: int
body:
return Math.add(5, 7)
end body
end function
end module
```

View File

@ -1,17 +1,17 @@
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
foo()
return 0
end body
end function
function: foo
return_type: void
body:
end body
end function
end module

View File

@ -1,12 +0,0 @@
## 编译器输出
### Snow 源代码
#### Main.snow
```snow
function: main
return_type: int
body:
3 L
return 65537
end body
end function
```

View File

@ -0,0 +1,21 @@
module: Main
function: main
return_type: int
body:
foo()
return 0
end body
end function
function: foo
return_type: int
body:
if false then
return 1
end if
return 0
end body
end function
end module

View File

@ -0,0 +1,69 @@
module: Main
function: main
return_type: int
body:
5 == 7
5 == 7s
5 == 7b
5 == 7l
5 == 7f
5 == 7d
5b == 5b
5b == 5s
5b == 5l
5b == 5f
5b == 5d
5s == 5s
5s == 5l
5s == 5f
5s == 5d
5l == 5l
5l == 5f
5l == 5d
5f == 5f
5f == 5d
5d == 5d
declare b: byte = 8b
declare s: short = 8s
declare i: int = 8
declare l: long = 8l
declare f: float = 8f
declare d: double = 8d
b == b
b == s
b == i
b == l
b == f
b == d
s == s
s == i
s == l
s == f
s == d
i == i
i == l
i == f
i == d
l == l
l == f
l == d
f == f
f == d
d == d
return 65537
end body
end function
end module

View File

@ -7,7 +7,7 @@
<groupId>org.jcnc.snow</groupId>
<artifactId>Snow</artifactId>
<version>0.4.0</version>
<version>0.4.2</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View File

@ -4,6 +4,7 @@ import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
import org.jcnc.snow.compiler.backend.utils.IROpCodeMapper;
import org.jcnc.snow.compiler.backend.utils.OpHelper;
import org.jcnc.snow.compiler.backend.utils.TypePromoteUtils;
import org.jcnc.snow.compiler.ir.core.IRValue;
import org.jcnc.snow.compiler.ir.instruction.BinaryOperationInstruction;
import org.jcnc.snow.compiler.ir.value.IRConstant;
@ -41,38 +42,12 @@ public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationIn
return fn + "$" + tag + "$" + COUNTER.getAndIncrement();
}
/**
* 类型优先级D &gt; F &gt; L &gt; I &gt; S &gt; B
*/
private static int rank(char p) {
return switch (p) {
case 'D' -> 6;
case 'F' -> 5;
case 'L' -> 4;
case 'I' -> 3;
case 'S' -> 2;
case 'B' -> 1;
default -> 0;
};
}
/**
* 返回优先级更高的类型字符
*/
private static char promote(char a, char b) {
return rank(a) >= rank(b) ? a : b;
}
/**
* 单字符转字符串
*/
private static String str(char p) {
return String.valueOf(p);
}
/**
* 判断常量值是否等于 0
* 仅支持 Java 原生数值类型
*
* @param v 常量值
* @return 等于 0 返回 true否则 false
*/
private static boolean isZero(Object v) {
if (v == null) return false;
@ -87,31 +62,6 @@ public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationIn
};
}
/**
* 获取从类型 {@code from} {@code to} 的转换指令名
* 相同类型或无显式转换需求返回 {@code null}
*/
private static String convert(char from, char to) {
if (from == to) return null;
return switch ("" + from + to) {
case "IL" -> "I2L";
case "ID" -> "I2D";
case "IF" -> "I2F";
case "LI" -> "L2I";
case "LD" -> "L2D";
case "LF" -> "L2F";
case "FI" -> "F2I";
case "FL" -> "F2L";
case "FD" -> "F2D";
case "DI" -> "D2I";
case "DL" -> "D2L";
case "DF" -> "D2F";
case "SI" -> "S2I";
case "BI" -> "B2I";
default -> null;
};
}
/* -------------------- 接口实现 -------------------- */
@Override
@ -161,16 +111,16 @@ public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationIn
char lType = out.getSlotType(lSlot); // 未登记默认 'I'
char rType = out.getSlotType(rSlot);
char tType = promote(lType, rType); // 类型提升结果
String tPre = str(tType);
char tType = TypePromoteUtils.promote(lType, rType); // 类型提升结果
String tPre = TypePromoteUtils.str(tType);
/* ---------- 2. 加载并做类型转换 ---------- */
out.emit(OpHelper.opcode(str(lType) + "_LOAD") + " " + lSlot);
String cvt = convert(lType, tType);
out.emit(OpHelper.opcode(TypePromoteUtils.str(lType) + "_LOAD") + " " + lSlot);
String cvt = TypePromoteUtils.convert(lType, tType);
if (cvt != null) out.emit(OpHelper.opcode(cvt));
out.emit(OpHelper.opcode(str(rType) + "_LOAD") + " " + rSlot);
cvt = convert(rType, tType);
out.emit(OpHelper.opcode(TypePromoteUtils.str(rType) + "_LOAD") + " " + rSlot);
cvt = TypePromoteUtils.convert(rType, tType);
if (cvt != null) out.emit(OpHelper.opcode(cvt));
/* ---------- 3. 区分算术 / 比较 ---------- */
@ -186,7 +136,7 @@ public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationIn
}
/* === 3-B. CMP_* —— 生成布尔结果 === */
String branchOp = OpHelper.opcode(IROpCodeMapper.toVMOp(ins.op())); // IC_E / IC_NE
String branchOp = OpHelper.opcode(IROpCodeMapper.toVMOp(ins.op())); // I_CE / I_CNE
String lblTrue = fresh(currentFn, "true");
String lblEnd = fresh(currentFn, "end");

View File

@ -3,74 +3,56 @@ package org.jcnc.snow.compiler.backend.generator;
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
import org.jcnc.snow.compiler.backend.utils.OpHelper;
import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable;
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import java.util.Map;
/**
* 函数调用指令生成器
* <p>
* 该类实现了函数调用CallInstruction的指令翻译逻辑
* 负责将 IR 层的函数调用转换为虚拟机可执行的低级指令
* IR CallInstruction 生成 VM 指令
*/
public class CallGenerator implements InstructionGenerator<CallInstruction> {
/**
* 返回本指令生成器支持的 IR 指令类型CallInstruction
*
* @return 指令类型的 Class 对象
*/
@Override
public Class<CallInstruction> supportedClass() {
return CallInstruction.class;
}
/**
* 生成函数调用相关的虚拟机指令
* <p>
* 步骤如下
* <ol>
* <li>预测返回值类型采用首个实参槽的类型作为近似</li>
* <li>为每个参数根据实际类型发出加载指令</li>
* <li>生成 CALL 调用指令</li>
* <li>将返回值存储到目标槽并记录类型信息</li>
* </ol>
*
* @param ins 待翻译的 CallInstruction 指令对象
* @param out 指令输出与类型槽管理器
* @param slotMap IR 寄存器到槽号的映射
* @param currentFn 当前函数名未用可用于递归/闭包等复杂场景
*/
@Override
public void generate(CallInstruction ins,
VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap,
String currentFn) {
// 1. 预测返回值类型用首个实参槽类型作为近似推断
char retType = 'I'; // 默认整型
/* 1. 推断返回值类型(用于非 void 情况下的 I/F/D/L_STORE */
char retType = 'I';
if (!ins.getArguments().isEmpty()) {
int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst());
retType = out.getSlotType(firstSlot); // 获取槽位实际类型
if (retType == '\0') retType = 'I'; // 默认整型
retType = out.getSlotType(firstSlot);
if (retType == '\0') retType = 'I';
}
// 2. 按真实类型加载每个参数到虚拟机操作栈
/* 2. 依次加载实参 */
for (var arg : ins.getArguments()) {
int slotId = slotMap.get((IRVirtualRegister) arg); // 获取参数槽号
char t = out.getSlotType(slotId); // 获取参数类型
if (t == '\0') t = 'I'; // 类型未知时默认整型
// 生成类型相关的加载指令 I_LOADF_LOAD
int slotId = slotMap.get((IRVirtualRegister) arg);
char t = out.getSlotType(slotId);
if (t == '\0') t = 'I';
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
}
// 3. 生成 CALL 调用指令
/* 3. 发出 CALL 指令 */
out.emitCall(ins.getFunctionName(), ins.getArguments().size());
// 4. 将返回值存入目标槽并记录槽的类型
int destSlot = slotMap.get(ins.getDest()); // 目标寄存器槽
/* 3.5 若被调用函数返回 void则无需保存返回值 */
String rt = GlobalFunctionTable.getReturnType(ins.getFunctionName());
if ("void".equals(rt)) {
return; // 直接结束 _STORE
}
/* 4. 保存返回值到目标槽 */
int destSlot = slotMap.get(ins.getDest());
out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
out.setSlotType(destSlot, retType); // 标记返回值类型
out.setSlotType(destSlot, retType);
}
}

View File

@ -4,20 +4,21 @@ import org.jcnc.snow.compiler.backend.utils.IROpCodeMapper;
import org.jcnc.snow.compiler.backend.utils.OpHelper;
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
import org.jcnc.snow.compiler.backend.utils.TypePromoteUtils;
import org.jcnc.snow.compiler.ir.instruction.IRCompareJumpInstruction;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import java.util.Map;
/**
* <b>条件比较跳转指令生成器</b>
* 条件比较跳转指令生成器
* <p>
* 该类实现了 {@link InstructionGenerator} 接口
* 负责将 IR 中的 {@link IRCompareJumpInstruction}条件比较并跳转指令
* 转换为目标虚拟机VM可执行的指令序列
* </p>
*
* <b>主要功能</b>
* 主要功能
* <ul>
* <li>根据 IR 比较指令左右操作数的类型自动进行类型提升与转换</li>
* <li>生成相应的 VM 加载类型转换比较与跳转指令</li>
@ -36,85 +37,6 @@ public class CmpJumpGenerator implements InstructionGenerator<IRCompareJumpInstr
return IRCompareJumpInstruction.class;
}
/**
* <b>类型宽度优先级</b>D > F > L > I > S > B
* <ul>
* <li>Ddouble6</li>
* <li>Ffloat5</li>
* <li>Llong4</li>
* <li>Iint3</li>
* <li>Sshort2</li>
* <li>Bbyte1</li>
* <li>未识别类型0</li>
* </ul>
*
* @param p 类型标记字符
* @return 优先级数值越大类型越宽
*/
private static int rank(char p) {
return switch (p) {
case 'D' -> 6;
case 'F' -> 5;
case 'L' -> 4;
case 'I' -> 3;
case 'S' -> 2;
case 'B' -> 1;
default -> 0;
};
}
/**
* 返回更的公共类型即优先级高的类型
*
* @param a 类型标记字符 1
* @param b 类型标记字符 2
* @return 宽度更高的类型标记字符
*/
private static char promote(char a, char b) {
return rank(a) >= rank(b) ? a : b;
}
/**
* 单字符类型标记转字符串
*
* @param p 类型标记字符
* @return 类型字符串
*/
private static String str(char p) {
return String.valueOf(p);
}
/**
* 获取 {@code from to} 的类型转换指令名如不需转换则返回 {@code null}
* <p>
* 仅覆盖目前常见的整数与浮点类型提升与转换后续有新类型可补充
* </p>
*
* @param from 源类型标记字符
* @param to 目标类型标记字符
* @return 转换指令名L2I无转换返回 {@code null}
*/
private static String convert(char from, char to) {
if (from == to) return null;
return switch ("" + from + to) {
case "IL" -> "I2L";
case "ID" -> "I2D";
case "IF" -> "I2F";
case "LI" -> "L2I";
case "LD" -> "L2D";
case "LF" -> "L2F";
case "FI" -> "F2I";
case "FL" -> "F2L";
case "FD" -> "F2D";
case "DI" -> "D2I";
case "DL" -> "D2L";
case "DF" -> "D2F";
case "SI" -> "S2I";
case "BI" -> "B2I";
default -> null;
};
}
/**
* 生成 IR 条件比较跳转指令的 VM 指令序列
* <ol>
@ -138,21 +60,21 @@ public class CmpJumpGenerator implements InstructionGenerator<IRCompareJumpInstr
// 1. 获取左右操作数的槽位与静态类型
int leftSlot = slotMap.get(ins.left());
int rightSlot = slotMap.get(ins.right());
char lType = out.getSlotType(leftSlot); // 若未登记则默认 'I'
char lType = out.getSlotType(leftSlot);
char rType = out.getSlotType(rightSlot);
char tType = promote(lType, rType); // 公共类型提升
char tType = TypePromoteUtils.promote(lType, rType); // 公共类型提升
// 2. 加载左右操作数并按需类型转换
// 左操作数
out.emit(OpHelper.opcode(str(lType) + "_LOAD") + " " + leftSlot);
String cvt = convert(lType, tType);
out.emit(OpHelper.opcode(TypePromoteUtils.str(lType) + "_LOAD") + " " + leftSlot);
String cvt = TypePromoteUtils.convert(lType, tType);
if (cvt != null) {
out.emit(OpHelper.opcode(cvt));
}
// 右操作数
out.emit(OpHelper.opcode(str(rType) + "_LOAD") + " " + rightSlot);
cvt = convert(rType, tType);
out.emit(OpHelper.opcode(TypePromoteUtils.str(rType) + "_LOAD") + " " + rightSlot);
cvt = TypePromoteUtils.convert(rType, tType);
if (cvt != null) {
out.emit(OpHelper.opcode(cvt));
}
@ -160,12 +82,12 @@ public class CmpJumpGenerator implements InstructionGenerator<IRCompareJumpInstr
// 3. 选择正确的比较指令前缀
String cmpOp = IROpCodeMapper.toVMOp(ins.op());
/*
* 指令前缀 int 类型要用 IC_*, long 类型要用 LC_*
* 指令前缀 int 类型要用 I_C*, long 类型要用 L_C*
*/
if (tType == 'I' && cmpOp.startsWith("LC_")) {
cmpOp = "IC_" + cmpOp.substring(3);
} else if (tType == 'L' && cmpOp.startsWith("IC_")) {
cmpOp = "LC_" + cmpOp.substring(3);
if (tType == 'I' && cmpOp.startsWith("L_C")) {
cmpOp = "I_C" + cmpOp.substring(3);
} else if (tType == 'L' && cmpOp.startsWith("I_C")) {
cmpOp = "L_C" + cmpOp.substring(3);
}
// 4. 发出比较与跳转指令

View File

@ -82,21 +82,53 @@ public final class IROpCodeMapper {
opcodeMap.put(IROpCode.NEG_D64, "D_NEG");
// 比较运算映射
// 整形32位比较运算映射
opcodeMap.put(IROpCode.CMP_IEQ, "IC_E"); // 相等
opcodeMap.put(IROpCode.CMP_INE, "IC_NE"); // 不等
opcodeMap.put(IROpCode.CMP_ILT, "IC_L"); // 小于
opcodeMap.put(IROpCode.CMP_IGT, "IC_G"); // 大于
opcodeMap.put(IROpCode.CMP_ILE, "IC_LE"); // 小于等于
opcodeMap.put(IROpCode.CMP_IGE, "IC_GE"); // 大于等于
// 8位整数比较运算映射
opcodeMap.put(IROpCode.CMP_BEQ, "B_CE"); // 相等
opcodeMap.put(IROpCode.CMP_BNE, "B_CNE"); // 不等
opcodeMap.put(IROpCode.CMP_BLT, "B_CL"); // 小于
opcodeMap.put(IROpCode.CMP_BGT, "B_CG"); // 大于
opcodeMap.put(IROpCode.CMP_BLE, "B_CLE"); // 小于等于
opcodeMap.put(IROpCode.CMP_BGE, "B_CGE"); // 大于等于
// 整形64位比较运算映射
opcodeMap.put(IROpCode.CMP_LEQ, "LC_E"); // 相等
opcodeMap.put(IROpCode.CMP_LNE, "LC_NE"); // 不等
opcodeMap.put(IROpCode.CMP_LLT, "LC_L"); // 小于
opcodeMap.put(IROpCode.CMP_LGT, "LC_G"); // 大于
opcodeMap.put(IROpCode.CMP_LLE, "LC_LE"); // 小于等于
opcodeMap.put(IROpCode.CMP_LGE, "LC_GE"); // 大于等于
// 16位整数比较运算映射
opcodeMap.put(IROpCode.CMP_SEQ, "S_CE"); // 相等
opcodeMap.put(IROpCode.CMP_SNE, "S_CNE"); // 不等
opcodeMap.put(IROpCode.CMP_SLT, "S_CL"); // 小于
opcodeMap.put(IROpCode.CMP_SGT, "S_CG"); // 大于
opcodeMap.put(IROpCode.CMP_SLE, "S_CLE"); // 小于等于
opcodeMap.put(IROpCode.CMP_SGE, "S_CGE"); // 大于等于
// 32位整数比较运算映射
opcodeMap.put(IROpCode.CMP_IEQ, "I_CE"); // 相等
opcodeMap.put(IROpCode.CMP_INE, "I_CNE"); // 不等
opcodeMap.put(IROpCode.CMP_ILT, "I_CL"); // 小于
opcodeMap.put(IROpCode.CMP_IGT, "I_CG"); // 大于
opcodeMap.put(IROpCode.CMP_ILE, "I_CLE"); // 小于等于
opcodeMap.put(IROpCode.CMP_IGE, "I_CGE"); // 大于等于
// 64位整数比较运算映射
opcodeMap.put(IROpCode.CMP_LEQ, "L_CE"); // 相等
opcodeMap.put(IROpCode.CMP_LNE, "L_CNE"); // 不等
opcodeMap.put(IROpCode.CMP_LLT, "L_CL"); // 小于
opcodeMap.put(IROpCode.CMP_LGT, "L_CG"); // 大于
opcodeMap.put(IROpCode.CMP_LLE, "L_CLE"); // 小于等于
opcodeMap.put(IROpCode.CMP_LGE, "L_CGE"); // 大于等于
// 32位浮点比较运算映射
opcodeMap.put(IROpCode.CMP_FEQ, "F_CE"); // 相等
opcodeMap.put(IROpCode.CMP_FNE, "F_CNE"); // 不等
opcodeMap.put(IROpCode.CMP_FLT, "F_CL"); // 小于
opcodeMap.put(IROpCode.CMP_FGT, "F_CG"); // 大于
opcodeMap.put(IROpCode.CMP_FLE, "F_CLE"); // 小于等于
opcodeMap.put(IROpCode.CMP_FGE, "F_CGE"); // 大于等于
// 64位浮点比较运算映射
opcodeMap.put(IROpCode.CMP_DEQ, "D_CE"); // 相等
opcodeMap.put(IROpCode.CMP_DNE, "D_CNE"); // 不等
opcodeMap.put(IROpCode.CMP_DLT, "D_CL"); // 小于
opcodeMap.put(IROpCode.CMP_DGT, "D_CG"); // 大于
opcodeMap.put(IROpCode.CMP_DLE, "D_CLE"); // 小于等于
opcodeMap.put(IROpCode.CMP_DGE, "D_CGE"); // 大于等于
// 加载与存储
opcodeMap.put(IROpCode.LOAD, "I_LOAD"); // 加载

View File

@ -29,113 +29,158 @@ public final class OpHelper {
static {
Map<String, String> map = new HashMap<>();
map.put("I_ADD", Integer.toString(VMOpCode.I_ADD));
map.put("I_SUB", Integer.toString(VMOpCode.I_SUB));
map.put("I_MUL", Integer.toString(VMOpCode.I_MUL));
map.put("I_DIV", Integer.toString(VMOpCode.I_DIV));
map.put("I_MOD", Integer.toString(VMOpCode.I_MOD));
map.put("I_INC", Integer.toString(VMOpCode.I_INC));
map.put("I_NEG", Integer.toString(VMOpCode.I_NEG));
map.put("L_ADD", Integer.toString(VMOpCode.L_ADD));
map.put("L_SUB", Integer.toString(VMOpCode.L_SUB));
map.put("L_MUL", Integer.toString(VMOpCode.L_MUL));
map.put("L_DIV", Integer.toString(VMOpCode.L_DIV));
map.put("L_MOD", Integer.toString(VMOpCode.L_MOD));
map.put("L_INC", Integer.toString(VMOpCode.L_INC));
map.put("L_NEG", Integer.toString(VMOpCode.L_NEG));
map.put("S_ADD", Integer.toString(VMOpCode.S_ADD));
map.put("S_SUB", Integer.toString(VMOpCode.S_SUB));
map.put("S_MUL", Integer.toString(VMOpCode.S_MUL));
map.put("S_DIV", Integer.toString(VMOpCode.S_DIV));
map.put("S_MOD", Integer.toString(VMOpCode.S_MOD));
map.put("S_INC", Integer.toString(VMOpCode.S_INC));
map.put("S_NEG", Integer.toString(VMOpCode.S_NEG));
map.put("B_ADD", Integer.toString(VMOpCode.B_ADD));
map.put("B_SUB", Integer.toString(VMOpCode.B_SUB));
map.put("B_MUL", Integer.toString(VMOpCode.B_MUL));
map.put("B_DIV", Integer.toString(VMOpCode.B_DIV));
map.put("B_MOD", Integer.toString(VMOpCode.B_MOD));
map.put("B_INC", Integer.toString(VMOpCode.B_INC));
map.put("B_NEG", Integer.toString(VMOpCode.B_NEG));
map.put("D_ADD", Integer.toString(VMOpCode.D_ADD));
map.put("D_SUB", Integer.toString(VMOpCode.D_SUB));
map.put("D_MUL", Integer.toString(VMOpCode.D_MUL));
map.put("D_DIV", Integer.toString(VMOpCode.D_DIV));
map.put("D_MOD", Integer.toString(VMOpCode.D_MOD));
map.put("D_NEG", Integer.toString(VMOpCode.D_NEG));
map.put("B_INC", Integer.toString(VMOpCode.B_INC));
map.put("B_AND", Integer.toString(VMOpCode.B_AND));
map.put("B_OR", Integer.toString(VMOpCode.B_OR));
map.put("B_XOR", Integer.toString(VMOpCode.B_XOR));
map.put("B_PUSH", Integer.toString(VMOpCode.B_PUSH));
map.put("B_LOAD", Integer.toString(VMOpCode.B_LOAD));
map.put("B_STORE", Integer.toString(VMOpCode.B_STORE));
map.put("B_CE", Integer.toString(VMOpCode.B_CE));
map.put("B_CNE", Integer.toString(VMOpCode.B_CNE));
map.put("B_CG", Integer.toString(VMOpCode.B_CG));
map.put("B_CGE", Integer.toString(VMOpCode.B_CGE));
map.put("B_CL", Integer.toString(VMOpCode.B_CL));
map.put("B_CLE", Integer.toString(VMOpCode.B_CLE));
map.put("S_ADD", Integer.toString(VMOpCode.S_ADD));
map.put("S_SUB", Integer.toString(VMOpCode.S_SUB));
map.put("S_MUL", Integer.toString(VMOpCode.S_MUL));
map.put("S_DIV", Integer.toString(VMOpCode.S_DIV));
map.put("S_MOD", Integer.toString(VMOpCode.S_MOD));
map.put("S_NEG", Integer.toString(VMOpCode.S_NEG));
map.put("S_INC", Integer.toString(VMOpCode.S_INC));
map.put("S_AND", Integer.toString(VMOpCode.S_AND));
map.put("S_OR", Integer.toString(VMOpCode.S_OR));
map.put("S_XOR", Integer.toString(VMOpCode.S_XOR));
map.put("S_PUSH", Integer.toString(VMOpCode.S_PUSH));
map.put("S_LOAD", Integer.toString(VMOpCode.S_LOAD));
map.put("S_STORE", Integer.toString(VMOpCode.S_STORE));
map.put("S_CE", Integer.toString(VMOpCode.S_CE));
map.put("S_CNE", Integer.toString(VMOpCode.S_CNE));
map.put("S_CG", Integer.toString(VMOpCode.S_CG));
map.put("S_CGE", Integer.toString(VMOpCode.S_CGE));
map.put("S_CL", Integer.toString(VMOpCode.S_CL));
map.put("S_CLE", Integer.toString(VMOpCode.S_CLE));
map.put("I_ADD", Integer.toString(VMOpCode.I_ADD));
map.put("I_SUB", Integer.toString(VMOpCode.I_SUB));
map.put("I_MUL", Integer.toString(VMOpCode.I_MUL));
map.put("I_DIV", Integer.toString(VMOpCode.I_DIV));
map.put("I_MOD", Integer.toString(VMOpCode.I_MOD));
map.put("I_NEG", Integer.toString(VMOpCode.I_NEG));
map.put("I_INC", Integer.toString(VMOpCode.I_INC));
map.put("I_AND", Integer.toString(VMOpCode.I_AND));
map.put("I_OR", Integer.toString(VMOpCode.I_OR));
map.put("I_XOR", Integer.toString(VMOpCode.I_XOR));
map.put("I_PUSH", Integer.toString(VMOpCode.I_PUSH));
map.put("I_LOAD", Integer.toString(VMOpCode.I_LOAD));
map.put("I_STORE", Integer.toString(VMOpCode.I_STORE));
map.put("I_CE", Integer.toString(VMOpCode.I_CE));
map.put("I_CNE", Integer.toString(VMOpCode.I_CNE));
map.put("I_CG", Integer.toString(VMOpCode.I_CG));
map.put("I_CGE", Integer.toString(VMOpCode.I_CGE));
map.put("I_CL", Integer.toString(VMOpCode.I_CL));
map.put("I_CLE", Integer.toString(VMOpCode.I_CLE));
map.put("L_ADD", Integer.toString(VMOpCode.L_ADD));
map.put("L_SUB", Integer.toString(VMOpCode.L_SUB));
map.put("L_MUL", Integer.toString(VMOpCode.L_MUL));
map.put("L_DIV", Integer.toString(VMOpCode.L_DIV));
map.put("L_MOD", Integer.toString(VMOpCode.L_MOD));
map.put("L_NEG", Integer.toString(VMOpCode.L_NEG));
map.put("L_INC", Integer.toString(VMOpCode.L_INC));
map.put("L_AND", Integer.toString(VMOpCode.L_AND));
map.put("L_OR", Integer.toString(VMOpCode.L_OR));
map.put("L_XOR", Integer.toString(VMOpCode.L_XOR));
map.put("L_PUSH", Integer.toString(VMOpCode.L_PUSH));
map.put("L_LOAD", Integer.toString(VMOpCode.L_LOAD));
map.put("L_STORE", Integer.toString(VMOpCode.L_STORE));
map.put("L_CE", Integer.toString(VMOpCode.L_CE));
map.put("L_CNE", Integer.toString(VMOpCode.L_CNE));
map.put("L_CG", Integer.toString(VMOpCode.L_CG));
map.put("L_CGE", Integer.toString(VMOpCode.L_CGE));
map.put("L_CL", Integer.toString(VMOpCode.L_CL));
map.put("L_CLE", Integer.toString(VMOpCode.L_CLE));
map.put("F_ADD", Integer.toString(VMOpCode.F_ADD));
map.put("F_SUB", Integer.toString(VMOpCode.F_SUB));
map.put("F_MUL", Integer.toString(VMOpCode.F_MUL));
map.put("F_DIV", Integer.toString(VMOpCode.F_DIV));
map.put("F_MOD", Integer.toString(VMOpCode.F_MOD));
map.put("F_NEG", Integer.toString(VMOpCode.F_NEG));
map.put("D_INC", Integer.toString(VMOpCode.D_INC));
map.put("F_INC", Integer.toString(VMOpCode.F_INC));
map.put("I2L", Integer.toString(VMOpCode.I2L));
map.put("I2S", Integer.toString(VMOpCode.I2S));
map.put("F_PUSH", Integer.toString(VMOpCode.F_PUSH));
map.put("F_LOAD", Integer.toString(VMOpCode.F_LOAD));
map.put("F_STORE", Integer.toString(VMOpCode.F_STORE));
map.put("F_CE", Integer.toString(VMOpCode.F_CE));
map.put("F_CNE", Integer.toString(VMOpCode.F_CNE));
map.put("F_CG", Integer.toString(VMOpCode.F_CG));
map.put("F_CGE", Integer.toString(VMOpCode.F_CGE));
map.put("F_CL", Integer.toString(VMOpCode.F_CL));
map.put("F_CLE", Integer.toString(VMOpCode.F_CLE));
map.put("D_ADD", Integer.toString(VMOpCode.D_ADD));
map.put("D_SUB", Integer.toString(VMOpCode.D_SUB));
map.put("D_MUL", Integer.toString(VMOpCode.D_MUL));
map.put("D_DIV", Integer.toString(VMOpCode.D_DIV));
map.put("D_MOD", Integer.toString(VMOpCode.D_MOD));
map.put("D_NEG", Integer.toString(VMOpCode.D_NEG));
map.put("D_INC", Integer.toString(VMOpCode.D_INC));
map.put("D_PUSH", Integer.toString(VMOpCode.D_PUSH));
map.put("D_LOAD", Integer.toString(VMOpCode.D_LOAD));
map.put("D_STORE", Integer.toString(VMOpCode.D_STORE));
map.put("D_CE", Integer.toString(VMOpCode.D_CE));
map.put("D_CNE", Integer.toString(VMOpCode.D_CNE));
map.put("D_CG", Integer.toString(VMOpCode.D_CG));
map.put("D_CGE", Integer.toString(VMOpCode.D_CGE));
map.put("D_CL", Integer.toString(VMOpCode.D_CL));
map.put("D_CLE", Integer.toString(VMOpCode.D_CLE));
map.put("B2S", Integer.toString(VMOpCode.B2S));
map.put("B2I", Integer.toString(VMOpCode.B2I));
map.put("B2L", Integer.toString(VMOpCode.B2L));
map.put("B2F", Integer.toString(VMOpCode.B2F));
map.put("B2D", Integer.toString(VMOpCode.B2D));
map.put("S2B", Integer.toString(VMOpCode.S2B));
map.put("S2I", Integer.toString(VMOpCode.S2I));
map.put("S2L", Integer.toString(VMOpCode.S2L));
map.put("S2F", Integer.toString(VMOpCode.S2F));
map.put("S2D", Integer.toString(VMOpCode.S2D));
map.put("I2B", Integer.toString(VMOpCode.I2B));
map.put("I2D", Integer.toString(VMOpCode.I2D));
map.put("I2S", Integer.toString(VMOpCode.I2S));
map.put("I2L", Integer.toString(VMOpCode.I2L));
map.put("I2F", Integer.toString(VMOpCode.I2F));
map.put("I2D", Integer.toString(VMOpCode.I2D));
map.put("L2B", Integer.toString(VMOpCode.L2B));
map.put("L2S", Integer.toString(VMOpCode.L2S));
map.put("L2I", Integer.toString(VMOpCode.L2I));
map.put("L2D", Integer.toString(VMOpCode.L2D));
map.put("L2F", Integer.toString(VMOpCode.L2F));
map.put("L2D", Integer.toString(VMOpCode.L2D));
map.put("F2B", Integer.toString(VMOpCode.F2B));
map.put("F2S", Integer.toString(VMOpCode.F2S));
map.put("F2I", Integer.toString(VMOpCode.F2I));
map.put("F2L", Integer.toString(VMOpCode.F2L));
map.put("F2D", Integer.toString(VMOpCode.F2D));
map.put("D2B", Integer.toString(VMOpCode.D2B));
map.put("D2S", Integer.toString(VMOpCode.D2S));
map.put("D2I", Integer.toString(VMOpCode.D2I));
map.put("D2L", Integer.toString(VMOpCode.D2L));
map.put("D2F", Integer.toString(VMOpCode.D2F));
map.put("S2I", Integer.toString(VMOpCode.S2I));
map.put("B2I", Integer.toString(VMOpCode.B2I));
map.put("I_AND", Integer.toString(VMOpCode.I_AND));
map.put("I_OR", Integer.toString(VMOpCode.I_OR));
map.put("I_XOR", Integer.toString(VMOpCode.I_XOR));
map.put("L_AND", Integer.toString(VMOpCode.L_AND));
map.put("L_OR", Integer.toString(VMOpCode.L_OR));
map.put("L_XOR", Integer.toString(VMOpCode.L_XOR));
map.put("JUMP", Integer.toString(VMOpCode.JUMP));
map.put("IC_E", Integer.toString(VMOpCode.I_CE));
map.put("IC_NE", Integer.toString(VMOpCode.I_CNE));
map.put("IC_G", Integer.toString(VMOpCode.I_CG));
map.put("IC_GE", Integer.toString(VMOpCode.I_CGE));
map.put("IC_L", Integer.toString(VMOpCode.I_CL));
map.put("IC_LE", Integer.toString(VMOpCode.I_CLE));
map.put("LC_E", Integer.toString(VMOpCode.L_CE));
map.put("LC_NE", Integer.toString(VMOpCode.L_CNE));
map.put("LC_G", Integer.toString(VMOpCode.L_CG));
map.put("LC_GE", Integer.toString(VMOpCode.L_CGE));
map.put("LC_L", Integer.toString(VMOpCode.L_CL));
map.put("LC_LE", Integer.toString(VMOpCode.L_CLE));
map.put("I_PUSH", Integer.toString(VMOpCode.I_PUSH));
map.put("L_PUSH", Integer.toString(VMOpCode.L_PUSH));
map.put("S_PUSH", Integer.toString(VMOpCode.S_PUSH));
map.put("B_PUSH", Integer.toString(VMOpCode.B_PUSH));
map.put("D_PUSH", Integer.toString(VMOpCode.D_PUSH));
map.put("F_PUSH", Integer.toString(VMOpCode.F_PUSH));
map.put("POP", Integer.toString(VMOpCode.POP));
map.put("DUP", Integer.toString(VMOpCode.DUP));
map.put("SWAP", Integer.toString(VMOpCode.SWAP));
map.put("I_STORE", Integer.toString(VMOpCode.I_STORE));
map.put("L_STORE", Integer.toString(VMOpCode.L_STORE));
map.put("S_STORE", Integer.toString(VMOpCode.S_STORE));
map.put("B_STORE", Integer.toString(VMOpCode.B_STORE));
map.put("D_STORE", Integer.toString(VMOpCode.D_STORE));
map.put("F_STORE", Integer.toString(VMOpCode.F_STORE));
map.put("I_LOAD", Integer.toString(VMOpCode.I_LOAD));
map.put("L_LOAD", Integer.toString(VMOpCode.L_LOAD));
map.put("S_LOAD", Integer.toString(VMOpCode.S_LOAD));
map.put("B_LOAD", Integer.toString(VMOpCode.B_LOAD));
map.put("D_LOAD", Integer.toString(VMOpCode.D_LOAD));
map.put("F_LOAD", Integer.toString(VMOpCode.F_LOAD));
map.put("MOV", Integer.toString(VMOpCode.MOV));
map.put("JUMP", Integer.toString(VMOpCode.JUMP));
map.put("CALL", Integer.toString(VMOpCode.CALL));
map.put("RET", Integer.toString(VMOpCode.RET));
map.put("MOV", Integer.toString(VMOpCode.MOV));
map.put("HALT", Integer.toString(VMOpCode.HALT));
map.put("SYSCALL", Integer.toString(VMOpCode.SYSCALL));
map.put("DEBUG_TRAP", Integer.toString(VMOpCode.DEBUG_TRAP));
OPCODE_MAP = Collections.unmodifiableMap(map);
Map<Integer, String> revmap = new HashMap<>(); // reverse map
OPCODE_MAP.forEach((key, value) -> revmap.put(Integer.parseInt(value), key));
OPCODE_NAME_MAP = Collections.unmodifiableMap(revmap);
}
@ -197,7 +242,7 @@ public final class OpHelper {
public static String opcodeName(int code) {
String name = OPCODE_NAME_MAP.get(code);
if (name == null) {
throw new IllegalStateException("Unknown opcode: " + name);
throw new IllegalStateException("Unknown opcode: " + code);
}
return name;
}

View File

@ -0,0 +1,126 @@
package org.jcnc.snow.compiler.backend.utils;
/**
* 工具类提供基本数值类型的提升与类型转换辅助功能
* <p>
* 在进行数值类型运算比较等操作时低优先级的类型会被提升为高优先级类型参与运算
* 例如 int + long 运算int 会被提升为 long最终运算结果类型为 long
* <p>
* 类型优先级从高到低依次为
* Ddouble6
* Ffloat 5
* Llong 4
* Iint 3
* Sshort 2
* Bbyte 1
* 未识别类型 0
*/
public class TypePromoteUtils {
/**
* 返回数值类型的宽度优先级数值越大类型越宽
* 类型及优先级映射如下
* Ddouble: 6
* Ffloat : 5
* Llong : 4
* Iint : 3
* Sshort : 2
* Bbyte : 1
* 未知类型 : 0
*
* @param p 类型标记字符B/S/I/L/F/D
* @return 优先级数值0 表示未知类型
*/
private static int rank(char p) {
return switch (p) {
case 'D' -> 6;
case 'F' -> 5;
case 'L' -> 4;
case 'I' -> 3;
case 'S' -> 2;
case 'B' -> 1;
default -> 0;
};
}
/**
* 返回两个类型中较的公共类型即优先级较高的类型
* 若优先级相等返回第一个参数的类型
*
* @param a 类型标记字符1
* @param b 类型标记字符2
* @return 优先级较高的类型标记字符
*/
public static char promote(char a, char b) {
return rank(a) >= rank(b) ? a : b;
}
/**
* 将单个字符的类型标记转为字符串
*
* @param p 类型标记字符
* @return 类型标记的字符串形式
*/
public static String str(char p) {
return String.valueOf(p);
}
/**
* 获取类型转换指令名例如 "I2L", "F2D"表示从源类型到目标类型的转换操作
* 如果源类型和目标类型相同则返回 null表示无需转换
* <p>
* 支持的类型标记字符包括BbyteSshortIintLlongFfloatDdouble
* 所有可能的类型转换均已覆盖如下所示
* B S/I/L/F/D
* S B/I/L/F/D
* I B/S/L/F/D
* L B/S/I/F/D
* F B/S/I/L/D
* D B/S/I/L/F
*
* @param from 源类型标记字符
* @param to 目标类型标记字符
* @return 类型转换指令名 "L2I"如无须转换则返回 null
*/
public static String convert(char from, char to) {
if (from == to) return null;
return switch ("" + from + to) {
case "BS" -> "B2S";
case "BI" -> "B2I";
case "BL" -> "B2L";
case "BF" -> "B2F";
case "BD" -> "B2D";
case "SB" -> "S2B";
case "SI" -> "S2I";
case "SL" -> "S2L";
case "SF" -> "S2F";
case "SD" -> "S2D";
case "IB" -> "I2B";
case "IS" -> "I2S";
case "IL" -> "I2L";
case "IF" -> "I2F";
case "ID" -> "I2D";
case "LB" -> "L2B";
case "LS" -> "L2S";
case "LI" -> "L2I";
case "LF" -> "L2F";
case "LD" -> "L2D";
case "FB" -> "F2B";
case "FS" -> "F2S";
case "FI" -> "F2I";
case "FL" -> "F2L";
case "FD" -> "F2D";
case "DB" -> "D2B";
case "DS" -> "D2S";
case "DI" -> "D2I";
case "DL" -> "D2L";
case "DF" -> "D2F";
default -> null;
};
}
}

View File

@ -145,7 +145,7 @@ public record ExpressionBuilder(IRContext ctx) {
if (ComparisonUtils.isComparisonOperator(op)) {
return InstructionFactory.binOp(
ctx,
ComparisonUtils.cmpOp(op, bin.left(), bin.right()),
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
left, right);
}
@ -171,7 +171,7 @@ public record ExpressionBuilder(IRContext ctx) {
if (ComparisonUtils.isComparisonOperator(op)) {
InstructionFactory.binOpInto(
ctx,
ComparisonUtils.cmpOp(op, bin.left(), bin.right()),
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
a, b, dest);
} else {
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());

View File

@ -1,5 +1,6 @@
package org.jcnc.snow.compiler.ir.builder;
import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable;
import org.jcnc.snow.compiler.ir.core.IRFunction;
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
@ -32,6 +33,10 @@ public class FunctionBuilder {
*/
public IRFunction build(FunctionNode functionNode) {
// 在全局函数表中注册函数信息
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
// 0) 基本初始化创建 IRFunction 实例与对应上下文
IRFunction irFunction = new IRFunction(functionNode.name());
IRContext irContext = new IRContext(irFunction);

View File

@ -13,7 +13,7 @@ import java.util.Map;
* <ul>
* <li>维护在当前作用域中已声明变量的寄存器分配信息</li>
* <li>支持将已有虚拟寄存器与变量名重新绑定</li>
* <li>根据变量名查找对应的虚拟寄存器实例</li>
* <li>根据变量名查找对应的虚拟寄存器实例或类型</li>
* </ul>
*/
final class IRBuilderScope {
@ -104,4 +104,12 @@ final class IRBuilderScope {
String lookupType(String name) {
return varTypes.get(name);
}
/**
* 获取 变量->类型的映射 的不可变副本
* @return 变量->类型的映射 的不可变副本
*/
Map<String, String> getVarTypes() {
return Map.copyOf(varTypes);
}
}

View File

@ -5,6 +5,7 @@ import org.jcnc.snow.compiler.ir.core.IRProgram;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.List;
@ -81,9 +82,7 @@ public final class IRProgramBuilder {
null,
String.valueOf(List.of()),
List.of(stmt),
-1,
-1,
""
new NodeContext(-1, -1, "")
);
}
}

View File

@ -6,6 +6,7 @@ import org.jcnc.snow.compiler.ir.utils.IROpCodeUtils;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.Locale;
@ -66,12 +67,12 @@ public class StatementBuilder {
buildIf(ifNode);
return;
}
if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, int _, int _, String _)) {
if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, NodeContext _)) {
// 纯表达式语句 foo();
expr.build(exp);
return;
}
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, int _, int _, String _)) {
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) {
// 赋值语句 a = b + 1;
final String type = ctx.getScope().lookupType(var);
@ -209,9 +210,7 @@ public class StatementBuilder {
ExpressionNode left,
String operator,
ExpressionNode right,
_,
_,
_
NodeContext _
)
&& ComparisonUtils.isComparisonOperator(operator)) {
@ -219,7 +218,7 @@ public class StatementBuilder {
IRVirtualRegister b = expr.build(right);
// 使用适配后位宽正确的比较指令
IROpCode cmp = ComparisonUtils.cmpOp(operator, left, right);
IROpCode cmp = ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), operator, left, right);
IROpCode falseOp = IROpCodeUtils.invert(cmp);
InstructionFactory.cmpJump(ctx, falseOp, a, b, falseLabel);

View File

@ -0,0 +1,76 @@
package org.jcnc.snow.compiler.ir.common;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 全局函数返回类型表
* <p>
* 此工具类用于在编译前端构建每个函数 IR中间表示时登记函数的返回类型
* 以便后端在生成 {@code CALL} 指令时判断是否需要保存返回值
* </p>
*
* 使用说明
* <ul>
* <li>在函数 IR 构建阶段调用 {@link #register(String, String)} 方法登记函数名与返回类型</li>
* <li>在生成调用指令阶段通过 {@link #getReturnType(String)} 查询函数的返回类型</li>
* <li>返回类型统一为小写若调用 {@code register} 时传入的返回类型为 {@code null}则登记为 {@code "void"}</li>
* </ul>
*/
public final class GlobalFunctionTable {
/**
* 存储全局函数返回类型映射表
* <ul>
* <li>Key函数名不含模块限定</li>
* <li>Value返回类型统一转换为小写字符串若无返回值则为 {@code "void"}</li>
* </ul>
*/
private static final Map<String, String> RETURN_TYPES = new ConcurrentHashMap<>();
/**
* 私有构造函数防止实例化
*/
private GlobalFunctionTable() {
// 工具类禁止实例化
}
/**
* 登记或更新指定函数的返回类型
*
* <p>若传入的 {@code returnType} {@code null}则登记为 {@code "void"}
* 否则将去除前后空白并转换为小写后登记</p>
*
* @param name 函数名不含模块限定
* @param returnType 函数返回类型字符串 {@code "int"}{@code "String"}
* 如果为 {@code null}则登记类型为 {@code "void"}
* @throws IllegalArgumentException 如果 {@code name} 为空或 {@code null}
*/
public static void register(String name, String returnType) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("函数名不能为空或 null");
}
RETURN_TYPES.put(
name,
returnType == null
? "void"
: returnType.trim().toLowerCase()
);
}
/**
* 查询指定函数的返回类型
*
* <p>返回类型为登记时的值小写如果函数未登记过则返回 {@code null}</p>
*
* @param name 函数名不含模块限定
* @return 已登记的返回类型小写 {@code null} 表示未知
* @throws IllegalArgumentException 如果 {@code name} 为空或 {@code null}
*/
public static String getReturnType(String name) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("函数名不能为空或 null");
}
return RETURN_TYPES.get(name);
}
}

View File

@ -66,21 +66,53 @@ public enum IROpCode {
DIV_D64, // 64位浮点除法
NEG_D64, // 64位浮点取负
/* ───── 逻辑与比较运算指令8位整数byte ───── */
CMP_BEQ, // 8位整数相等比较a == b
CMP_BNE, // 8位整数不等比较a != b
CMP_BLT, // 8位整数小于比较a < b
CMP_BGT, // 8位整数大于比较a > b
CMP_BLE, // 8位整数小于等于a <= b
CMP_BGE, // 8位整数大于等于a >= b
/* ───── 逻辑与比较运算指令16位整数int ───── */
CMP_SEQ, // 16位整数相等比较a == b
CMP_SNE, // 16位整数不等比较a != b
CMP_SLT, // 16位整数小于比较a < b
CMP_SGT, // 16位整数大于比较a > b
CMP_SLE, // 16位整数小于等于a <= b
CMP_SGE, // 16位整数大于等于a >= b
/* ───── 逻辑与比较运算指令32位整数int ───── */
CMP_IEQ, // 32位相等比较a == b
CMP_INE, // 32位不等比较a != b
CMP_ILT, // 32位小于比较a < b
CMP_IGT, // 32位大于比较a > b
CMP_ILE, // 32位小于等于a <= b
CMP_IGE, // 32位大于等于a >= b
CMP_IEQ, // 32位整数相等比较a == b
CMP_INE, // 32位整数不等比较a != b
CMP_ILT, // 32位整数小于比较a < b
CMP_IGT, // 32位整数大于比较a > b
CMP_ILE, // 32位整数小于等于a <= b
CMP_IGE, // 32位整数大于等于a >= b
/* ───── 逻辑与比较运算指令64位整数long ───── */
CMP_LEQ, // 64位相等比较a == b
CMP_LNE, // 64位不等比较a != b
CMP_LLT, // 64位小于比较a < b
CMP_LGT, // 64位大于比较a > b
CMP_LLE, // 64位小于等于a <= b
CMP_LGE, // 64位大于等于a >= b
CMP_LEQ, // 64位整数相等比较a == b
CMP_LNE, // 64位整数不等比较a != b
CMP_LLT, // 64位整数小于比较a < b
CMP_LGT, // 64位整数大于比较a > b
CMP_LLE, // 64位整数小于等于a <= b
CMP_LGE, // 64位整数大于等于a >= b
/* ───── 逻辑与比较运算指令32位浮点数float ───── */
CMP_FEQ, // 32位浮点相等比较a == b
CMP_FNE, // 32位浮点不等比较a != b
CMP_FLT, // 32位浮点小于比较a < b
CMP_FGT, // 32位浮点大于比较a > b
CMP_FLE, // 32位浮点小于等于a <= b
CMP_FGE, // 32位浮点大于等于a >= b
/* ───── 逻辑与比较运算指令64位浮点数double ───── */
CMP_DEQ, // 64位浮点相等比较a == b
CMP_DNE, // 64位浮点不等比较a != b
CMP_DLT, // 64位浮点小于比较a < b
CMP_DGT, // 64位浮点大于比较a > b
CMP_DLE, // 64位浮点小于等于a <= b
CMP_DGE, // 64位浮点大于等于a >= b
/* ───── 数据访问与常量操作 ───── */
LOAD, // 从内存加载数据至寄存器

View File

@ -40,6 +40,26 @@ public final class IROpCodeMappings {
);
/* ────── 比较运算符映射 ────── */
/** 8-bitbyte比较 */
public static final Map<String, IROpCode> CMP_B8 = Map.of(
"==", IROpCode.CMP_BEQ,
"!=", IROpCode.CMP_BNE,
"<", IROpCode.CMP_BLT,
">", IROpCode.CMP_BGT,
"<=", IROpCode.CMP_BLE,
">=", IROpCode.CMP_BGE
);
/** 16-bitshort比较 */
public static final Map<String, IROpCode> CMP_S16 = Map.of(
"==", IROpCode.CMP_SEQ,
"!=", IROpCode.CMP_SNE,
"<", IROpCode.CMP_SLT,
">", IROpCode.CMP_SGT,
"<=", IROpCode.CMP_SLE,
">=", IROpCode.CMP_SGE
);
/** 32-bitint比较 */
public static final Map<String, IROpCode> CMP_I32 = Map.of(
"==", IROpCode.CMP_IEQ,
@ -60,5 +80,23 @@ public final class IROpCodeMappings {
">=", IROpCode.CMP_LGE
);
/** 32-bitfloat比较 */
public static final Map<String, IROpCode> CMP_F32 = Map.of(
"==", IROpCode.CMP_FEQ,
"!=", IROpCode.CMP_FNE,
"<", IROpCode.CMP_FLT,
">", IROpCode.CMP_FGT,
"<=", IROpCode.CMP_FLE,
">=", IROpCode.CMP_FGE
);
/** 64-bitdouble比较 */
public static final Map<String, IROpCode> CMP_D64 = Map.of(
"==", IROpCode.CMP_DEQ,
"!=", IROpCode.CMP_DNE,
"<", IROpCode.CMP_DLT,
">", IROpCode.CMP_DGT,
"<=", IROpCode.CMP_DLE,
">=", IROpCode.CMP_DGE
);
}

View File

@ -1,41 +1,63 @@
package org.jcnc.snow.compiler.ir.instruction;
import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable;
import org.jcnc.snow.compiler.ir.core.IRInstruction;
import org.jcnc.snow.compiler.ir.core.IROpCode;
import org.jcnc.snow.compiler.ir.core.IRValue;
import org.jcnc.snow.compiler.ir.core.IRVisitor;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import org.jcnc.snow.compiler.ir.core.IRValue;
import java.util.ArrayList;
import java.util.List;
/**
* CallInstruction 表示一次函数调用格式dest = CALL functionName, arg1, arg2, ...
* CallInstruction 表示一次函数调用的中间代码指令
* <pre>
* dest = CALL foo, arg1, arg2, ...
* </pre>
* 若被调函数返回 void {@code dest} 可以为 {@code null}
* 并且不会参与寄存器分配
*/
public class CallInstruction extends IRInstruction {
/** 调用结果目标寄存器void 返回时可为 null */
private final IRVirtualRegister dest;
/** 被调用的函数名(含模块限定) */
private final String functionName;
/** 实参列表 */
private final List<IRValue> arguments;
public CallInstruction(IRVirtualRegister dest, String functionName, List<IRValue> args) {
public CallInstruction(IRVirtualRegister dest,
String functionName,
List<IRValue> args) {
this.dest = dest;
this.functionName = functionName;
this.arguments = List.copyOf(args);
}
// === 基本信息 ===
@Override
public IROpCode op() {
return IROpCode.CALL;
}
/** 仅在函数有返回值时才暴露目标寄存器 */
@Override
public IRVirtualRegister dest() {
return isVoidReturn() ? null : dest;
}
/** 操作数列表void 调用不包含 dest */
@Override
public List<IRValue> operands() {
List<IRValue> ops = new ArrayList<>();
if (!isVoidReturn() && dest != null) {
ops.add(dest);
}
ops.addAll(arguments);
return ops;
}
// === Getter ===
public IRVirtualRegister getDest() {
return dest;
}
@ -48,14 +70,26 @@ public class CallInstruction extends IRInstruction {
return arguments;
}
// === 帮助方法 ===
/** 判断被调函数是否返回 void */
private boolean isVoidReturn() {
return "void".equals(GlobalFunctionTable.getReturnType(functionName));
}
// === 访客模式 ===
@Override
public void accept(IRVisitor visitor) {
visitor.visit(this);
}
// === 字符串表示 ===
@Override
public String toString() {
StringBuilder sb = new StringBuilder(dest + " = CALL " + functionName);
StringBuilder sb = new StringBuilder();
if (!isVoidReturn() && dest != null) {
sb.append(dest).append(" = ");
}
sb.append("CALL ").append(functionName);
for (IRValue arg : arguments) {
sb.append(", ").append(arg);
}

View File

@ -2,48 +2,142 @@ package org.jcnc.snow.compiler.ir.utils;
import org.jcnc.snow.compiler.ir.core.IROpCode;
import org.jcnc.snow.compiler.ir.core.IROpCodeMappings;
import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.Map;
/**
* 比较运算辅助工具
* 根据左右操作数类型目前通过字面量后缀 <code>L/l</code> 判定选择
* 正确的 IR 比较指令保证 int/long 均能正常运行
* 工具类用于比较运算相关的类型推断和指令选择
* <p>
* 该类主要用于根据左右操作数的静态类型自动选择正确的 IR 层比较操作码
* 支持自动类型提升保证 intlongfloatdouble 等类型的比较均能得到正确的 IR 指令
* </p>
*
* 类型判定支持
* <ul>
* <li>字面量后缀支持 B/S/I/L/F/D大小写均可</li>
* <li>浮点数支持如无后缀但有小数点视为 double</li>
* <li>变量类型根据传入变量表推断类型未识别则默认 int</li>
* </ul>
*/
public final class ComparisonUtils {
private ComparisonUtils() {
}
private ComparisonUtils() {}
/**
* 判断给定操作符是否为比较运算符
* 判断给定字符串是否为受支持的比较运算符==, !=, <, >, <=, >=
* 仅检查 int 类型指令表因所有类型的比较符号集合相同
*
* @param op 比较运算符字符串
* @return 若为比较运算符返回 true否则返回 false
*/
public static boolean isComparisonOperator(String op) {
// 两张表 key 完全一致只需检查一张
// 只需查 int 类型表即可
return IROpCodeMappings.CMP_I32.containsKey(op);
}
/**
* 返回符合操作数位宽的比较 IROpCode
* 返回类型宽度优先级越大代表类型越宽类型对应的优先级
* - D (double): 6
* - F (float): 5
* - L (long): 4
* - I (int): 3
* - S (short): 2
* - B (byte): 1
* - 未知类型: 0
*
* @param op 比较符号==, !=, <, >, <=, >=
* @param left 左操作数 AST
* @param right 右操作数 AST
* @param p 类型标记字符
* @return 类型优先级数值
*/
public static IROpCode cmpOp(String op, ExpressionNode left, ExpressionNode right) {
boolean useLong = isLongLiteral(left) || isLongLiteral(right);
Map<String, IROpCode> table = useLong ? IROpCodeMappings.CMP_L64
: IROpCodeMappings.CMP_I32;
private static int rank(char p) {
return switch (p) {
case 'D' -> 6;
case 'F' -> 5;
case 'L' -> 4;
case 'I' -> 3;
case 'S' -> 2;
case 'B' -> 1;
default -> 0;
};
}
/**
* 返回两个类型中较的类型即优先级较高的类型
* 若优先级相等返回第一个参数的类型
*
* @param a 类型标记字符1
* @param b 类型标记字符2
* @return 宽度更高的类型字符
*/
public static char promote(char a, char b) {
return rank(a) >= rank(b) ? a : b;
}
/**
* 根据变量类型映射和操作数表达式推断操作数类型
* 并自动类型提升后选择正确的比较操作码IROpCode
* 若未能推断类型或操作符不受支持会抛出异常
*
* @param variables 变量名到类型的映射 "a" -> "int"
* @param op 比较符号==, !=, <, >, <=, >=
* @param left 左操作数表达式
* @param right 右操作数表达式
* @return 适用的比较 IROpCode
* @throws IllegalStateException 如果无法推断合适的类型
*/
public static IROpCode cmpOp(Map<String, String> variables, String op, ExpressionNode left, ExpressionNode right) {
char typeLeft = analysisType(variables, left);
char typeRight = analysisType(variables, right);
char type = promote(typeLeft, typeRight);
Map<String, IROpCode> table = switch (type) {
case 'B' -> IROpCodeMappings.CMP_B8;
case 'S' -> IROpCodeMappings.CMP_S16;
case 'I' -> IROpCodeMappings.CMP_I32;
case 'L' -> IROpCodeMappings.CMP_L64;
case 'F' -> IROpCodeMappings.CMP_F32;
case 'D' -> IROpCodeMappings.CMP_D64;
default -> throw new IllegalStateException("Unexpected value: " + type);
};
return table.get(op);
}
/* ------------ 内部工具 ------------ */
private static boolean isLongLiteral(ExpressionNode node) {
if (node instanceof NumberLiteralNode(String value, int _, int _, String _)) {
return value.endsWith("L") || value.endsWith("l");
/**
* 内部工具方法根据表达式节点和变量表推断类型标记字符
* 字面量支持 B/S/I/L/F/D大小写均可浮点数默认 double
* 标识符类型按变量表映射未知则默认 int
*
* @param variables 变量名到类型的映射
* @param node 表达式节点
* @return 类型标记字符B/S/I/L/F/D未知时返回 I
*/
private static char analysisType(Map<String, String> variables, ExpressionNode node) {
if (node instanceof NumberLiteralNode(String value, NodeContext _)) {
char suffix = Character.toUpperCase(value.charAt(value.length() - 1));
if ("BSILFD".indexOf(suffix) != -1) {
return suffix;
}
return false; // 变量暂不处理后续可扩展符号表查询
if (value.indexOf('.') != -1) {
return 'D';
}
return 'I'; // 默认 int
}
if (node instanceof IdentifierNode(String name, NodeContext _)) {
final String type = variables.get(name);
if (type != null) {
switch (type) {
case "byte": return 'B';
case "short": return 'S';
case "int": return 'I';
case "long": return 'L';
case "float": return 'F';
case "double": return 'D';
}
}
}
return 'I'; // 默认 int
}
}

View File

@ -7,37 +7,51 @@ import org.jcnc.snow.compiler.ir.value.IRConstant;
import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode;
import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.Map;
/**
* 表达式分析与运算符辅助工具类
*
* <p>主要功能</p>
* <ul>
* <li>字面量常量的解析与类型推断</li>
* <li>自动匹配算术/比较操作码</li>
* <li>表达式类型合并与提升</li>
* </ul>
* 表达式分析与操作符选择工具类
* <p>
* 主要功能
* - 解析字面量常量自动推断类型
* - 自动匹配并选择适合的算术/比较操作码
* - 表达式类型的合并与类型提升
* - 支持线程隔离的函数级默认类型后缀
*/
public final class ExpressionUtils {
private ExpressionUtils() {}
/* ────────────────── 线程级默认类型后缀 ────────────────── */
// 线程级默认类型后缀
/** 默认类型后缀(如当前函数返回类型),线程隔离。 */
/**
* 当前线程的默认类型后缀如当前函数返回类型等用于类型推断兜底
*/
private static final ThreadLocal<Character> DEFAULT_SUFFIX =
ThreadLocal.withInitial(() -> '\0');
/**
* 设置当前线程的默认类型后缀
*
* @param suffix 类型后缀字符b/s/i/l/f/d'\0'表示无
*/
public static void setDefaultSuffix(char suffix) { DEFAULT_SUFFIX.set(suffix); }
/**
* 清除当前线程的默认类型后缀重置为无
*/
public static void clearDefaultSuffix() { DEFAULT_SUFFIX.set('\0'); }
/* ───────────────────── 字面量 & 常量 ───────────────────── */
// 字面量常量解析
/**
* 解析整数字面量字符串自动去除类型后缀b/s/l/f/d/B/S/L/F/D并转换为 int
* 安全解析整数字面量字符串自动去除单字符类型后缀b/s/l/f/d大小写均可并转换为 int
*
* @param literal 字面量字符串
* @return 字面量对应的 int 数值
* @throws NumberFormatException 如果字面量无法转换为整数
*/
public static int parseIntSafely(String literal) {
String digits = literal.replaceAll("[bslfdBSDLF]$", "");
@ -45,8 +59,14 @@ public final class ExpressionUtils {
}
/**
* 根据数字字面量字符串自动判断类型生成对应类型的 {@link IRConstant}
* 支持 b/s/l/f/d 后缀与浮点格式
* 根据数字字面量字符串推断类型并生成对应的 IRConstant 常量值
* <p>
* 支持的字面量后缀有 b/s/l/f/d大小写均可
* 无后缀时优先参考 IRContext 当前变量类型否则根据字面量格式'.''e'判断为 double否则为 int
*
* @param ctx IRContext允许参考变量声明类型
* @param value 数字字面量字符串
* @return 对应类型的 IRConstant 常量
*/
public static IRConstant buildNumberConstant(IRContext ctx, String value) {
char suffix = value.isEmpty() ? '\0'
@ -55,7 +75,7 @@ public final class ExpressionUtils {
String digits = switch (suffix) {
case 'b','s','l','f','d' -> value.substring(0, value.length() - 1);
default -> {
/* 如果字面量本身没有后缀,则回退到变量目标类型(如声明语句左值) */
// 无后缀优先参考变量类型
if (ctx.getVarType() != null) {
String t = ctx.getVarType();
suffix = switch (t) {
@ -72,7 +92,7 @@ public final class ExpressionUtils {
}
};
/* 创建常量 */
// 生成常量对象
return switch (suffix) {
case 'b' -> new IRConstant(Byte.parseByte(digits));
case 's' -> new IRConstant(Short.parseShort(digits));
@ -85,10 +105,13 @@ public final class ExpressionUtils {
};
}
/* ────────────────────── 一元运算 ────────────────────── */
// 一元运算指令匹配
/**
* 推断一元取负-运算应使用的 {@link IROpCode}
* 根据表达式节点的类型后缀选择对应的取负-运算操作码
*
* @param operand 操作数表达式
* @return 匹配类型的 IROpCode
*/
public static IROpCode negOp(ExpressionNode operand) {
char t = typeChar(operand);
@ -98,36 +121,56 @@ public final class ExpressionUtils {
case 'l' -> IROpCode.NEG_L64;
case 'f' -> IROpCode.NEG_F32;
case 'd' -> IROpCode.NEG_D64;
default -> IROpCode.NEG_I32; // '\0' 'i'
default -> IROpCode.NEG_I32; // 无法推断或为 int
};
}
/* ────────────────── 比较运算(已适配 long ────────────────── */
// 比较运算相关
/** 判断给定字符串是否是比较运算符(==, !=, <, >, <=, >=)。 */
/**
* 判断给定字符串是否为支持的比较运算符==, !=, <, >, <=, >=
*
* @param op 操作符字符串
* @return 若为比较运算符返回 true否则返回 false
*/
public static boolean isComparisonOperator(String op) {
return ComparisonUtils.isComparisonOperator(op);
}
/**
* 兼容旧调用仅凭操作符返回 <em>int32</em> 比较指令
* 兼容旧逻辑仅凭操作符直接返回 int32 比较指令
*
* @param op 比较操作符
* @return int32 类型的比较操作码
*/
public static IROpCode cmpOp(String op) {
return IROpCodeMappings.CMP_I32.get(op); // 旧逻辑一律 i32
return IROpCodeMappings.CMP_I32.get(op);
}
/**
* 推荐调用根据左右表达式类型自动选择 int / long 比较指令
* 推荐调用根据左右表达式类型自动选择 int/long/float/double 等合适的比较操作码
*
* @param variables 变量名到类型的映射
* @param op 比较符号
* @param left 左操作数表达式
* @param right 右操作数表达式
* @return 匹配类型的比较操作码
*/
public static IROpCode cmpOp(String op, ExpressionNode left, ExpressionNode right) {
return ComparisonUtils.cmpOp(op, left, right);
public static IROpCode cmpOp(Map<String, String> variables, String op, ExpressionNode left, ExpressionNode right) {
return ComparisonUtils.cmpOp(variables, op, left, right);
}
/* ──────────────── 类型推断 & 算术操作码匹配 ──────────────── */
// 类型推断与算术操作码匹配
/** 递归推断单个表达式节点的类型后缀b/s/i/l/f/d。 */
/**
* 递归推断表达式节点的类型后缀b/s/i/l/f/d
* 优先从字面量和二元表达式合并类型变量节点暂不处理返回 '\0'
*
* @param node 表达式节点
* @return 推断的类型后缀小写不确定时返回 '\0'
*/
private static char typeChar(ExpressionNode node) {
if (node instanceof NumberLiteralNode(String value, int _, int _, String _)) {
if (node instanceof NumberLiteralNode(String value, NodeContext _)) {
char last = Character.toLowerCase(value.charAt(value.length() - 1));
return switch (last) {
case 'b','s','i','l','f','d' -> last;
@ -140,12 +183,25 @@ public final class ExpressionUtils {
return '\0'; // 变量等暂不处理
}
/** 合并两侧表达式的类型后缀。 */
/**
* 合并两个表达式节点的类型后缀按照 d > f > l > i > s > b > '\0' 优先级返回最宽类型
*
* @param left 左表达式
* @param right 右表达式
* @return 合并后的类型后缀
*/
public static char resolveSuffix(ExpressionNode left, ExpressionNode right) {
return maxTypeChar(typeChar(left), typeChar(right));
}
/** 类型优先级d > f > l > i > s > b > '\0' */
/**
* 返回两个类型后缀中的最大类型宽度优先
* 优先级d > f > l > i > s > b > '\0'
*
* @param l 类型后缀1
* @param r 类型后缀2
* @return 最宽类型后缀
*/
private static char maxTypeChar(char l, char r) {
if (l == 'd' || r == 'd') return 'd';
if (l == 'f' || r == 'f') return 'f';
@ -157,35 +213,42 @@ public final class ExpressionUtils {
}
/**
* 根据操作符和两侧表达式选择正确的算术 {@link IROpCode}
* 根据操作符和两侧表达式选择匹配的算术操作码IROpCode
* <p>
* 类型推断优先使用左右表达式的类型后缀推断失败时回退为线程级默认类型后缀再失败则默认为 int32
*
* @param op 算术操作符
* @param left 左表达式
* @param right 右表达式
* @return 匹配类型的 IROpCode
*/
public static IROpCode resolveOpCode(String op,
ExpressionNode left,
ExpressionNode right) {
/* 1. 尝试根据字面量推断 */
// 1. 优先根据表达式类型推断
char suffix = resolveSuffix(left, right);
/* 2. 若失败则使用函数级默认类型 */
// 2. 推断失败则使用线程默认类型
if (suffix == '\0') suffix = DEFAULT_SUFFIX.get();
/* 3. 仍失败则默认为 int32 */
// 3. 仍失败则默认为 int32
Map<String, IROpCode> table = switch (suffix) {
case 'b' -> IROpCodeMappings.OP_B8;
case 's' -> IROpCodeMappings.OP_S16;
case 'i' -> IROpCodeMappings.OP_I32;
case 'l' -> IROpCodeMappings.OP_L64;
case 'f' -> IROpCodeMappings.OP_F32;
case 'd' -> IROpCodeMappings.OP_D64;
default -> IROpCodeMappings.OP_I32;
};
return table.get(op);
}
/* ────────────────────────── 工具 ───────────────────────── */
// 字符串辅助工具
/** 是否像浮点字面量(包含 '.' 或 e/E。 */
/**
* 判断字面量字符串是否看起来像浮点数包含小数点或 e/E 科学计数法
*
* @param digits 字面量字符串
* @return 是浮点格式则返回 true
*/
private static boolean looksLikeFloat(String digits) {
return digits.indexOf('.') >= 0
|| digits.indexOf('e') >= 0

View File

@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code AssignmentNode} 表示抽象语法树AST中的赋值语句节点
@ -16,16 +17,12 @@ import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
*
* @param variable 左值变量名即赋值目标
* @param value 表达式右值即赋值来源
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点的上下文信息包含行号列号等
*/
public record AssignmentNode(
String variable,
ExpressionNode value,
int line,
int column,
String file
NodeContext context
) implements StatementNode {
/**

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code BinaryExpressionNode} 表示抽象语法树AST中的二元运算表达式节点
@ -12,17 +13,13 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* @param left 左操作数子表达式
* @param operator 运算符字符串 "+", "-", "*", "/"
* @param right 右操作数子表达式
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文包含行号列号等信息
*/
public record BinaryExpressionNode(
ExpressionNode left,
String operator,
ExpressionNode right,
int line,
int column,
String file
NodeContext context
) implements ExpressionNode {
/**

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* 表示布尔字面量boolean literal的抽象语法树AST节点
@ -10,15 +11,11 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* </p>
*
* @param value 字面量的布尔值
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息行号列号等
*/
public record BoolLiteralNode(
boolean value,
int line,
int column,
String file
NodeContext context
) implements ExpressionNode {
/**
@ -29,9 +26,10 @@ public record BoolLiteralNode(
* </p>
*
* @param lexeme 布尔字面量的字符串表示
* @param context 节点上下文信息行号列号等
*/
public BoolLiteralNode(String lexeme, int line, int column, String file) {
this(Boolean.parseBoolean(lexeme), line, column, file);
public BoolLiteralNode(String lexeme, NodeContext context) {
this(Boolean.parseBoolean(lexeme), context);
}
/**

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.List;
@ -12,16 +13,12 @@ import java.util.List;
*
* @param callee 被调用的表达式节点通常为函数标识符或成员访问表达式表示函数名或方法名等
* @param arguments 参数表达式列表表示函数调用中传递给函数的实际参数参数的顺序与调用顺序一致
* @param line 当前表达式所在的行号方便调试和错误定位
* @param column 当前表达式所在的列号用于精确定位错误位置
* @param file 当前表达式所在的文件用于错误定位
* @param context 节点上下文信息包含行号列号等
*/
public record CallExpressionNode(
ExpressionNode callee, // 被调用的表达式节点表示函数或方法名
List<ExpressionNode> arguments, // 函数调用的参数表达式列表
int line, // 当前节点所在的行号
int column, // 当前节点所在的列号
String file // 当前节点所在的文件
NodeContext context // 节点上下文信息包含行号列号等
) implements ExpressionNode {
/**
@ -43,31 +40,4 @@ public record CallExpressionNode(
sb.append(")"); // 拼接右括号
return sb.toString(); // 返回拼接好的字符串
}
/**
* 获取当前表达式所在的行号
*
* @return 当前表达式的行号
*/
public int line() {
return line;
}
/**
* 获取当前表达式所在的列号
*
* @return 当前表达式的列号
*/
public int column() {
return column;
}
/**
* 获取当前表达式所在的文件名
*
* @return 当前表达式所在的文件名
*/
public String file() {
return file;
}
}

View File

@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.Optional;
@ -23,14 +24,8 @@ public class DeclarationNode implements StatementNode {
/** 可选的初始化表达式 */
private final Optional<ExpressionNode> initializer;
/** 当前节点所在的行号 **/
private final int line;
/** 当前节点所在的列号 **/
private final int column;
/** 当前节点所在的文件 **/
private final String file;
/** 节点上下文信息(包含行号、列号等) */
private final NodeContext context;
/**
* 构造一个 {@code DeclarationNode} 实例
@ -38,14 +33,13 @@ public class DeclarationNode implements StatementNode {
* @param name 变量名称
* @param type 变量类型字符串 "int""string"
* @param initializer 可选初始化表达式若为 {@code null} 表示未初始化
* @param context 节点上下文信息包含行号列号等
*/
public DeclarationNode(String name, String type, ExpressionNode initializer, int line, int column, String file) {
public DeclarationNode(String name, String type, ExpressionNode initializer, NodeContext context) {
this.name = name;
this.type = type;
this.initializer = Optional.ofNullable(initializer);
this.line = line;
this.column = column;
this.file = file;
this.context = context;
}
/**
@ -76,27 +70,12 @@ public class DeclarationNode implements StatementNode {
}
/**
* 获取当前表达式所在的行号
* 获取节点上下文信息包含行号列号等
*
* @return 当前表达式的行号
* @return NodeContext 实例
*/
public int line() {
return line;
@Override
public NodeContext context() {
return context;
}
/**
* 获取当前表达式所在的列号
*
* @return 当前表达式的列号
*/
public int column() {
return column;
}
/**
* 获取当前表达式所在的文件名
*
* @return 当前表达式所在的文件名
*/
public String file() { return file; }
}

View File

@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code ExpressionStatementNode} 表示抽象语法树AST中的表达式语句节点
@ -11,14 +12,10 @@ import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
* </p>
*
* @param expression 表达式主体通常为函数调用赋值方法链式调用等可求值表达式
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record ExpressionStatementNode(
ExpressionNode expression,
int line,
int column,
String file
NodeContext context
) implements StatementNode {
}

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.List;
@ -17,17 +18,13 @@ import java.util.List;
* @param parameters 参数列表每项为 {@link ParameterNode} 表示一个形参定义
* @param returnType 函数的返回类型 "int""void"
* @param body 函数体语句块由一组 {@link StatementNode} 构成
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record FunctionNode(
String name,
List<ParameterNode> parameters,
String returnType,
List<StatementNode> body,
int line,
int column,
String file
NodeContext context
) implements Node {
}

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code IdentifierNode} 表示抽象语法树AST中的标识符表达式节点
@ -10,15 +11,11 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* </p>
*
* @param name 标识符的文本名称如变量名 "x"函数名 "foo"
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record IdentifierNode(
String name,
int line,
int column,
String file
NodeContext context
) implements ExpressionNode {
/**

View File

@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.List;
@ -16,29 +17,16 @@ import java.util.List;
* condition 为假则执行 else 分支如果提供
* </p>
* <p>
* 示例语法结构
* </p>
* <pre>{@code
* if (x > 0) {
* print("Positive");
* } else {
* print("Negative");
* }
* }</pre>
*
* @param condition 控制分支执行的条件表达式
* @param thenBranch 条件为 true 时执行的语句块
* @param elseBranch 条件为 false 时执行的语句块可为空
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record IfNode(
ExpressionNode condition,
List<StatementNode> thenBranch,
List<StatementNode> elseBranch,
int line,
int column,
String file
NodeContext context
) implements StatementNode {
}

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code ImportNode} 表示抽象语法树AST中的 import 语句节点
@ -14,14 +15,10 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
* </p>
*
* @param moduleName 被导入的模块名称通常为点分层次结构 "core.utils"
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record ImportNode(
String moduleName,
int line,
int column,
String file
NodeContext context
) implements Node {
}

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.List;
@ -17,17 +18,13 @@ import java.util.List;
* @param condition 每次迭代前评估的条件表达式控制循环是否继续
* @param update 每轮迭代完成后执行的更新语句
* @param body 循环体语句列表表示循环主体执行逻辑
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点的上下文信息包含行号列号文件名等
*/
public record LoopNode(
StatementNode initializer,
ExpressionNode condition,
StatementNode update,
List<StatementNode> body,
int line,
int column,
String file
NodeContext context
) implements StatementNode {
}

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code MemberExpressionNode} 表示抽象语法树AST中的成员访问表达式节点
@ -11,16 +12,12 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
*
* @param object 左侧对象表达式表示成员所属的作用域或容器
* @param member 要访问的成员名称字段名或方法名
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record MemberExpressionNode(
ExpressionNode object,
String member,
int line,
int column,
String file
NodeContext context
) implements ExpressionNode {
/**

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.List;
import java.util.StringJoiner;
@ -8,22 +9,17 @@ import java.util.StringJoiner;
/**
* 表示模块定义的 AST 节点
* 一个模块通常由模块名导入语句列表和函数定义列表组成
* }
*
* @param name 模块名称
* @param imports 模块导入列表每个导入是一个 {@link ImportNode}
* @param functions 模块中的函数列表每个函数是一个 {@link FunctionNode}
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record ModuleNode(
String name,
List<ImportNode> imports,
List<FunctionNode> functions,
int line,
int column,
String file
NodeContext context
) implements Node {
/**

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code NumberLiteralNode} 表示抽象语法树AST中的数字字面量表达式节点
@ -11,15 +12,11 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* </p>
*
* @param value 数字字面量的原始字符串表示
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record NumberLiteralNode(
String value,
int line,
int column,
String file
NodeContext context
) implements ExpressionNode {
/**

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code ParameterNode} 表示抽象语法树AST中的函数参数定义节点
@ -11,16 +12,12 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
*
* @param name 参数名称标识符
* @param type 参数类型字符串 "int""string"
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record ParameterNode(
String name,
String type,
int line,
int column,
String file
NodeContext context
) implements Node {
/**

View File

@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.Optional;
@ -23,25 +24,18 @@ public class ReturnNode implements StatementNode {
/** 可选的返回值表达式 */
private final Optional<ExpressionNode> expression;
/** 当前节点所在的行号 **/
private final int line;
/** 当前节点所在的列号 **/
private final int column;
/** 当前节点所在的文件 **/
private final String file;
/** 节点上下文信息(包含行号、列号等) */
private final NodeContext context;
/**
* 构造一个 {@code ReturnNode} 实例
*
* @param expression 返回值表达式如果无返回值则可为 {@code null}
* @param context 节点上下文信息包含行号列号等
*/
public ReturnNode(ExpressionNode expression, int line, int column, String file) {
public ReturnNode(ExpressionNode expression, NodeContext context) {
this.expression = Optional.ofNullable(expression);
this.line = line;
this.column = column;
this.file = file;
this.context = context;
}
/**
@ -54,27 +48,12 @@ public class ReturnNode implements StatementNode {
}
/**
* 获取当前表达式所在的行号
* 获取节点上下文信息包含行号列号等
*
* @return 当前表达式的行号
* @return NodeContext 实例
*/
public int line() {
return line;
@Override
public NodeContext context() {
return context;
}
/**
* 获取当前表达式所在的列号
*
* @return 当前表达式的列号
*/
public int column() {
return column;
}
/**
* 获取当前表达式所在的文件名
*
* @return 当前表达式所在的文件名
*/
public String file() { return file; }
}

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code StringLiteralNode} 表示抽象语法树AST中的字符串字面量表达式节点
@ -10,15 +11,11 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
* </p>
*
* @param value 字符串常量的内容原始值中不包含双引号
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record StringLiteralNode(
String value,
int line,
int column,
String file
NodeContext context
) implements ExpressionNode {
/**

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code UnaryExpressionNode} 前缀一元运算 AST 节点
@ -15,16 +16,12 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
*
* @param operator 一元运算符 "-" "!"
* @param operand 运算对象 / 右操作数
* @param line 当前节点所在的行号
* @param column 当前节点所在的列号
* @param file 当前节点所在的文件
* @param context 节点上下文信息包含行号列号等
*/
public record UnaryExpressionNode(
String operator,
ExpressionNode operand,
int line,
int column,
String file
NodeContext context
) implements ExpressionNode {
/**

View File

@ -17,23 +17,7 @@ package org.jcnc.snow.compiler.parser.ast.base;
*/
public interface Node {
/**
* 获取当前表达式所在的行号
*
* @return 当前表达式的行号
* 获取节点的上下文//文件等信息
*/
int line();
/**
* 获取当前表达式所在的列号
*
* @return 当前表达式的列号
*/
int column();
/**
* 获取当前表达式所在的文件名
*
* @return 当前表达式所在的文件名
*/
String file();
NodeContext context();
}

View File

@ -0,0 +1,11 @@
package org.jcnc.snow.compiler.parser.ast.base;
/**
* NodeContext 记录 AST 节点的位置信息文件
*/
public record NodeContext(int line, int column, String file) {
@Override
public String toString() {
return file + ":" + line + ":" + column;
}
}

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.base.InfixParselet;
@ -46,12 +47,9 @@ public record BinaryOperatorParselet(Precedence precedence, boolean leftAssoc) i
int prec = precedence.ordinal();
// 右侧表达式根据结合性确定优先级绑定
ExpressionNode right = new PrattExpressionParser().parseExpression(
ctx,
leftAssoc ? Precedence.values()[prec] : Precedence.values()[prec - 1]
);
ExpressionNode right = new PrattExpressionParser().parseExpression(ctx, leftAssoc ? Precedence.values()[prec] : Precedence.values()[prec - 1]);
return new BinaryExpressionNode(left, op.getLexeme(), right, line, column, file);
return new BinaryExpressionNode(left, op.getLexeme(), right, new NodeContext(line, column, file));
}
/**

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.parser.ast.BoolLiteralNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
@ -28,6 +29,6 @@ public class BoolLiteralParselet implements PrefixParselet {
*/
@Override
public ExpressionNode parse(ParserContext ctx, Token token) {
return new BoolLiteralNode(token.getLexeme(), token.getLine(), token.getCol(), ctx.getSourceName());
return new BoolLiteralNode(token.getLexeme(), new NodeContext(token.getLine(), token.getCol(), ctx.getSourceName()));
}
}

View File

@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.parser.ast.CallExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.base.InfixParselet;
@ -45,7 +46,8 @@ public class CallParselet implements InfixParselet {
ctx.getTokens().expect(")"); // 消费并验证 ")"
// 创建 CallExpressionNode 并传递位置信息
return new CallExpressionNode(left, args, line, column, file);
return new CallExpressionNode(left, args, new NodeContext(line, column, file));
}
/**

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
@ -29,6 +30,6 @@ public class IdentifierParselet implements PrefixParselet {
int column = ctx.getTokens().peek(-1).getCol();
String file = ctx.getSourceName();
return new IdentifierNode(token.getLexeme(), line, column, file);
return new IdentifierNode(token.getLexeme(), new NodeContext(line, column, file));
}
}

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.MemberExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
import org.jcnc.snow.compiler.parser.expression.base.InfixParselet;
@ -34,7 +35,7 @@ public class MemberParselet implements InfixParselet {
String file = ctx.getSourceName();
String member = ts.expectType(TokenType.IDENTIFIER).getLexeme();
return new MemberExpressionNode(left, member, line, column, file);
return new MemberExpressionNode(left, member, new NodeContext(line, column, file));
}
/**

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
@ -24,6 +25,6 @@ public class NumberLiteralParselet implements PrefixParselet {
*/
@Override
public ExpressionNode parse(ParserContext ctx, Token token) {
return new NumberLiteralNode(token.getLexeme(), token.getLine(), token.getCol(), ctx.getSourceName());
return new NumberLiteralNode(token.getLexeme(), new NodeContext(token.getLine(), token.getCol(), ctx.getSourceName()));
}
}

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.StringLiteralNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
@ -27,6 +28,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, token.getLine(), token.getCol(), ctx.getSourceName());
return new StringLiteralNode(content, new NodeContext(token.getLine(), token.getCol(), ctx.getSourceName()));
}
}

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.parser.ast.UnaryExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
@ -55,6 +56,6 @@ public class UnaryOperatorParselet implements PrefixParselet {
/* ------------------------------------------------------------
* 2. 封装成 AST 节点并返回
* ------------------------------------------------------------ */
return new UnaryExpressionNode(token.getLexeme(), operand, line, column, file);
return new UnaryExpressionNode(token.getLexeme(), operand, new NodeContext(line, column, file));
}
}

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.function;
import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.utils.ASTJsonSerializer;
import org.jcnc.snow.compiler.parser.utils.JsonFormatter;
@ -66,8 +67,8 @@ public class ASTPrinter {
}
}
case FunctionNode(
String name, List<ParameterNode> parameters, String returnType, List<StatementNode> body
, int _, int _, String _
String name, List<ParameterNode> parameters, String returnType, List<StatementNode> body,
NodeContext _
) -> {
System.out.println(pad + "function " + name
+ "(params=" + parameters + ", return=" + returnType + ")");
@ -82,11 +83,10 @@ public class ASTPrinter {
.orElse("");
System.out.println(pad + "declare " + d.getName() + ":" + d.getType() + init);
}
case AssignmentNode(String variable, ExpressionNode value, int _, int _, String _) ->
case AssignmentNode(String variable, ExpressionNode value, NodeContext _) ->
System.out.println(pad + variable + " = " + value);
case IfNode(
ExpressionNode condition, List<StatementNode> thenBranch, List<StatementNode> elseBranch, int _,
int _, String _
ExpressionNode condition, List<StatementNode> thenBranch, List<StatementNode> elseBranch, NodeContext _
) -> {
System.out.println(pad + "if " + condition);
for (StatementNode stmt : thenBranch) {
@ -100,8 +100,8 @@ public class ASTPrinter {
}
}
case LoopNode(
StatementNode initializer, ExpressionNode condition, StatementNode update, List<StatementNode> body
, int _, int _, String _
StatementNode initializer, ExpressionNode condition, StatementNode update, List<StatementNode> body,
NodeContext _
) -> {
System.out.println(pad + "loop {");
print(initializer, indent + 1);
@ -116,7 +116,7 @@ public class ASTPrinter {
}
case ReturnNode r -> System.out.println(pad + "return" +
r.getExpression().map(e -> " " + e).orElse(""));
case ExpressionStatementNode(ExpressionNode expression, int _, int _, String _) ->
case ExpressionStatementNode(ExpressionNode expression, NodeContext _) ->
System.out.println(pad + expression);
case null, default -> System.out.println(pad + n); // 回退处理
}

View File

@ -2,20 +2,20 @@ package org.jcnc.snow.compiler.parser.function;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ParameterNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.context.ParseException;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
import org.jcnc.snow.compiler.parser.factory.StatementParserFactory;
import org.jcnc.snow.compiler.parser.utils.FlexibleSectionParser;
import org.jcnc.snow.compiler.parser.utils.FlexibleSectionParser.SectionDefinition;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* {@code FunctionParser} 是顶层函数定义的语法解析器
@ -55,26 +55,55 @@ public class FunctionParser implements TopLevelParser {
*/
@Override
public FunctionNode parse(ParserContext ctx) {
// 获取当前解析上下文中的 token
TokenStream ts = ctx.getTokens();
// 获取当前 token 的行号列号和文件名
// 记录当前 token 的行号列号和文件名用于错误报告或生成节点上下文
int line = ctx.getTokens().peek().getLine();
int column = ctx.getTokens().peek().getCol();
String file = ctx.getSourceName();
// 解析函数头例如可能是 `function` 关键字等
parseFunctionHeader(ts);
// 解析函数名
String functionName = parseFunctionName(ts);
// 用于存放解析出来的参数列表
List<ParameterNode> parameters = new ArrayList<>();
// 用于存放返回类型这里用数组是为了在闭包中修改其值
String[] returnType = new String[1];
// 用于存放函数体语句列表
List<StatementNode> body = new ArrayList<>();
// 获取函数可以包含的可选节如参数返回类型主体等的定义映射
Map<String, SectionDefinition> sections = getSectionDefinitions(parameters, returnType, body);
// 调用通用的多节解析器实际根据 sections 中注册的规则解析各部分内容
FlexibleSectionParser.parse(ctx, ts, sections);
// 如果函数体为空且返回类型为 void自动补充一个空的 return 语句
if (body.isEmpty() && returnType[0].equals("void")) {
body.add(new ReturnNode(null, new NodeContext(line, column, file)));
}
// 检查参数名称是否重复
Set<String> set = new HashSet<>();
parameters.forEach((node) -> {
final String name = node.name();
if (set.contains(name)) {
// 如果参数名重复抛出带具体行列信息的解析异常
throw new ParseException(String.format("参数 `%s` 重定义", name),
node.context().line(), node.context().column());
}
set.add(name);
});
// 解析函数的尾部例如右大括号或者 end 标志
parseFunctionFooter(ts);
return new FunctionNode(functionName, parameters, returnType[0], body, line, column, file);
// 返回完整的函数节点包含函数名参数返回类型函数体以及源位置信息
return new FunctionNode(functionName, parameters, returnType[0], body, new NodeContext(line, column, file));
}
/**
@ -194,7 +223,7 @@ public class FunctionParser implements TopLevelParser {
String ptype = ts.expectType(TokenType.TYPE).getLexeme();
skipComments(ts);
ts.expectType(TokenType.NEWLINE);
list.add(new ParameterNode(pname, ptype, line, column, file));
list.add(new ParameterNode(pname, ptype, new NodeContext(line, column, file)));
}
return list;
}

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.module;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.ast.ImportNode;
@ -57,7 +58,7 @@ public class ImportParser {
.getLexeme();
// 创建 ImportNode 节点并加入列表
imports.add(new ImportNode(mod, line, column, file));
imports.add(new ImportNode(mod, new NodeContext(line, column, file)));
} while (ctx.getTokens().match(",")); // 如果匹配到逗号继续解析下一个模块名
// 最后必须匹配换行符标志 import 语句的结束

View File

@ -4,6 +4,7 @@ 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.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
@ -93,6 +94,6 @@ public class ModuleParser implements TopLevelParser {
ts.expect("end");
ts.expect("module");
return new ModuleNode(name, imports, functions, line, column, file);
return new ModuleNode(name, imports, functions, new NodeContext(line, column, file));
}
}

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.statement;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
@ -74,6 +75,6 @@ public class DeclarationStatementParser implements StatementParser {
ctx.getTokens().expectType(TokenType.NEWLINE);
// 返回构建好的声明语法树节点
return new DeclarationNode(name, type, init, line, column, file);
return new DeclarationNode(name, type, init, new NodeContext(line, column, file));
}
}

View File

@ -4,6 +4,7 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.AssignmentNode;
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.NodeContext;
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;
@ -56,12 +57,12 @@ public class ExpressionStatementParser implements StatementParser {
ts.expect("=");
ExpressionNode value = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
return new AssignmentNode(varName, value, line, column, file);
return new AssignmentNode(varName, value, new NodeContext(line, column, file));
}
// 普通表达式语句
ExpressionNode expr = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
return new ExpressionStatementNode(expr, line, column, file);
return new ExpressionStatementNode(expr, new NodeContext(line, column, file));
}
}

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.statement;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.IfNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
@ -125,6 +126,6 @@ public class IfStatementParser implements StatementParser {
ts.expectType(TokenType.NEWLINE);
// 构建并返回 IfNode包含条件then 分支和 else 分支
return new IfNode(condition, thenBranch, elseBranch, line, column, file);
return new IfNode(condition, thenBranch, elseBranch, new NodeContext(line, column, file));
}
}

View File

@ -4,6 +4,7 @@ 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.LoopNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
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;
@ -116,7 +117,7 @@ public class LoopStatementParser implements StatementParser {
ts1.expect("=");
ExpressionNode expr = new PrattExpressionParser().parse(ctx1);
ts1.expectType(TokenType.NEWLINE);
update[0] = new AssignmentNode(varName, expr, line, column, file);
update[0] = new AssignmentNode(varName, expr, new NodeContext(line, column, file));
ParserUtils.skipNewlines(ts1);
}
));
@ -150,6 +151,6 @@ public class LoopStatementParser implements StatementParser {
ParserUtils.matchFooter(ts, "loop");
// 返回构造完成的 LoopNode
return new LoopNode(initializer[0], condition[0], update[0], body, loop_line, loop_column, file);
return new LoopNode(initializer[0], condition[0], update[0], body, new NodeContext(loop_line, loop_column, file));
}
}

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.statement;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
@ -54,6 +55,6 @@ public class ReturnStatementParser implements StatementParser {
ctx.getTokens().expectType(TokenType.NEWLINE);
// 构建并返回 ReturnNode可能为空表达式
return new ReturnNode(expr, line, column, file);
return new ReturnNode(expr, new NodeContext(line, column, file));
}
}

View File

@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.utils;
import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.*;
@ -82,8 +83,7 @@ public class ASTJsonSerializer {
return switch (n) {
// 模块节点
case ModuleNode(
String name, List<ImportNode> imports, List<FunctionNode> functions, _, int _,
String _
String name, List<ImportNode> imports, List<FunctionNode> functions, NodeContext _
) -> {
Map<String, Object> map = newNodeMap("Module");
map.put("name", name);
@ -181,34 +181,34 @@ public class ASTJsonSerializer {
return switch (expr) {
// 二元表达式
case BinaryExpressionNode(
ExpressionNode left, String operator, ExpressionNode right, int _, int _, String _
ExpressionNode left, String operator, ExpressionNode right, NodeContext _
) -> exprMap("BinaryExpression",
"left", exprToMap(left),
"operator", operator,
"right", exprToMap(right)
);
// 一元表达式
case UnaryExpressionNode(String operator, ExpressionNode operand, int _, int _, String _) ->
case UnaryExpressionNode(String operator, ExpressionNode operand, NodeContext _) ->
exprMap("UnaryExpression",
"operator", operator,
"operand", exprToMap(operand)
);
// 布尔字面量
case BoolLiteralNode(boolean value, int _, int _, String _) -> exprMap("BoolLiteral", "value", value);
case BoolLiteralNode(boolean value, NodeContext _) -> exprMap("BoolLiteral", "value", value);
// 标识符
case IdentifierNode(String name, int _, int _, String _) -> exprMap("Identifier", "name", name);
case IdentifierNode(String name, NodeContext _) -> exprMap("Identifier", "name", name);
// 数字字面量
case NumberLiteralNode(String value, int _, int _, String _) -> exprMap("NumberLiteral", "value", value);
case NumberLiteralNode(String value, NodeContext _) -> exprMap("NumberLiteral", "value", value);
// 字符串字面量
case StringLiteralNode(String value, int _, int _, String _) -> exprMap("StringLiteral", "value", value);
case StringLiteralNode(String value, NodeContext _) -> exprMap("StringLiteral", "value", value);
// 调用表达式
case CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments, int _, int _, String _) -> {
case CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments, NodeContext _) -> {
List<Object> args = new ArrayList<>(arguments.size());
for (ExpressionNode arg : arguments) args.add(exprToMap(arg));
yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args);
}
// 成员访问表达式
case MemberExpressionNode(ExpressionNode object, String member, int _, int _, String _) ->
case MemberExpressionNode(ExpressionNode object, String member, NodeContext _) ->
exprMap("MemberExpression",
"object", exprToMap(object),
"member", member

View File

@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
@ -51,8 +52,8 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
ExpressionNode callee = call.callee();
// 支持模块调用形式ModuleName.FunctionName(...)
if (callee instanceof MemberExpressionNode(var obj, String member, int _, int _, String _)
&& obj instanceof IdentifierNode(String mod, int _, int _, String _)) {
if (callee instanceof MemberExpressionNode(var obj, String member, NodeContext _)
&& obj instanceof IdentifierNode(String mod, NodeContext _)) {
// 验证模块是否存在并已导入
if (!ctx.getModules().containsKey(mod)
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) {
@ -65,7 +66,7 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
functionName = member;
// 简单函数名形式func(...)
} else if (callee instanceof IdentifierNode(String name, int _, int _, String _)) {
} else if (callee instanceof IdentifierNode(String name, NodeContext _)) {
functionName = name;
// 不支持的 callee 形式

View File

@ -2,11 +2,13 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.symbol.Symbol;
import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
/**
* {@code FunctionChecker} 是语义分析阶段中用于检查函数体语句合法性的调度器
@ -79,6 +81,19 @@ public record FunctionChecker(Context ctx) {
));
}
}
// 检查非 void 函数是否至少包含一条 return 语句
var returnType = ctx.parseType(fn.returnType());
if (returnType != BuiltinType.VOID) {
boolean hasReturn = fn.body().stream()
.anyMatch(stmtNode -> stmtNode instanceof ReturnNode);
if (!hasReturn) {
ctx.errors().add(new SemanticError(
fn,
"非 void 函数必须包含至少一条 return 语句"
));
}
}
}
}
}

View File

@ -42,11 +42,12 @@ public record SemanticError(Node node, String message) {
String file = null;
if (node != null) {
line = node.line();
col = node.column();
file = node.file();
line = node.context().line();
col = node.context().column();
file = node.context().file();
}
StringBuilder sb = new StringBuilder();
if (file != null && !file.isBlank()) sb.append(file).append(": ");
sb.append((line >= 0 && col >= 0) ? "" + line + ", 列 " + col : "未知位置");

View File

@ -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.
* <p>This opcode is implemented by the {@link B2DCommand} class, which defines its specific execution logic.</p>
*
* <p>Execution Steps:</p>
* <ol>
* <li>Pop the top byte8 value from the operand stack.</li>
* <li>Convert the byte8 value to a double64 value.</li>
* <li>Push the converted double64 value back onto the operand stack for subsequent operations.</li>
* </ol>
*
* <p>This opcode is used to widen a byte8 value to a double64 type.</p>
*/
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;
}
}

View File

@ -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.
* <p>This opcode is implemented by the {@link B2FCommand} class, which defines its specific execution logic.</p>
*
* <p>Execution Steps:</p>
* <ol>
* <li>Pop the top byte8 value from the operand stack.</li>
* <li>Convert the byte8 value to a float32 value.</li>
* <li>Push the converted float32 value back onto the operand stack for subsequent operations.</li>
* </ol>
*
* <p>This opcode is used to widen a byte8 value to a float32 type.</p>
*/
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;
}
}

View File

@ -16,7 +16,7 @@ import org.jcnc.snow.vm.module.OperandStack;
* <li>Push the converted int32 value back onto the operand stack for subsequent operations.</li>
* </ol>
*
* <p>This opcode is used to widen a byte8 value to an int32 type to ensure compatibility with integer-based operations.</p>
* <p>This opcode is used to widen a byte8 value to an int32 type.</p>
*/
public class B2ICommand implements Command {

View File

@ -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.
* <p>This opcode is implemented by the {@link B2LCommand} class, which defines its specific execution logic.</p>
*
* <p>Execution Steps:</p>
* <ol>
* <li>Pop the top byte8 value from the operand stack.</li>
* <li>Convert the byte8 value to a long64 value.</li>
* <li>Push the converted long64 value back onto the operand stack for subsequent operations.</li>
* </ol>
*
* <p>This opcode is used to widen a byte8 value to a long64 type.</p>
*/
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;
}
}

View File

@ -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.
* <p>This opcode is implemented by the {@link B2SCommand} class, which defines its specific execution logic.</p>
*
* <p>Execution Steps:</p>
* <ol>
* <li>Pop the top byte8 value from the operand stack.</li>
* <li>Convert the byte8 value to a short16 value.</li>
* <li>Push the converted short16 value back onto the operand stack for subsequent operations.</li>
* </ol>
*
* <p>This opcode is used to widen a byte8 value to a short16 type.</p>
*/
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;
}
}

View File

@ -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.
* <p>This opcode is implemented by the {@link D2BCommand} class, which defines its specific execution logic.</p>
*
* <p>Execution Steps:</p>
* <ol>
* <li>Pop the top double64 value from the operand stack.</li>
* <li>Convert the double64 value to a byte8 value (this may involve truncation).</li>
* <li>Push the converted byte8 value back onto the operand stack for subsequent operations.</li>
* </ol>
*
* <p>This opcode is used to narrow a double64 value to a byte8 type.</p>
*/
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;
}
}

View File

@ -16,7 +16,7 @@ import org.jcnc.snow.vm.module.OperandStack;
* <li>Push the converted float32 value back onto the operand stack for subsequent operations.</li>
* </ol>
*
* <p>This opcode is used to narrow a double64 value to a float32 type when lower precision floating-point arithmetic is acceptable.</p>
* <p>This opcode is used to narrow a double64 value to a float32 type.</p>
*/
public class D2FCommand implements Command {

View File

@ -16,7 +16,7 @@ import org.jcnc.snow.vm.module.OperandStack;
* <li>Push the converted int32 value back onto the operand stack for subsequent operations.</li>
* </ol>
*
* <p>This opcode is used to narrow a double64 value to an int32 type for further integer-based operations.</p>
* <p>This opcode is used to narrow a double64 value to an int32 type.</p>
*/
public class D2ICommand implements Command {

Some files were not shown because too many files have changed in this diff Show More