!61 release: 合并 v0.7.0 版本至 main 分支
Merge pull request !61 from Luke/release/v0.7.0
This commit is contained in:
commit
39dff9efc5
@ -69,7 +69,7 @@ body:
|
||||
attributes:
|
||||
label: 软件版本/分支
|
||||
options:
|
||||
- v0.6.0
|
||||
- v0.7.0
|
||||
- main
|
||||
- dev
|
||||
- 其他
|
||||
|
||||
10
.run/Bug6.run.xml
Normal file
10
.run/Bug6.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Bug6" type="Application" factoryName="Application" folderName="BugFarm">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/BugFarm/Bug6 -o target/Bug6 --debug" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
10
.run/Demo19.run.xml
Normal file
10
.run/Demo19.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo19" 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/Demo19 -o target/Demo19 --debug" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
10
.run/Demo20.run.xml
Normal file
10
.run/Demo20.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo20" 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/Demo20 -o target/Demo20 --debug" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
10
.run/Demo21.run.xml
Normal file
10
.run/Demo21.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo21" 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/Demo21 -o target/Demo21 --debug" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -9,7 +9,10 @@
|
||||
<toRun name="Demo15" type="Application" />
|
||||
<toRun name="Demo16" type="Application" />
|
||||
<toRun name="Demo17" type="Application" />
|
||||
<toRun name="Demo18" type="Application" />
|
||||
<toRun name="Demo19" type="Application" />
|
||||
<toRun name="Demo2" type="Application" />
|
||||
<toRun name="Demo20" type="Application" />
|
||||
<toRun name="Demo3" type="Application" />
|
||||
<toRun name="Demo4" type="Application" />
|
||||
<toRun name="Demo6" type="Application" />
|
||||
|
||||
24
README.md
24
README.md
@ -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.6.0/">
|
||||
<img src="https://img.shields.io/badge/version-v0.6.0-blue" alt="">
|
||||
<a href="https://gitee.com/jcnc-org/snow/tree/v0.7.0/">
|
||||
<img src="https://img.shields.io/badge/version-v0.7.0-blue" alt="">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@ -44,7 +44,7 @@ SnowVM) 的完整编译-执行链路。
|
||||
|
||||
Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的语法和严格的类型系统,以帮助 LLM 更好地理解程序。
|
||||
|
||||
语言使用显式的 `module` 声明来组织代码,用 `function`,`parameter`,`return_type`,`body` 等关键字分隔不同代码块,语法结构固定且易读。此外,Snow
|
||||
语言使用显式的 `module` 声明来组织代码,用 `function`,`params`,`returns`,`body` 等关键字分隔不同代码块,语法结构固定且易读。此外,Snow
|
||||
实现了语义分析来检查变量作用域和类型一致性,在编译阶段捕获错误并确保生成的中间代码正确无误。这种自上而下的编译流程,使得代码设计和生成更加模块化,可解释,也有利于调试和优化。
|
||||
|
||||
相关背景: [心路历程](docs/Snow-Lang-Journey/Snow-Lang-Journey.md)
|
||||
@ -111,7 +111,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
module: Main
|
||||
import:Math
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
Math.add(6,1)
|
||||
return 0
|
||||
@ -135,7 +135,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
3 15 IDENTIFIER main
|
||||
3 19 NEWLINE \n
|
||||
|
||||
4 9 KEYWORD return_type
|
||||
4 9 KEYWORD returns
|
||||
4 20 COLON :
|
||||
4 22 TYPE int
|
||||
4 25 NEWLINE \n
|
||||
@ -174,10 +174,10 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
#### Math.snow
|
||||
module: Math
|
||||
function: add
|
||||
parameter:
|
||||
params:
|
||||
declare n1: int
|
||||
declare n2: int
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
return n1 + n2
|
||||
end body
|
||||
@ -195,7 +195,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
2 15 IDENTIFIER add
|
||||
2 18 NEWLINE \n
|
||||
|
||||
3 9 KEYWORD parameter
|
||||
3 9 KEYWORD params
|
||||
3 18 COLON :
|
||||
3 19 NEWLINE \n
|
||||
|
||||
@ -211,7 +211,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
5 25 TYPE int
|
||||
5 28 NEWLINE \n
|
||||
|
||||
6 9 KEYWORD return_type
|
||||
6 9 KEYWORD returns
|
||||
6 20 COLON :
|
||||
6 22 TYPE int
|
||||
6 25 NEWLINE \n
|
||||
@ -494,7 +494,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
```snow
|
||||
module: Math
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
Math.factorial(6)
|
||||
return 0
|
||||
@ -502,9 +502,9 @@ module: Math
|
||||
end function
|
||||
|
||||
function: factorial
|
||||
parameter:
|
||||
params:
|
||||
declare n:int
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
declare num1:int = 1
|
||||
loop:
|
||||
|
||||
@ -76,8 +76,8 @@
|
||||
module: Main
|
||||
import:Math
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
Math.factorial(6L,1L)
|
||||
|
||||
@ -90,10 +90,10 @@ end module
|
||||
## 源代码 (test.snow)
|
||||
module: Math
|
||||
function: factorial
|
||||
parameter:
|
||||
params:
|
||||
declare n1: long
|
||||
declare n2: long
|
||||
return_type: long
|
||||
returns: long
|
||||
body:
|
||||
return n1+n2
|
||||
end body
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
```snow
|
||||
module: Main
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
|
||||
return 1 + 1
|
||||
@ -57,10 +57,8 @@ bool 类型:
|
||||
|---------|----|
|
||||
| b、B | 7b |
|
||||
| s、S | 7s |
|
||||
| i、I | 7i |
|
||||
| l、L | 7l |
|
||||
| f、F | 7f |
|
||||
| d、D | 7d |
|
||||
|
||||
|
||||
### 变量
|
||||
@ -116,7 +114,7 @@ cond 可以是表达式(结果为 bool 类型)或者 bool 字面量
|
||||
```snow
|
||||
module: Main
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
if 5 > 7 then
|
||||
return 5
|
||||
@ -154,7 +152,7 @@ end loop
|
||||
```snow
|
||||
module: Main
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
declare sum: int = 0
|
||||
loop:
|
||||
@ -179,17 +177,17 @@ end module
|
||||
函数的形式如下:
|
||||
```snow
|
||||
function: add
|
||||
parameter:
|
||||
params:
|
||||
declare a: int
|
||||
declare b: int
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
return a + b
|
||||
end body
|
||||
end function
|
||||
```
|
||||
其中 add 是函数名,parameter 下面是参数列表(可省略),与变量的定义类似,但是不允许赋初值,
|
||||
接着 return_type 设置返回值类型,最后的 body 为函数体。
|
||||
其中 add 是函数名,params 下面是参数列表(可省略),与变量的定义类似,但是不允许赋初值,
|
||||
接着 returns 设置返回值类型,最后的 body 为函数体。
|
||||
|
||||
## 模块
|
||||
|
||||
@ -202,7 +200,7 @@ snow 会自动将同名模块的函数合并。
|
||||
```snow
|
||||
module: Main
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
|
||||
return 1 + 1
|
||||
@ -218,10 +216,10 @@ end module
|
||||
// Math.snow
|
||||
module: Math
|
||||
function: add
|
||||
parameter:
|
||||
params:
|
||||
declare a: int
|
||||
declare b: int
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
return a + b
|
||||
end body
|
||||
@ -235,7 +233,7 @@ end module
|
||||
module: Main
|
||||
import: Math
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
|
||||
return Math.add(5, 7)
|
||||
@ -250,7 +248,7 @@ end module
|
||||
module: Main
|
||||
import: Math, Time
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
|
||||
return Math.add(5, 7)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Math
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
Math.factorial(6)
|
||||
return 0
|
||||
@ -9,9 +9,9 @@ module: Math
|
||||
end function
|
||||
|
||||
function: factorial
|
||||
parameter:
|
||||
params:
|
||||
declare n:int
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
declare num1:int = 1
|
||||
loop:
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
params:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
module: Main
|
||||
function: main
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
declare abc:int =1
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
params:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
// 合法
|
||||
declare b1: byte = 127b
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
params:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: ModuleB
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
return ModuleB.fun()
|
||||
end body
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
module: ModuleA
|
||||
function: fun
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
return 123
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: ModuleB
|
||||
import: ModuleA
|
||||
function: fun
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
return ModuleA.fun() + ModuleA.fun()
|
||||
end body
|
||||
|
||||
@ -3,8 +3,8 @@ module: Main
|
||||
globals:
|
||||
declare sum: int = 0
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
sum = 20
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
params:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
13
playground/BugFarm/Bug6/Main.snow
Normal file
13
playground/BugFarm/Bug6/Main.snow
Normal file
@ -0,0 +1,13 @@
|
||||
module: Main
|
||||
import: os
|
||||
globals:
|
||||
declare sum: int = 123
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
os.print(sum)
|
||||
return 0
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
11
playground/BugFarm/Bug6/OS.snow
Normal file
11
playground/BugFarm/Bug6/OS.snow
Normal file
@ -0,0 +1,11 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import:Math
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
Math.add(6,1)
|
||||
return 0
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: Math
|
||||
function: add
|
||||
parameter:
|
||||
params:
|
||||
declare n1: int
|
||||
declare n2: int
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
return n1 + n2
|
||||
end body
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
declare res: boolean = 8L > 7L
|
||||
if res then
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
return 65537
|
||||
end body
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
module: Main
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
foo()
|
||||
|
||||
@ -9,7 +9,7 @@ module: Main
|
||||
end function
|
||||
|
||||
function: foo
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
if false then
|
||||
return 1
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
module: Main
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
5 == 7
|
||||
5 == 7s
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
os.print(222)
|
||||
end body
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
params:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -3,7 +3,7 @@ module: Main
|
||||
globals:
|
||||
declare num2:int=10
|
||||
function: main
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
declare num1:int=11
|
||||
os.print(num1+num2+abc())
|
||||
@ -11,7 +11,7 @@ module: Main
|
||||
end function
|
||||
|
||||
function: abc
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
return 1
|
||||
end body
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
params:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
params:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
params:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
@ -31,8 +31,8 @@ module: Main
|
||||
if j % 2 == 0 then
|
||||
continue
|
||||
end if
|
||||
print(i)
|
||||
print(j)
|
||||
os.print(i)
|
||||
os.print(j)
|
||||
end body
|
||||
end loop
|
||||
end body
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
params:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
26
playground/Demo/Demo19/Main.snow
Normal file
26
playground/Demo/Demo19/Main.snow
Normal file
@ -0,0 +1,26 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare n: int[][][][] = [
|
||||
[
|
||||
[ [17, 18], [19, 20] ],
|
||||
[ [21, 22], [23, 24] ]
|
||||
],
|
||||
[
|
||||
[ [25, 26], [27, 28] ],
|
||||
[ [29, 30], [31, 32] ]
|
||||
]
|
||||
]
|
||||
|
||||
declare i: int = 0
|
||||
declare j: int = 1
|
||||
declare k: int = 0
|
||||
declare l: int = 0
|
||||
|
||||
declare x: int = n[i][j][k][l]
|
||||
os.print(x)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
11
playground/Demo/Demo19/OS.snow
Normal file
11
playground/Demo/Demo19/OS.snow
Normal file
@ -0,0 +1,11 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
return (1+2) / 3 * 4 + 2 *2
|
||||
end body
|
||||
|
||||
12
playground/Demo/Demo20/Main.snow
Normal file
12
playground/Demo/Demo20/Main.snow
Normal file
@ -0,0 +1,12 @@
|
||||
module: Main
|
||||
import:os
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare arr: int[] = [1, 2, 3]
|
||||
arr[0] = 5
|
||||
|
||||
os.print(arr[0])
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
11
playground/Demo/Demo20/OS.snow
Normal file
11
playground/Demo/Demo20/OS.snow
Normal file
@ -0,0 +1,11 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
35
playground/Demo/Demo21/Main.snow
Normal file
35
playground/Demo/Demo21/Main.snow
Normal file
@ -0,0 +1,35 @@
|
||||
module: Main
|
||||
import: os
|
||||
globals:
|
||||
declare sum: int = 123
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
declare arr: int[][][][][][][][][][][][][][][][][][][][][][] = [[[[[[[[[[[[[[[[[[[[[[1], [2], [3]]]]]]]]]]]]]]]]]]]]]]
|
||||
loop:
|
||||
init:
|
||||
declare i: int = 0
|
||||
cond:
|
||||
i < 3
|
||||
step:
|
||||
i = i + 1
|
||||
body:
|
||||
arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0] = arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0] + 1
|
||||
end body
|
||||
end loop
|
||||
loop:
|
||||
init:
|
||||
declare i: int = 0
|
||||
cond:
|
||||
i < 3
|
||||
step:
|
||||
i = i + 1
|
||||
body:
|
||||
os.print(arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0])
|
||||
end body
|
||||
end loop
|
||||
return 0
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
11
playground/Demo/Demo21/OS.snow
Normal file
11
playground/Demo/Demo21/OS.snow
Normal file
@ -0,0 +1,11 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
declare n1: int =1
|
||||
declare n2: int =2
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
parameter:
|
||||
return_type: boolean
|
||||
params:
|
||||
returns: boolean
|
||||
body:
|
||||
declare b1: boolean =true
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
declare b1: boolean = true
|
||||
loop:
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
declare b1 :int = -1
|
||||
return b1
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
parameter:
|
||||
return_type: boolean
|
||||
params:
|
||||
returns: boolean
|
||||
body:
|
||||
declare b1 :boolean = true
|
||||
return !b1
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
parameter:
|
||||
return_type: long
|
||||
params:
|
||||
returns: long
|
||||
body:
|
||||
declare n: long
|
||||
n = 2147483647
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Math
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
Math.factorial(6)
|
||||
return 0
|
||||
@ -9,9 +9,9 @@ module: Math
|
||||
end function
|
||||
|
||||
function: factorial
|
||||
parameter:
|
||||
params:
|
||||
declare n:int
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
declare num1:int = 1
|
||||
loop:
|
||||
|
||||
2
pom.xml
2
pom.xml
@ -7,7 +7,7 @@
|
||||
|
||||
<groupId>org.jcnc.snow</groupId>
|
||||
<artifactId>Snow</artifactId>
|
||||
<version>0.6.0</version>
|
||||
<version>0.7.0</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
@ -10,45 +10,46 @@ import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
import org.jcnc.snow.vm.engine.VMOpCode;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* <b>CallGenerator - 将 IR {@code CallInstruction} 生成 VM 指令</b>
|
||||
*
|
||||
* {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令。
|
||||
* <p>
|
||||
* 本类负责将中间表示(IR)中的函数调用指令 {@link CallInstruction} 转换为虚拟机(VM)指令。
|
||||
* 支持普通函数调用和特殊的 syscall 指令转换。
|
||||
* 支持 syscall、普通函数调用、一维/多维数组下标访问、以及字符串常量池绑定等功能。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>能力说明:</b>
|
||||
* <ul>
|
||||
* <li>支持识别 {@code IRConstant} 直接字面量或已绑定到虚拟寄存器的字符串常量,直接降级为 {@code SYSCALL <SUBCMD>} 指令。</li>
|
||||
* <li>对普通函数,完成参数加载、调用、返回值保存等指令生成。</li>
|
||||
* <li>syscall: 支持字符串常量与寄存器到子命令的绑定与解析</li>
|
||||
* <li>数组下标访问: 支持所有主流基础类型 “__index_b/s/i/l/f/d/r”</li>
|
||||
* <li>普通函数: 支持自动推断返回值类型与槽位类型</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
|
||||
/**
|
||||
* <虚拟寄存器 id, 对应的字符串常量>
|
||||
* <p>用于记录虚拟寄存器与其绑定字符串常量的映射。由 {@link LoadConstGenerator} 在编译期间填充。</p>
|
||||
* 字符串常量池:用于绑定虚拟寄存器 id 到字符串值(供 syscall 子命令使用)。
|
||||
* <br>
|
||||
* 使用 ConcurrentHashMap 保证并发安全,所有 registerStringConst 的注册和读取都是线程安全的。
|
||||
*/
|
||||
private static final Map<Integer, String> STRING_CONST_POOL = new HashMap<>();
|
||||
private static final Map<Integer, String> STRING_CONST_POOL = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 注册字符串常量到虚拟寄存器
|
||||
* <p>供 {@link LoadConstGenerator} 在加载字符串常量时调用。</p>
|
||||
* 注册一个字符串常量,绑定到虚拟寄存器 id。
|
||||
*
|
||||
* @param regId 虚拟寄存器 id
|
||||
* @param value 字符串常量值
|
||||
* @param value 字符串常量
|
||||
*/
|
||||
public static void registerStringConst(int regId, String value) {
|
||||
STRING_CONST_POOL.put(regId, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回本生成器支持的 IR 指令类型(CallInstruction)
|
||||
* 返回本生成器支持的 IR 指令类型。
|
||||
*
|
||||
* @return {@link CallInstruction} 类型的 Class 对象
|
||||
*/
|
||||
@Override
|
||||
public Class<CallInstruction> supportedClass() {
|
||||
@ -56,11 +57,11 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 VM 指令的主逻辑
|
||||
* 生成指定的调用指令,包括 syscall、数组下标、普通函数。
|
||||
*
|
||||
* @param ins 当前 IR 指令(函数调用)
|
||||
* @param out 指令输出构建器
|
||||
* @param slotMap IR 虚拟寄存器与物理槽位映射
|
||||
* @param ins IR 层的调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位的映射表
|
||||
* @param currentFn 当前函数名
|
||||
*/
|
||||
@Override
|
||||
@ -68,79 +69,357 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
String currentFn) {
|
||||
String fn = ins.getFunctionName();
|
||||
|
||||
/* ========== 特殊处理 syscall 调用 ========== */
|
||||
if ("syscall".equals(ins.getFunctionName()) ||
|
||||
ins.getFunctionName().endsWith(".syscall")) {
|
||||
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.isEmpty()) {
|
||||
throw new IllegalStateException("syscall 需要子命令参数");
|
||||
}
|
||||
|
||||
// ---------- 0. 解析 syscall 子命令 ----------
|
||||
// 子命令支持 IRConstant(直接字面量)或虚拟寄存器(需已绑定字符串)
|
||||
String subcmd;
|
||||
IRValue first = args.getFirst();
|
||||
|
||||
if (first instanceof IRConstant(Object value)) { // 直接字面量
|
||||
if (!(value instanceof String s))
|
||||
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
|
||||
subcmd = s.toUpperCase(Locale.ROOT);
|
||||
|
||||
} else if (first instanceof IRVirtualRegister vr) { // 虚拟寄存器
|
||||
// 从常量池中查找是否已绑定字符串
|
||||
subcmd = Optional.ofNullable(STRING_CONST_POOL.get(vr.id()))
|
||||
.orElseThrow(() ->
|
||||
new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr));
|
||||
subcmd = subcmd.toUpperCase(Locale.ROOT);
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
|
||||
}
|
||||
|
||||
// ---------- 1. 压栈其余 syscall 参数(index 1 开始) ----------
|
||||
for (int i = 1; i < args.size(); i++) {
|
||||
IRVirtualRegister vr = (IRVirtualRegister) args.get(i);
|
||||
int slotId = slotMap.get(vr);
|
||||
char t = out.getSlotType(slotId);
|
||||
if (t == '\0') t = 'I'; // 默认整型
|
||||
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
|
||||
}
|
||||
|
||||
// ---------- 2. 生成 SYSCALL 指令 ----------
|
||||
out.emit(VMOpCode.SYSCALL + " " + subcmd);
|
||||
return; // syscall 无返回值,直接返回
|
||||
}
|
||||
|
||||
/* ========== 普通函数调用 ========== */
|
||||
|
||||
// ---------- 1. 推断返回值类型(非 void 返回时用) ----------
|
||||
char retType = 'I'; // 默认为整型
|
||||
if (!ins.getArguments().isEmpty()) {
|
||||
int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst());
|
||||
retType = out.getSlotType(firstSlot);
|
||||
if (retType == '\0') retType = 'I';
|
||||
}
|
||||
|
||||
// ---------- 2. 加载全部实参 ----------
|
||||
for (var arg : ins.getArguments()) {
|
||||
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 指令 ----------
|
||||
out.emitCall(ins.getFunctionName(), ins.getArguments().size());
|
||||
|
||||
// ---------- 3.5 如果为 void 返回直接结束 ----------
|
||||
if ("void".equals(GlobalFunctionTable.getReturnType(ins.getFunctionName()))) {
|
||||
// 特殊处理 syscall 调用
|
||||
if ("syscall".equals(fn) || fn.endsWith(".syscall")) {
|
||||
generateSyscall(ins, out, slotMap, fn);
|
||||
return;
|
||||
}
|
||||
|
||||
// ---------- 4. 保存返回值 ----------
|
||||
int destSlot = slotMap.get(ins.getDest());
|
||||
// 各种一维数组类型(byte/short/int/long/float/double/boolean)读取
|
||||
switch (fn) {
|
||||
case "__index_b" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'B');
|
||||
return;
|
||||
}
|
||||
case "__index_s" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'S');
|
||||
return;
|
||||
}
|
||||
case "__index_i" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'I');
|
||||
return;
|
||||
}
|
||||
case "__index_l" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'L');
|
||||
return;
|
||||
}
|
||||
case "__index_f" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'F');
|
||||
return;
|
||||
}
|
||||
case "__index_d" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'D');
|
||||
return;
|
||||
}
|
||||
case "__index_r" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'R');
|
||||
return;
|
||||
}
|
||||
|
||||
case "__setindex_b" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'B');
|
||||
return;
|
||||
}
|
||||
case "__setindex_s" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'S');
|
||||
return;
|
||||
}
|
||||
case "__setindex_i" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'I');
|
||||
return;
|
||||
}
|
||||
case "__setindex_l" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'L');
|
||||
return;
|
||||
}
|
||||
case "__setindex_f" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'F');
|
||||
return;
|
||||
}
|
||||
case "__setindex_d" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'D');
|
||||
return;
|
||||
}
|
||||
case "__setindex_r" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'R');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 普通函数调用
|
||||
generateNormalCall(ins, out, slotMap, fn);
|
||||
}
|
||||
|
||||
// ========== 私有辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 生成数组元素赋值指令(arr[idx] = value),无返回值。
|
||||
* <p>
|
||||
* 调用栈压栈顺序为:arr (引用类型, R),idx (整型, I),value (元素类型, T)。
|
||||
* 执行 SYSCALL ARR_SET 指令以完成数组赋值操作。
|
||||
* </p>
|
||||
*
|
||||
* @param ins 当前的调用指令(包含参数信息)
|
||||
* @param out VM 指令生成器(用于输出 VM 指令)
|
||||
* @param slotMap 虚拟寄存器与槽位的映射表
|
||||
* @param valType 待写入的 value 的类型标识('B': byte, 'S': short, 'I': int, 'L': long, 'F': float, 'D': double, 其余为引用类型'R')
|
||||
* @throws IllegalStateException 如果参数数量不为3,则抛出异常
|
||||
*/
|
||||
private void generateSetIndexInstruction(CallInstruction ins,
|
||||
VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
char valType) {
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.size() != 3) {
|
||||
// 参数数量错误,抛出异常并输出实际参数列表
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] __setindex_* 需要三个参数(arr, idx, value),实际: " + args);
|
||||
}
|
||||
|
||||
// 第一个参数为数组对象,压入引用类型寄存器('R'),如 arr
|
||||
loadArgument(out, slotMap, args.get(0), 'R', ins.getFunctionName());
|
||||
|
||||
// 第二个参数为索引值,压入整型寄存器('I'),如 idx
|
||||
loadArgument(out, slotMap, args.get(1), 'I', ins.getFunctionName());
|
||||
|
||||
// 第三个参数为待赋值元素,根据元素类型压入相应类型寄存器
|
||||
// 支持类型:'B'(byte), 'S'(short), 'I'(int), 'L'(long), 'F'(float), 'D'(double)
|
||||
// 其他情况(如引用类型),按'R'处理
|
||||
switch (valType) {
|
||||
case 'B' -> loadArgument(out, slotMap, args.get(2), 'B', ins.getFunctionName());
|
||||
case 'S' -> loadArgument(out, slotMap, args.get(2), 'S', ins.getFunctionName());
|
||||
case 'I' -> loadArgument(out, slotMap, args.get(2), 'I', ins.getFunctionName());
|
||||
case 'L' -> loadArgument(out, slotMap, args.get(2), 'L', ins.getFunctionName());
|
||||
case 'F' -> loadArgument(out, slotMap, args.get(2), 'F', ins.getFunctionName());
|
||||
case 'D' -> loadArgument(out, slotMap, args.get(2), 'D', ins.getFunctionName());
|
||||
default -> loadArgument(out, slotMap, args.get(2), 'R', ins.getFunctionName());
|
||||
}
|
||||
|
||||
// 输出 VM 指令:SYSCALL ARR_SET,完成数组元素写入操作
|
||||
out.emit(VMOpCode.SYSCALL + " " + "ARR_SET");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析 syscall 子命令(第一个参数),支持字符串常量与已绑定字符串的虚拟寄存器。
|
||||
*
|
||||
* @param arg 子命令参数(应为字符串常量或寄存器)
|
||||
* @param fn 当前函数名(仅用于报错信息)
|
||||
* @return 子命令(大写字符串)
|
||||
* @throws IllegalStateException 如果参数不是字符串常量或已绑定寄存器
|
||||
*/
|
||||
private String resolveSyscallSubcmd(IRValue arg, String fn) {
|
||||
switch (arg) {
|
||||
case IRConstant(String s) -> {
|
||||
return s.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
case IRConstant(Object value) -> throw new IllegalStateException(
|
||||
"[CallGenerator] syscall 第一个参数必须是字符串常量 (function: %s, value: %s)"
|
||||
.formatted(fn, value)
|
||||
);
|
||||
case IRVirtualRegister(int id) -> {
|
||||
String s = STRING_CONST_POOL.get(id);
|
||||
if (s == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 未找到 syscall 字符串常量绑定 (function: %s, regId: %d)"
|
||||
.formatted(fn, id)
|
||||
);
|
||||
}
|
||||
return s.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
case null, default -> throw new IllegalStateException(
|
||||
"[CallGenerator] syscall 第一个参数必须是字符串常量或已绑定字符串的寄存器 (function: %s, arg: %s)"
|
||||
.formatted(fn, arg)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载一个参数到栈,支持指定默认类型。如果未设置类型则采用默认类型。
|
||||
*
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位的映射
|
||||
* @param arg 参数值(应为虚拟寄存器)
|
||||
* @param defaultType 默认类型
|
||||
* @param fn 当前函数名(用于错误提示)
|
||||
* @throws IllegalStateException 如果参数不是虚拟寄存器,或槽位未找到
|
||||
*/
|
||||
private void loadArgument(VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, IRValue arg, char defaultType, String fn) {
|
||||
if (!(arg instanceof IRVirtualRegister vr)) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 参数必须为虚拟寄存器 (function: %s, arg: %s)".formatted(fn, arg));
|
||||
}
|
||||
Integer slot = slotMap.get(vr);
|
||||
if (slot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 未找到虚拟寄存器的槽位映射 (function: %s, reg: %s)".formatted(fn, vr));
|
||||
}
|
||||
char t = out.getSlotType(slot);
|
||||
if (t == '\0') t = defaultType;
|
||||
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一维/多维数组的下标访问指令(支持类型分派)。
|
||||
* <p>
|
||||
* 本方法用于将 IR 层的数组下标访问表达式(如 arr[idx]),生成对应的 VM 指令序列。
|
||||
* 支持 byte/short/int/long/float/double/reference 等所有基础类型的数组元素访问。
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>1. 依次加载数组参数(arr)和下标参数(idx)到操作数栈</li>
|
||||
* <li>2. 发出 ARR_GET 系统调用指令(SYSCALL ARR_GET),通过 VM 访问对应元素</li>
|
||||
* <li>3. 根据元素声明类型,将结果写入目标槽位,支持类型分派(B/S/I/L/F/D/R)</li>
|
||||
* <li>4. 若参数或返回寄存器缺失,则抛出异常提示</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ins 下标访问对应的 IR 调用指令,函数名通常为 __index_x
|
||||
* @param out VM 程序构建器,用于发出 VM 指令
|
||||
* @param slotMap IR 虚拟寄存器到 VM 槽位的映射表
|
||||
* @param retType 元素类型标识('B' byte, 'S' short, 'I' int, 'L' long, 'F' float, 'D' double, 'R' ref/obj)
|
||||
* @throws IllegalStateException 参数个数不符、缺少目标寄存器、未找到槽位等情况
|
||||
*/
|
||||
private void generateIndexInstruction(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, char retType) {
|
||||
String fn = ins.getFunctionName();
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.size() != 2) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] %s 需要两个参数(arr, idx),实际: %s".formatted(fn, args));
|
||||
}
|
||||
// 加载数组参数(寄存器类型按 R/ref 处理,默认对象槽位)
|
||||
loadArgument(out, slotMap, args.get(0), 'R', fn);
|
||||
// 加载下标参数(寄存器类型按 I/int 处理)
|
||||
loadArgument(out, slotMap, args.get(1), 'I', fn);
|
||||
|
||||
// 发出 ARR_GET 系统调用(元素访问由 VM 完成类型分派)
|
||||
out.emit(VMOpCode.SYSCALL + " " + "ARR_GET");
|
||||
|
||||
// 保存返回值到目标寄存器
|
||||
IRVirtualRegister dest = ins.getDest();
|
||||
if (dest == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] %s 需要有目标寄存器用于保存返回值".formatted(fn));
|
||||
}
|
||||
Integer destSlot = slotMap.get(dest);
|
||||
if (destSlot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] %s 未找到目标寄存器的槽位映射 (dest: %s)".formatted(fn, dest));
|
||||
}
|
||||
|
||||
// 按元素类型分派写入 VM 槽位
|
||||
switch (retType) {
|
||||
case 'B' -> {
|
||||
out.emit(OpHelper.opcode("B_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'B');
|
||||
}
|
||||
case 'S' -> {
|
||||
out.emit(OpHelper.opcode("S_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'S');
|
||||
}
|
||||
case 'I' -> {
|
||||
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'I');
|
||||
}
|
||||
case 'L' -> {
|
||||
out.emit(OpHelper.opcode("L_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'L');
|
||||
}
|
||||
case 'F' -> {
|
||||
out.emit(OpHelper.opcode("F_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'F');
|
||||
}
|
||||
case 'D' -> {
|
||||
out.emit(OpHelper.opcode("D_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'D');
|
||||
}
|
||||
default -> {
|
||||
out.emit(OpHelper.opcode("R_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'R');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 syscall 指令分支逻辑。
|
||||
* <ol>
|
||||
* <li>解析 syscall 子命令</li>
|
||||
* <li>压栈剩余参数</li>
|
||||
* <li>发出 SYSCALL 指令</li>
|
||||
* <li>若有返回值则保存至目标槽位</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param ins 调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param fn 当前函数名
|
||||
*/
|
||||
private void generateSyscall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] syscall 需要子命令参数 (function: %s)".formatted(fn));
|
||||
}
|
||||
|
||||
// 0. 解析 syscall 子命令(第一个参数)
|
||||
String subcmd = resolveSyscallSubcmd(args.getFirst(), fn);
|
||||
|
||||
// 1. 压栈其余 syscall 参数(从 index 1 开始)
|
||||
for (int i = 1; i < args.size(); i++) {
|
||||
loadArgument(out, slotMap, args.get(i), 'R', fn);
|
||||
}
|
||||
|
||||
// 2. 生成 SYSCALL 指令
|
||||
out.emit(VMOpCode.SYSCALL + " " + subcmd);
|
||||
|
||||
// 3. 有返回值则保存到目标槽位
|
||||
IRVirtualRegister dest = ins.getDest();
|
||||
if (dest != null) {
|
||||
Integer destSlot = slotMap.get(dest);
|
||||
if (destSlot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] syscall 未找到目标寄存器的槽位映射 (function: %s, dest: %s)"
|
||||
.formatted(fn, dest));
|
||||
}
|
||||
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'I');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成普通函数调用指令:
|
||||
* <ol>
|
||||
* <li>推断返回值类型</li>
|
||||
* <li>压栈所有参数</li>
|
||||
* <li>生成 CALL 指令</li>
|
||||
* <li>保存返回值(若非 void)</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param ins 调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param fn 当前函数名
|
||||
*/
|
||||
private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
|
||||
// 1. 推断返回值类型(首字母大写,缺省为 'I')
|
||||
String retTypeName = GlobalFunctionTable.getReturnType(fn);
|
||||
char retType = (retTypeName != null && !retTypeName.isEmpty()) ? Character.toUpperCase(retTypeName.charAt(0)) : 'I';
|
||||
|
||||
// 2. 压栈所有参数
|
||||
for (IRValue arg : ins.getArguments()) {
|
||||
loadArgument(out, slotMap, arg, 'R', fn);
|
||||
}
|
||||
|
||||
// 3. 发出 CALL 指令
|
||||
out.emitCall(fn, ins.getArguments().size());
|
||||
|
||||
// 3.5 void 返回直接结束
|
||||
if ("void".equals(retTypeName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 保存返回值到目标寄存器
|
||||
IRVirtualRegister dest = ins.getDest();
|
||||
if (dest == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 普通函数调用未找到目标寄存器 (function: %s)".formatted(fn));
|
||||
}
|
||||
Integer destSlot = slotMap.get(dest);
|
||||
if (destSlot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 普通函数调用未找到目标寄存器的槽位映射 (function: %s, dest: %s)".formatted(fn, dest));
|
||||
}
|
||||
out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, retType);
|
||||
}
|
||||
|
||||
@ -7,21 +7,93 @@ import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
|
||||
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <b>LoadConstGenerator - 将 IR {@code LoadConstInstruction} 生成 VM 指令</b>
|
||||
* <b>LoadConstGenerator - Generates VM instructions from IR {@code LoadConstInstruction}</b>
|
||||
*
|
||||
* <p>
|
||||
* 本类负责将 IR 层的常量加载指令 {@link LoadConstInstruction} 转换为对应的虚拟机指令。
|
||||
* 额外支持:如果常量类型为 {@code String},会同步登记到
|
||||
* {@link CallGenerator} 的字符串常量池,方便 syscall 降级场景使用。
|
||||
* This class is responsible for converting IR-level {@link LoadConstInstruction} into corresponding VM instructions.
|
||||
* If the constant is a {@code String}, it will also be registered in the
|
||||
* {@link CallGenerator} string constant pool to support syscall downgrade scenarios.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Fix: When the constant is an array (List), type information is preserved in R_PUSH payload:
|
||||
* <ul>
|
||||
* <li>Float is output with <code>f</code> suffix (e.g., 0.1f);</li>
|
||||
* <li>Long is output with <code>L</code> suffix (e.g., 123L);</li>
|
||||
* <li>Double/Integer are output in their default format (e.g., 1.0, 42);</li>
|
||||
* <li>Supports recursive serialization of nested arrays.</li>
|
||||
* </ul>
|
||||
* This prevents float values from being misinterpreted as double on the VM side,
|
||||
* and avoids Double→Float cast exceptions in later F_STORE operations.
|
||||
* </p>
|
||||
*/
|
||||
public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruction> {
|
||||
|
||||
/**
|
||||
* 指定本生成器支持的 IR 指令类型(LoadConstInstruction)
|
||||
* Formats a constant value as a string for use as a VM payload.
|
||||
* Lists are recursively serialized, and Float/Long types include suffixes to preserve type information.
|
||||
*
|
||||
* @param v The constant value to format.
|
||||
* @return The formatted string for use in VM code.
|
||||
*/
|
||||
private static String formatConst(Object v) {
|
||||
return formatConst(v, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper for recursively formatting constant values (including nested arrays)
|
||||
* with appropriate type suffixes for array payloads.
|
||||
*
|
||||
* @param v The constant value to format.
|
||||
* @param insideArray True if currently formatting inside an array context; affects whether type suffixes are applied.
|
||||
* @return The formatted string for use in VM code.
|
||||
*/
|
||||
private static String formatConst(Object v, boolean insideArray) {
|
||||
if (v instanceof List<?> list) {
|
||||
// Recursively process each element in the list
|
||||
return "[" + list.stream()
|
||||
.map(x -> formatConst(x, true))
|
||||
.collect(Collectors.joining(", ")) + "]";
|
||||
}
|
||||
if (v instanceof String s) {
|
||||
return s;
|
||||
}
|
||||
if (v instanceof Float f) {
|
||||
// Always keep .0 for integer values
|
||||
float fv = f;
|
||||
String s = (fv == (long) fv) ? String.format("%.1f", fv) : f.toString();
|
||||
return insideArray ? (s + "f") : s;
|
||||
}
|
||||
if (v instanceof Long l) {
|
||||
return insideArray ? (l + "L") : l.toString();
|
||||
}
|
||||
if (v instanceof Double d) {
|
||||
double dv = d;
|
||||
// Always keep .0 for integer values
|
||||
return (dv == (long) dv) ? String.format("%.1f", dv) : Double.toString(dv);
|
||||
}
|
||||
if (v instanceof Short s) {
|
||||
return insideArray ? (s + "s") : Short.toString(s);
|
||||
}
|
||||
if (v instanceof Byte b) {
|
||||
return insideArray ? (b + "b") : Byte.toString(b);
|
||||
}
|
||||
if (v instanceof Boolean b) {
|
||||
return b ? "1" : "0";
|
||||
}
|
||||
return String.valueOf(v);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies the type of IR instruction supported by this generator.
|
||||
*
|
||||
* @return The class object representing {@link LoadConstInstruction}.
|
||||
*/
|
||||
@Override
|
||||
public Class<LoadConstInstruction> supportedClass() {
|
||||
@ -29,12 +101,16 @@ public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruc
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 VM 指令主流程
|
||||
* Generates the VM instructions for a given {@link LoadConstInstruction}.
|
||||
* <p>
|
||||
* This includes formatting the constant value, emitting the corresponding PUSH and STORE instructions,
|
||||
* marking the local slot type for later operations, and registering string constants if necessary.
|
||||
* </p>
|
||||
*
|
||||
* @param ins 当前常量加载指令
|
||||
* @param out 指令输出构建器
|
||||
* @param slotMap 虚拟寄存器与物理槽位映射
|
||||
* @param currentFn 当前函数名
|
||||
* @param ins The {@link LoadConstInstruction} to generate code for.
|
||||
* @param out The {@link VMProgramBuilder} used to collect the generated instructions.
|
||||
* @param slotMap A mapping from {@link IRVirtualRegister} to physical slot indices.
|
||||
* @param currentFn The name of the current function.
|
||||
*/
|
||||
@Override
|
||||
public void generate(LoadConstInstruction ins,
|
||||
@ -42,34 +118,35 @@ public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruc
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
String currentFn) {
|
||||
|
||||
/* 1. 获取常量值 */
|
||||
// 1. Get the constant value
|
||||
IRConstant constant = (IRConstant) ins.operands().getFirst();
|
||||
Object value = constant.value();
|
||||
|
||||
/* 2. 生成 PUSH 指令,将常量值入栈 */
|
||||
out.emit(OpHelper.pushOpcodeFor(value) + " " + value);
|
||||
// 2. Generate PUSH instruction (array constants use type-aware formatting)
|
||||
String payload = formatConst(value);
|
||||
out.emit(OpHelper.pushOpcodeFor(value) + " " + payload);
|
||||
|
||||
/* 3. STORE 到目标槽位 */
|
||||
// 3. STORE the result to the destination slot
|
||||
int slot = slotMap.get(ins.dest());
|
||||
out.emit(OpHelper.storeOpcodeFor(value) + " " + slot);
|
||||
|
||||
/* 4. 标记槽位数据类型(用于后续类型推断和 LOAD/STORE 指令选择) */
|
||||
// 4. Mark the slot's data type for later inference and instruction selection
|
||||
char prefix = switch (value) {
|
||||
case Integer _ -> 'I'; // 整型
|
||||
case Long _ -> 'L'; // 长整型
|
||||
case Short _ -> 'S'; // 短整型
|
||||
case Byte _ -> 'B'; // 字节型
|
||||
case Double _ -> 'D'; // 双精度
|
||||
case Float _ -> 'F'; // 单精度
|
||||
case Boolean _ -> 'I'; // 布尔类型用 I 处理
|
||||
case String _ -> 'R'; // 字符串常量
|
||||
case null, default -> // 其它类型异常
|
||||
throw new IllegalStateException("未知的常量类型: "
|
||||
case Integer _ -> 'I'; // Integer
|
||||
case Long _ -> 'L'; // Long
|
||||
case Short _ -> 'S'; // Short
|
||||
case Byte _ -> 'B'; // Byte
|
||||
case Double _ -> 'D'; // Double
|
||||
case Float _ -> 'F'; // Float
|
||||
case Boolean _ -> 'I'; // Boolean handled as Integer (typically lowered to 1/0)
|
||||
case String _ -> 'R'; // String constant
|
||||
case java.util.List<?> _ -> 'R'; // Reference type (arrays, etc.)
|
||||
case null, default -> throw new IllegalStateException("Unknown constant type: "
|
||||
+ (value != null ? value.getClass() : null));
|
||||
};
|
||||
out.setSlotType(slot, prefix);
|
||||
|
||||
/* 5. 如果是字符串常量,则登记到 CallGenerator 的常量池,便于 syscall 字符串降级使用 */
|
||||
// 5. If the constant is a string, register it for the CallGenerator string pool
|
||||
if (value instanceof String s) {
|
||||
CallGenerator.registerStringConst(ins.dest().id(), s);
|
||||
}
|
||||
|
||||
@ -234,6 +234,7 @@ public final class OpHelper {
|
||||
if (v instanceof Double) return "D";
|
||||
if (v instanceof Float) return "F";
|
||||
if (v instanceof String) return "R"; //引用类型
|
||||
if (v instanceof java.util.List) return "R"; // 引用类型(数组等)
|
||||
throw new IllegalStateException("Unknown const type: " + v.getClass());
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.jcnc.snow.compiler.ir.builder;
|
||||
|
||||
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
|
||||
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
|
||||
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
|
||||
@ -11,40 +12,26 @@ 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 java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <b>ExpressionBuilder - 表达式 → IR 构建器</b>
|
||||
*
|
||||
* {@code ExpressionBuilder} 表达式 → IR 构建器。
|
||||
* <p>
|
||||
* 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作,并生成对应的 IR 指令序列。
|
||||
* 支持字面量、标识符、二元表达式、一元表达式、函数调用等多种类型表达式。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 主要功能:
|
||||
* 支持字面量、标识符、二元表达式、一元表达式、函数调用、数组下标等多种类型表达式。
|
||||
* <ul>
|
||||
* <li>将表达式节点映射为虚拟寄存器</li>
|
||||
* <li>为每种表达式类型生成对应 IR 指令</li>
|
||||
* <li>支持表达式嵌套的递归构建</li>
|
||||
* <li>支持写入指定目标寄存器,避免冗余的 move 指令</li>
|
||||
* <li>支持 IndexExpressionNode 的编译期折叠(arr[2]),并在运行时降级为 __index_i/__index_r</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public record ExpressionBuilder(IRContext ctx) {
|
||||
|
||||
/* ───────────────── 顶层入口 ───────────────── */
|
||||
|
||||
/**
|
||||
* 构建任意 AST 表达式节点,自动为其分配一个新的虚拟寄存器,并返回该寄存器。
|
||||
*
|
||||
* <p>
|
||||
* 这是表达式 IR 生成的核心入口。它会根据不同的表达式类型进行分派,递归构建 IR 指令。
|
||||
* </p>
|
||||
*
|
||||
* @param expr 任意 AST 表达式节点
|
||||
* @return 存储该表达式结果的虚拟寄存器
|
||||
* @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符
|
||||
* 构建表达式,返回结果寄存器。
|
||||
*/
|
||||
public IRVirtualRegister build(ExpressionNode expr) {
|
||||
return switch (expr) {
|
||||
@ -58,8 +45,7 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
case IdentifierNode id -> {
|
||||
// 查找当前作用域中的变量寄存器
|
||||
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
|
||||
if (reg == null)
|
||||
throw new IllegalStateException("未定义标识符: " + id.name());
|
||||
if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
|
||||
yield reg;
|
||||
}
|
||||
// 二元表达式(如 a+b, x==y)
|
||||
@ -68,7 +54,8 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
case CallExpressionNode call -> buildCall(call);
|
||||
// 一元表达式(如 -a, !a)
|
||||
case UnaryExpressionNode un -> buildUnary(un);
|
||||
|
||||
case IndexExpressionNode idx -> buildIndex(idx);
|
||||
case ArrayLiteralNode arr -> buildArrayLiteral(arr);
|
||||
// 默认分支:遇到未知表达式类型则直接抛异常
|
||||
default -> throw new IllegalStateException(
|
||||
"不支持的表达式类型: " + expr.getClass().getSimpleName());
|
||||
@ -78,33 +65,40 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
/* ───────────────── 写入指定寄存器 ───────────────── */
|
||||
|
||||
/**
|
||||
* 生成表达式,并将其结果直接写入目标寄存器,避免冗余的 move 操作。
|
||||
*
|
||||
* 将表达式节点 {@link ExpressionNode} 的结果写入指定的虚拟寄存器 {@code dest}。
|
||||
* <p>
|
||||
* 某些简单表达式(如字面量、变量名)可以直接写入目标寄存器;复杂表达式则会先 build 到新寄存器,再 move 到目标寄存器。
|
||||
* 按表达式类型分派处理,包括:
|
||||
* <ul>
|
||||
* <li>字面量(数字、字符串、布尔、数组):生成 loadConst 指令直接写入目标寄存器</li>
|
||||
* <li>变量标识符:查表获取源寄存器,并 move 到目标寄存器</li>
|
||||
* <li>二元表达式、下标、调用表达式:递归生成子表达式结果,并写入目标寄存器</li>
|
||||
* <li>其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param node 要生成的表达式节点
|
||||
* @param dest 目标虚拟寄存器(用于存储结果)
|
||||
* @param node 要求值的表达式节点
|
||||
* @param dest 结果目标虚拟寄存器
|
||||
* @throws IllegalStateException 若标识符未定义(如变量未声明时引用)
|
||||
*/
|
||||
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
|
||||
switch (node) {
|
||||
// 数字字面量:生成 loadConst 指令写入目标寄存器
|
||||
case NumberLiteralNode n ->
|
||||
InstructionFactory.loadConstInto(
|
||||
// 数字字面量:生成 loadConst 指令,将数值常量写入目标寄存器
|
||||
case NumberLiteralNode n -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
|
||||
|
||||
// 字符串字面量:生成 loadConst 指令写入目标寄存器
|
||||
case StringLiteralNode s ->
|
||||
InstructionFactory.loadConstInto(
|
||||
// 字符串字面量:生成 loadConst 指令,将字符串常量写入目标寄存器
|
||||
case StringLiteralNode s -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(s.value()));
|
||||
|
||||
// 布尔字面量:转换为 int 1/0,生成 loadConst
|
||||
case BoolLiteralNode b ->
|
||||
InstructionFactory.loadConstInto(
|
||||
// 布尔字面量:转换为 int 1/0,生成 loadConst 指令写入目标寄存器
|
||||
case BoolLiteralNode b -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
|
||||
|
||||
// 标识符:查表获取原寄存器,然后 move 到目标寄存器
|
||||
// 数组字面量:生成数组常量并写入目标寄存器
|
||||
case ArrayLiteralNode arr -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, buildArrayConstant(arr));
|
||||
|
||||
// 变量标识符:查表获得源寄存器,move 到目标寄存器
|
||||
case IdentifierNode id -> {
|
||||
IRVirtualRegister src = ctx.getScope().lookup(id.name());
|
||||
if (src == null)
|
||||
@ -112,10 +106,22 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
InstructionFactory.move(ctx, src, dest);
|
||||
}
|
||||
|
||||
// 二元表达式:递归生成并写入目标寄存器
|
||||
// 二元表达式:递归生成左右子表达式,并将结果写入目标寄存器
|
||||
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
|
||||
|
||||
// 其它复杂情况:先 build 到新寄存器,再 move 到目标寄存器
|
||||
// 下标表达式:递归生成索引结果,move 到目标寄存器
|
||||
case IndexExpressionNode idx -> {
|
||||
IRVirtualRegister tmp = buildIndex(idx);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
}
|
||||
|
||||
// 调用表达式:递归生成调用结果,move 到目标寄存器
|
||||
case CallExpressionNode call -> {
|
||||
IRVirtualRegister tmp = buildCall(call);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
}
|
||||
|
||||
// 其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器
|
||||
default -> {
|
||||
IRVirtualRegister tmp = build(node);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
@ -123,7 +129,201 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ───────────────── 具体表达式类型 ───────────────── */
|
||||
|
||||
/**
|
||||
* 构建下标访问表达式(IndexExpressionNode)。
|
||||
* <ul>
|
||||
* <li>若数组和下标均为编译期常量,则直接进行常量折叠(直接返回目标常量寄存器);</li>
|
||||
* <li>否则:
|
||||
* <ul>
|
||||
* <li>若数组表达式本身是下一个下标的中间值(即多维数组链式下标),则先用 __index_r 获取“引用”;</li>
|
||||
* <li>最后一级用 __index_b/s/i/l/f/d/r,按声明类型智能分派。</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param node 下标访问表达式节点
|
||||
* @return 存放结果的虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildIndex(IndexExpressionNode node) {
|
||||
// 1. 常量折叠:如果 array 和 index 都是编译期常量,直接取值
|
||||
Object arrConst = tryFoldConst(node.array());
|
||||
Object idxConst = tryFoldConst(node.index());
|
||||
if (arrConst instanceof java.util.List<?> list && idxConst instanceof Number num) {
|
||||
int i = num.intValue();
|
||||
// 越界检查
|
||||
if (i < 0 || i >= list.size())
|
||||
throw new IllegalStateException("数组下标越界: " + i + " (长度 " + list.size() + ")");
|
||||
Object elem = list.get(i);
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 加载常量元素到新寄存器
|
||||
InstructionFactory.loadConstInto(ctx, r, new IRConstant(elem));
|
||||
return r;
|
||||
}
|
||||
|
||||
// 2. 处理多级下标(如 arr[1][2]):中间层用 __index_r 返回“引用”
|
||||
IRVirtualRegister arrReg = (node.array() instanceof IndexExpressionNode inner)
|
||||
? buildIndexRef(inner) // 递归获取引用
|
||||
: build(node.array()); // 否则直接生成 array 的值
|
||||
|
||||
// 3. 生成下标值
|
||||
IRVirtualRegister idxReg = build(node.index());
|
||||
|
||||
// 4. 创建目标寄存器
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
|
||||
// 5. 准备参数
|
||||
List<IRValue> argv = new ArrayList<>();
|
||||
argv.add(arrReg);
|
||||
argv.add(idxReg);
|
||||
|
||||
// 6. 选择调用指令
|
||||
if (node.array() instanceof IndexExpressionNode) {
|
||||
// 非最末层,下标取“引用”
|
||||
ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
|
||||
} else {
|
||||
// 最末层,下标取实际元素值,按声明类型分派
|
||||
String func = "__index_i"; // 默认整型
|
||||
if (node.array() instanceof IdentifierNode id) {
|
||||
String declType = ctx.getScope().lookupType(id.name()); // 如 "double[]"、"int[]"
|
||||
if (declType != null) {
|
||||
String base = declType.toLowerCase();
|
||||
int p = base.indexOf('[');
|
||||
if (p > 0) base = base.substring(0, p); // 基本类型
|
||||
switch (base) {
|
||||
case "byte" -> func = "__index_b";
|
||||
case "short" -> func = "__index_s";
|
||||
case "int" -> func = "__index_i";
|
||||
case "long" -> func = "__index_l";
|
||||
case "float" -> func = "__index_f";
|
||||
case "double" -> func = "__index_d";
|
||||
case "boolean" -> func = "__index_i"; // 布尔型用 int 通道返回 1/0
|
||||
case "string" -> func = "__index_r"; // 字符串/其它未识别类型均走引用
|
||||
default -> func = "__index_r";
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.addInstruction(new CallInstruction(dest, func, argv));
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建中间层下标访问表达式(返回引用)。
|
||||
* <p>
|
||||
* 用于多维数组的链式下标访问(如 arr[1][2]),保证中间结果是“可被再次下标”的引用。
|
||||
* <ul>
|
||||
* <li>若数组和下标均为编译期常量,则直接常量折叠,返回目标常量寄存器;</li>
|
||||
* <li>否则,递归处理 array,生成“引用”指令(__index_r)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param node 下标访问表达式节点
|
||||
* @return 存放引用结果的虚拟寄存器
|
||||
*/
|
||||
public IRVirtualRegister buildIndexRef(IndexExpressionNode node) {
|
||||
// 1. 常量折叠:如果 array 和 index 都是编译期常量,直接取值
|
||||
Object arrConst = tryFoldConst(node.array());
|
||||
Object idxConst = tryFoldConst(node.index());
|
||||
if (arrConst instanceof java.util.List<?> list && idxConst instanceof Number num) {
|
||||
int i = num.intValue();
|
||||
// 越界检查
|
||||
if (i < 0 || i >= list.size())
|
||||
throw new IllegalStateException("数组下标越界: " + i + " (长度 " + list.size() + ")");
|
||||
Object elem = list.get(i);
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 加载常量元素到新寄存器
|
||||
InstructionFactory.loadConstInto(ctx, r, new IRConstant(elem));
|
||||
return r;
|
||||
}
|
||||
|
||||
// 2. 递归生成 array 的“引用”,用于支持链式多级下标
|
||||
IRVirtualRegister arrReg = (node.array() instanceof IndexExpressionNode inner)
|
||||
? buildIndexRef(inner) // 递归向下返回引用
|
||||
: build(node.array()); // 基础数组直接 build
|
||||
|
||||
// 3. 生成下标值
|
||||
IRVirtualRegister idxReg = build(node.index());
|
||||
|
||||
// 4. 创建目标寄存器
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
|
||||
// 5. 组织参数列表
|
||||
List<IRValue> argv = new ArrayList<>();
|
||||
argv.add(arrReg);
|
||||
argv.add(idxReg);
|
||||
|
||||
// 6. 生成 __index_r 调用指令(总是返回引用)
|
||||
ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 常量折叠工具(支持嵌套数组)。
|
||||
* <p>
|
||||
* 尝试将表达式节点 {@code expr} 折叠为常量值,用于编译期计算/优化。
|
||||
* <ul>
|
||||
* <li>数字字面量:返回 int 或 double。</li>
|
||||
* <li>字符串字面量:返回字符串。</li>
|
||||
* <li>布尔字面量:返回 1(true)或 0(false)。</li>
|
||||
* <li>数组字面量:递归折叠所有元素为 List,如果有一项不能折叠则返回 null。</li>
|
||||
* <li>标识符:尝试从作用域查找编译期常量值。</li>
|
||||
* <li>其它类型:无法折叠,返回 null。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param expr 需要折叠的表达式节点
|
||||
* @return 编译期常量值(支持 int、double、String、List),否则返回 null
|
||||
*/
|
||||
private Object tryFoldConst(ExpressionNode expr) {
|
||||
if (expr == null) return null;
|
||||
|
||||
// 数字字面量:尝试解析为 int 或 double
|
||||
if (expr instanceof NumberLiteralNode n) {
|
||||
String s = n.value();
|
||||
try {
|
||||
if (s.contains(".") || s.contains("e") || s.contains("E"))
|
||||
return Double.parseDouble(s); // 带小数或科学计数法为 double
|
||||
return Integer.parseInt(s); // 否则为 int
|
||||
} catch (NumberFormatException e) {
|
||||
return null; // 无法解析为数字
|
||||
}
|
||||
}
|
||||
|
||||
// 字符串字面量:直接返回字符串
|
||||
if (expr instanceof StringLiteralNode s) return s.value();
|
||||
|
||||
// 布尔字面量:true 返回 1,false 返回 0
|
||||
if (expr instanceof BoolLiteralNode b) return b.getValue() ? 1 : 0;
|
||||
|
||||
// 数组字面量:递归折叠所有元素
|
||||
if (expr instanceof ArrayLiteralNode arr) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (ExpressionNode e : arr.elements()) {
|
||||
Object v = tryFoldConst(e);
|
||||
if (v == null) return null; // 有一项无法折叠则整体失败
|
||||
list.add(v);
|
||||
}
|
||||
return List.copyOf(list);
|
||||
}
|
||||
|
||||
// 标识符:尝试查找作用域中的常量值
|
||||
if (expr instanceof IdentifierNode id) {
|
||||
Object v = null;
|
||||
try {
|
||||
v = ctx.getScope().getConstValue(id.name());
|
||||
} catch (Throwable ignored) {
|
||||
// 查不到常量或异常都视为无法折叠
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// 其它类型:不支持折叠,返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 一元表达式构建
|
||||
@ -140,59 +340,67 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
IRVirtualRegister src = build(un.operand());
|
||||
// 分配目标寄存器
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
|
||||
switch (un.operator()) {
|
||||
// 算术负号:生成取负指令
|
||||
// 算术负号:生成取负指令(例如:-a)
|
||||
case "-" -> ctx.addInstruction(
|
||||
new UnaryOperationInstruction(ExpressionUtils.negOp(un.operand()), dest, src));
|
||||
// 逻辑非:等价于 a == 0,生成比较指令
|
||||
|
||||
// 逻辑非:等价于 a == 0,生成整数等于比较指令(!a)
|
||||
case "!" -> {
|
||||
// 生成常量0的寄存器
|
||||
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
|
||||
// 比较 src 是否等于0,等价于逻辑非
|
||||
return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, src, zero);
|
||||
}
|
||||
// 其它一元运算符不支持,抛异常
|
||||
// 其它一元运算符不支持,抛出异常
|
||||
default -> throw new IllegalStateException("未知一元运算符: " + un.operator());
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建函数或方法调用表达式。模块内未限定调用会自动补全当前模块名。
|
||||
* 构建函数或方法调用表达式。
|
||||
* <p>
|
||||
* 支持普通函数调用(foo(a, b))与成员方法调用(obj.method(a, b))。
|
||||
* <ul>
|
||||
* <li>首先递归生成所有参数的虚拟寄存器列表。</li>
|
||||
* <li>根据 callee 类型区分成员访问或直接标识符调用,并规范化方法名(如加前缀)。</li>
|
||||
* <li>为返回值分配新寄存器,生成 Call 指令。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param call AST 调用表达式节点
|
||||
* @return 存储调用结果的虚拟寄存器
|
||||
* @param call 函数/方法调用表达式节点
|
||||
* @return 存放调用结果的虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildCall(CallExpressionNode call) {
|
||||
// 递归生成所有参数(实参)对应的寄存器
|
||||
// 1. 递归生成所有参数的寄存器
|
||||
List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList();
|
||||
|
||||
// 解析被调用目标名,支持普通函数/成员方法
|
||||
// 2. 规范化被调用方法名(区分成员方法与普通函数)
|
||||
String callee = switch (call.callee()) {
|
||||
// 成员方法调用,例如 obj.foo()
|
||||
case MemberExpressionNode m when m.object() instanceof IdentifierNode id
|
||||
-> id.name() + "." + m.member();
|
||||
// 普通函数调用,如果未指定模块,自动补全当前模块名
|
||||
// 成员方法调用,如 obj.method()
|
||||
case MemberExpressionNode m when m.object() instanceof IdentifierNode id -> id.name() + "." + m.member();
|
||||
// 普通函数调用,或处理命名空间前缀(如当前方法名为 namespace.func)
|
||||
case IdentifierNode id -> {
|
||||
String current = ctx.getFunction().name();
|
||||
int dot = current.lastIndexOf('.');
|
||||
if (dot > 0) {
|
||||
// 当前处于模块内函数(Module.func),补全为同模块下的全限定名
|
||||
yield current.substring(0, dot) + "." + id.name();
|
||||
} else {
|
||||
// 顶层/脚本函数等不含模块前缀,保持原样
|
||||
yield id.name();
|
||||
if (dot > 0)
|
||||
yield current.substring(0, dot) + "." + id.name(); // 同命名空间内调用
|
||||
yield id.name(); // 全局函数调用
|
||||
}
|
||||
}
|
||||
// 其它情况暂不支持
|
||||
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
// 其它类型不支持
|
||||
default -> throw new IllegalStateException(
|
||||
"不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
};
|
||||
|
||||
// 为返回值分配新寄存器,生成 Call 指令
|
||||
// 3. 分配用于存放返回值的新寄存器,并生成 Call 指令
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 二元表达式构建,结果存储到新寄存器。
|
||||
* <br>
|
||||
@ -207,18 +415,21 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
IRVirtualRegister b = build(bin.right());
|
||||
String op = bin.operator();
|
||||
|
||||
// 比较运算符(==、!=、>、< 等),需要生成条件跳转或布尔值寄存器
|
||||
// 判断是否为比较运算符(==、!=、>、<等)
|
||||
if (ComparisonUtils.isComparisonOperator(op)) {
|
||||
// 生成比较操作,返回布尔值寄存器
|
||||
return InstructionFactory.binOp(
|
||||
ctx,
|
||||
// 通过比较工具获得合适的 IR 操作码
|
||||
// 根据运算符和操作数类型获得合适的 IR 操作码
|
||||
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
|
||||
a, b);
|
||||
}
|
||||
|
||||
// 其它算术/位运算
|
||||
// 其它二元运算(算术、位运算等)
|
||||
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||
if (code == null)
|
||||
throw new IllegalStateException("不支持的运算符: " + op);
|
||||
// 生成二元操作指令
|
||||
return InstructionFactory.binOp(ctx, code, a, b);
|
||||
}
|
||||
|
||||
@ -229,22 +440,29 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @param dest 目标虚拟寄存器
|
||||
*/
|
||||
private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) {
|
||||
// 递归生成左、右操作数的寄存器
|
||||
IRVirtualRegister a = build(bin.left());
|
||||
IRVirtualRegister b = build(bin.right());
|
||||
String op = bin.operator();
|
||||
|
||||
// 处理比较运算符(==、!=、>、< 等)
|
||||
if (ComparisonUtils.isComparisonOperator(op)) {
|
||||
InstructionFactory.binOpInto(
|
||||
ctx,
|
||||
// 选择对应类型和符号的比较操作码
|
||||
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
|
||||
a, b, dest);
|
||||
} else {
|
||||
// 算术或位运算符
|
||||
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||
if (code == null)
|
||||
throw new IllegalStateException("不支持的运算符: " + op);
|
||||
// 生成二元操作指令,写入目标寄存器
|
||||
InstructionFactory.binOpInto(ctx, code, a, b, dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ───────────────── 字面量辅助方法 ───────────────── */
|
||||
|
||||
/**
|
||||
@ -254,8 +472,11 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储该字面量的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildNumberLiteral(String value) {
|
||||
// 解析数字常量
|
||||
IRConstant c = ExpressionUtils.buildNumberConstant(ctx, value);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
@ -267,8 +488,11 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储该字符串的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildStringLiteral(String value) {
|
||||
// 构建字符串常量
|
||||
IRConstant c = new IRConstant(value);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
@ -280,9 +504,74 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储 1/0 的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildBoolLiteral(boolean v) {
|
||||
// 转换为 1 或 0 的常量
|
||||
IRConstant c = new IRConstant(v ? 1 : 0);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建数组字面量表达式(元素均为常量时)。
|
||||
*
|
||||
* @param arr 数组字面量节点
|
||||
* @return 存储该数组的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildArrayLiteral(ArrayLiteralNode arr) {
|
||||
// 递归生成支持嵌套的数组常量
|
||||
IRConstant c = buildArrayConstant(arr);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建支持嵌套的数组常量表达式。
|
||||
* <p>
|
||||
* 遍历并递归处理数组字面量的所有元素:
|
||||
* <ul>
|
||||
* <li>数字字面量:根据内容生成 int 或 double 常量</li>
|
||||
* <li>字符串字面量:直接存储字符串内容</li>
|
||||
* <li>布尔字面量:转换为 1(true)或 0(false)存储</li>
|
||||
* <li>数组字面量:递归构建,允许多层嵌套,最终生成嵌套的 List</li>
|
||||
* </ul>
|
||||
* 若包含非常量元素,则抛出异常。
|
||||
* </p>
|
||||
*
|
||||
* @param arr 数组字面量节点
|
||||
* @return 封装所有常量元素(支持嵌套 List)的 {@link IRConstant}
|
||||
* @throws IllegalStateException 如果数组中存在非常量元素
|
||||
*/
|
||||
|
||||
private IRConstant buildArrayConstant(ArrayLiteralNode arr) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (ExpressionNode e : arr.elements()) {
|
||||
switch (e) {
|
||||
// 数字字面量,解析并加入
|
||||
case NumberLiteralNode n -> {
|
||||
IRConstant num = ExpressionUtils.buildNumberConstant(ctx, n.value());
|
||||
list.add(num.value());
|
||||
}
|
||||
// 字符串字面量,直接加入
|
||||
case StringLiteralNode s -> list.add(s.value());
|
||||
// 布尔字面量,转成 1/0
|
||||
case BoolLiteralNode b -> list.add(b.getValue() ? 1 : 0);
|
||||
// 嵌套数组,递归生成并加入
|
||||
case ArrayLiteralNode inner -> {
|
||||
IRConstant innerConst = buildArrayConstant(inner);
|
||||
list.add(innerConst.value());
|
||||
}
|
||||
// 其它类型暂不支持
|
||||
default -> throw new IllegalStateException(
|
||||
"暂不支持含非常量元素的数组字面量: " + e.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
// 返回不可变的 List 封装为 IRConstant
|
||||
return new IRConstant(List.copyOf(list));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7,109 +7,143 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系。
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* {@code IRBuilderScope} 用于管理单个函数作用域内的变量名与虚拟寄存器的映射关系。
|
||||
* <p>
|
||||
* 主要职责:
|
||||
* <ul>
|
||||
* <li>维护在当前作用域中已声明变量的寄存器分配信息;</li>
|
||||
* <li>支持将已有虚拟寄存器与变量名重新绑定;</li>
|
||||
* <li>根据变量名查找对应的虚拟寄存器实例或类型。</li>
|
||||
* <li>维护当前作用域内所有已声明变量的寄存器和类型信息</li>
|
||||
* <li>支持变量与虚拟寄存器的重新绑定与查找</li>
|
||||
* <li>支持变量的类型信息记录与查询</li>
|
||||
* <li>支持变量的编译期常量值记录与查询(便于常量折叠等优化)</li>
|
||||
* </ul>
|
||||
*/
|
||||
final class IRBuilderScope {
|
||||
|
||||
/**
|
||||
* 存储变量名到对应 IRVirtualRegister 的映射。
|
||||
* 变量名为键,虚拟寄存器对象为值,用于查找和更新。
|
||||
* 变量名到虚拟寄存器的映射表。
|
||||
* 用于跟踪所有声明和分配的变量。
|
||||
*/
|
||||
private final Map<String, IRVirtualRegister> vars = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 存储变量名到对应类型的映射。
|
||||
* <br>
|
||||
* 变量名为键,变量类型为值,用于变量类型提升。
|
||||
* 变量名到类型字符串的映射表。
|
||||
* 用于类型分析与推断。
|
||||
*/
|
||||
private final Map<String, String> varTypes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 当前作用域所绑定的 IRFunction 对象,用于申请新的虚拟寄存器。
|
||||
* 变量名到编译期常量值的映射表。
|
||||
* 用于常量折叠优化(如 int、string、数组等常量)。
|
||||
*/
|
||||
private final Map<String, Object> varConstValues = new HashMap<>();
|
||||
/**
|
||||
* 当前作用域所绑定的 IRFunction 实例。
|
||||
* 用于申请新的虚拟寄存器。
|
||||
*/
|
||||
private IRFunction fn;
|
||||
|
||||
/**
|
||||
* 将指定的 IRFunction 关联到当前作用域,以便后续声明变量时能够
|
||||
* 调用该函数的 newRegister() 方法生成新的寄存器。
|
||||
* 绑定当前作用域到指定 IRFunction。
|
||||
*
|
||||
* @param fn 要绑定到本作用域的 IRFunction 实例
|
||||
* @param fn 目标 IRFunction
|
||||
*/
|
||||
void attachFunction(IRFunction fn) {
|
||||
this.fn = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前作用域中声明一个新变量,并为其分配一个新的虚拟寄存器。
|
||||
* 调用绑定的 IRFunction.newRegister() 生成寄存器后保存到映射表中。
|
||||
* 声明一个新变量并分配新的虚拟寄存器。
|
||||
*
|
||||
* @param name 变量名称,作为映射键使用
|
||||
* @param type 变量类型
|
||||
* @param name 变量名称
|
||||
* @param type 变量类型名
|
||||
*/
|
||||
void declare(String name, String type) {
|
||||
IRVirtualRegister reg = fn.newRegister();
|
||||
vars.put(name, reg);
|
||||
varTypes.put(name, type);
|
||||
varConstValues.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前作用域中声明或导入一个已有的虚拟寄存器,并将其与指定变量名绑定。
|
||||
* 该方法可用于将外部或前一作用域的寄存器导入到本作用域。
|
||||
* 声明新变量,并绑定到指定的寄存器。
|
||||
*
|
||||
* @param name 变量名称,作为映射键使用
|
||||
* @param type 变量类型
|
||||
* @param reg 要绑定到该名称的 IRVirtualRegister 实例
|
||||
* @param name 变量名称
|
||||
* @param type 变量类型名
|
||||
* @param reg 绑定的虚拟寄存器
|
||||
*/
|
||||
void declare(String name, String type, IRVirtualRegister reg) {
|
||||
vars.put(name, reg);
|
||||
varTypes.put(name, type);
|
||||
varConstValues.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新已存在变量的虚拟寄存器绑定关系。若变量已声明,则替换其对应的寄存器;
|
||||
* 若尚未声明,则等同于声明新变量。
|
||||
* 更新变量的虚拟寄存器绑定(如变量已存在则覆盖,否则等同于新声明)。
|
||||
*
|
||||
* @param name 变量名称,作为映射键使用
|
||||
* @param reg 新的 IRVirtualRegister 实例,用于替换旧绑定
|
||||
* @param name 变量名称
|
||||
* @param reg 新的虚拟寄存器
|
||||
*/
|
||||
void put(String name, IRVirtualRegister reg) {
|
||||
vars.put(name, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据变量名称在当前作用域中查找对应的虚拟寄存器。
|
||||
* 查找变量名对应的虚拟寄存器。
|
||||
*
|
||||
* @param name 需要查询的变量名称
|
||||
* @return 如果该名称已绑定寄存器,则返回对应的 IRVirtualRegister;
|
||||
* 如果未声明,则返回 null
|
||||
* @param name 变量名
|
||||
* @return 已绑定的虚拟寄存器,若未声明则返回 null
|
||||
*/
|
||||
IRVirtualRegister lookup(String name) {
|
||||
return vars.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据变量名称在当前作用域中查找对应的类型。
|
||||
* 查找变量名对应的类型名。
|
||||
*
|
||||
* @param name 需要查询的变量名称
|
||||
* @return 如果该名称已声明,则返回对应的类型
|
||||
* 如果未声明,则返回 null
|
||||
* @param name 变量名
|
||||
* @return 已声明类型字符串,若未声明则返回 null
|
||||
*/
|
||||
String lookupType(String name) {
|
||||
return varTypes.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 变量->类型的映射 的不可变副本
|
||||
* @return 变量->类型的映射 的不可变副本
|
||||
* 获取变量名到类型名映射的不可变副本。
|
||||
*
|
||||
* @return 变量名→类型名映射的只读视图
|
||||
*/
|
||||
Map<String, String> getVarTypes() {
|
||||
return Map.copyOf(varTypes);
|
||||
}
|
||||
|
||||
// ---------------- 编译期常量相关接口 ----------------
|
||||
|
||||
/**
|
||||
* 设置变量的编译期常量值。
|
||||
*
|
||||
* @param name 变量名称
|
||||
* @param value 常量值(null 表示清除)
|
||||
*/
|
||||
void setConstValue(String name, Object value) {
|
||||
if (value == null) varConstValues.remove(name);
|
||||
else varConstValues.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取变量的编译期常量值(如没有则返回 null)。
|
||||
*
|
||||
* @param name 变量名称
|
||||
* @return 编译期常量值,或 null
|
||||
*/
|
||||
Object getConstValue(String name) {
|
||||
return varConstValues.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除变量的编译期常量值绑定。
|
||||
*
|
||||
* @param name 变量名称
|
||||
*/
|
||||
void clearConstValue(String name) {
|
||||
varConstValues.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,13 +10,23 @@ import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* StatementBuilder —— 将 AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器。
|
||||
* <b>StatementBuilder</b> —— AST 语句节点 ({@link StatementNode}) 到 IR 指令序列的构建器。
|
||||
* <p>
|
||||
* 负责将各种语句节点(循环、分支、表达式、赋值、声明、返回等)生成对应的 IR 指令,并管理作用域和控制流标签。
|
||||
* 负责将各类语句(如循环、分支、表达式、赋值、声明、返回、break、continue 等)
|
||||
* 转换为对应的 IR 指令,并自动管理作用域、虚拟寄存器分配以及控制流标签(如 break/continue 目标)。
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>支持多种语句类型的分发与转换。</li>
|
||||
* <li>与 {@link ExpressionBuilder} 协作完成表达式相关 IR 生成。</li>
|
||||
* <li>负责控制流跳转(分支、循环)的标签分配与维护。</li>
|
||||
* <li>在变量赋值和声明时自动常量折叠和登记。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author [你的名字]
|
||||
* @since 1.0
|
||||
*/
|
||||
public class StatementBuilder {
|
||||
|
||||
@ -24,79 +34,126 @@ public class StatementBuilder {
|
||||
* 当前 IR 上下文,包含作用域、指令序列等信息。
|
||||
*/
|
||||
private final IRContext ctx;
|
||||
|
||||
/**
|
||||
* 表达式 IR 构建器,用于将表达式节点转为 IR 指令。
|
||||
*/
|
||||
private final ExpressionBuilder expr;
|
||||
|
||||
/**
|
||||
* break 目标标签栈(保存每层循环的结束标签)
|
||||
* break 目标标签栈(保存每层循环的结束标签,用于 break 跳转)
|
||||
*/
|
||||
private final ArrayDeque<String> breakTargets = new ArrayDeque<>();
|
||||
|
||||
/**
|
||||
* continue 目标标签栈(保存每层循环的 step 起始标签)
|
||||
* continue 目标标签栈(保存每层循环的 step 起始标签,用于 continue 跳转)
|
||||
*/
|
||||
private final ArrayDeque<String> continueTargets = new ArrayDeque<>();
|
||||
|
||||
/**
|
||||
* 构造方法。
|
||||
* 构造方法。初始化 StatementBuilder。
|
||||
*
|
||||
* @param ctx IR 编译上下文环境
|
||||
* @param ctx IR 编译上下文环境,包含作用域、标签、指令等信息
|
||||
*/
|
||||
public StatementBuilder(IRContext ctx) {
|
||||
this.ctx = ctx;
|
||||
this.expr = new ExpressionBuilder(ctx);
|
||||
}
|
||||
|
||||
private static char typeSuffixFromType(String type) {
|
||||
if (type == null) return '\0';
|
||||
return switch (type.toLowerCase(Locale.ROOT)) {
|
||||
case "byte" -> 'b';
|
||||
case "short" -> 's';
|
||||
case "long" -> 'l';
|
||||
case "float" -> 'f';
|
||||
case "double" -> 'd';
|
||||
default -> '\0'; // 其余默认按 32-bit 整型处理
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个 AST 语句节点转为 IR 指令序列。
|
||||
* <p>
|
||||
* 根据节点类型分发到对应的处理方法。
|
||||
* 支持:循环、分支、表达式语句、赋值、声明、返回、break、continue。
|
||||
* 不支持的语句类型会抛出异常。
|
||||
* </p>
|
||||
*
|
||||
* @param stmt 待转换的语句节点
|
||||
* @param stmt 待转换的语句节点,不能为空
|
||||
* @throws IllegalStateException 若遇到不支持的语句类型,或 break/continue 不在循环体中
|
||||
*/
|
||||
public void build(StatementNode stmt) {
|
||||
if (stmt instanceof LoopNode loop) {
|
||||
// 循环语句
|
||||
buildLoop(loop);
|
||||
return;
|
||||
}
|
||||
if (stmt instanceof IfNode ifNode) {
|
||||
// 分支(if-else)语句
|
||||
buildIf(ifNode);
|
||||
return;
|
||||
}
|
||||
if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, NodeContext _)) {
|
||||
// 纯表达式语句,如 foo();
|
||||
expr.build(exp);
|
||||
return;
|
||||
}
|
||||
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) {
|
||||
// 赋值语句,如 a = b + 1;
|
||||
|
||||
final String type = ctx.getScope().lookupType(var);
|
||||
|
||||
// 1. 设置声明变量的类型
|
||||
ctx.setVarType(type);
|
||||
|
||||
IRVirtualRegister target = getOrDeclareRegister(var, type);
|
||||
expr.buildInto(rhs, target);
|
||||
|
||||
// 2. 清除变量声明
|
||||
// 赋值时尝试记录/清除常量
|
||||
try {
|
||||
Object constVal = tryFoldConst(rhs);
|
||||
if (constVal != null)
|
||||
ctx.getScope().setConstValue(var, constVal);
|
||||
else
|
||||
ctx.getScope().clearConstValue(var);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
ctx.clearVarType();
|
||||
return;
|
||||
}
|
||||
|
||||
// ==== 支持数组下标赋值 arr[idx] = value ====
|
||||
if (stmt instanceof IndexAssignmentNode idxAssign) {
|
||||
IndexExpressionNode target = idxAssign.target();
|
||||
|
||||
// 1. 目标数组寄存器:多维时用 buildIndexRef 拿引用
|
||||
IRVirtualRegister arrReg = (target.array() instanceof IndexExpressionNode inner)
|
||||
? expr.buildIndexRef(inner)
|
||||
: expr.build(target.array());
|
||||
|
||||
// 2. 下标与右值
|
||||
IRVirtualRegister idxReg = expr.build(target.index());
|
||||
IRVirtualRegister valReg = expr.build(idxAssign.value());
|
||||
|
||||
// 3. 选择内置函数名 __setindex_x,根据元素类型分派
|
||||
String func = "__setindex_r";
|
||||
org.jcnc.snow.compiler.parser.ast.base.ExpressionNode base = target.array();
|
||||
while (base instanceof IndexExpressionNode innerIdx) base = innerIdx.array();
|
||||
if (base instanceof IdentifierNode id) {
|
||||
String arrType = ctx.getScope().lookupType(id.name());
|
||||
if (arrType != null) {
|
||||
String elemType = arrType.endsWith("[]") ? arrType.substring(0, arrType.length() - 2) : arrType;
|
||||
switch (elemType) {
|
||||
case "byte" -> func = "__setindex_b";
|
||||
case "short" -> func = "__setindex_s";
|
||||
case "int" -> func = "__setindex_i";
|
||||
case "long" -> func = "__setindex_l";
|
||||
case "float" -> func = "__setindex_f";
|
||||
case "double" -> func = "__setindex_d";
|
||||
case "boolean" -> func = "__setindex_i";
|
||||
case "string" -> func = "__setindex_r";
|
||||
default -> func = "__setindex_r";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 生成 CALL 指令
|
||||
java.util.List<org.jcnc.snow.compiler.ir.core.IRValue> argv =
|
||||
java.util.List.of(arrReg, idxReg, valReg);
|
||||
ctx.addInstruction(new org.jcnc.snow.compiler.ir.instruction.CallInstruction(null, func, argv));
|
||||
|
||||
// 5. 赋值后清理常量绑定
|
||||
try {
|
||||
if (base instanceof IdentifierNode id2) {
|
||||
ctx.getScope().clearConstValue(id2.name());
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (stmt instanceof DeclarationNode decl) {
|
||||
// 变量声明语句(如 int a = 1;)
|
||||
if (decl.getInitializer().isPresent()) {
|
||||
@ -113,16 +170,21 @@ public class StatementBuilder {
|
||||
// 即使初始值是某个已存在变量(如 outer_i),这里是值的拷贝
|
||||
expr.buildInto(decl.getInitializer().get(), dest);
|
||||
|
||||
// 4. 清理类型设置,防止影响后续变量声明
|
||||
ctx.clearVarType();
|
||||
// 声明赋初值时登记常量
|
||||
try {
|
||||
Object constVal = tryFoldConst(decl.getInitializer().get());
|
||||
if (constVal != null)
|
||||
ctx.getScope().setConstValue(decl.getName(), constVal);
|
||||
else
|
||||
ctx.getScope().clearConstValue(decl.getName());
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
// 5. 在作用域内将变量名与新分配的寄存器进行绑定
|
||||
// 这样后续对该变量的任何操作都只会影响 dest,不会反向影响初值表达式中的源变量
|
||||
ctx.clearVarType();
|
||||
ctx.getScope().declare(decl.getName(), decl.getType(), dest);
|
||||
} else {
|
||||
// 仅声明变量,无初值(如 int a;)
|
||||
// 在作用域内声明并分配新寄存器,但不进行初始化
|
||||
ctx.getScope().declare(decl.getName(), decl.getType());
|
||||
ctx.getScope().clearConstValue(decl.getName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -159,10 +221,11 @@ public class StatementBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取变量名对应的寄存器,不存在则声明一个新的。
|
||||
* 获取变量名对应的寄存器,如果尚未声明则新声明一个并返回。
|
||||
*
|
||||
* @param name 变量名
|
||||
* @return 变量对应的虚拟寄存器
|
||||
* @param name 变量名,不能为空
|
||||
* @param type 变量类型,不能为空
|
||||
* @return 变量对应的虚拟寄存器 {@link IRVirtualRegister}
|
||||
*/
|
||||
private IRVirtualRegister getOrDeclareRegister(String name, String type) {
|
||||
IRVirtualRegister reg = ctx.getScope().lookup(name);
|
||||
@ -174,19 +237,21 @@ public class StatementBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量构建一组语句节点,顺序处理每个语句。
|
||||
* 批量构建一组语句节点,按顺序依次处理。
|
||||
*
|
||||
* @param stmts 语句节点集合
|
||||
* @param stmts 语句节点集合,不可为 null
|
||||
*/
|
||||
private void buildStatements(Iterable<StatementNode> stmts) {
|
||||
for (StatementNode s : stmts) build(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建循环语句(for/while)。
|
||||
* 处理流程: 初始语句 → 条件判断 → 循环体 → 更新语句 → 跳回条件。
|
||||
* 构建循环语句(for/while),包括初始语句、条件判断、循环体、更新语句、跳回判断等 IR 指令。
|
||||
* <p>
|
||||
* 自动维护 break/continue 的目标标签。
|
||||
* </p>
|
||||
*
|
||||
* @param loop 循环节点
|
||||
* @param loop 循环节点,不能为空
|
||||
*/
|
||||
private void buildLoop(LoopNode loop) {
|
||||
if (loop.init() != null) build(loop.init());
|
||||
@ -222,9 +287,11 @@ public class StatementBuilder {
|
||||
|
||||
/**
|
||||
* 构建分支语句(if/else)。
|
||||
* 处理流程: 条件判断 → then 分支 → else 分支(可选)。
|
||||
* <p>
|
||||
* 包括:条件判断、then 分支、else 分支(可选)、结束标签等。
|
||||
* </p>
|
||||
*
|
||||
* @param ifNode if 语句节点
|
||||
* @param ifNode if 语句节点,不能为空
|
||||
*/
|
||||
private void buildIf(IfNode ifNode) {
|
||||
String lblElse = ctx.newLabel();
|
||||
@ -245,11 +312,14 @@ public class StatementBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件跳转指令的生成。
|
||||
* 如果是二元比较表达式,直接使用对应比较操作码;否则等价于与 0 比较。
|
||||
* 发射条件跳转指令:如果 cond 不成立,则跳转到 falseLabel。
|
||||
* <p>
|
||||
* 对于二元比较表达式,会选择恰当的比较指令。
|
||||
* 其他类型表达式,等价于 (cond == 0) 时跳转。
|
||||
* </p>
|
||||
*
|
||||
* @param cond 条件表达式
|
||||
* @param falseLabel 条件不成立时跳转到的标签
|
||||
* @param cond 条件表达式节点,不可为 null
|
||||
* @param falseLabel 条件不成立时跳转的标签,不可为 null
|
||||
*/
|
||||
private void emitConditionalJump(ExpressionNode cond, String falseLabel) {
|
||||
if (cond instanceof BinaryExpressionNode(
|
||||
@ -271,7 +341,79 @@ public class StatementBuilder {
|
||||
} else {
|
||||
IRVirtualRegister condReg = expr.build(cond);
|
||||
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
|
||||
InstructionFactory.cmpJump(ctx, IROpCode.CMP_IEQ, condReg, zero, falseLabel);
|
||||
InstructionFactory.cmpJump(ctx, org.jcnc.snow.compiler.ir.core.IROpCode.CMP_IEQ, condReg, zero, falseLabel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归尝试对表达式做常量折叠(constant folding)。
|
||||
* <p>
|
||||
* 该方法会根据表达式类型进行常量求值:
|
||||
* <ul>
|
||||
* <li>如果是数字字面量,解析为 Integer 或 Double。</li>
|
||||
* <li>如果是字符串字面量,直接返回字符串内容。</li>
|
||||
* <li>如果是布尔字面量,返回 1(true)或 0(false)。</li>
|
||||
* <li>如果是数组字面量,会递归所有元素全部是常量时,返回包含常量元素的 List。</li>
|
||||
* <li>如果是标识符节点,尝试从作用域查找是否登记为常量,如果找到则返回。</li>
|
||||
* <li>其余类型或无法确定常量时,返回 null。</li>
|
||||
* </ul>
|
||||
* 用于全局/局部常量传播优化与类型推断。
|
||||
*
|
||||
* @param expr 待折叠的表达式节点,允许为 null
|
||||
* @return 如果可折叠则返回其常量值(如 Integer、Double、String、List),否则返回 null
|
||||
*/
|
||||
private Object tryFoldConst(ExpressionNode expr) {
|
||||
// 1. 空节点直接返回 null
|
||||
if (expr == null) return null;
|
||||
|
||||
// 2. 数字字面量:尝试解析为 Integer 或 Double
|
||||
if (expr instanceof NumberLiteralNode n) {
|
||||
String s = n.value(); // 获取文本内容
|
||||
try {
|
||||
// 判断是否为浮点型(包含 . 或 e/E 科学计数法)
|
||||
if (s.contains(".") || s.contains("e") || s.contains("E")) {
|
||||
return Double.parseDouble(s); // 解析为 Double
|
||||
}
|
||||
return Integer.parseInt(s); // 否则解析为 Integer
|
||||
} catch (NumberFormatException e) {
|
||||
// 解析失败,返回 null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 字符串字面量:直接返回字符串内容
|
||||
if (expr instanceof StringLiteralNode s) {
|
||||
return s.value();
|
||||
}
|
||||
|
||||
// 4. 布尔字面量:true 返回 1,false 返回 0
|
||||
if (expr instanceof BoolLiteralNode b) {
|
||||
return b.getValue() ? 1 : 0;
|
||||
}
|
||||
|
||||
// 5. 数组字面量:递归所有元素做常量折叠,只有全为常量时才返回 List
|
||||
if (expr instanceof ArrayLiteralNode arr) {
|
||||
java.util.List<Object> list = new java.util.ArrayList<>();
|
||||
for (ExpressionNode e : arr.elements()) {
|
||||
Object v = tryFoldConst(e); // 递归折叠每个元素
|
||||
if (v == null) return null; // 只要有一个不是常量,则整个数组不是常量
|
||||
list.add(v);
|
||||
}
|
||||
// 所有元素均为常量,返回只读 List
|
||||
return java.util.List.copyOf(list);
|
||||
}
|
||||
|
||||
// 6. 标识符:尝试查找该变量在当前作用域是否登记为常量
|
||||
if (expr instanceof IdentifierNode id) {
|
||||
try {
|
||||
Object v = ctx.getScope().getConstValue(id.name());
|
||||
if (v != null) return v; // 查到常量则返回
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// 7. 其他情况均视为不可折叠,返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ public class LexerEngine {
|
||||
* @param sourceName 文件名(诊断用)
|
||||
*/
|
||||
public LexerEngine(String source, String sourceName) {
|
||||
this.absPath = new File(sourceName).getAbsolutePath();
|
||||
this.absPath = new File(sourceName).getAbsolutePath().replace('\\', '/');
|
||||
this.context = new LexerContext(source);
|
||||
this.scanners = List.of(
|
||||
new WhitespaceTokenScanner(),
|
||||
@ -56,9 +56,9 @@ public class LexerEngine {
|
||||
/* 2. 后置整体校验 */
|
||||
validateTokens();
|
||||
/* 3. 打印 token */
|
||||
if (SnowConfig.isDebug()) {
|
||||
TokenPrinter.print(tokens);
|
||||
}
|
||||
// if (SnowConfig.isDebug()) {
|
||||
// TokenPrinter.print(tokens);
|
||||
// }
|
||||
|
||||
/* 4. 统一报告错误 */
|
||||
report(errors);
|
||||
|
||||
@ -50,6 +50,6 @@ public class LexicalError {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return file + ": 行 " + line + ", 列 " + column + ": " + message;
|
||||
return "file:///" + file + ":" + line + ":" + column + ": " + message;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
|
||||
*/
|
||||
@Override
|
||||
public boolean canHandle(char c, LexerContext ctx) {
|
||||
return ":,().+-*/".indexOf(c) >= 0;
|
||||
return ":,().+-*/[]".indexOf(c) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -53,6 +53,8 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
|
||||
case '/' -> TokenType.DIVIDE;
|
||||
case '(' -> TokenType.LPAREN;
|
||||
case ')' -> TokenType.RPAREN;
|
||||
case '[' -> TokenType.LBRACKET;
|
||||
case ']' -> TokenType.RBRACKET;
|
||||
default -> TokenType.UNKNOWN;
|
||||
};
|
||||
return new Token(type, String.valueOf(c), line, col);
|
||||
|
||||
@ -26,7 +26,7 @@ public class TokenFactory {
|
||||
* 语言的保留关键字集合。
|
||||
*/
|
||||
private static final Set<String> KEYWORDS = Set.of
|
||||
("module", "function", "parameter", "return_type", "body", "end",
|
||||
("module", "function", "params", "returns", "body", "end",
|
||||
"if", "then", "else", "loop", "declare", "return", "import", "init",
|
||||
"cond", "step", "globals", "break", "continue");
|
||||
|
||||
|
||||
@ -14,10 +14,10 @@ public enum TokenType {
|
||||
/** 普通标识符,如变量名、函数名等 */
|
||||
IDENTIFIER,
|
||||
|
||||
/** 语言保留关键字(如 if、return、module 等) */
|
||||
/** 关键字(declare、if、else、loop、break、continue、return 等) */
|
||||
KEYWORD,
|
||||
|
||||
/** 内置类型名称(如 int、string、bool 等) */
|
||||
/** 内置类型名(byte、short、int、long、float、double、string、boolean、void 等) */
|
||||
TYPE,
|
||||
|
||||
/* ---------- 字面量 ---------- */
|
||||
@ -67,6 +67,12 @@ public enum TokenType {
|
||||
/** 右括号 ')' */
|
||||
RPAREN,
|
||||
|
||||
/** 左中括号 '[' */
|
||||
LBRACKET,
|
||||
|
||||
/** 右中括号 ']' */
|
||||
RBRACKET,
|
||||
|
||||
/** 相等比较符号 '==' */
|
||||
DOUBLE_EQUALS,
|
||||
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@code ArrayLiteralNode} 表示数组字面量表达式节点。
|
||||
* <p>
|
||||
* 例如:[1, 2, 3] 或 [[1, 2], [3, 4]] 这样的语法结构,均对应本节点类型。
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #elements()} 保存所有元素表达式节点。</li>
|
||||
* <li>{@link #context()} 表示该节点在源代码中的上下文信息(如位置、父节点等)。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public record ArrayLiteralNode(
|
||||
/*
|
||||
数组字面量中的所有元素表达式(按顺序)。
|
||||
*/
|
||||
List<ExpressionNode> elements,
|
||||
|
||||
/*
|
||||
节点的上下文信息(如源码位置等)。
|
||||
*/
|
||||
NodeContext context
|
||||
) implements ExpressionNode {
|
||||
|
||||
/**
|
||||
* 返回字符串形式,如 {@code Array[1, 2, 3]}。
|
||||
*
|
||||
* @return 表示该数组字面量节点的字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Array" + elements;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@code IndexAssignmentNode} 表示数组元素赋值语句节点,例如 {@code arr[i] = value}。
|
||||
* <p>
|
||||
* 对于多维数组,{@code target} 可以是嵌套的 {@link IndexExpressionNode}。
|
||||
* </p>
|
||||
*
|
||||
* @param target 要赋值的数组元素(支持多维数组下标)
|
||||
* @param value 右侧赋值表达式
|
||||
* @param context 该节点的源代码上下文信息
|
||||
*/
|
||||
public record IndexAssignmentNode(
|
||||
/*
|
||||
数组元素目标,支持多维数组下标(如 arr[i][j])。
|
||||
*/
|
||||
IndexExpressionNode target,
|
||||
|
||||
/*
|
||||
被赋的右侧表达式
|
||||
*/
|
||||
ExpressionNode value,
|
||||
|
||||
/*
|
||||
节点的上下文信息(如源码位置等)
|
||||
*/
|
||||
NodeContext context
|
||||
) implements StatementNode {
|
||||
/**
|
||||
* 返回赋值语句的字符串表示(如 "arr[i] = value")。
|
||||
*
|
||||
* @return 字符串形式
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return target + " = " + value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
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 IndexExpressionNode} 表示数组/集合的下标访问表达式节点,例如 {@code arr[i]}。
|
||||
* <p>
|
||||
* 支持多维数组嵌套(如 arr[i][j])。
|
||||
* </p>
|
||||
*
|
||||
* @param array 被访问的目标表达式(通常是数组、集合或嵌套下标表达式)
|
||||
* @param index 下标表达式(必须为数值类型)
|
||||
* @param context 源码上下文信息(如位置)
|
||||
*/
|
||||
public record IndexExpressionNode(
|
||||
/*
|
||||
下标访问的目标表达式(如 arr)。
|
||||
*/
|
||||
ExpressionNode array,
|
||||
|
||||
/*
|
||||
下标表达式(如 i)。
|
||||
*/
|
||||
ExpressionNode index,
|
||||
|
||||
/*
|
||||
源码上下文信息(如行号、文件名等)。
|
||||
*/
|
||||
NodeContext context
|
||||
) implements ExpressionNode {
|
||||
|
||||
/**
|
||||
* 返回形如 "arr[i]" 的字符串表示。
|
||||
* @return 表达式的字符串形式
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return array + "[" + index + "]";
|
||||
}
|
||||
}
|
||||
@ -40,6 +40,6 @@ public class ParseError {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return file + ": 行 " + line + ", 列 " + column + ": " + message;
|
||||
return "file:///" + file + ":" + line + ":" + column + ": " + message;
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ public class ParserContext {
|
||||
*/
|
||||
public ParserContext(List<Token> tokens, String sourceName) {
|
||||
this.tokens = new TokenStream(tokens);
|
||||
this.sourceName = Paths.get(sourceName).toAbsolutePath().toString();
|
||||
this.sourceName = Paths.get(sourceName).toAbsolutePath().toString().replace('\\', '/');
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
package org.jcnc.snow.compiler.parser.expression;
|
||||
|
||||
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
import org.jcnc.snow.compiler.parser.ast.ArrayLiteralNode;
|
||||
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.PrefixParselet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code ArrayLiteralParselet} 用于解析数组字面量表达式。
|
||||
* <p>
|
||||
* 支持语法形如 {@code [a, b, c]} 或 {@code [ [1,2], [3,4] ]},
|
||||
* 允许元素及逗号前后出现换行符,便于多行书写。
|
||||
* </p>
|
||||
* <p>
|
||||
* 语法结构为:<br>
|
||||
* <pre>[ (element (',' element)*)? ]</pre>
|
||||
* </p>
|
||||
*/
|
||||
public class ArrayLiteralParselet implements PrefixParselet {
|
||||
|
||||
/**
|
||||
* 解析数组字面量表达式。
|
||||
*
|
||||
* @param ctx 解析上下文(包含词法流、源文件信息等)
|
||||
* @param token 已消费的左中括号(LBRACKET),用于定位节点源信息
|
||||
* @return 解析得到的 {@link ArrayLiteralNode} 节点
|
||||
*/
|
||||
@Override
|
||||
public ExpressionNode parse(ParserContext ctx, Token token) {
|
||||
// token 为已消费的 LBRACKET,使用其位置生成 NodeContext
|
||||
int line = token.getLine();
|
||||
int col = token.getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
TokenStream ts = ctx.getTokens();
|
||||
skipNewlines(ts);
|
||||
|
||||
List<ExpressionNode> elements = new ArrayList<>();
|
||||
|
||||
// 空数组: 直接遇到 RBRACKET
|
||||
if (ts.peek().getType() != TokenType.RBRACKET) {
|
||||
while (true) {
|
||||
// 解析一个元素
|
||||
ExpressionNode elem = new PrattExpressionParser().parse(ctx);
|
||||
elements.add(elem);
|
||||
|
||||
skipNewlines(ts);
|
||||
// 逗号继续,右中括号结束
|
||||
if (ts.peek().getType() == TokenType.COMMA) {
|
||||
ts.next();
|
||||
skipNewlines(ts);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 期望并消费右中括号
|
||||
ts.expectType(TokenType.RBRACKET);
|
||||
return new ArrayLiteralNode(elements, new NodeContext(line, col, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过词法流中连续的换行符,允许数组元素跨多行书写。
|
||||
*
|
||||
* @param ts 词法流
|
||||
*/
|
||||
private static void skipNewlines(TokenStream ts) {
|
||||
while (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package org.jcnc.snow.compiler.parser.expression;
|
||||
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@code IndexParselet} 负责解析数组或集合的下标访问表达式,语法结构为:
|
||||
* <pre>
|
||||
* primary '[' expr ']'
|
||||
* </pre>
|
||||
* 例如 {@code arr[expr]}。
|
||||
*/
|
||||
public class IndexParselet implements InfixParselet {
|
||||
|
||||
/**
|
||||
* 解析下标访问表达式。
|
||||
*
|
||||
* @param ctx 解析上下文,包含词法流、错误信息等
|
||||
* @param left 已解析的 primary 表达式(如数组名、表达式等)
|
||||
* @return {@link IndexExpressionNode} 下标访问表达式节点
|
||||
*/
|
||||
@Override
|
||||
public ExpressionNode parse(ParserContext ctx, ExpressionNode left) {
|
||||
int line = left.context().line();
|
||||
int col = left.context().column();
|
||||
String file = left.context().file();
|
||||
|
||||
// 消耗左中括号 '['
|
||||
ctx.getTokens().expectType(TokenType.LBRACKET);
|
||||
// 解析索引表达式
|
||||
ExpressionNode index = new PrattExpressionParser().parse(ctx);
|
||||
// 消耗右中括号 ']'
|
||||
ctx.getTokens().expectType(TokenType.RBRACKET);
|
||||
// 构造下标访问表达式节点
|
||||
return new IndexExpressionNode(left, index, new NodeContext(line, col, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下标访问优先级与函数调用一致。
|
||||
*
|
||||
* @return {@link Precedence#CALL} 优先级
|
||||
*/
|
||||
@Override
|
||||
public Precedence precedence() {
|
||||
return Precedence.CALL;
|
||||
}
|
||||
}
|
||||
@ -25,45 +25,80 @@ import java.util.Map;
|
||||
*/
|
||||
public class PrattExpressionParser implements ExpressionParser {
|
||||
|
||||
/** 前缀解析器注册表(按 Token 类型名索引) */
|
||||
/**
|
||||
* 前缀解析器注册表(按 Token 类型名或词素索引)。
|
||||
* <p>
|
||||
* 用于存储所有支持的前缀表达式解析器,例如字面量、变量、分组、数组、一元运算等。
|
||||
* 支持通过 TokenType 的名称和特定词素(如 "(", "[")两种方式索引。
|
||||
* </p>
|
||||
*/
|
||||
private static final Map<String, PrefixParselet> prefixes = new HashMap<>();
|
||||
/** 中缀解析器注册表(按运算符词素索引) */
|
||||
|
||||
/**
|
||||
* 中缀解析器注册表(按运算符词素索引)。
|
||||
* <p>
|
||||
* 用于存储所有支持的中缀表达式解析器,如二元运算、函数调用、下标、成员访问等。
|
||||
* 仅通过词素索引(如 "+", "-", "(", "[" 等)。
|
||||
* </p>
|
||||
*/
|
||||
private static final Map<String, InfixParselet> infixes = new HashMap<>();
|
||||
|
||||
static {
|
||||
// 前缀解析器注册
|
||||
// -------- 前缀解析器注册 --------
|
||||
// 注册数字字面量解析
|
||||
prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
|
||||
// 注册标识符(变量名)解析
|
||||
prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
|
||||
prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
|
||||
// 注册字符串字面量解析
|
||||
prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
|
||||
// 注册布尔字面量解析
|
||||
prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
|
||||
|
||||
// 一元前缀运算符
|
||||
// 支持括号分组、数组字面量
|
||||
prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
|
||||
prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet());
|
||||
// 兼容直接以词素注册(如 '(' '[')
|
||||
prefixes.put("(", new GroupingParselet());
|
||||
prefixes.put("[", new ArrayLiteralParselet());
|
||||
|
||||
// 一元前缀运算符(负号、逻辑非)
|
||||
prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet());
|
||||
prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
|
||||
prefixes.put("-", new UnaryOperatorParselet());
|
||||
prefixes.put("!", new UnaryOperatorParselet());
|
||||
|
||||
// 中缀解析器注册
|
||||
// -------- 中缀解析器注册 --------
|
||||
// 注册常见二元运算符(加减乘除、取模)
|
||||
infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
|
||||
infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true));
|
||||
infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true));
|
||||
infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put("!=", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put(">=", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put("<=", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
// 比较运算符
|
||||
infixes.put(">", new BinaryOperatorParselet(Precedence.COMPARISON, true));
|
||||
infixes.put("<", new BinaryOperatorParselet(Precedence.COMPARISON, true));
|
||||
infixes.put(">=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
|
||||
infixes.put("<=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
|
||||
// 相等性
|
||||
infixes.put("==", new BinaryOperatorParselet(Precedence.EQUALITY, true));
|
||||
infixes.put("!=", new BinaryOperatorParselet(Precedence.EQUALITY, true));
|
||||
// 逻辑与或
|
||||
infixes.put("&&", new BinaryOperatorParselet(Precedence.AND, true));
|
||||
infixes.put("||", new BinaryOperatorParselet(Precedence.OR, true));
|
||||
// 调用、索引、成员访问
|
||||
infixes.put("(", new CallParselet());
|
||||
infixes.put("[", new IndexParselet());
|
||||
infixes.put(".", new MemberParselet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 表达式解析统一入口。
|
||||
* 以最低优先级启动递归下降,适配任意表达式复杂度。
|
||||
* 解析任意表达式的统一入口。
|
||||
* <p>
|
||||
* 该方法将以最低优先级启动表达式递归解析,能够自动适配和处理多层嵌套或复杂组合表达式。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前解析上下文
|
||||
* @return 解析后的表达式 AST 节点
|
||||
* @param ctx 当前解析上下文对象(持有 token 流等信息)
|
||||
* @return 解析得到的表达式 AST 节点对象
|
||||
*/
|
||||
@Override
|
||||
public ExpressionNode parse(ParserContext ctx) {
|
||||
@ -71,21 +106,32 @@ public class PrattExpressionParser implements ExpressionParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定优先级解析表达式。Pratt 算法主循环。
|
||||
* 按指定优先级解析表达式(Pratt 算法核心)。
|
||||
* <p>
|
||||
* 先根据当前 Token 类型查找前缀解析器进行初始解析,
|
||||
* 然后根据优先级不断递归处理中缀运算符和右侧表达式。
|
||||
* 1. 先取当前 token,查找对应的前缀解析器进行初始解析,构建表达式左侧(如字面量、变量等)。
|
||||
* 2. 然后循环检测是否有更高优先级的中缀操作符,
|
||||
* 若有则递归处理右侧表达式并组合为新的表达式节点。
|
||||
* </p>
|
||||
* <p>
|
||||
* 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 解析上下文
|
||||
* @param prec 当前运算符优先级阈值
|
||||
* @return 构建完成的表达式节点
|
||||
* @throws UnsupportedFeature 若遇到未注册的前缀或中缀解析器
|
||||
* @param prec 当前运算符优先级(用于控制递归层级)
|
||||
* @return 解析构建好的表达式节点
|
||||
* @throws UnsupportedFeature 遇到未注册的解析器时抛出
|
||||
*/
|
||||
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
|
||||
// 取下一个 token 作为本轮前缀表达式起始
|
||||
Token token = ctx.getTokens().next();
|
||||
|
||||
// 查找前缀解析器(先按类型名,再按词素)
|
||||
PrefixParselet prefix = prefixes.get(token.getType().name());
|
||||
if (prefix == null) {
|
||||
prefix = prefixes.get(token.getLexeme());
|
||||
}
|
||||
if (prefix == null) {
|
||||
// 未找到前缀解析器则报错
|
||||
throw new UnsupportedFeature(
|
||||
"没有为该 Token 类型注册前缀解析器: " + token.getType(),
|
||||
token.getLine(),
|
||||
@ -93,13 +139,17 @@ public class PrattExpressionParser implements ExpressionParser {
|
||||
);
|
||||
}
|
||||
|
||||
// 执行前缀解析,获得左侧表达式
|
||||
ExpressionNode left = prefix.parse(ctx, token);
|
||||
|
||||
// 不断尝试查找优先级更高的中缀运算符,递归处理表达式链
|
||||
while (!ctx.getTokens().isAtEnd()
|
||||
&& prec.ordinal() < nextPrecedence(ctx)) {
|
||||
// 查看下一个 token 词素,查找中缀解析器
|
||||
String lex = ctx.getTokens().peek().getLexeme();
|
||||
InfixParselet infix = infixes.get(lex);
|
||||
if (infix == null) {
|
||||
// 若未注册中缀解析器,则直接抛异常(常见于语法错误)
|
||||
Token t = ctx.getTokens().peek();
|
||||
throw new UnsupportedFeature(
|
||||
"没有为该运算符注册中缀解析器: '" + lex + "'",
|
||||
@ -107,16 +157,21 @@ public class PrattExpressionParser implements ExpressionParser {
|
||||
t.getCol()
|
||||
);
|
||||
}
|
||||
// 使用中缀解析器处理表达式组合
|
||||
left = infix.parse(ctx, left);
|
||||
}
|
||||
// 返回本层递归已解析的表达式节点
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个中缀解析器的优先级(Pratt 算法核心)。
|
||||
* 获取下一个 token 词素对应的中缀运算符优先级(Pratt 算法关键)。
|
||||
* <p>
|
||||
* 用于决定当前是否需要递归处理更高优先级的中缀操作。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前解析上下文
|
||||
* @return 下一个中缀运算符的优先级序号;若无解析器则为 -1
|
||||
* @return 下一个中缀运算符的优先级序号;若无注册解析器则返回 -1
|
||||
*/
|
||||
private int nextPrecedence(ParserContext ctx) {
|
||||
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());
|
||||
|
||||
@ -9,26 +9,30 @@ package org.jcnc.snow.compiler.parser.expression;
|
||||
*/
|
||||
public enum Precedence {
|
||||
|
||||
/**
|
||||
* 最低优先级,通常用于整个表达式解析的起始入口。
|
||||
*/
|
||||
/** 最低优先级,通常用于整个表达式解析的起始入口。 */
|
||||
LOWEST,
|
||||
|
||||
/**
|
||||
* 加法和减法的优先级(例如 +、-)。
|
||||
*/
|
||||
/** 逻辑或(||) */
|
||||
OR,
|
||||
|
||||
/** 逻辑与(&&) */
|
||||
AND,
|
||||
|
||||
/** 相等/不等(==, !=) */
|
||||
EQUALITY,
|
||||
|
||||
/** 大小比较(<, >, <=, >=) */
|
||||
COMPARISON,
|
||||
|
||||
/** 加法和减法(+、-) */
|
||||
SUM,
|
||||
|
||||
/**
|
||||
* 乘法、除法、取模等更高优先级的二元运算符(例如 *、/、%)。
|
||||
*/
|
||||
/** 乘法、除法、取模(*、/、%) */
|
||||
PRODUCT,
|
||||
|
||||
/** 一元前缀(-x !x) */
|
||||
UNARY,
|
||||
|
||||
/**
|
||||
* 函数调用、成员访问等最强绑定(例如 foo()、obj.prop)。
|
||||
*/
|
||||
/** 函数调用、成员访问等最强绑定(foo()、obj.prop) */
|
||||
CALL
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ public class ASTPrinter {
|
||||
NodeContext _
|
||||
) -> {
|
||||
System.out.println(pad + "function " + name
|
||||
+ "(params=" + parameters + ", return=" + returnType + ")");
|
||||
+ "(params=" + parameters + ", returns=" + returnType + ")");
|
||||
for (StatementNode stmt : body) {
|
||||
print(stmt, indent + 1);
|
||||
}
|
||||
|
||||
@ -27,8 +27,8 @@ import java.util.*;
|
||||
*
|
||||
* <ul>
|
||||
* <li>函数头(关键字 {@code function:} 与函数名)</li>
|
||||
* <li>参数列表(parameter 区块)</li>
|
||||
* <li>返回类型(return_type 区块)</li>
|
||||
* <li>参数列表(params 区块)</li>
|
||||
* <li>返回类型(returns 区块)</li>
|
||||
* <li>函数体(body 区块)</li>
|
||||
* <li>函数结束(关键字 {@code end function})</li>
|
||||
* </ul>
|
||||
@ -107,7 +107,7 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数定义中各区块的解析规则(parameter、return_type、body)。
|
||||
* 构造函数定义中各区块的解析规则(params、returns、body)。
|
||||
*
|
||||
* <p>
|
||||
* 每个 {@link SectionDefinition} 包含两个部分: 区块起始判断器(基于关键字)与具体的解析逻辑。
|
||||
@ -124,13 +124,13 @@ public class FunctionParser implements TopLevelParser {
|
||||
List<StatementNode> body) {
|
||||
Map<String, SectionDefinition> map = new HashMap<>();
|
||||
|
||||
map.put("parameter", new SectionDefinition(
|
||||
(TokenStream stream) -> stream.peek().getLexeme().equals("parameter"),
|
||||
map.put("params", new SectionDefinition(
|
||||
(TokenStream stream) -> stream.peek().getLexeme().equals("params"),
|
||||
(ParserContext context, TokenStream stream) -> params.addAll(parseParameters(context))
|
||||
));
|
||||
|
||||
map.put("return_type", new SectionDefinition(
|
||||
(TokenStream stream) -> stream.peek().getLexeme().equals("return_type"),
|
||||
map.put("returns", new SectionDefinition(
|
||||
(TokenStream stream) -> stream.peek().getLexeme().equals("returns"),
|
||||
(ParserContext context, TokenStream stream) -> returnType[0] = parseReturnType(stream)
|
||||
));
|
||||
|
||||
@ -182,7 +182,7 @@ public class FunctionParser implements TopLevelParser {
|
||||
* <p>
|
||||
* 支持声明后附加注释,格式示例:
|
||||
* <pre>
|
||||
* parameter:
|
||||
* params:
|
||||
* declare x: int // 说明文字
|
||||
* declare y: float
|
||||
* </pre>
|
||||
@ -194,7 +194,7 @@ public class FunctionParser implements TopLevelParser {
|
||||
private List<ParameterNode> parseParameters(ParserContext ctx) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
ts.expect("parameter");
|
||||
ts.expect("params");
|
||||
ts.expect(":");
|
||||
skipComments(ts);
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
@ -208,7 +208,7 @@ public class FunctionParser implements TopLevelParser {
|
||||
continue;
|
||||
}
|
||||
String lex = ts.peek().getLexeme();
|
||||
if (lex.equals("return_type") || lex.equals("body") || lex.equals("end")) {
|
||||
if (lex.equals("returns") || lex.equals("body") || lex.equals("end")) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -232,14 +232,14 @@ public class FunctionParser implements TopLevelParser {
|
||||
* 解析返回类型声明。
|
||||
*
|
||||
* <p>
|
||||
* 格式为 {@code return_type: TYPE},支持前置或行尾注释。
|
||||
* 格式为 {@code returns: TYPE},支持前置或行尾注释。
|
||||
* </p>
|
||||
*
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
* @return 返回类型名称字符串。
|
||||
*/
|
||||
private String parseReturnType(TokenStream ts) {
|
||||
ts.expect("return_type");
|
||||
ts.expect("returns");
|
||||
ts.expect(":");
|
||||
skipComments(ts);
|
||||
Token typeToken = ts.expectType(TokenType.TYPE);
|
||||
|
||||
@ -8,39 +8,29 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||
|
||||
/**
|
||||
* {@code DeclarationStatementParser} 类负责解析变量声明语句,是语句级解析器的一部分。
|
||||
* {@code DeclarationStatementParser} 负责解析变量声明语句节点。
|
||||
* <p>
|
||||
* 本解析器支持以下两种形式的声明语法:
|
||||
* 支持以下两种语法结构:
|
||||
* <pre>{@code
|
||||
* declare myVar:Integer
|
||||
* declare myVar:Integer = 42 + 3
|
||||
* }</pre>
|
||||
* 其中:
|
||||
* <ul>
|
||||
* <li>{@code myVar} 为变量名(必须为标识符类型);</li>
|
||||
* <li>{@code Integer} 为类型标注(必须为类型标记);</li>
|
||||
* <li>可选的初始化表达式由 {@link PrattExpressionParser} 解析;</li>
|
||||
* <li>每条声明语句必须以换行符({@code NEWLINE})结束。</li>
|
||||
* </ul>
|
||||
* 若语法不满足上述结构,将在解析过程中抛出异常。
|
||||
* 解析器能够识别多维数组类型(如 {@code int[]}, {@code string[][]}),并支持可选初始化表达式。
|
||||
* <p>
|
||||
* 每个声明语句均要求以换行符结尾,语法不合法时会抛出异常。
|
||||
* </p>
|
||||
*/
|
||||
public class DeclarationStatementParser implements StatementParser {
|
||||
|
||||
/**
|
||||
* 解析一条 {@code declare} 声明语句,并返回对应的抽象语法树节点 {@link DeclarationNode}。
|
||||
* 解析一条 {@code declare} 语句,生成对应的抽象语法树节点 {@link DeclarationNode}。
|
||||
* <p>
|
||||
* 解析流程如下:
|
||||
* <ol>
|
||||
* <li>匹配关键字 {@code declare};</li>
|
||||
* <li>读取变量名称(标识符类型);</li>
|
||||
* <li>读取类型标注(在冒号后,要求为 {@code TYPE} 类型);</li>
|
||||
* <li>若存在 {@code =},则继续解析其后的表达式作为初始化值;</li>
|
||||
* <li>最终必须匹配 {@code NEWLINE} 表示语句结束。</li>
|
||||
* </ol>
|
||||
* 若遇到非法语法结构,将触发异常并中断解析过程。
|
||||
* 支持类型标注和可选初始化表达式。类型部分自动拼接数组维度(如 int[][])。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前语法解析上下文,包含词法流、错误信息等。
|
||||
* @return 返回一个 {@link DeclarationNode} 节点,表示解析完成的声明语法结构。
|
||||
* @param ctx 当前语法解析上下文(包含词法流、错误信息等)
|
||||
* @return {@link DeclarationNode} 表示声明语句结构
|
||||
* @throws RuntimeException 语法不合法时抛出
|
||||
*/
|
||||
@Override
|
||||
public DeclarationNode parse(ParserContext ctx) {
|
||||
@ -61,11 +51,19 @@ public class DeclarationStatementParser implements StatementParser {
|
||||
ctx.getTokens().expect(":");
|
||||
|
||||
// 获取变量类型(类型标识符)
|
||||
String type = ctx.getTokens()
|
||||
StringBuilder type = new StringBuilder(
|
||||
ctx.getTokens()
|
||||
.expectType(TokenType.TYPE)
|
||||
.getLexeme();
|
||||
.getLexeme()
|
||||
);
|
||||
|
||||
// 可选的初始化表达式,若存在 "=",则解析等号右侧表达式
|
||||
// 消费多维数组类型后缀 "[]"
|
||||
while (ctx.getTokens().match("[")) {
|
||||
ctx.getTokens().expectType(TokenType.RBRACKET); // 必须配对
|
||||
type.append("[]"); // 类型名称拼接 [],如 int[][] 等
|
||||
}
|
||||
|
||||
// 可选初始化表达式(=号右侧)
|
||||
ExpressionNode init = null;
|
||||
if (ctx.getTokens().match("=")) {
|
||||
init = new PrattExpressionParser().parse(ctx);
|
||||
@ -75,6 +73,6 @@ public class DeclarationStatementParser implements StatementParser {
|
||||
ctx.getTokens().expectType(TokenType.NEWLINE);
|
||||
|
||||
// 返回构建好的声明语法树节点
|
||||
return new DeclarationNode(name, type, init, new NodeContext(line, column, file));
|
||||
return new DeclarationNode(name, type.toString(), init, new NodeContext(line, column, file));
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ public class ExpressionStatementParser implements StatementParser {
|
||||
int column = ts.peek().getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
// 赋值语句: IDENTIFIER = expr
|
||||
// 简单形式: IDENTIFIER = expr
|
||||
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
|
||||
String varName = ts.next().getLexeme();
|
||||
ts.expect("=");
|
||||
@ -60,9 +60,23 @@ public class ExpressionStatementParser implements StatementParser {
|
||||
return new AssignmentNode(varName, value, new NodeContext(line, column, file));
|
||||
}
|
||||
|
||||
// 普通表达式语句
|
||||
ExpressionNode expr = new PrattExpressionParser().parse(ctx);
|
||||
// 尝试解析更通用的左值形式(支持下标): <expr> = <expr>
|
||||
ExpressionNode lhs = new PrattExpressionParser().parse(ctx);
|
||||
if ("=".equals(ts.peek().getLexeme())) {
|
||||
ts.next(); // consume '='
|
||||
ExpressionNode rhs = new PrattExpressionParser().parse(ctx);
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
return new ExpressionStatementNode(expr, new NodeContext(line, column, file));
|
||||
// 根据左值类型构造具体赋值节点
|
||||
if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode id) {
|
||||
return new AssignmentNode(id.name(), rhs, new NodeContext(line, column, file));
|
||||
} else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IndexExpressionNode idx) {
|
||||
return new org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode(idx, rhs, new NodeContext(line, column, file));
|
||||
} else {
|
||||
throw new UnexpectedToken("不支持的赋值左值类型: " + lhs.getClass().getSimpleName(), line, column);
|
||||
}
|
||||
}
|
||||
// 普通表达式语句
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
return new ExpressionStatementNode(lhs, new NodeContext(line, column, file));
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,68 +12,64 @@ import java.util.Map;
|
||||
/**
|
||||
* {@code AnalyzerRegistry} 是语义分析器的注册与分发中心。
|
||||
* <p>
|
||||
* 它负责根据 AST 节点的类型,查找并返回相应的 {@link StatementAnalyzer} 或 {@link ExpressionAnalyzer} 实例。
|
||||
* 同时支持注册自定义分析器,并在未找到对应表达式分析器时提供默认兜底处理器。
|
||||
* 该类维护了 AST 节点类型与相应 {@link StatementAnalyzer}、
|
||||
* {@link ExpressionAnalyzer} 实例的映射关系。调用者可以通过节点类型注册自定义的分析器,
|
||||
* 并在后续通过节点对象高效获取对应分析器,实现语义分析分发。
|
||||
* </p>
|
||||
* <p>
|
||||
* 对于表达式分析器的获取({@link #getExpressionAnalyzer(ExpressionNode)}),
|
||||
* 支持“最近父类匹配”查找机制:若找不到节点的精确类型分析器,则向上递归查找已注册的最近父类类型分析器;
|
||||
* 若依然未找到,则自动 fallback 到默认的 {@link UnsupportedExpressionAnalyzer},确保分析流程健壮性。
|
||||
* </p>
|
||||
* <p>
|
||||
* 主要职责:
|
||||
* <ul>
|
||||
* <li>支持注册语句和表达式节点类型对应的分析器;</li>
|
||||
* <li>在语义分析阶段,根据 AST 节点动态查找对应的分析器;</li>
|
||||
* <li>为未注册的表达式类型提供默认处理器 {@link UnsupportedExpressionAnalyzer};</li>
|
||||
* <li>不为语句提供默认兜底分析器,未注册类型将返回 {@code null}。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class AnalyzerRegistry {
|
||||
/** Statement 节点类型 → 对应语义分析器映射表 */
|
||||
private final Map<Class<?>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>();
|
||||
|
||||
/** Expression 节点类型 → 对应语义分析器映射表 */
|
||||
private final Map<Class<?>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>();
|
||||
|
||||
/** 默认兜底表达式分析器,用于处理未注册的表达式类型 */
|
||||
private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported =
|
||||
new UnsupportedExpressionAnalyzer<>();
|
||||
|
||||
// ========================= 注册方法 =========================
|
||||
/**
|
||||
* Statement 节点类型 → 对应语义分析器映射表
|
||||
*/
|
||||
private final Map<Class<? extends StatementNode>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 注册一个 {@link StatementAnalyzer} 实例,用于处理指定类型的语句节点。
|
||||
*
|
||||
* @param cls 要注册的语句节点类型(Class 对象)
|
||||
* @param analyzer 与该类型匹配的分析器实例
|
||||
* @param <S> {@link StatementNode} 的具体子类
|
||||
* Expression 节点类型 → 对应语义分析器映射表
|
||||
*/
|
||||
public <S extends StatementNode> void registerStatementAnalyzer(
|
||||
Class<S> cls,
|
||||
StatementAnalyzer<S> analyzer
|
||||
) {
|
||||
stmtAnalyzers.put(cls, analyzer);
|
||||
private final Map<Class<? extends ExpressionNode>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 默认表达式兜底分析器:用于未注册或不能识别的表达式类型
|
||||
*/
|
||||
private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported = new UnsupportedExpressionAnalyzer<>();
|
||||
|
||||
/**
|
||||
* 注册 Statement 类型的语义分析器。
|
||||
*
|
||||
* @param clazz AST 语句节点类型(class对象),例如 {@code IfStatementNode.class}
|
||||
* @param analyzer 针对该节点类型的语义分析器实例
|
||||
* @param <S> 语句节点类型参数,必须是 {@link StatementNode} 的子类
|
||||
*/
|
||||
public <S extends StatementNode> void registerStatementAnalyzer(Class<S> clazz, StatementAnalyzer<S> analyzer) {
|
||||
stmtAnalyzers.put(clazz, analyzer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个 {@link ExpressionAnalyzer} 实例,用于处理指定类型的表达式节点。
|
||||
* 注册 Expression 类型的语义分析器。
|
||||
*
|
||||
* @param cls 要注册的表达式节点类型(Class 对象)
|
||||
* @param analyzer 与该类型匹配的分析器实例
|
||||
* @param <E> {@link ExpressionNode} 的具体子类
|
||||
* @param clazz AST 表达式节点类型(class对象),例如 {@code BinaryExprNode.class}
|
||||
* @param analyzer 针对该节点类型的语义分析器实例
|
||||
* @param <E> 表达式节点类型参数,必须是 {@link ExpressionNode} 的子类
|
||||
*/
|
||||
public <E extends ExpressionNode> void registerExpressionAnalyzer(
|
||||
Class<E> cls,
|
||||
ExpressionAnalyzer<E> analyzer
|
||||
) {
|
||||
exprAnalyzers.put(cls, analyzer);
|
||||
public <E extends ExpressionNode> void registerExpressionAnalyzer(Class<E> clazz, ExpressionAnalyzer<E> analyzer) {
|
||||
exprAnalyzers.put(clazz, analyzer);
|
||||
}
|
||||
|
||||
// ========================= 获取方法 =========================
|
||||
|
||||
/**
|
||||
* 根据语句节点的实际类型查找对应的 {@link StatementAnalyzer}。
|
||||
* 获取指定语句节点对象的分析器实例。
|
||||
* <p>
|
||||
* 若节点类型未注册,返回 {@code null}。
|
||||
* 只支持“精确类型匹配”,即仅当注册过该节点 class 时才返回分析器,否则返回 null。
|
||||
* </p>
|
||||
*
|
||||
* @param stmt 要分析的语句节点实例
|
||||
* @param <S> 语句类型(推断自参数)
|
||||
* @return 与该节点类型对应的分析器,若未注册则为 {@code null}
|
||||
* @param stmt 语句节点对象
|
||||
* @param <S> 节点类型,需为 {@link StatementNode} 子类
|
||||
* @return 匹配的 {@link StatementAnalyzer},若未注册则返回 null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends StatementNode> StatementAnalyzer<S> getStatementAnalyzer(S stmt) {
|
||||
@ -81,17 +77,34 @@ public class AnalyzerRegistry {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据表达式节点的实际类型查找对应的 {@link ExpressionAnalyzer}。
|
||||
* 获取指定表达式节点对象的分析器实例。
|
||||
* <p>
|
||||
* 若节点类型未注册,返回默认兜底分析器 {@link UnsupportedExpressionAnalyzer}。
|
||||
* 首先尝试节点类型的精确匹配;若未注册,向上递归查找其最近的已注册父类类型分析器;
|
||||
* 若依然未命中,则返回默认的 {@link UnsupportedExpressionAnalyzer},保证分析器始终有返回值。
|
||||
* </p>
|
||||
*
|
||||
* @param expr 要分析的表达式节点实例
|
||||
* @param <E> 表达式类型(推断自参数)
|
||||
* @return 与该节点类型对应的分析器,或默认兜底分析器
|
||||
* @param expr 表达式节点对象
|
||||
* @param <E> 节点类型,需为 {@link ExpressionNode} 子类
|
||||
* @return 匹配的 {@link ExpressionAnalyzer} 实例;若未注册,则返回兜底分析器
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E extends ExpressionNode> ExpressionAnalyzer<E> getExpressionAnalyzer(E expr) {
|
||||
return (ExpressionAnalyzer<E>)
|
||||
exprAnalyzers.getOrDefault(expr.getClass(), defaultUnsupported);
|
||||
Class<?> cls = expr.getClass();
|
||||
// 精确匹配
|
||||
ExpressionAnalyzer<?> analyzer = exprAnalyzers.get(cls);
|
||||
if (analyzer != null) {
|
||||
return (ExpressionAnalyzer<E>) analyzer;
|
||||
}
|
||||
// 向上遍历父类尝试匹配
|
||||
Class<?> current = cls.getSuperclass();
|
||||
while (current != null && ExpressionNode.class.isAssignableFrom(current)) {
|
||||
analyzer = exprAnalyzers.get(current);
|
||||
if (analyzer != null) {
|
||||
return (ExpressionAnalyzer<E>) analyzer;
|
||||
}
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
// fallback
|
||||
return (ExpressionAnalyzer<E>) defaultUnsupported;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
package org.jcnc.snow.compiler.semantic.analyzers.expression;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.ArrayLiteralNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
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;
|
||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.type.ArrayType;
|
||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code ArrayLiteralAnalyzer} 用于分析数组字面量表达式(ArrayLiteralNode)。
|
||||
* <p>
|
||||
* 主要负责:
|
||||
* <ul>
|
||||
* <li>推断数组字面量的元素类型,生成对应的 {@link ArrayType}。</li>
|
||||
* <li>检查所有元素类型是否一致,不一致时报错并降级为 {@code int[]}。</li>
|
||||
* <li>若数组为空,默认类型为 {@code int[]},并产生相应语义错误。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class ArrayLiteralAnalyzer implements ExpressionAnalyzer<ArrayLiteralNode> {
|
||||
/**
|
||||
* 分析数组字面量表达式,推断类型,并检查元素类型一致性。
|
||||
*
|
||||
* @param ctx 全局/当前语义分析上下文
|
||||
* @param mi 所属模块信息
|
||||
* @param fn 当前分析的函数节点
|
||||
* @param locals 当前作用域符号表
|
||||
* @param expr 当前数组字面量节点
|
||||
* @return 推断出的数组类型,若类型冲突或无法推断,则为 {@code int[]}
|
||||
*/
|
||||
@Override
|
||||
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, ArrayLiteralNode expr) {
|
||||
List<ExpressionNode> elems = expr.elements();
|
||||
if (elems.isEmpty()) {
|
||||
ctx.getErrors().add(new SemanticError(expr, "空数组字面量的类型无法推断,已默认为 int[]"));
|
||||
return new ArrayType(BuiltinType.INT);
|
||||
}
|
||||
// 以第一个元素为基准
|
||||
Type first = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(elems.getFirst())
|
||||
.analyze(ctx, mi, fn, locals, elems.getFirst());
|
||||
|
||||
for (int i = 1; i < elems.size(); i++) {
|
||||
ExpressionNode e = elems.get(i);
|
||||
Type t = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(e)
|
||||
.analyze(ctx, mi, fn, locals, e);
|
||||
if (!t.equals(first)) {
|
||||
ctx.getErrors().add(new SemanticError(e, "数组元素类型不一致: 期望 " + first.name() + ",实际 " + t.name()));
|
||||
return new ArrayType(BuiltinType.INT);
|
||||
}
|
||||
}
|
||||
return new ArrayType(first);
|
||||
}
|
||||
}
|
||||
@ -80,35 +80,7 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
|
||||
// 查找目标函数签名(先在当前模块/显式模块查找)
|
||||
FunctionType ft = target.getFunctions().get(functionName);
|
||||
|
||||
// 如果当前模块未找到,再自动遍历所有已导入模块寻找唯一同名函数
|
||||
if (ft == null && target == mi) {
|
||||
ModuleInfo foundModule = null;
|
||||
FunctionType foundType = null;
|
||||
for (String importName : mi.getImports()) {
|
||||
ModuleInfo imported = ctx.getModules().get(importName);
|
||||
if (imported == null) continue;
|
||||
FunctionType candidate = imported.getFunctions().get(functionName);
|
||||
if (candidate != null) {
|
||||
if (foundModule != null) {
|
||||
// 多个导入模块含有同名函数,二义性,报错
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"函数调用不明确: " + functionName +
|
||||
" 同时存在于模块 " + foundModule.getName() +
|
||||
" 和 " + imported.getName()));
|
||||
ctx.log("错误: 函数调用不明确 " + functionName);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
foundModule = imported;
|
||||
foundType = candidate;
|
||||
}
|
||||
}
|
||||
if (foundType != null) {
|
||||
target = foundModule;
|
||||
ft = foundType;
|
||||
}
|
||||
}
|
||||
|
||||
// 最终未找到则报错
|
||||
// 未找到则报错
|
||||
if (ft == null) {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"函数未定义: " + functionName));
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
package org.jcnc.snow.compiler.semantic.analyzers.expression;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
|
||||
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;
|
||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.type.ArrayType;
|
||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
/**
|
||||
* {@code IndexExpressionAnalyzer} 负责下标访问表达式(如 {@code array[index]})的类型推断和检查。
|
||||
* <p>
|
||||
* 语义规则:
|
||||
* <ol>
|
||||
* <li>先分析 array 的类型,必须为 {@link ArrayType}</li>
|
||||
* <li>再分析 index 的类型,必须为数值类型</li>
|
||||
* <li>表达式结果类型为 array 的元素类型;若 array 非数组类型,报错并返回 int 类型兜底</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class IndexExpressionAnalyzer implements ExpressionAnalyzer<IndexExpressionNode> {
|
||||
|
||||
/**
|
||||
* 分析并类型检查下标访问表达式。
|
||||
*
|
||||
* @param ctx 当前语义分析上下文
|
||||
* @param mi 当前模块信息
|
||||
* @param fn 当前函数节点
|
||||
* @param locals 当前作用域符号表
|
||||
* @param node 要分析的下标访问表达式节点
|
||||
* @return 若 array 合法,则返回元素类型;否则兜底为 int 类型
|
||||
*/
|
||||
@Override
|
||||
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IndexExpressionNode node) {
|
||||
// 先分析被下标访问的数组表达式
|
||||
Type arrType = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(node.array())
|
||||
.analyze(ctx, mi, fn, locals, node.array());
|
||||
|
||||
if (arrType instanceof ArrayType(Type elementType)) {
|
||||
// 再分析下标表达式
|
||||
Type idxType = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(node.index())
|
||||
.analyze(ctx, mi, fn, locals, node.index());
|
||||
|
||||
if (!idxType.isNumeric()) {
|
||||
ctx.getErrors().add(new SemanticError(node, "数组下标必须是数值类型"));
|
||||
}
|
||||
// array[index] 的类型是数组元素类型
|
||||
return elementType;
|
||||
}
|
||||
|
||||
ctx.getErrors().add(new SemanticError(node, "仅数组类型支持下标访问"));
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package org.jcnc.snow.compiler.semantic.analyzers.statement;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
|
||||
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
|
||||
import org.jcnc.snow.compiler.semantic.core.Context;
|
||||
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
|
||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.type.ArrayType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
/**
|
||||
* {@code IndexAssignmentAnalyzer} 用于分析和类型检查数组下标赋值语句。
|
||||
* <p>
|
||||
* 主要职责:
|
||||
* <ul>
|
||||
* <li>检查左侧是否为数组类型</li>
|
||||
* <li>检查下标是否为数值类型</li>
|
||||
* <li>检查右值(被赋值表达式)类型是否与数组元素类型兼容</li>
|
||||
* <li>如有类型不符,将语义错误写入 {@link Context#getErrors()}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class IndexAssignmentAnalyzer implements StatementAnalyzer<IndexAssignmentNode> {
|
||||
|
||||
/**
|
||||
* 分析并类型检查数组下标赋值语句,如 {@code arr[i] = v}。
|
||||
* <ul>
|
||||
* <li>左侧必须是数组类型</li>
|
||||
* <li>下标必须为数值类型</li>
|
||||
* <li>右侧赋值类型需与数组元素类型一致</li>
|
||||
* <li>任何不合法情况均会产生 {@link SemanticError}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ctx 当前语义分析上下文
|
||||
* @param mi 当前模块信息
|
||||
* @param fn 所属函数节点
|
||||
* @param locals 局部符号表
|
||||
* @param node 赋值语句 AST 节点
|
||||
*/
|
||||
@Override
|
||||
public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IndexAssignmentNode node) {
|
||||
IndexExpressionNode target = node.target();
|
||||
|
||||
// 分析左侧类型(必须为数组类型)
|
||||
Type arrT = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(target.array())
|
||||
.analyze(ctx, mi, fn, locals, target.array());
|
||||
if (!(arrT instanceof ArrayType(Type elementType))) {
|
||||
ctx.getErrors().add(new SemanticError(node, "左侧不是数组,无法进行下标赋值"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 分析下标类型(必须为数值类型)
|
||||
Type idxT = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(target.index())
|
||||
.analyze(ctx, mi, fn, locals, target.index());
|
||||
if (!idxT.isNumeric()) {
|
||||
ctx.getErrors().add(new SemanticError(node, "数组下标必须是数值类型"));
|
||||
}
|
||||
|
||||
// 分析右值类型(必须与元素类型一致)
|
||||
Type rhsT = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(node.value())
|
||||
.analyze(ctx, mi, fn, locals, node.value());
|
||||
if (!rhsT.equals(elementType)) {
|
||||
ctx.getErrors().add(new SemanticError(node,
|
||||
"数组元素赋值类型不匹配: 期望 " + elementType.name() + ",实际 " + rhsT.name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,6 +57,11 @@ public final class AnalyzerRegistrar {
|
||||
registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer());
|
||||
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
|
||||
|
||||
registry.registerExpressionAnalyzer(ArrayLiteralNode.class, new ArrayLiteralAnalyzer());
|
||||
registry.registerExpressionAnalyzer(IndexExpressionNode.class,new IndexExpressionAnalyzer()); // ★ 关键行
|
||||
registry.registerStatementAnalyzer(IndexAssignmentNode.class, new IndexAssignmentAnalyzer());
|
||||
|
||||
|
||||
// ---------- 注册一元表达式分析器 ----------
|
||||
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());
|
||||
|
||||
|
||||
@ -2,33 +2,45 @@ package org.jcnc.snow.compiler.semantic.core;
|
||||
|
||||
import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry;
|
||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||
import org.jcnc.snow.compiler.semantic.type.ArrayType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@code Context} 表示语义分析阶段的共享上下文环境。
|
||||
* {@code Context} 表示语义分析阶段的全局上下文环境。
|
||||
* <p>
|
||||
* 它贯穿整个语义分析流程,用于维护并提供以下核心服务:
|
||||
* 该类贯穿整个语义分析流程,集中管理以下内容:
|
||||
* <ul>
|
||||
* <li>模块信息管理: 包含所有已加载模块(源模块与内置模块);</li>
|
||||
* <li>错误收集: 集中存储语义分析期间产生的 {@link SemanticError};</li>
|
||||
* <li>日志控制: 支持按需输出详细调试日志;</li>
|
||||
* <li>分析器调度: 通过 {@link AnalyzerRegistry} 管理语句/表达式的分析器分发。</li>
|
||||
* <li>模块信息管理(所有已加载模块,包括源模块和内置模块);</li>
|
||||
* <li>错误收集(集中存储语义分析期间产生的 {@link SemanticError});</li>
|
||||
* <li>日志输出控制(可选,支持调试信息);</li>
|
||||
* <li>分析器调度(通过 {@link AnalyzerRegistry} 分发对应分析器);</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 提供便捷的 getter 方法和类型解析工具方法。
|
||||
* </p>
|
||||
*/
|
||||
public class Context {
|
||||
/** 模块表: 模块名 → {@link ModuleInfo},用于模块查找与跨模块引用 */
|
||||
/**
|
||||
* 模块表:模块名 → {@link ModuleInfo},用于模块查找与跨模块引用。
|
||||
*/
|
||||
private final Map<String, ModuleInfo> modules;
|
||||
|
||||
/** 错误列表: 语义分析过程中收集的所有 {@link SemanticError} */
|
||||
/**
|
||||
* 错误列表:语义分析过程中收集的所有 {@link SemanticError}。
|
||||
*/
|
||||
private final List<SemanticError> errors;
|
||||
|
||||
/** 日志开关: 若为 true,将启用 {@link #log(String)} 输出日志信息 */
|
||||
/**
|
||||
* 日志开关:若为 true,将启用 {@link #log(String)} 输出日志信息。
|
||||
*/
|
||||
private final boolean verbose;
|
||||
|
||||
/** 语义分析器注册表: 用于按节点类型动态调度分析器 */
|
||||
/**
|
||||
* 语义分析器注册表:用于按节点类型动态调度分析器。
|
||||
*/
|
||||
private final AnalyzerRegistry registry;
|
||||
|
||||
/**
|
||||
@ -54,13 +66,17 @@ public class Context {
|
||||
/**
|
||||
* 获取所有模块信息映射表。
|
||||
*
|
||||
* @return 模块名 → 模块信息 {@link ModuleInfo} 的映射
|
||||
* @return 模块名到模块信息({@link ModuleInfo})的映射表
|
||||
*/
|
||||
public Map<String, ModuleInfo> getModules() {
|
||||
return modules;
|
||||
}
|
||||
|
||||
/** @return 模块信息(快捷方式) */
|
||||
/**
|
||||
* 模块信息 getter(快捷方式)。
|
||||
*
|
||||
* @return 模块名到模块信息({@link ModuleInfo})的映射表
|
||||
*/
|
||||
public Map<String, ModuleInfo> modules() {
|
||||
return modules;
|
||||
}
|
||||
@ -76,7 +92,11 @@ public class Context {
|
||||
return errors;
|
||||
}
|
||||
|
||||
/** @return 错误列表(快捷方式) */
|
||||
/**
|
||||
* 错误列表 getter(快捷方式)。
|
||||
*
|
||||
* @return 错误列表
|
||||
*/
|
||||
public List<SemanticError> errors() {
|
||||
return errors;
|
||||
}
|
||||
@ -92,7 +112,11 @@ public class Context {
|
||||
return registry;
|
||||
}
|
||||
|
||||
/** @return 注册表(快捷方式) */
|
||||
/**
|
||||
* 注册表 getter(快捷方式)。
|
||||
*
|
||||
* @return {@link AnalyzerRegistry} 实例
|
||||
*/
|
||||
public AnalyzerRegistry registry() {
|
||||
return registry;
|
||||
}
|
||||
@ -113,15 +137,26 @@ public class Context {
|
||||
// ------------------ 工具函数 ------------------
|
||||
|
||||
/**
|
||||
* 将类型名称字符串解析为对应的内置 {@link Type} 实例。
|
||||
* 将类型名称字符串解析为对应的类型实例(支持多维数组后缀)。
|
||||
* <p>
|
||||
* 若类型在 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在,则返回对应类型;
|
||||
* 否则返回 {@code null},调用方可据此决定是否降级处理。
|
||||
* 例如,"int" → int 类型,"int[][]" → 二维整型数组类型。
|
||||
* </p>
|
||||
*
|
||||
* @param name 类型名称(如 "int", "float", "void", "string" 等)
|
||||
* @return 匹配的 {@link Type},若无匹配项则返回 {@code null}
|
||||
* @param name 类型名称(支持 "[]" 数组后缀)
|
||||
* @return 对应的 {@link Type} 实例,若无法识别返回 null
|
||||
*/
|
||||
public Type parseType(String name) {
|
||||
return BuiltinTypeRegistry.BUILTIN_TYPES.get(name);
|
||||
int dims = 0;
|
||||
while (name.endsWith("[]")) {
|
||||
name = name.substring(0, name.length() - 2);
|
||||
dims++;
|
||||
}
|
||||
Type base = BuiltinTypeRegistry.BUILTIN_TYPES.get(name);
|
||||
if (base == null) return null;
|
||||
Type t = base;
|
||||
for (int i = 0; i < dims; i++) {
|
||||
t = new ArrayType(t);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +59,13 @@ public record FunctionChecker(Context ctx) {
|
||||
SymbolTable globalScope = new SymbolTable(null);
|
||||
for (DeclarationNode g : mod.globals()) {
|
||||
var t = ctx.parseType(g.getType());
|
||||
globalScope.define(new Symbol(g.getName(), t, SymbolKind.VARIABLE));
|
||||
// 检查全局变量是否重复声明
|
||||
if (!globalScope.define(new Symbol(g.getName(), t, SymbolKind.VARIABLE))) {
|
||||
ctx.errors().add(new SemanticError(
|
||||
g,
|
||||
"全局变量重复声明: " + g.getName()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历模块中所有函数定义
|
||||
|
||||
@ -48,9 +48,9 @@ public record SemanticError(Node node, String message) {
|
||||
}
|
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (file != null && !file.isBlank()) sb.append(file).append(": ");
|
||||
sb.append((line >= 0 && col >= 0) ? "行 " + line + ", 列 " + col : "未知位置");
|
||||
StringBuilder sb = new StringBuilder("file:///");
|
||||
if (file != null && !file.isBlank()) sb.append(file).append(":");
|
||||
sb.append((line >= 0 && col >= 0) ? line + ":" + col : "未知位置");
|
||||
sb.append(": ").append(message);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
package org.jcnc.snow.compiler.semantic.type;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@code ArrayType} 表示数组类型,每个数组类型包含其元素类型。
|
||||
* <p>
|
||||
* 例如,int[]、string[] 等均可用本类表示。内部通过 {@link #elementType()} 字段保存元素类型。
|
||||
* </p>
|
||||
*/
|
||||
public record ArrayType(
|
||||
/*
|
||||
数组元素的类型。
|
||||
*/
|
||||
Type elementType
|
||||
) implements Type {
|
||||
|
||||
/**
|
||||
* 判断当前数组类型能否与另一类型兼容(主要用于类型检查)。
|
||||
* <p>
|
||||
* 只有当 {@code other} 也是 ArrayType,且元素类型兼容时返回 true。
|
||||
* </p>
|
||||
*
|
||||
* @param other 需判断的类型
|
||||
* @return 类型兼容性结果
|
||||
*/
|
||||
@Override
|
||||
public boolean isCompatible(Type other) {
|
||||
if (!(other instanceof ArrayType(Type type))) return false;
|
||||
return elementType.isCompatible(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组类型不是数值类型,直接调用父接口默认实现。
|
||||
*
|
||||
* @return 总为 false
|
||||
*/
|
||||
@Override
|
||||
public boolean isNumeric() {
|
||||
return Type.super.isNumeric();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个 ArrayType 是否等价(元素类型完全一致即视为等价)。
|
||||
*
|
||||
* @param o 比较对象
|
||||
* @return 是否等价
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof ArrayType(Type type) && Objects.equals(elementType, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组类型的字符串描述,如 "int[]"。
|
||||
*
|
||||
* @return 类型名称
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return name();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数组类型的名称描述,如 "int[]"。
|
||||
*
|
||||
* @return 类型名称
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return elementType.name() + "[]";
|
||||
}
|
||||
}
|
||||
@ -47,4 +47,6 @@ public interface Type {
|
||||
int ia = order.indexOf(a), ib = order.indexOf(b);
|
||||
return order.get(Math.max(ia, ib));
|
||||
}
|
||||
/** 类型名字符串(如 int、double[]) */
|
||||
default String name() { return toString(); }
|
||||
}
|
||||
|
||||
@ -31,7 +31,9 @@ import java.util.List;
|
||||
*/
|
||||
public final class GenerateTask implements Task {
|
||||
|
||||
/** 项目信息元数据 */
|
||||
/**
|
||||
* 项目信息元数据
|
||||
*/
|
||||
private final Project project;
|
||||
|
||||
/**
|
||||
@ -90,6 +92,13 @@ public final class GenerateTask implements Task {
|
||||
System.out.println("[generate] created " + root.relativize(mainSnow));
|
||||
}
|
||||
|
||||
/* ---------- 5. 写入系统库文件 os.snow ---------- */
|
||||
Path osSnow = packageDir.resolve("OS.snow");
|
||||
if (Files.notExists(osSnow)) {
|
||||
Files.writeString(osSnow, SnowExample.getOsModule());
|
||||
System.out.println("[generate] created " + root.relativize(osSnow));
|
||||
}
|
||||
|
||||
System.out.println("[generate] project scaffold is ready.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package org.jcnc.snow.pkg.utils;
|
||||
|
||||
/**
|
||||
* 示例模块模板工具类,提供 main.snow 的标准示例代码字符串。
|
||||
* 示例模块模板工具类,提供标准的示例 Snow 代码片段。
|
||||
* <p>
|
||||
* 用于项目脚手架生成或帮助用户快速上手 .snow 语言。
|
||||
* </p>
|
||||
@ -23,19 +23,18 @@ public final class SnowExample {
|
||||
public static String getMainModule() {
|
||||
return """
|
||||
module: Math
|
||||
import: os
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
returns: void
|
||||
body:
|
||||
Math.factorial(6)
|
||||
return 0
|
||||
os.print(Math.factorial(6))
|
||||
end body
|
||||
end function
|
||||
|
||||
function: factorial
|
||||
parameter:
|
||||
params:
|
||||
declare n: int
|
||||
return_type: int
|
||||
returns: int
|
||||
body:
|
||||
declare num1: int = 1
|
||||
loop:
|
||||
@ -55,4 +54,25 @@ public final class SnowExample {
|
||||
end module
|
||||
""";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统库 os.snow 模块的内容字符串。
|
||||
*
|
||||
* @return os.snow 模块的完整代码
|
||||
*/
|
||||
public static String getOsModule() {
|
||||
return """
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT", i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
""";
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,59 +5,287 @@ import org.jcnc.snow.vm.module.CallStack;
|
||||
import org.jcnc.snow.vm.module.LocalVariableStore;
|
||||
import org.jcnc.snow.vm.module.OperandStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@code RPushCommand} class implements the {@link Command} interface and represents the
|
||||
* reference push instruction ({@code R_PUSH}) in the virtual machine.
|
||||
* The {@code RPushCommand} class implements the {@link Command} interface
|
||||
* and represents the "reference push" instruction ({@code R_PUSH}) in the virtual machine.
|
||||
*
|
||||
* <p>
|
||||
* This instruction pushes a reference object, such as a String literal, onto the operand stack.
|
||||
* This instruction pushes a reference-type value onto the operand stack.
|
||||
* The input is parsed from the textual instruction form, which can represent:
|
||||
* <ul>
|
||||
* <li>String literals</li>
|
||||
* <li>Array literals (e.g., {@code [1, 2, 3]}), including nested arrays</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>Instruction format: {@code R_PUSH <literal>}</p>
|
||||
* <ul>
|
||||
* <li>{@code <literal>}: The reference value (e.g., string) to be pushed onto the stack.
|
||||
* If the literal contains spaces, all parts after {@code R_PUSH} are joined into a single string.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Behavior:</p>
|
||||
* <ul>
|
||||
* <li>Checks that the instruction has at least one parameter after the operator.</li>
|
||||
* <li>Concatenates all parameters after {@code R_PUSH} into a single string (separated by spaces).</li>
|
||||
* <li>Pushes the resulting string as a reference object onto the operand stack.</li>
|
||||
* <li>Increments the program counter to the next instruction.</li>
|
||||
* <li>Throws an {@code IllegalStateException} if the instruction is missing required parameters.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* For array literals, a nested list structure is constructed. In this implementation,
|
||||
* array literals are pushed as <b>mutable</b> {@link java.util.ArrayList} structures,
|
||||
* so that subsequent system calls such as {@code ARR_SET} can modify elements in-place.
|
||||
* </p>
|
||||
*/
|
||||
public final class RPushCommand implements Command {
|
||||
public class RPushCommand implements Command {
|
||||
|
||||
/**
|
||||
* Executes the {@code R_PUSH} instruction, pushing a reference (such as a string literal)
|
||||
* onto the operand stack.
|
||||
* Executes the R_PUSH command.
|
||||
*
|
||||
* @param parts The instruction parameters. {@code parts[0]} is the operator ("R_PUSH"),
|
||||
* {@code parts[1..]} are the parts of the literal to be concatenated and pushed.
|
||||
* @param pc The current program counter value, indicating the instruction address being executed.
|
||||
* @param stack The operand stack manager. The literal will be pushed onto this stack.
|
||||
* @param lvs The local variable store. (Not used in this instruction.)
|
||||
* @param cs The call stack manager. (Not used in this instruction.)
|
||||
* @return The next program counter value ({@code pc + 1}), pointing to the next instruction.
|
||||
* @throws IllegalStateException if the instruction is missing required parameters.
|
||||
* @param parts The parts of the instruction, where {@code parts[1..n]} are concatenated as the literal.
|
||||
* @param pc The current program counter.
|
||||
* @param stack The operand stack where the result will be pushed.
|
||||
* @param local The local variable store (unused in this instruction).
|
||||
* @param callStack The call stack (unused in this instruction).
|
||||
* @return The new program counter (typically {@code pc+1}).
|
||||
* @throws IllegalStateException if no literal parameter is provided.
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] parts, int pc,
|
||||
OperandStack stack,
|
||||
LocalVariableStore lvs,
|
||||
CallStack cs) {
|
||||
|
||||
public int execute(String[] parts, int pc, OperandStack stack, LocalVariableStore local, CallStack callStack) {
|
||||
if (parts.length < 2)
|
||||
throw new IllegalStateException("R_PUSH missing parameter");
|
||||
|
||||
// Join all arguments into a complete literal string
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
if (i > 1) sb.append(' ');
|
||||
sb.append(parts[i]);
|
||||
}
|
||||
stack.push(sb.toString());
|
||||
String literal = sb.toString().trim();
|
||||
|
||||
// Check if this is an array literal
|
||||
if (literal.startsWith("[") && literal.endsWith("]")) {
|
||||
Object parsed = parseValue(new Cursor(literal));
|
||||
if (!(parsed instanceof List<?> list)) {
|
||||
// Should not happen in theory; safety fallback
|
||||
stack.push(parsed);
|
||||
} else {
|
||||
// Push a deep-mutable copy so ARR_SET can modify elements in-place
|
||||
stack.push(deepMutable(list));
|
||||
}
|
||||
} else {
|
||||
// Regular string, push as-is
|
||||
stack.push(literal);
|
||||
}
|
||||
return pc + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple string cursor, supporting index increment and character reading, for use by the parser.
|
||||
*/
|
||||
static class Cursor {
|
||||
final String s;
|
||||
int i;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code Cursor} for the given string.
|
||||
*
|
||||
* @param s The string to parse.
|
||||
*/
|
||||
Cursor(String s) {
|
||||
this.s = s;
|
||||
this.i = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the cursor by one character.
|
||||
*/
|
||||
void skip() {
|
||||
i++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if the cursor has reached the end of the string.
|
||||
*/
|
||||
boolean end() {
|
||||
return i >= s.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character at the current cursor position.
|
||||
*
|
||||
* @return current character
|
||||
* @throws StringIndexOutOfBoundsException if at end of string
|
||||
*/
|
||||
char ch() {
|
||||
return s.charAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a value from the input string at the current cursor position.
|
||||
* This can be an array literal, a quoted string, or a simple atom (number, word).
|
||||
*
|
||||
* @param c The cursor for parsing.
|
||||
* @return The parsed value (could be List, String, Number).
|
||||
*/
|
||||
Object parseValue(Cursor c) {
|
||||
skipWs(c);
|
||||
if (c.end()) return "";
|
||||
char ch = c.ch();
|
||||
if (ch == '[') return parseArray(c);
|
||||
if (ch == '\"') return parseQuoted(c);
|
||||
return parseAtom(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips whitespace characters in the input string.
|
||||
*
|
||||
* @param c The cursor to advance.
|
||||
*/
|
||||
private static void skipWs(Cursor c) {
|
||||
while (!c.end()) {
|
||||
char ch = c.ch();
|
||||
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') c.skip();
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array literal from the input, including nested arrays.
|
||||
*
|
||||
* @param c The cursor (positioned at '[' at entry).
|
||||
* @return A List representing the parsed array.
|
||||
*/
|
||||
private Object parseArray(Cursor c) {
|
||||
// assumes current char is '['
|
||||
c.skip(); // skip '['
|
||||
List<Object> out = new ArrayList<>();
|
||||
skipWs(c);
|
||||
while (!c.end()) {
|
||||
if (c.ch() == ']') {
|
||||
c.skip(); // skip ']'
|
||||
break;
|
||||
}
|
||||
Object v = parseValue(c);
|
||||
out.add(v);
|
||||
skipWs(c);
|
||||
if (!c.end() && c.ch() == ',') {
|
||||
c.skip();
|
||||
skipWs(c);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a quoted string literal, handling escape characters.
|
||||
*
|
||||
* @param c The cursor (positioned at '"' at entry).
|
||||
* @return The parsed string value.
|
||||
*/
|
||||
private static String parseQuoted(Cursor c) {
|
||||
// assumes current char is '"'
|
||||
c.skip(); // skip opening quote
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (!c.end()) {
|
||||
char ch = c.ch();
|
||||
c.skip();
|
||||
if (ch == '\\') {
|
||||
if (c.end()) break;
|
||||
char esc = c.ch();
|
||||
c.skip();
|
||||
switch (esc) {
|
||||
case 'n' -> sb.append('\n');
|
||||
case 'r' -> sb.append('\r');
|
||||
case 't' -> sb.append('\t');
|
||||
case '\"' -> sb.append('\"');
|
||||
case '\\' -> sb.append('\\');
|
||||
default -> sb.append(esc);
|
||||
}
|
||||
} else if (ch == '\"') {
|
||||
break;
|
||||
} else {
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an atom (number, hexadecimal, binary, or plain string token).
|
||||
*
|
||||
* @param c The cursor.
|
||||
* @return An Integer, Double, or String, depending on the content.
|
||||
*/
|
||||
private static Object parseAtom(Cursor c) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (!c.end()) {
|
||||
char ch = c.ch();
|
||||
if (ch == ',' || ch == ']' || ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') break;
|
||||
sb.append(ch);
|
||||
c.skip();
|
||||
}
|
||||
String token = sb.toString();
|
||||
// try number
|
||||
try {
|
||||
if (token.startsWith("0x") || token.startsWith("0X")) {
|
||||
return Integer.parseInt(token.substring(2), 16);
|
||||
}
|
||||
if (token.startsWith("0b") || token.startsWith("0B")) {
|
||||
return Integer.parseInt(token.substring(2), 2);
|
||||
}
|
||||
if (token.contains(".")) {
|
||||
return Double.parseDouble(token);
|
||||
}
|
||||
return Integer.parseInt(token);
|
||||
} catch (NumberFormatException e) {
|
||||
// fallback as string
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- helpers for immutability/mutability ----------------------
|
||||
|
||||
/**
|
||||
* Recursively creates an unmodifiable copy of a list, with all nested lists also unmodifiable.
|
||||
*
|
||||
* @param l The list to make unmodifiable.
|
||||
* @return An unmodifiable deep copy of the list.
|
||||
*/
|
||||
List<?> deepUnmodifiable(List<?> l) {
|
||||
List<Object> out = new ArrayList<>(l.size());
|
||||
for (Object v : l) out.add(deepUnmodifiableObject(v));
|
||||
return Collections.unmodifiableList(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for {@link #deepUnmodifiable(List)}. Recursively processes each element.
|
||||
*
|
||||
* @param v The object to process.
|
||||
* @return Unmodifiable list if input is a list, otherwise the value itself.
|
||||
*/
|
||||
Object deepUnmodifiableObject(Object v) {
|
||||
if (v instanceof List<?> l) {
|
||||
return deepUnmodifiable(l);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep mutable copy of a nested List structure, preserving element values.
|
||||
* Nested lists are turned into {@link java.util.ArrayList} so they can be modified by ARR_SET.
|
||||
*
|
||||
* @param l The source list.
|
||||
* @return Deep mutable copy of the list.
|
||||
*/
|
||||
private static java.util.List<?> deepMutable(java.util.List<?> l) {
|
||||
java.util.List<Object> out = new java.util.ArrayList<>(l.size());
|
||||
for (Object v : l) out.add(deepMutableObject(v));
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for {@link #deepMutable(List)}. Recursively processes each element.
|
||||
*
|
||||
* @param v The object to process.
|
||||
* @return Mutable list if input is a list, otherwise the value itself.
|
||||
*/
|
||||
private static Object deepMutableObject(Object v) {
|
||||
if (v instanceof java.util.List<?> l) {
|
||||
return deepMutable(l);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +46,97 @@ import static java.nio.file.StandardOpenOption.*;
|
||||
*/
|
||||
public class SyscallCommand implements Command {
|
||||
|
||||
/**
|
||||
* 根据传入的文件打开标志,构造 NIO {@link OpenOption} 集合。
|
||||
* <p>
|
||||
* 本方法负责将底层虚拟机传递的 flags 整数型位域,转换为 Java NIO 标准的文件打开选项集合,
|
||||
* 以支持文件读、写、创建、截断、追加等多种访问场景。
|
||||
* 常用于 SYSCALL 的 OPEN 子命令。
|
||||
* </p>
|
||||
*
|
||||
* @param flags 文件打开模式标志。各标志可组合使用,具体含义请参见虚拟机文档。
|
||||
* @return 转换后的 OpenOption 集合,可直接用于 FileChannel.open 等 NIO 方法
|
||||
*/
|
||||
private static Set<OpenOption> flagsToOptions(int flags) {
|
||||
Set<OpenOption> opts = new HashSet<>();
|
||||
// 如果有写入标志,则添加WRITE,否则默认为READ。
|
||||
if ((flags & 0x1) != 0) opts.add(WRITE);
|
||||
else opts.add(READ);
|
||||
// 如果包含创建标志,允许创建文件。
|
||||
if ((flags & 0x40) != 0) opts.add(CREATE);
|
||||
// 包含截断标志,打开时清空内容。
|
||||
if ((flags & 0x200) != 0) opts.add(TRUNCATE_EXISTING);
|
||||
// 包含追加标志,文件写入时追加到末尾。
|
||||
if ((flags & 0x400) != 0) opts.add(APPEND);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获所有异常并统一处理,操作数栈压入 -1 代表本次系统调用失败。
|
||||
* <p>
|
||||
* 本方法是全局错误屏障,任何命令异常都会转换为虚拟机通用的失败信号,
|
||||
* 保证上层调用逻辑不会被异常打断。实际应用中可拓展错误码机制。
|
||||
* </p>
|
||||
*
|
||||
* @param stack 操作数栈,将失败信号写入此栈
|
||||
* @param e 抛出的异常对象,可在调试时输出日志
|
||||
*/
|
||||
private static void pushErr(OperandStack stack, Exception e) {
|
||||
stack.push(-1);
|
||||
System.err.println("Syscall exception: " + e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制台输出通用方法,支持基本类型、字节数组、任意数组、对象等。
|
||||
* <p>
|
||||
* 该方法用于 SYSCALL PRINT/PRINTLN,将任意类型对象转为易读字符串输出到标准输出流。
|
||||
* 字节数组自动按 UTF-8 解码,其它原生数组按格式化字符串输出。
|
||||
* </p>
|
||||
*
|
||||
* @param obj 待输出的内容,可以为任何类型(如基本类型、byte[]、数组、对象等)
|
||||
* @param newline 是否自动换行。如果为 true,则在输出后换行;否则直接输出。
|
||||
*/
|
||||
private static void output(Object obj, boolean newline) {
|
||||
String str;
|
||||
if (obj == null) {
|
||||
str = "null";
|
||||
} else if (obj instanceof byte[] bytes) {
|
||||
// 字节数组作为文本输出
|
||||
str = new String(bytes);
|
||||
} else if (obj.getClass().isArray()) {
|
||||
// 其它数组格式化输出
|
||||
str = arrayToString(obj);
|
||||
} else {
|
||||
str = obj.toString();
|
||||
}
|
||||
if (newline) System.out.println(str);
|
||||
else System.out.print(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将各种原生数组和对象数组转换为可读字符串,便于控制台输出和调试。
|
||||
* <p>
|
||||
* 本方法针对 int、long、double、float、short、char、byte、boolean 等所有原生数组类型
|
||||
* 以及对象数组都能正确格式化,统一输出格式风格,避免显示为类型 hashCode。
|
||||
* 若为不支持的类型,返回通用提示字符串。
|
||||
* </p>
|
||||
*
|
||||
* @param array 任意原生数组或对象数组
|
||||
* @return 该数组的可读字符串表示
|
||||
*/
|
||||
private static String arrayToString(Object array) {
|
||||
if (array instanceof int[] a) return Arrays.toString(a);
|
||||
if (array instanceof long[] a) return Arrays.toString(a);
|
||||
if (array instanceof double[] a) return Arrays.toString(a);
|
||||
if (array instanceof float[] a) return Arrays.toString(a);
|
||||
if (array instanceof short[] a) return Arrays.toString(a);
|
||||
if (array instanceof char[] a) return Arrays.toString(a);
|
||||
if (array instanceof byte[] a) return Arrays.toString(a);
|
||||
if (array instanceof boolean[] a) return Arrays.toString(a);
|
||||
if (array instanceof Object[] a) return Arrays.deepToString(a);
|
||||
return "Unsupported array";
|
||||
}
|
||||
|
||||
/**
|
||||
* 分发并执行 SYSCALL 子命令,根据子命令类型从操作数栈取出参数、操作底层资源,并将结果压回栈顶。
|
||||
*
|
||||
@ -226,6 +317,105 @@ public class SyscallCommand implements Command {
|
||||
sel.close();
|
||||
}
|
||||
|
||||
// 数组元素访问:arr[idx] —— 保留所有类型精度(byte/short/int/long/float/double/boolean/string/ref)
|
||||
case "ARR_GET" -> {
|
||||
/*
|
||||
执行数组下标访问操作 arr[idx],并将对应元素以真实类型压入操作数栈。
|
||||
<ul>
|
||||
<li>支持 List 与任意原生数组类型(int[]、double[] 等);</li>
|
||||
<li>idx 参数支持 Number/String 类型,自动转 int;</li>
|
||||
<li>下标越界将抛出异常,非数组类型将报错;</li>
|
||||
<li>返回结果保持类型精度:byte/short/int/long/float/double/boolean/string/object;</li>
|
||||
<li>boolean 元素以 1/0 压栈,string/引用直接压栈;</li>
|
||||
</ul>
|
||||
|
||||
异常与出错行为:
|
||||
<ul>
|
||||
<li>索引类型非法、目标非数组/列表,将抛 IllegalArgumentException;</li>
|
||||
<li>索引越界,将抛 IndexOutOfBoundsException;</li>
|
||||
</ul>
|
||||
*/
|
||||
Object idxObj = stack.pop();
|
||||
Object arrObj = stack.pop();
|
||||
int idx;
|
||||
if (idxObj instanceof Number n) idx = n.intValue();
|
||||
else if (idxObj instanceof String s) idx = Integer.parseInt(s.trim());
|
||||
else throw new IllegalArgumentException("ARR_GET: invalid index type " + idxObj);
|
||||
|
||||
Object elem;
|
||||
if (arrObj instanceof java.util.List<?> list) {
|
||||
if (idx < 0 || idx >= list.size())
|
||||
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + list.size() + ")");
|
||||
elem = list.get(idx);
|
||||
} else if (arrObj != null && arrObj.getClass().isArray()) {
|
||||
int len = java.lang.reflect.Array.getLength(arrObj);
|
||||
if (idx < 0 || idx >= len)
|
||||
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + len + ")");
|
||||
elem = java.lang.reflect.Array.get(arrObj, idx);
|
||||
} else {
|
||||
throw new IllegalArgumentException("ARR_GET: not an array/list: " + arrObj);
|
||||
}
|
||||
|
||||
// === 按真实类型压栈(byte/short/int/long/float/double/boolean/string/ref)===
|
||||
if (elem instanceof Number n) {
|
||||
if (elem instanceof Double) {
|
||||
stack.push(n.doubleValue());
|
||||
} else if (elem instanceof Float) {
|
||||
stack.push(n.floatValue());
|
||||
} else if (elem instanceof Long) {
|
||||
stack.push(n.longValue());
|
||||
} else if (elem instanceof Integer) {
|
||||
stack.push(n.intValue());
|
||||
} else if (elem instanceof Short) {
|
||||
stack.push(n.shortValue());
|
||||
} else if (elem instanceof Byte) {
|
||||
stack.push(n.byteValue());
|
||||
} else {
|
||||
stack.push(n.intValue()); // 兜底
|
||||
}
|
||||
} else if (elem instanceof Boolean b) {
|
||||
stack.push(b ? 1 : 0);
|
||||
} else {
|
||||
// string 或其它引用类型,直接返回
|
||||
stack.push(elem);
|
||||
}
|
||||
}
|
||||
|
||||
case "ARR_SET" -> {
|
||||
/*
|
||||
arr[idx] = value
|
||||
支持 List 和所有原生数组类型(int[], double[], ...)
|
||||
参数顺序:栈顶 value、idx、arr
|
||||
不返回值(成功/失败由异常控制)
|
||||
*/
|
||||
Object value = stack.pop();
|
||||
Object idxObj = stack.pop();
|
||||
Object arrObj = stack.pop();
|
||||
int idx;
|
||||
if (idxObj instanceof Number n) idx = n.intValue();
|
||||
else if (idxObj instanceof String s) idx = Integer.parseInt(s.trim());
|
||||
else throw new IllegalArgumentException("ARR_SET: invalid index type " + idxObj);
|
||||
|
||||
if (arrObj instanceof java.util.List<?> list) {
|
||||
// 必须是可变 List
|
||||
@SuppressWarnings("unchecked")
|
||||
java.util.List<Object> mlist = (java.util.List<Object>) list;
|
||||
if (idx < 0 || idx >= mlist.size())
|
||||
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + mlist.size() + ")");
|
||||
mlist.set(idx, value);
|
||||
} else if (arrObj != null && arrObj.getClass().isArray()) {
|
||||
int len = java.lang.reflect.Array.getLength(arrObj);
|
||||
if (idx < 0 || idx >= len)
|
||||
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + len + ")");
|
||||
java.lang.reflect.Array.set(arrObj, idx, value);
|
||||
} else {
|
||||
throw new IllegalArgumentException("ARR_SET: not an array/list: " + arrObj);
|
||||
}
|
||||
// 操作成功,push 0 作为 ok 信号;不需要返回时可省略
|
||||
stack.push(0);
|
||||
}
|
||||
|
||||
|
||||
// 控制台输出
|
||||
case "PRINT" -> {
|
||||
Object dataObj = stack.pop();
|
||||
@ -246,95 +436,4 @@ public class SyscallCommand implements Command {
|
||||
|
||||
return pc + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的文件打开标志,构造 NIO {@link OpenOption} 集合。
|
||||
* <p>
|
||||
* 本方法负责将底层虚拟机传递的 flags 整数型位域,转换为 Java NIO 标准的文件打开选项集合,
|
||||
* 以支持文件读、写、创建、截断、追加等多种访问场景。
|
||||
* 常用于 SYSCALL 的 OPEN 子命令。
|
||||
* </p>
|
||||
*
|
||||
* @param flags 文件打开模式标志。各标志可组合使用,具体含义请参见虚拟机文档。
|
||||
* @return 转换后的 OpenOption 集合,可直接用于 FileChannel.open 等 NIO 方法
|
||||
*/
|
||||
private static Set<OpenOption> flagsToOptions(int flags) {
|
||||
Set<OpenOption> opts = new HashSet<>();
|
||||
// 如果有写入标志,则添加WRITE,否则默认为READ。
|
||||
if ((flags & 0x1) != 0) opts.add(WRITE);
|
||||
else opts.add(READ);
|
||||
// 如果包含创建标志,允许创建文件。
|
||||
if ((flags & 0x40) != 0) opts.add(CREATE);
|
||||
// 包含截断标志,打开时清空内容。
|
||||
if ((flags & 0x200) != 0) opts.add(TRUNCATE_EXISTING);
|
||||
// 包含追加标志,文件写入时追加到末尾。
|
||||
if ((flags & 0x400) != 0) opts.add(APPEND);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获所有异常并统一处理,操作数栈压入 -1 代表本次系统调用失败。
|
||||
* <p>
|
||||
* 本方法是全局错误屏障,任何命令异常都会转换为虚拟机通用的失败信号,
|
||||
* 保证上层调用逻辑不会被异常打断。实际应用中可拓展错误码机制。
|
||||
* </p>
|
||||
*
|
||||
* @param stack 操作数栈,将失败信号写入此栈
|
||||
* @param e 抛出的异常对象,可在调试时输出日志
|
||||
*/
|
||||
private static void pushErr(OperandStack stack, Exception e) {
|
||||
stack.push(-1);
|
||||
System.err.println("Syscall exception: " + e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制台输出通用方法,支持基本类型、字节数组、任意数组、对象等。
|
||||
* <p>
|
||||
* 该方法用于 SYSCALL PRINT/PRINTLN,将任意类型对象转为易读字符串输出到标准输出流。
|
||||
* 字节数组自动按 UTF-8 解码,其它原生数组按格式化字符串输出。
|
||||
* </p>
|
||||
*
|
||||
* @param obj 待输出的内容,可以为任何类型(如基本类型、byte[]、数组、对象等)
|
||||
* @param newline 是否自动换行。如果为 true,则在输出后换行;否则直接输出。
|
||||
*/
|
||||
private static void output(Object obj, boolean newline) {
|
||||
String str;
|
||||
if (obj == null) {
|
||||
str = "null";
|
||||
} else if (obj instanceof byte[] bytes) {
|
||||
// 字节数组作为文本输出
|
||||
str = new String(bytes);
|
||||
} else if (obj.getClass().isArray()) {
|
||||
// 其它数组格式化输出
|
||||
str = arrayToString(obj);
|
||||
} else {
|
||||
str = obj.toString();
|
||||
}
|
||||
if (newline) System.out.println(str);
|
||||
else System.out.print(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将各种原生数组和对象数组转换为可读字符串,便于控制台输出和调试。
|
||||
* <p>
|
||||
* 本方法针对 int、long、double、float、short、char、byte、boolean 等所有原生数组类型
|
||||
* 以及对象数组都能正确格式化,统一输出格式风格,避免显示为类型 hashCode。
|
||||
* 若为不支持的类型,返回通用提示字符串。
|
||||
* </p>
|
||||
*
|
||||
* @param array 任意原生数组或对象数组
|
||||
* @return 该数组的可读字符串表示
|
||||
*/
|
||||
private static String arrayToString(Object array) {
|
||||
if (array instanceof int[] a) return Arrays.toString(a);
|
||||
if (array instanceof long[] a) return Arrays.toString(a);
|
||||
if (array instanceof double[] a) return Arrays.toString(a);
|
||||
if (array instanceof float[] a) return Arrays.toString(a);
|
||||
if (array instanceof short[] a) return Arrays.toString(a);
|
||||
if (array instanceof char[] a) return Arrays.toString(a);
|
||||
if (array instanceof byte[] a) return Arrays.toString(a);
|
||||
if (array instanceof boolean[] a) return Arrays.toString(a);
|
||||
if (array instanceof Object[] a) return Arrays.deepToString(a);
|
||||
return "Unsupported array";
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package org.jcnc.snow.vm.engine;
|
||||
|
||||
import org.jcnc.snow.common.Mode;
|
||||
import org.jcnc.snow.vm.execution.CommandExecutionHandler;
|
||||
import org.jcnc.snow.vm.module.*;
|
||||
|
||||
@ -51,8 +50,6 @@ public class VirtualMachineEngine {
|
||||
|
||||
/**
|
||||
* Builds a VM engine with fresh runtime structures.
|
||||
*
|
||||
* @param vmMode execution mode (DEBUG / RUN)
|
||||
*/
|
||||
public VirtualMachineEngine() {
|
||||
this.operandStack = new OperandStack();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user