!38 release: 合并 v0.4.2 版本至 main 分支
Merge pull request !38 from Luke/release/v0.4.2
This commit is contained in:
commit
3e59529666
@ -69,7 +69,7 @@ body:
|
||||
attributes:
|
||||
label: 软件版本/分支
|
||||
options:
|
||||
- v0.4.0
|
||||
- v0.4.2
|
||||
- main
|
||||
- dev
|
||||
- 其他
|
||||
|
||||
@ -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.*" />
|
||||
|
||||
@ -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.*" />
|
||||
|
||||
@ -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
17
.run/Demo12.run.xml
Normal 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
10
.run/Demo13.run.xml
Normal 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>
|
||||
@ -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.*" />
|
||||
|
||||
@ -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.*" />
|
||||
|
||||
@ -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.*" />
|
||||
|
||||
@ -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.*" />
|
||||
|
||||
@ -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.*" />
|
||||
|
||||
@ -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.*" />
|
||||
|
||||
@ -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.*" />
|
||||
|
||||
@ -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.*" />
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
260
docs/Snow-Lang-Syntax/Snow-Lang-Syntax.md
Normal file
260
docs/Snow-Lang-Syntax/Snow-Lang-Syntax.md
Normal 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
|
||||
```
|
||||
@ -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
|
||||
@ -1,12 +0,0 @@
|
||||
## 编译器输出
|
||||
### Snow 源代码
|
||||
#### Main.snow
|
||||
```snow
|
||||
function: main
|
||||
return_type: int
|
||||
body:
|
||||
3 L
|
||||
return 65537
|
||||
end body
|
||||
end function
|
||||
```
|
||||
21
playground/Demo/Demo12/Main.snow
Normal file
21
playground/Demo/Demo12/Main.snow
Normal 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
|
||||
69
playground/Demo/Demo13/Main.snow
Normal file
69
playground/Demo/Demo13/Main.snow
Normal 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
|
||||
2
pom.xml
2
pom.xml
@ -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>
|
||||
|
||||
@ -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 > F > L > I > S > 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");
|
||||
|
||||
|
||||
@ -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_LOAD、F_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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>D(double):6</li>
|
||||
* <li>F(float):5</li>
|
||||
* <li>L(long):4</li>
|
||||
* <li>I(int):3</li>
|
||||
* <li>S(short):2</li>
|
||||
* <li>B(byte):1</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. 发出比较与跳转指令
|
||||
|
||||
@ -82,34 +82,66 @@ 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"); // 加载
|
||||
opcodeMap.put(IROpCode.STORE, "I_STORE"); // 存储
|
||||
opcodeMap.put(IROpCode.CONST, "I_PUSH"); // 常量入栈
|
||||
opcodeMap.put(IROpCode.LOAD, "I_LOAD"); // 加载
|
||||
opcodeMap.put(IROpCode.STORE, "I_STORE"); // 存储
|
||||
opcodeMap.put(IROpCode.CONST, "I_PUSH"); // 常量入栈
|
||||
|
||||
// 跳转与标签
|
||||
opcodeMap.put(IROpCode.JUMP, "JMP"); // 无条件跳转
|
||||
opcodeMap.put(IROpCode.LABEL, "LABEL"); // 标签
|
||||
opcodeMap.put(IROpCode.JUMP, "JMP"); // 无条件跳转
|
||||
opcodeMap.put(IROpCode.LABEL, "LABEL"); // 标签
|
||||
|
||||
// 函数相关
|
||||
opcodeMap.put(IROpCode.CALL, "CALL"); // 调用
|
||||
opcodeMap.put(IROpCode.RET, "RET"); // 返回
|
||||
opcodeMap.put(IROpCode.CALL, "CALL"); // 调用
|
||||
opcodeMap.put(IROpCode.RET, "RET"); // 返回
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -0,0 +1,126 @@
|
||||
package org.jcnc.snow.compiler.backend.utils;
|
||||
|
||||
/**
|
||||
* 工具类:提供基本数值类型的提升与类型转换辅助功能。
|
||||
* <p>
|
||||
* 在进行数值类型运算、比较等操作时,低优先级的类型会被提升为高优先级类型参与运算。
|
||||
* 例如 int + long 运算,int 会被提升为 long,最终运算结果类型为 long。
|
||||
* <p>
|
||||
* 类型优先级从高到低依次为:
|
||||
* D(double):6
|
||||
* F(float) :5
|
||||
* L(long) :4
|
||||
* I(int) :3
|
||||
* S(short) :2
|
||||
* B(byte) :1
|
||||
* 未识别类型 :0
|
||||
*/
|
||||
public class TypePromoteUtils {
|
||||
|
||||
/**
|
||||
* 返回数值类型的宽度优先级,数值越大类型越宽。
|
||||
* 类型及优先级映射如下:
|
||||
* D(double): 6
|
||||
* F(float) : 5
|
||||
* L(long) : 4
|
||||
* I(int) : 3
|
||||
* S(short) : 2
|
||||
* B(byte) : 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>
|
||||
* 支持的类型标记字符包括:B(byte)、S(short)、I(int)、L(long)、F(float)、D(double)。
|
||||
* 所有可能的类型转换均已覆盖,如下所示:
|
||||
* 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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, "")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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, // 从内存加载数据至寄存器
|
||||
|
||||
@ -40,6 +40,26 @@ public final class IROpCodeMappings {
|
||||
);
|
||||
|
||||
/* ────── 比较运算符映射 ────── */
|
||||
/** 8-bit(byte)比较 */
|
||||
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-bit(short)比较 */
|
||||
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-bit(int)比较 */
|
||||
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-bit(float)比较 */
|
||||
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-bit(double)比较 */
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
@ -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<>();
|
||||
ops.add(dest);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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 层比较操作码。
|
||||
* 支持自动类型提升,保证 int、long、float、double 等类型的比较均能得到正确的 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;
|
||||
}
|
||||
if (value.indexOf('.') != -1) {
|
||||
return 'D';
|
||||
}
|
||||
return 'I'; // 默认 int
|
||||
}
|
||||
return false; // 变量暂不处理(后续可扩展符号表查询)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
@ -40,4 +37,4 @@ public record AssignmentNode(
|
||||
public String toString() {
|
||||
return variable + " = " + value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@ -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)节点。
|
||||
@ -9,16 +10,12 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
* 表达布尔类型的字面量常量(如 "true" 或 "false")。
|
||||
* </p>
|
||||
*
|
||||
* @param value 字面量的布尔值
|
||||
* @param line 当前节点所在的行号
|
||||
* @param column 当前节点所在的列号
|
||||
* @param file 当前节点所在的文件
|
||||
* @param value 字面量的布尔值
|
||||
* @param context 节点上下文信息(行号、列号等)
|
||||
*/
|
||||
public record BoolLiteralNode(
|
||||
boolean value,
|
||||
int line,
|
||||
int column,
|
||||
String file
|
||||
NodeContext context
|
||||
) implements ExpressionNode {
|
||||
|
||||
/**
|
||||
@ -28,10 +25,11 @@ public record BoolLiteralNode(
|
||||
* 如果传入的字符串为 "true"(忽略大小写),则解析结果为 {@code true};否则为 {@code false}。
|
||||
* </p>
|
||||
*
|
||||
* @param lexeme 布尔字面量的字符串表示
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)中的标识符表达式节点。
|
||||
@ -9,16 +10,12 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
* 在语义分析中,通常需要将此类节点绑定到其声明位置或符号表项。
|
||||
* </p>
|
||||
*
|
||||
* @param name 标识符的文本名称(如变量名 "x",函数名 "foo")
|
||||
* @param line 当前节点所在的行号
|
||||
* @param column 当前节点所在的列号
|
||||
* @param file 当前节点所在的文件
|
||||
* @param name 标识符的文本名称(如变量名 "x",函数名 "foo")
|
||||
* @param context 节点上下文信息(包含行号、列号等)
|
||||
*/
|
||||
public record IdentifierNode(
|
||||
String name,
|
||||
int line,
|
||||
int column,
|
||||
String file
|
||||
NodeContext context
|
||||
) implements ExpressionNode {
|
||||
|
||||
/**
|
||||
|
||||
@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)中的成员访问表达式节点。
|
||||
@ -9,18 +10,14 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
* 成员访问常见于结构体、模块、对象导入等上下文中,是表达式链中常见的构件之一。
|
||||
* </p>
|
||||
*
|
||||
* @param object 左侧对象表达式,表示成员所属的作用域或容器
|
||||
* @param member 要访问的成员名称(字段名或方法名)
|
||||
* @param line 当前节点所在的行号
|
||||
* @param column 当前节点所在的列号
|
||||
* @param file 当前节点所在的文件
|
||||
* @param object 左侧对象表达式,表示成员所属的作用域或容器
|
||||
* @param member 要访问的成员名称(字段名或方法名)
|
||||
* @param context 节点上下文信息(包含行号、列号等)
|
||||
*/
|
||||
public record MemberExpressionNode(
|
||||
ExpressionNode object,
|
||||
String member,
|
||||
int line,
|
||||
int column,
|
||||
String file
|
||||
NodeContext context
|
||||
) implements ExpressionNode {
|
||||
|
||||
/**
|
||||
@ -35,4 +32,4 @@ public record MemberExpressionNode(
|
||||
public String toString() {
|
||||
return object + "." + member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@ -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)中的函数参数定义节点。
|
||||
@ -9,18 +10,14 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
* 用于构成函数签名并参与类型检查与函数调用匹配。
|
||||
* </p>
|
||||
*
|
||||
* @param name 参数名称标识符
|
||||
* @param type 参数类型字符串(如 "int"、"string")
|
||||
* @param line 当前节点所在的行号
|
||||
* @param column 当前节点所在的列号
|
||||
* @param file 当前节点所在的文件
|
||||
* @param name 参数名称标识符
|
||||
* @param type 参数类型字符串(如 "int"、"string")
|
||||
* @param context 节点上下文信息(包含行号、列号等)
|
||||
*/
|
||||
public record ParameterNode(
|
||||
String name,
|
||||
String type,
|
||||
int line,
|
||||
int column,
|
||||
String file
|
||||
NodeContext context
|
||||
) implements Node {
|
||||
|
||||
/**
|
||||
@ -35,4 +32,4 @@ public record ParameterNode(
|
||||
public String toString() {
|
||||
return name + ":" + type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
@ -33,4 +30,4 @@ public record StringLiteralNode(
|
||||
public String toString() {
|
||||
return "\"" + value + "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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); // 回退处理
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 语句的结束
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 形式
|
||||
|
||||
@ -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 语句"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 : "未知位置");
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user