!52 release: 合并 v0.6.0 版本至 main 分支

Merge pull request !52 from Luke/release/v0.6.0
This commit is contained in:
Luke 2025-07-31 09:53:38 +00:00 committed by Gitee
commit f1069d6e5d
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
70 changed files with 1304 additions and 308 deletions

View File

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

11
.run/Bug2.run.xml Normal file
View File

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

11
.run/Bug3.run.xml Normal file
View File

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

11
.run/Bug4.run.xml Normal file
View File

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

11
.run/Bug5.run.xml Normal file
View File

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

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo1 -o target/Demo1 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo1 -o target/Demo1 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo10 -o target/Demo10 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo10 -o target/Demo10 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo11 -o target/Demo11 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo11 -o target/Demo11 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo12 -o target/Demo12 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo12 -o target/Demo12 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

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

View File

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

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

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo15" 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/Demo15 -o target/Demo15 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

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

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo16" 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/Demo16 -o target/Demo16 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

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

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo17" 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/Demo17 -o target/Demo17 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

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

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo18" 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/Demo18 -o target/Demo18 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo2 -o target/Demo2 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo2 -o target/Demo2 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo3 -o target/Demo3 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo3 -o target/Demo3 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo4 -o target/Demo4 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo4 -o target/Demo4 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo5 -o target/Demo5 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo5 -o target/Demo5 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo6 -o target/Demo6 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo6 -o target/Demo6 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo7 -o target/Demo7 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo7 -o target/Demo7 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo8 -o target/Demo8 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo8 -o target/Demo8 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" /> <option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" /> <option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" /> <module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo9 -o target/Demo9 -debug" /> <option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo9 -o target/Demo9 --debug" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -5,6 +5,10 @@
<toRun name="Demo11" type="Application" /> <toRun name="Demo11" type="Application" />
<toRun name="Demo12" type="Application" /> <toRun name="Demo12" type="Application" />
<toRun name="Demo13" type="Application" /> <toRun name="Demo13" type="Application" />
<toRun name="Demo14" type="Application" />
<toRun name="Demo15" type="Application" />
<toRun name="Demo16" type="Application" />
<toRun name="Demo17" type="Application" />
<toRun name="Demo2" type="Application" /> <toRun name="Demo2" type="Application" />
<toRun name="Demo3" type="Application" /> <toRun name="Demo3" type="Application" />
<toRun name="Demo4" type="Application" /> <toRun name="Demo4" type="Application" />

View File

@ -11,8 +11,8 @@
<a href="https://gitee.com/jcnc-org/snow/blob/main/LICENSE"> <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=""> <img src="https://img.shields.io/badge/%20license-Apache--2.0%20-blue" alt="">
</a> </a>
<a href="https://gitee.com/jcnc-org/snow/tree/v0.5.0/"> <a href="https://gitee.com/jcnc-org/snow/tree/v0.6.0/">
<img src="https://img.shields.io/badge/version-v0.5.0-blue" alt=""> <img src="https://img.shields.io/badge/version-v0.6.0-blue" alt="">
</a> </a>
</p> </p>

View File

@ -0,0 +1,33 @@
module: Main
import: os
function: main
return_type: void
body:
loop:
init:
declare outer_i: int = 1
cond:
outer_i <= 10
step:
outer_i = outer_i + 1
body:
print(outer_i)
loop:
init:
// 注意这一行使用了外层循环的变量 outer_i
declare inter_j: int = outer_i
cond:
inter_j <= 10
step:
inter_j = inter_j + 1
body:
end body
end loop
end body
end loop
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
parameter:
declare i1: int
return_type: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,14 @@
module: Main
import: os
function: main
return_type: void
body:
// 合法
declare b1: byte = 127b
declare s1: short = 32767s
declare i1: int = 2147483647
declare l1: long = 9223372036854775807L
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
parameter:
declare i1: int
return_type: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,9 @@
module: Main
import: ModuleB
function: main
return_type: int
body:
return ModuleB.fun()
end body
end function
end module

View File

@ -0,0 +1,8 @@
module: ModuleA
function: fun
return_type: int
body:
return 123
end body
end function
end module

View File

@ -0,0 +1,9 @@
module: ModuleB
import: ModuleA
function: fun
return_type: int
body:
return ModuleA.fun() + ModuleA.fun()
end body
end function
end module

View File

@ -0,0 +1,16 @@
module: Main
import: os
globals:
declare sum: int = 0
function: main
parameter:
return_type: int
body:
sum = 20
os.print(sum)
return 0
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
parameter:
declare i1: int
return_type: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -3,7 +3,7 @@ module: Main
function: main function: main
return_type: void return_type: void
body: body:
print(222) os.print(222)
end body end body
end function end function
end module end module

View File

@ -0,0 +1,20 @@
module: Main
import: os
globals:
declare num2:int=10
function: main
return_type: void
body:
declare num1:int=11
os.print(num1+num2+abc())
end body
end function
function: abc
return_type: int
body:
return 1
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
parameter:
declare i1: int
return_type: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,22 @@
module: Main
import: os
function: main
return_type: int
body:
loop:
init:
declare i:int = 1
cond:
i <= 10
step:
i = i + 1
body:
if i % 2 == 0 then
os.print(i)
end if
end body
end loop
return 0
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
parameter:
declare i1: int
return_type: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,23 @@
module: Main
import: os
function: main
return_type: int
body:
loop:
init:
declare i:int = 1
cond:
i <= 10
step:
i = i + 1
body:
if i % 2 == 0 then
os.print(i)
break
end if
end body
end loop
return 0
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
parameter:
declare i1: int
return_type: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

@ -0,0 +1,43 @@
module: Main
import: os
function: main
return_type: int
body:
loop:
init:
declare i:int = 1
cond:
i <= 5
step:
i = i + 1
body:
if i % 2 == 0 then
continue
end if
if i > 3 then
break
end if
loop:
init:
declare j:int = 1
cond:
j <= 5
step:
j = j + 1
body:
if j == 4 then
break
end if
if j % 2 == 0 then
continue
end if
print(i)
print(j)
end body
end loop
end body
end loop
return 0
end body
end function
end module

View File

@ -0,0 +1,11 @@
module: os
import: os
function: print
parameter:
declare i1: int
return_type: void
body:
syscall("PRINT",i1)
end body
end function
end module

View File

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

View File

@ -60,9 +60,9 @@ public final class CompileCommand implements CLICommand {
List<String> argList = new ArrayList<>(); List<String> argList = new ArrayList<>();
// 保留用户在 cloud 模式下传入的 run / -debug 标志 // 保留用户在 cloud 模式下传入的 run / --debug 标志
for (String a : args) { for (String a : args) {
if ("run".equals(a) || "-debug".equals(a)) { if ("run".equals(a) || "--debug".equals(a)) {
argList.add(a); argList.add(a);
} }
} }

View File

@ -5,96 +5,83 @@ import org.jcnc.snow.compiler.backend.utils.OpHelper;
import org.jcnc.snow.compiler.ir.core.IRFunction; import org.jcnc.snow.compiler.ir.core.IRFunction;
import org.jcnc.snow.compiler.ir.core.IRInstruction; import org.jcnc.snow.compiler.ir.core.IRInstruction;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 虚拟机代码生成器VMCodeGenerator * VMCodeGenerator 负责将中间表示IR函数转换为目标虚拟机VM的指令序列
* <p> * <p>
* 本类作为指令生成器调度中心不负责任何具体 IR 指令到 VM 指令的转换实现 * 每个 IR 指令根据类型由对应的 InstructionGenerator 处理并将结果输出到 VMProgramBuilder
* 仅负责根据指令类型分发到对应的 {@link InstructionGenerator} 子类完成实际生成 * 该类通过注册表registry实现 IR VM 指令生成器的快速分发
* </p>
* <p>
* 工作流程简述:
* <ol>
* <li>接收一组已注册的 IR 指令生成器并建立类型到生成器的映射表</li>
* <li>遍历 IR 函数体的每条指令根据类型找到对应的生成器调用其 generate 方法生成 VM 指令</li>
* <li>生成流程以函数为单位beginFunction/endFunction</li>
* </ol>
*/ */
public final class VMCodeGenerator { public final class VMCodeGenerator {
/** /**
* 指令类型到生成器的注册表调度表 * IR 指令类型到指令生成器的映射
* <p> * 每种 IRInstruction 都有对应的 InstructionGenerator 处理
* : IR 指令类型Class对象
* : 对应的指令生成器实例
* </p>
*/ */
private final Map<Class<? extends IRInstruction>, InstructionGenerator<? extends IRInstruction>> registry; private final Map<Class<? extends IRInstruction>, InstructionGenerator<? extends IRInstruction>> registry;
/** /**
* 虚拟寄存器到槽号的映射表 RegisterAllocator 负责生成 * 虚拟寄存器到 VM 局部槽位的映射表
* 用于寄存器分配与指令生成
*/ */
private final Map<IRVirtualRegister, Integer> slotMap; private final Map<IRVirtualRegister, Integer> slotMap;
/** /**
* 虚拟机程序构建器用于输出 VM 指令 * 输出目标 VM 程序的构建器
* 提供 emitbeginFunctionendFunction 等接口
*/ */
private final VMProgramBuilder out; private final VMProgramBuilder out;
/** /**
* 当前处理的函数名用于部分指令生成逻辑如主函数判断等 * 构造 VMCodeGenerator
*/
private String currentFn;
/**
* 构造方法
* *
* @param slotMap 虚拟寄存器到槽号的映射 * @param slotMap 虚拟寄存器到 VM 局部槽位的分配表
* @param out 虚拟机程序构建器 * @param out 输出 VM 程序的 builder
* @param generators 各类 IR 指令生成器集合需预先构建 * @param generators 可用的 IR 指令生成器列表每个类型只应有一个
*/ */
public VMCodeGenerator(Map<IRVirtualRegister, Integer> slotMap, public VMCodeGenerator(Map<IRVirtualRegister, Integer> slotMap,
VMProgramBuilder out, VMProgramBuilder out,
List<InstructionGenerator<? extends IRInstruction>> generators) { List<InstructionGenerator<? extends IRInstruction>> generators) {
this.slotMap = slotMap; this.slotMap = slotMap;
this.out = out; this.out = out;
// 按类型注册各 IR 指令生成器建立不可变类型-生成器映射 // 构建不可变的类型到生成器的注册
this.registry = generators.stream() this.registry = generators.stream()
.collect(Collectors.toUnmodifiableMap(InstructionGenerator::supportedClass, g -> g)); .collect(Collectors.toUnmodifiableMap(InstructionGenerator::supportedClass, g -> g));
} }
/** /**
* 为一个 IR 函数生成虚拟机指令 * IRFunction 生成对应 VM 代码并写入输出
* *
* @param fn 待生成的 IR 函数对象 * <ol>
* @throws IllegalStateException 若遇到不支持的 IR 指令类型 * <li>调用 {@code out.beginFunction} 标记函数起始</li>
* <li>遍历函数体的每条 IR 指令查找对应 InstructionGenerator 并生成目标代码</li>
* <li> main/main.xxx 函数追加 HALT 指令其它函数追加 RET</li>
* <li>调用 {@code out.endFunction} 结束函数</li>
* </ol>
*
* @param fn 需要生成 VM 代码的 IRFunction
* @throws IllegalStateException 如果遇到不支持的 IR 指令类型
*/ */
public void generate(IRFunction fn) { public void generate(IRFunction fn) {
this.currentFn = fn.name(); String currentFn = fn.name();
/* 登记函数入口地址 —— 解决 CALL 未解析符号问题 */
out.beginFunction(currentFn); out.beginFunction(currentFn);
/* 逐条分发 IR 指令给对应的生成器 */
for (IRInstruction ins : fn.body()) { for (IRInstruction ins : fn.body()) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// 查找合适的指令生成器
InstructionGenerator<IRInstruction> gen = InstructionGenerator<IRInstruction> gen =
(InstructionGenerator<IRInstruction>) registry.get(ins.getClass()); (InstructionGenerator<IRInstruction>) registry.get(ins.getClass());
if (gen == null) { if (gen == null) {
throw new IllegalStateException("Unsupported IR: " + ins); throw new IllegalStateException("Unsupported IR: " + ins);
} }
// 调用生成器生成对应的 VM 指令
gen.generate(ins, out, slotMap, currentFn); gen.generate(ins, out, slotMap, currentFn);
} }
// 结尾指令main 函数统一用 HALT其他函数用 RET
/* 强制补上函数结尾的返回/终止指令 */ String retOpcode = ("main".equals(currentFn) || currentFn.endsWith(".main")) ? "HALT" : "RET";
String retOpcode = "main".equals(currentFn) ? "HALT" : "RET";
out.emit(OpHelper.opcode(retOpcode)); out.emit(OpHelper.opcode(retOpcode));
/* 结束函数 */
out.endFunction(); out.endFunction();
} }
} }

View File

@ -1,109 +1,109 @@
package org.jcnc.snow.compiler.backend.builder; package org.jcnc.snow.compiler.backend.builder;
import org.jcnc.snow.vm.engine.VMOpCode; import org.jcnc.snow.vm.engine.VMOpCode;
import java.util.*; import java.util.*;
/** /**
* VMProgramBuilder: 构建线性 VM 程序即按顺序存放所有 VM 指令 * VMProgramBuilder 用于构建虚拟机(VM)的最终指令列表
* <p> * <p>
* 本类用于编译器后端将所有生成的 VM 指令包括分支和调用指令统一存储管理 * 主要职责
* 支持符号如函数入口标签地址的延迟解析与回填fix-up机制 * <ul>
* 可在目标尚未定义时提前生成分支或调用指令定义后自动修正 * <li>维护代码指令序列和符号地址表</li>
* </p> * <li>支持跨函数标签跳转的延后修补(Call/Branch Fixup)</li>
* <p> * <li>支持虚拟机本地槽位类型的管理( I/F...)</li>
* 常用于处理跨函数跨标签的 CALL/JUMP 等复杂控制流确保最终生成的 VM 指令地址一致正确 * </ul>
* </p>
*/ */
public final class VMProgramBuilder { public final class VMProgramBuilder {
/** 未解析目标的 CALL 指令信息(待修补) */
/**
* 未知目标的 CALL 指令修补记录(待目标地址确定后修正)
* @param index CALL 指令在 code 列表中的位置
* @param target 目标函数的全名
* @param nArgs 参数个数
*/
private record CallFix(int index, String target, int nArgs) {} private record CallFix(int index, String target, int nArgs) {}
/** 未解析目标的分支指令JUMP/IC_* 等待修补) */ /**
* 未知目标的分支指令修补记录(待目标标签确定后修正)
* @param index 分支指令在 code 列表中的位置
* @param label 跳转目标标签名
*/
private record BranchFix(int index, String label) {} private record BranchFix(int index, String label) {}
/** 占位符: 用于表示尚未确定的符号地址 */ /** 未解析地址的占位符,便于后期批量修补 */
private static final String PLACEHOLDER = "-1"; private static final String PLACEHOLDER = "-1";
/** 按顺序存放的 VM 指令文本 */ /** VM 指令列表 */
private final List<String> code = new ArrayList<>(); private final List<String> code = new ArrayList<>();
/** 槽位(寄存器)类型映射表(如 I/F...,用于类型检查或代码生成优化) */
// 虚拟机槽位编号到数据类型前缀的映射 0 -> 'I', 1 -> 'D'
private final Map<Integer, Character> slotType = new HashMap<>(); private final Map<Integer, Character> slotType = new HashMap<>();
/** 符号(函数名/标签)到指令序号的映射表 */
/** 符号如函数名标签名到其首地址即指令序号/偏移量的映射表
* 主要用于跳转和调用定位具体的代码位置 */
private final Map<String, Integer> addr = new HashMap<>(); private final Map<String, Integer> addr = new HashMap<>();
/** 所有待修补的 CALL 指令集合 */
/** 所有待回填fix-up CALL 调用指令记录
* 由于被调用目标地址在编译时可能尚未确定需要先记录最终统一回填 */
private final List<CallFix> callFixes = new ArrayList<>(); private final List<CallFix> callFixes = new ArrayList<>();
/** 所有待修补的分支指令集合 */
/** 所有待回填fix-up的分支跳转指令记录
* CALL 类似分支指令的目标地址也可能需要编译后期再补充 */
private final List<BranchFix> branchFixes = new ArrayList<>(); private final List<BranchFix> branchFixes = new ArrayList<>();
/** 当前代码指针(已生成指令的数量/下一个指令的位置) */
/** 程序计数器Program Counter表示下一个生成指令将插入的位置 */
private int pc = 0; private int pc = 0;
/** /**
* 设置某个槽位对应的数据类型前缀 * 设置槽位(局部变量/虚拟寄存器)的类型前缀
*
* @param slot 槽位编号 * @param slot 槽位编号
* @param prefix 类型前缀 'I' 表示 int'D' 表示 double * @param prefix 类型前缀( 'I', 'F')
*/ */
public void setSlotType(int slot, char prefix) { public void setSlotType(int slot, char prefix) {
slotType.put(slot, prefix); slotType.put(slot, prefix);
} }
/** /**
* 获取某个槽位对应数据类型前缀 * 获取槽位的类型前缀默认为 'I'(整数类型)
* 若未指定则返回默认类型 'I'int *
* @param slot 槽位编号 * @param slot 槽位编号
* @return 类型前缀 'I', 'D' * @return 类型前缀字符
*/ */
public char getSlotType(int slot) { public char getSlotType(int slot) {
return slotType.getOrDefault(slot, 'I'); return slotType.getOrDefault(slot, 'I');
} }
/** /**
* 标记函数入口或标签并尝试修补所有等候该符号的指令 * 标记一个函数或标签的起始位置
* @param name 符号名函数名/标签名 * <p>
* 1. 记录符号到当前 pc 的映射
* 2. 立即尝试修补之前所有针对该符号的延后调用和分支
*
* @param name 函数名或标签名(全限定名)
*/ */
public void beginFunction(String name) { public void beginFunction(String name) {
addr.put(name, pc); // 记录当前地址为入口 addr.put(name, pc);
patchCallFixes(name); // 修补所有待该符号的 CALL patchCallFixes(name);
patchBranchFixes(name); // 修补所有待该符号的分支 patchBranchFixes(name);
} }
/** /** 函数结尾的处理(占位,无需特殊处理)。 */
* 结束函数当前实现为空方便 API 统一 public void endFunction() {}
*/
public void endFunction() { /* no-op */ }
/** /**
* 写入一条 VM 指令或标签 * 添加一条指令或标签到代码列表
* <ul> *
* <li>如果以冒号结尾视为标签仅登记其地址不写入指令流也不递增 pc</li> * @param line 指令字符串或标签字符串(若以冒号结尾为标签)
* <li>否则写入实际指令并自增 pc</li>
* </ul>
* @param line 一行 VM 指令文本或标签名结尾有冒号
*/ */
public void emit(String line) { public void emit(String line) {
if (line.endsWith(":")) { // 是标签定义行 if (line.endsWith(":")) {
// 标签定义
String label = line.substring(0, line.length() - 1); String label = line.substring(0, line.length() - 1);
addr.put(label, pc); // 记录标签地址 addr.put(label, pc);
patchBranchFixes(label); // 修补所有以该标签为目标的分支指令 patchBranchFixes(label);
return; // 标签行不写入 code不递增 pc return;
} }
code.add(line); // 普通指令写入 code code.add(line);
pc++; pc++;
} }
/** /**
* 生成 CALL 指令 * 添加一条 CALL 指令若目标未定义则延后修补
* 支持延迟修补: 若目标已知直接写入地址否则写入占位并登记 fix-up *
* @param target 目标函数 * @param target 目标函数
* @param nArgs 参数个数 * @param nArgs 参数个数
*/ */
public void emitCall(String target, int nArgs) { public void emitCall(String target, int nArgs) {
@ -117,10 +117,10 @@ public final class VMProgramBuilder {
} }
/** /**
* 生成分支JUMP IC_*指令 * 添加一条分支指令( JMP/BR/BEQ)若目标未定义则延后修补
* 支持延迟修补机制 *
* @param opcode 指令 * @param opcode 指令操作码
* @param label 目标标签名 * @param label 跳转目标标签名
*/ */
public void emitBranch(String opcode, String label) { public void emitBranch(String opcode, String label) {
Integer a = resolve(label); Integer a = resolve(label);
@ -133,13 +133,12 @@ public final class VMProgramBuilder {
} }
/** /**
* 构建最终 VM 代码文本列表 * 完成代码生成输出最终 VM 指令序列
* <ul> * <p>
* <li>若存在未解析符号CALL 或分支则抛出异常</li> * 如果存在未修补的调用或分支将抛出异常
* <li>否则返回不可变指令流</li> *
* </ul> * @return 指令序列(不可变)
* @return 完整 VM 指令流 * @throws IllegalStateException 如果存在未修补符号
* @throws IllegalStateException 若有未修补的符号引用
*/ */
public List<String> build() { public List<String> build() {
if (!callFixes.isEmpty() || !branchFixes.isEmpty()) { if (!callFixes.isEmpty() || !branchFixes.isEmpty()) {
@ -151,27 +150,26 @@ public final class VMProgramBuilder {
} }
/** /**
* 解析符号地址若全限定名找不到则降级尝试简单名 * 解析符号地址仅支持全名精准匹配
* @param sym 符号名 *
* @return 地址或 null未定义 * @param sym 符号全名
* @return 地址(指令序号)未找到返回 null
*/ */
private Integer resolve(String sym) { private Integer resolve(String sym) {
Integer a = addr.get(sym); return addr.get(sym);
if (a == null && sym.contains(".")) {
a = addr.get(sym.substring(sym.lastIndexOf('.') + 1));
}
return a;
} }
/** /**
* 修补所有以 name 为目标的 CALL 占位符 * 修补所有等待目标函数 name CALL 指令
* @param name 新定义的函数名 * <p>
* 只支持全名精确修补不做模糊查找或短名回退
*
* @param name 目标函数全名
*/ */
private void patchCallFixes(String name) { private void patchCallFixes(String name) {
for (Iterator<CallFix> it = callFixes.iterator(); it.hasNext();) { for (Iterator<CallFix> it = callFixes.iterator(); it.hasNext();) {
CallFix f = it.next(); CallFix f = it.next();
// 目标函数名完全匹配或后缀匹配兼容全限定名 if (f.target.equals(name)) {
if (f.target.equals(name) || f.target.endsWith("." + name)) {
code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs); code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs);
it.remove(); it.remove();
} }
@ -179,8 +177,9 @@ public final class VMProgramBuilder {
} }
/** /**
* 修补所有以 label 为目标的分支占位符 * 修补所有等待目标 label 的分支指令
* @param label 新定义的标签名 *
* @param label 目标标签
*/ */
private void patchBranchFixes(String label) { private void patchBranchFixes(String label) {
for (Iterator<BranchFix> it = branchFixes.iterator(); it.hasNext();) { for (Iterator<BranchFix> it = branchFixes.iterator(); it.hasNext();) {

View File

@ -44,6 +44,7 @@ public final class IROpCodeMapper {
opcodeMap.put(IROpCode.MUL_B8, "B_MUL"); opcodeMap.put(IROpCode.MUL_B8, "B_MUL");
opcodeMap.put(IROpCode.DIV_B8, "B_DIV"); opcodeMap.put(IROpCode.DIV_B8, "B_DIV");
opcodeMap.put(IROpCode.NEG_B8, "B_NEG"); opcodeMap.put(IROpCode.NEG_B8, "B_NEG");
opcodeMap.put(IROpCode.MOD_B8, "B_MOD");
// 整形16位算术运算映射 // 整形16位算术运算映射
opcodeMap.put(IROpCode.ADD_S16, "S_ADD"); opcodeMap.put(IROpCode.ADD_S16, "S_ADD");
@ -51,6 +52,7 @@ public final class IROpCodeMapper {
opcodeMap.put(IROpCode.MUL_S16, "S_MUL"); opcodeMap.put(IROpCode.MUL_S16, "S_MUL");
opcodeMap.put(IROpCode.DIV_S16, "S_DIV"); opcodeMap.put(IROpCode.DIV_S16, "S_DIV");
opcodeMap.put(IROpCode.NEG_S16, "S_NEG"); opcodeMap.put(IROpCode.NEG_S16, "S_NEG");
opcodeMap.put(IROpCode.MOD_S16, "S_MOD");
// 整形32位算术运算映射 // 整形32位算术运算映射
opcodeMap.put(IROpCode.ADD_I32, "I_ADD"); opcodeMap.put(IROpCode.ADD_I32, "I_ADD");
@ -58,6 +60,7 @@ public final class IROpCodeMapper {
opcodeMap.put(IROpCode.MUL_I32, "I_MUL"); opcodeMap.put(IROpCode.MUL_I32, "I_MUL");
opcodeMap.put(IROpCode.DIV_I32, "I_DIV"); opcodeMap.put(IROpCode.DIV_I32, "I_DIV");
opcodeMap.put(IROpCode.NEG_I32, "I_NEG"); opcodeMap.put(IROpCode.NEG_I32, "I_NEG");
opcodeMap.put(IROpCode.MOD_I32, "I_MOD");
// 整形64位算术运算映射 // 整形64位算术运算映射
opcodeMap.put(IROpCode.ADD_L64, "L_ADD"); opcodeMap.put(IROpCode.ADD_L64, "L_ADD");
@ -65,6 +68,7 @@ public final class IROpCodeMapper {
opcodeMap.put(IROpCode.MUL_L64, "L_MUL"); opcodeMap.put(IROpCode.MUL_L64, "L_MUL");
opcodeMap.put(IROpCode.DIV_L64, "L_DIV"); opcodeMap.put(IROpCode.DIV_L64, "L_DIV");
opcodeMap.put(IROpCode.NEG_L64, "L_NEG"); opcodeMap.put(IROpCode.NEG_L64, "L_NEG");
opcodeMap.put(IROpCode.MOD_L64, "L_MOD");
// --- 32-bit floating point --- // --- 32-bit floating point ---
@ -73,6 +77,7 @@ public final class IROpCodeMapper {
opcodeMap.put(IROpCode.MUL_F32, "F_MUL"); opcodeMap.put(IROpCode.MUL_F32, "F_MUL");
opcodeMap.put(IROpCode.DIV_F32, "F_DIV"); opcodeMap.put(IROpCode.DIV_F32, "F_DIV");
opcodeMap.put(IROpCode.NEG_F32, "F_NEG"); opcodeMap.put(IROpCode.NEG_F32, "F_NEG");
opcodeMap.put(IROpCode.MOD_F32, "F_MOD");
// --- 64-bit floating point --- // --- 64-bit floating point ---
opcodeMap.put(IROpCode.ADD_D64, "D_ADD"); opcodeMap.put(IROpCode.ADD_D64, "D_ADD");
@ -80,6 +85,7 @@ public final class IROpCodeMapper {
opcodeMap.put(IROpCode.MUL_D64, "D_MUL"); opcodeMap.put(IROpCode.MUL_D64, "D_MUL");
opcodeMap.put(IROpCode.DIV_D64, "D_DIV"); opcodeMap.put(IROpCode.DIV_D64, "D_DIV");
opcodeMap.put(IROpCode.NEG_D64, "D_NEG"); opcodeMap.put(IROpCode.NEG_D64, "D_NEG");
opcodeMap.put(IROpCode.MOD_D64, "D_MOD");
// 比较运算映射 // 比较运算映射
// 8位整数比较运算映射 // 8位整数比较运算映射

View File

@ -157,7 +157,7 @@ public record ExpressionBuilder(IRContext ctx) {
} }
/** /**
* 构建函数或方法调用表达式 * 构建函数或方法调用表达式模块内未限定调用会自动补全当前模块名
* *
* @param call AST 调用表达式节点 * @param call AST 调用表达式节点
* @return 存储调用结果的虚拟寄存器 * @return 存储调用结果的虚拟寄存器
@ -171,8 +171,18 @@ public record ExpressionBuilder(IRContext ctx) {
// 成员方法调用例如 obj.foo() // 成员方法调用例如 obj.foo()
case MemberExpressionNode m when m.object() instanceof IdentifierNode id case MemberExpressionNode m when m.object() instanceof IdentifierNode id
-> id.name() + "." + m.member(); -> id.name() + "." + m.member();
// 普通函数调用 // 普通函数调用如果未指定模块自动补全当前模块名
case IdentifierNode id -> id.name(); 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();
}
}
// 其它情况暂不支持 // 其它情况暂不支持
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName()); default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
}; };

View File

@ -8,24 +8,23 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext; import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode; import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 本类负责将解析生成的 AST 根节点列表转换为可执行的 IRProgram * IRProgramBuilder 负责将 AST 根节点(如模块函数顶层语句)转换为可执行的 IRProgram 实例
* * <p>
* <p>主要职责: * 主要职责:
* <ul> * <ul>
* <li>遍历输入的顶层节点识别 ModuleNodeFunctionNode 及脚本式顶层 StatementNode</li> * <li>遍历 AST 根节点根据类型分别处理(模块函数顶层语句)</li>
* <li> ModuleNode 中的所有函数节点调用 FunctionBuilder 构建 IRFunction 并添加至 IRProgram</li> * <li>对模块内的函数添加全限定名并在函数体前注入全局变量声明</li>
* <li>对单独的 FunctionNode 节点直接构建并纳入 IRProgram</li> * <li>将单独的顶层语句封装为特殊的 "_start" 函数</li>
* <li>对顶层脚本式 StatementNode 自动封装为名称固定的_start函数再行构建并纳入 IRProgram</li>
* <li>对不支持的节点类型抛出 IllegalStateException以确保编译流程严谨</li>
* </ul> * </ul>
*/ */
public final class IRProgramBuilder { public final class IRProgramBuilder {
/** /**
* 构建完整的 IRProgram 实例 * 将解析生成的 AST 根节点列表转换为 IRProgram
* *
* @param roots ModuleNodeFunctionNode StatementNode 的顶层 AST 根节点列表 * @param roots ModuleNodeFunctionNode StatementNode 的顶层 AST 根节点列表
* @return 包含所有转换后 IRFunction IRProgram 对象 * @return 包含所有转换后 IRFunction IRProgram 对象
@ -35,17 +34,20 @@ public final class IRProgramBuilder {
IRProgram irProgram = new IRProgram(); IRProgram irProgram = new IRProgram();
for (Node node : roots) { for (Node node : roots) {
switch (node) { switch (node) {
case ModuleNode moduleNode -> case ModuleNode moduleNode -> {
// 模块节点: 批量构建并添加模块内所有函数 // 处理模块节点:遍历其中所有函数统一用模块名.函数名作为全限定名
moduleNode.functions().forEach(f -> irProgram.add(buildFunction(f))); for (FunctionNode f : moduleNode.functions()) {
irProgram.add(buildFunctionWithGlobals(moduleNode, f));
}
}
case FunctionNode functionNode -> case FunctionNode functionNode ->
// 顶层函数节点: 直接构建并添加 // 处理顶层函数节点:直接构建 IRFunction 并加
irProgram.add(buildFunction(functionNode)); irProgram.add(buildFunction(functionNode));
case StatementNode statementNode -> case StatementNode statementNode ->
// 脚本式顶层语句: 封装为_start函数后构建并添加 // 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
irProgram.add(buildFunction(wrapTopLevel(statementNode))); irProgram.add(buildFunction(wrapTopLevel(statementNode)));
default -> default ->
// 严格校验节点类型遇不支持者立即失败 // 遇到未知类型节点抛出异常
throw new IllegalStateException("Unsupported top-level node: " + node); throw new IllegalStateException("Unsupported top-level node: " + node);
} }
} }
@ -53,34 +55,75 @@ public final class IRProgramBuilder {
} }
/** /**
* 利用 FunctionBuilder FunctionNode 转换为 IRFunction * 构建带有模块全局声明注入的函数并将函数名加上模块前缀保证模块内函数名唯一
* <p>
* 如果模块有全局声明则这些声明会被插入到函数体前部
* *
* @param functionNode 待构建的 AST FunctionNode * @param moduleNode 当前模块节点
* @return 构建完成的 IRFunction 实例 * @param functionNode 模块中的函数节点
* @return 包含全局声明已加前缀函数名的 IRFunction
*/
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
// 拼接模块名和函数名生成全限定名
String qualifiedName = moduleNode.name() + "." + functionNode.name();
// 若无全局声明仅重命名后直接构建
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
return buildFunction(renameFunction(functionNode, qualifiedName));
}
// 若有全局声明插入到函数体最前面
List<StatementNode> newBody = new ArrayList<>(moduleNode.globals().size() + functionNode.body().size());
newBody.addAll(moduleNode.globals());
newBody.addAll(functionNode.body());
FunctionNode wrapped = new FunctionNode(
qualifiedName,
functionNode.parameters(),
functionNode.returnType(),
newBody,
functionNode.context()
);
return buildFunction(wrapped);
}
/**
* 生成一个重命名的 FunctionNode(只修改函数名其他属性保持不变)
*
* @param fn 原始函数节点
* @param newName 新的函数名(通常为全限定名)
* @return 重命名后的 FunctionNode
*/
private FunctionNode renameFunction(FunctionNode fn, String newName) {
return new FunctionNode(
newName,
fn.parameters(),
fn.returnType(),
fn.body(),
fn.context()
);
}
/**
* 构建 IRFunction
*
* @param functionNode 要转换的函数节点
* @return 转换结果 IRFunction
*/ */
private IRFunction buildFunction(FunctionNode functionNode) { private IRFunction buildFunction(FunctionNode functionNode) {
return new FunctionBuilder().build(functionNode); return new FunctionBuilder().build(functionNode);
} }
/** /**
* 将单个脚本式顶层 StatementNode 封装为名称固定的_start函数节点 * 将顶层语句节点封装成特殊的 "_start" 函数
* <p>
* 这对于脚本式文件支持至关重要(即文件最外层直接写语句)
* *
* <p>封装规则: * @param stmt 要封装的顶层语句
* <ul> * @return 包装成 FunctionNode "_start" 函数
* <li>函数名固定为_start</li>
* <li>返回类型设为 null由后续流程处理</li>
* <li>参数列表为空</li>
* <li>函数主体仅包含传入的单条语句</li>
* </ul>
*
* @param stmt 待封装的顶层脚本语句节点
* @return 生成的 FunctionNode用于后续 IRFunction 构建
*/ */
private FunctionNode wrapTopLevel(StatementNode stmt) { private FunctionNode wrapTopLevel(StatementNode stmt) {
return new FunctionNode( return new FunctionNode(
"_start", "_start",
null, List.of(),
String.valueOf(List.of()), "void",
List.of(stmt), List.of(stmt),
new NodeContext(-1, -1, "") new NodeContext(-1, -1, "")
); );

View File

@ -9,6 +9,7 @@ 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.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode; import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import java.util.ArrayDeque;
import java.util.Locale; import java.util.Locale;
/** /**
@ -27,6 +28,14 @@ public class StatementBuilder {
* 表达式 IR 构建器用于将表达式节点转为 IR 指令 * 表达式 IR 构建器用于将表达式节点转为 IR 指令
*/ */
private final ExpressionBuilder expr; private final ExpressionBuilder expr;
/**
* break 目标标签栈保存每层循环的结束标签
*/
private final ArrayDeque<String> breakTargets = new ArrayDeque<>();
/**
* continue 目标标签栈保存每层循环的 step 起始标签
*/
private final ArrayDeque<String> continueTargets = new ArrayDeque<>();
/** /**
* 构造方法 * 构造方法
@ -89,21 +98,30 @@ public class StatementBuilder {
return; return;
} }
if (stmt instanceof DeclarationNode decl) { if (stmt instanceof DeclarationNode decl) {
// 变量声明 int a = 1; // 变量声明语句 int a = 1;
if (decl.getInitializer().isPresent()) { if (decl.getInitializer().isPresent()) {
// 声明时有初值 // 如果声明时有初 int a = b;
// 1. 设置声明变量的类型 // 1. 设置变量类型便于表达式求值/指令生成时推断类型信息
ctx.setVarType(decl.getType()); ctx.setVarType(decl.getType());
IRVirtualRegister r = expr.build(decl.getInitializer().get()); // 2. 为当前声明的变量分配一个全新的虚拟寄存器
// 这样可以保证该变量和初始值表达式中的变量物理上独立不会发生别名/串扰
IRVirtualRegister dest = ctx.newRegister();
// 2. 清除变量声明 // 3. 将初始值表达式的计算结果写入新分配的寄存器
// 即使初始值是某个已存在变量 outer_i这里是值的拷贝
expr.buildInto(decl.getInitializer().get(), dest);
// 4. 清理类型设置防止影响后续变量声明
ctx.clearVarType(); ctx.clearVarType();
ctx.getScope().declare(decl.getName(), decl.getType(), r); // 5. 在作用域内将变量名与新分配的寄存器进行绑定
// 这样后续对该变量的任何操作都只会影响 dest不会反向影响初值表达式中的源变量
ctx.getScope().declare(decl.getName(), decl.getType(), dest);
} else { } else {
// 仅声明无初值 // 仅声明变量无初值 int a;
// 在作用域内声明并分配新寄存器但不进行初始化
ctx.getScope().declare(decl.getName(), decl.getType()); ctx.getScope().declare(decl.getName(), decl.getType());
} }
return; return;
@ -120,6 +138,22 @@ public class StatementBuilder {
} }
return; return;
} }
if (stmt instanceof BreakNode) {
// break 语句跳转到当前最近一层循环的结束标签
if (breakTargets.isEmpty()) {
throw new IllegalStateException("`break` appears outside of a loop");
}
InstructionFactory.jmp(ctx, breakTargets.peek());
return;
}
if (stmt instanceof ContinueNode) {
// continue 语句跳转到当前最近一层循环的 step 起始标签
if (continueTargets.isEmpty()) {
throw new IllegalStateException("`continue` appears outside of a loop");
}
InstructionFactory.jmp(ctx, continueTargets.peek());
return;
}
// 不支持的语句类型 // 不支持的语句类型
throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt); throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt);
} }
@ -163,12 +197,24 @@ public class StatementBuilder {
// 条件不满足则跳出循环 // 条件不满足则跳出循环
emitConditionalJump(loop.cond(), lblEnd); emitConditionalJump(loop.cond(), lblEnd);
// 构建循环体 // 在进入循环体前记录本层循环的结束标签 break 使用
buildStatements(loop.body()); breakTargets.push(lblEnd);
// 更新部分 for i++ // 记录本层循环的 step 起始标签 continue 使用
String lblStep = ctx.newLabel();
continueTargets.push(lblStep);
try {
// 构建循环体
buildStatements(loop.body());
} finally {
// 离开循环体时弹出标签避免影响外层
breakTargets.pop();
continueTargets.pop();
}
// step 起始标签(所有 continue 会跳到这里)
InstructionFactory.label(ctx, lblStep);
if (loop.step() != null) build(loop.step()); if (loop.step() != null) build(loop.step());
// 跳回循环起点 // 回到 cond 检查
InstructionFactory.jmp(ctx, lblStart); InstructionFactory.jmp(ctx, lblStart);
// 循环结束标签 // 循环结束标签
InstructionFactory.label(ctx, lblEnd); InstructionFactory.label(ctx, lblEnd);

View File

@ -25,11 +25,12 @@ public enum IROpCode {
/* ───── 算术运算8位整数: byte───── */ /* ───── 算术运算8位整数: byte───── */
ADD_B8, // 8位整型加法: a = b + c ADD_B8, // 8位整型加法:
SUB_B8, // 8位整型减法: a = b - c SUB_B8, // 8位整型减法:
MUL_B8, // 8位整型乘法: a = b * c MUL_B8, // 8位整型乘法:
DIV_B8, // 8位整型除法: a = b / c DIV_B8, // 8位整型除法:
NEG_B8, // 8位整型取负: a = -b NEG_B8, // 8位整型取负:
MOD_B8, // 8位整型取余
/* ───── 算术运算16位整数: short───── */ /* ───── 算术运算16位整数: short───── */
ADD_S16, // 16位整型加法 ADD_S16, // 16位整型加法
@ -37,6 +38,7 @@ public enum IROpCode {
MUL_S16, // 16位整型乘法 MUL_S16, // 16位整型乘法
DIV_S16, // 16位整型除法 DIV_S16, // 16位整型除法
NEG_S16, // 16位整型取负 NEG_S16, // 16位整型取负
MOD_S16, // 16位整型取余
/* ───── 算术运算32位整数: int───── */ /* ───── 算术运算32位整数: int───── */
ADD_I32, // 32位整型加法 ADD_I32, // 32位整型加法
@ -44,6 +46,7 @@ public enum IROpCode {
MUL_I32, // 32位整型乘法 MUL_I32, // 32位整型乘法
DIV_I32, // 32位整型除法 DIV_I32, // 32位整型除法
NEG_I32, // 32位整型取负 NEG_I32, // 32位整型取负
MOD_I32, // 32位整型取余
/* ───── 算术运算64位整数: long───── */ /* ───── 算术运算64位整数: long───── */
ADD_L64, // 64位整型加法 ADD_L64, // 64位整型加法
@ -51,6 +54,7 @@ public enum IROpCode {
MUL_L64, // 64位整型乘法 MUL_L64, // 64位整型乘法
DIV_L64, // 64位整型除法 DIV_L64, // 64位整型除法
NEG_L64, // 64位整型取负 NEG_L64, // 64位整型取负
MOD_L64, // 64位整型取余
/* ───── 算术运算32位浮点数: float───── */ /* ───── 算术运算32位浮点数: float───── */
ADD_F32, // 32位浮点加法 ADD_F32, // 32位浮点加法
@ -58,6 +62,7 @@ public enum IROpCode {
MUL_F32, // 32位浮点乘法 MUL_F32, // 32位浮点乘法
DIV_F32, // 32位浮点除法 DIV_F32, // 32位浮点除法
NEG_F32, // 32位浮点取负 NEG_F32, // 32位浮点取负
MOD_F32, // 32位浮点取余
/* ───── 算术运算64位浮点数: double───── */ /* ───── 算术运算64位浮点数: double───── */
ADD_D64, // 64位浮点加法 ADD_D64, // 64位浮点加法
@ -65,6 +70,7 @@ public enum IROpCode {
MUL_D64, // 64位浮点乘法 MUL_D64, // 64位浮点乘法
DIV_D64, // 64位浮点除法 DIV_D64, // 64位浮点除法
NEG_D64, // 64位浮点取负 NEG_D64, // 64位浮点取负
MOD_D64, // 64位浮点取余
/* ───── 逻辑与比较运算指令8位整数: byte ───── */ /* ───── 逻辑与比较运算指令8位整数: byte ───── */
CMP_BEQ, // 8位整数相等比较: a == b CMP_BEQ, // 8位整数相等比较: a == b

View File

@ -6,97 +6,123 @@ import java.util.Map;
* 操作符与IR操作码映射表统一管理所有类型的算术和比较操作映射 * 操作符与IR操作码映射表统一管理所有类型的算术和比较操作映射
*/ */
public final class IROpCodeMappings { public final class IROpCodeMappings {
private IROpCodeMappings() {} // 禁止实例化
// 8位整型运算符映射 // 8位整型运算符映射
public static final Map<String, IROpCode> OP_B8 = Map.of( public static final Map<String, IROpCode> OP_B8 = Map.of(
"+", IROpCode.ADD_B8, "-", IROpCode.SUB_B8, "+", IROpCode.ADD_B8,
"*", IROpCode.MUL_B8, "/", IROpCode.DIV_B8 "-", IROpCode.SUB_B8,
"*", IROpCode.MUL_B8,
"/", IROpCode.DIV_B8,
"%", IROpCode.MOD_B8
); );
// 16位整型 // 16位整型
public static final Map<String, IROpCode> OP_S16 = Map.of( public static final Map<String, IROpCode> OP_S16 = Map.of(
"+", IROpCode.ADD_S16, "-", IROpCode.SUB_S16, "+", IROpCode.ADD_S16,
"*", IROpCode.MUL_S16, "/", IROpCode.DIV_S16 "-", IROpCode.SUB_S16,
"*", IROpCode.MUL_S16,
"/", IROpCode.DIV_S16,
"%", IROpCode.MOD_S16
); );
// 32位整型 // 32位整型
public static final Map<String, IROpCode> OP_I32 = Map.of( public static final Map<String, IROpCode> OP_I32 = Map.of(
"+", IROpCode.ADD_I32, "-", IROpCode.SUB_I32, "+", IROpCode.ADD_I32,
"*", IROpCode.MUL_I32, "/", IROpCode.DIV_I32 "-", IROpCode.SUB_I32,
"*", IROpCode.MUL_I32,
"/", IROpCode.DIV_I32,
"%", IROpCode.MOD_I32
); );
// 64位长整型 // 64位长整型
public static final Map<String, IROpCode> OP_L64 = Map.of( public static final Map<String, IROpCode> OP_L64 = Map.of(
"+", IROpCode.ADD_L64, "-", IROpCode.SUB_L64, "+", IROpCode.ADD_L64,
"*", IROpCode.MUL_L64, "/", IROpCode.DIV_L64 "-", IROpCode.SUB_L64,
"*", IROpCode.MUL_L64,
"/", IROpCode.DIV_L64,
"%", IROpCode.MOD_L64
); );
// 32位浮点型 // 32位浮点型
public static final Map<String, IROpCode> OP_F32 = Map.of( public static final Map<String, IROpCode> OP_F32 = Map.of(
"+", IROpCode.ADD_F32, "-", IROpCode.SUB_F32, "+", IROpCode.ADD_F32,
"*", IROpCode.MUL_F32, "/", IROpCode.DIV_F32 "-", IROpCode.SUB_F32,
"*", IROpCode.MUL_F32,
"/", IROpCode.DIV_F32,
"%", IROpCode.MOD_F32
); );
// 64位双精度浮点型 // 64位双精度浮点型
public static final Map<String, IROpCode> OP_D64 = Map.of( public static final Map<String, IROpCode> OP_D64 = Map.of(
"+", IROpCode.ADD_D64, "-", IROpCode.SUB_D64, "+", IROpCode.ADD_D64,
"*", IROpCode.MUL_D64, "/", IROpCode.DIV_D64 "-", IROpCode.SUB_D64,
"*", IROpCode.MUL_D64,
"/", IROpCode.DIV_D64,
"%", IROpCode.MOD_D64
); );
/**
/* ────── 比较运算符映射 ────── */ * 8-bitbyte比较
/** 8-bitbyte比较 */ */
public static final Map<String, IROpCode> CMP_B8 = Map.of( public static final Map<String, IROpCode> CMP_B8 = Map.of(
"==", IROpCode.CMP_BEQ, "==", IROpCode.CMP_BEQ,
"!=", IROpCode.CMP_BNE, "!=", IROpCode.CMP_BNE,
"<", IROpCode.CMP_BLT, "<", IROpCode.CMP_BLT,
">", IROpCode.CMP_BGT, ">", IROpCode.CMP_BGT,
"<=", IROpCode.CMP_BLE, "<=", IROpCode.CMP_BLE,
">=", IROpCode.CMP_BGE ">=", IROpCode.CMP_BGE
); );
/** 16-bitshort比较 */ /* ────── 比较运算符映射 ────── */
/**
* 16-bitshort比较
*/
public static final Map<String, IROpCode> CMP_S16 = Map.of( public static final Map<String, IROpCode> CMP_S16 = Map.of(
"==", IROpCode.CMP_SEQ, "==", IROpCode.CMP_SEQ,
"!=", IROpCode.CMP_SNE, "!=", IROpCode.CMP_SNE,
"<", IROpCode.CMP_SLT, "<", IROpCode.CMP_SLT,
">", IROpCode.CMP_SGT, ">", IROpCode.CMP_SGT,
"<=", IROpCode.CMP_SLE, "<=", IROpCode.CMP_SLE,
">=", IROpCode.CMP_SGE ">=", IROpCode.CMP_SGE
); );
/**
/** 32-bitint比较 */ * 32-bitint比较
*/
public static final Map<String, IROpCode> CMP_I32 = Map.of( public static final Map<String, IROpCode> CMP_I32 = Map.of(
"==", IROpCode.CMP_IEQ, "==", IROpCode.CMP_IEQ,
"!=", IROpCode.CMP_INE, "!=", IROpCode.CMP_INE,
"<", IROpCode.CMP_ILT, "<", IROpCode.CMP_ILT,
">", IROpCode.CMP_IGT, ">", IROpCode.CMP_IGT,
"<=", IROpCode.CMP_ILE, "<=", IROpCode.CMP_ILE,
">=", IROpCode.CMP_IGE ">=", IROpCode.CMP_IGE
); );
/**
/** 64-bitlong比较 */ * 64-bitlong比较
*/
public static final Map<String, IROpCode> CMP_L64 = Map.of( public static final Map<String, IROpCode> CMP_L64 = Map.of(
"==", IROpCode.CMP_LEQ, "==", IROpCode.CMP_LEQ,
"!=", IROpCode.CMP_LNE, "!=", IROpCode.CMP_LNE,
"<", IROpCode.CMP_LLT, "<", IROpCode.CMP_LLT,
">", IROpCode.CMP_LGT, ">", IROpCode.CMP_LGT,
"<=", IROpCode.CMP_LLE, "<=", IROpCode.CMP_LLE,
">=", IROpCode.CMP_LGE ">=", IROpCode.CMP_LGE
); );
/**
/** 32-bitfloat比较 */ * 32-bitfloat比较
*/
public static final Map<String, IROpCode> CMP_F32 = Map.of( public static final Map<String, IROpCode> CMP_F32 = Map.of(
"==", IROpCode.CMP_FEQ, "==", IROpCode.CMP_FEQ,
"!=", IROpCode.CMP_FNE, "!=", IROpCode.CMP_FNE,
"<", IROpCode.CMP_FLT, "<", IROpCode.CMP_FLT,
">", IROpCode.CMP_FGT, ">", IROpCode.CMP_FGT,
"<=", IROpCode.CMP_FLE, "<=", IROpCode.CMP_FLE,
">=", IROpCode.CMP_FGE ">=", IROpCode.CMP_FGE
); );
/**
/** 64-bitdouble比较 */ * 64-bitdouble比较
*/
public static final Map<String, IROpCode> CMP_D64 = Map.of( public static final Map<String, IROpCode> CMP_D64 = Map.of(
"==", IROpCode.CMP_DEQ, "==", IROpCode.CMP_DEQ,
"!=", IROpCode.CMP_DNE, "!=", IROpCode.CMP_DNE,
"<", IROpCode.CMP_DLT, "<", IROpCode.CMP_DLT,
">", IROpCode.CMP_DGT, ">", IROpCode.CMP_DGT,
"<=", IROpCode.CMP_DLE, "<=", IROpCode.CMP_DLE,
">=", IROpCode.CMP_DGE ">=", IROpCode.CMP_DGE
); );
private IROpCodeMappings() {
} // 禁止实例化
} }

View File

@ -12,7 +12,7 @@ import java.util.Set;
* </p> * </p>
* *
* <p> * <p>
* 主要功能与特性: * 主要功能与特性:
* <ul> * <ul>
* <li>统一管理语言关键字和类型名集合便于扩展与维护</li> * <li>统一管理语言关键字和类型名集合便于扩展与维护</li>
* <li>自动推断 Token 类型无需外部干预</li> * <li>自动推断 Token 类型无需外部干预</li>
@ -25,7 +25,10 @@ public class TokenFactory {
/** /**
* 语言的保留关键字集合 * 语言的保留关键字集合
*/ */
private static final Set<String> KEYWORDS = Set.of("module", "function", "parameter", "return_type", "body", "end", "if", "then", "else", "loop", "declare", "return", "import", "init", "cond", "step"); private static final Set<String> KEYWORDS = Set.of
("module", "function", "parameter", "return_type", "body", "end",
"if", "then", "else", "loop", "declare", "return", "import", "init",
"cond", "step", "globals", "break", "continue");
/** /**
* 内置类型名称集合 intstring * 内置类型名称集合 intstring

View File

@ -0,0 +1,16 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
/**
* {@code BreakNode} 表示循环体中的 break 语句
* 出现时应立即终止当前最内层循环
*/
public record BreakNode(NodeContext context) implements StatementNode {
@Override
public String toString() {
return "break@" + context;
}
}

View File

@ -0,0 +1,19 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
/**
* {@code ContinueNode} 表示循环体中的 continue 语句节点
* <p>
* continue 语句用于跳过当前循环剩余部分直接进入下一次循环的 stepcond 阶段
* 该节点仅作为语法树中的一种语句类型出现
* </p>
*/
public record ContinueNode(NodeContext context) implements StatementNode {
@Override
public String toString() {
return "continue";
}
}

View File

@ -18,6 +18,7 @@ import java.util.StringJoiner;
public record ModuleNode( public record ModuleNode(
String name, String name,
List<ImportNode> imports, List<ImportNode> imports,
List<DeclarationNode> globals,
List<FunctionNode> functions, List<FunctionNode> functions,
NodeContext context NodeContext context
) implements Node { ) implements Node {
@ -33,12 +34,17 @@ public record ModuleNode(
for (ImportNode imp : imports) { for (ImportNode imp : imports) {
importJoiner.add(imp.moduleName()); importJoiner.add(imp.moduleName());
} }
StringJoiner globalJoiner = new StringJoiner(", ");
for (DeclarationNode g : globals) {
globalJoiner.add(g.getType() + " " + g.getName());
}
StringJoiner funcJoiner = new StringJoiner(", "); StringJoiner funcJoiner = new StringJoiner(", ");
for (FunctionNode fn : functions) { for (FunctionNode fn : functions) {
funcJoiner.add(fn.name()); funcJoiner.add(fn.name());
} }
return "Module(name=" + name return "Module(name=" + name
+ ", imports=[" + importJoiner + "]" + ", imports=[" + importJoiner + "]"
+ ", globals=[" + globalJoiner + "]"
+ ", functions=[" + funcJoiner + "])"; + ", functions=[" + funcJoiner + "])";
} }
} }

View File

@ -27,6 +27,8 @@ public class StatementParserFactory {
registry.put("if", new IfStatementParser()); registry.put("if", new IfStatementParser());
registry.put("loop", new LoopStatementParser()); registry.put("loop", new LoopStatementParser());
registry.put("return", new ReturnStatementParser()); registry.put("return", new ReturnStatementParser());
registry.put("break", new BreakStatementParser());
registry.put("continue",new ContinueStatementParser());
// 默认处理器: 表达式语句 // 默认处理器: 表达式语句
registry.put("", new ExpressionStatementParser()); registry.put("", new ExpressionStatementParser());

View File

@ -116,6 +116,7 @@ public class ASTPrinter {
} }
case ReturnNode r -> System.out.println(pad + "return" + case ReturnNode r -> System.out.println(pad + "return" +
r.getExpression().map(e -> " " + e).orElse("")); r.getExpression().map(e -> " " + e).orElse(""));
case BreakNode _ -> System.out.println(pad + "break");
case ExpressionStatementNode(ExpressionNode expression, NodeContext _) -> case ExpressionStatementNode(ExpressionNode expression, NodeContext _) ->
System.out.println(pad + expression); System.out.println(pad + expression);
case null, default -> System.out.println(pad + n); // 回退处理 case null, default -> System.out.println(pad + n); // 回退处理

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.parser.module; package org.jcnc.snow.compiler.parser.module;
import org.jcnc.snow.compiler.lexer.token.TokenType; import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ImportNode; import org.jcnc.snow.compiler.parser.ast.ImportNode;
import org.jcnc.snow.compiler.parser.ast.ModuleNode; import org.jcnc.snow.compiler.parser.ast.ModuleNode;
@ -10,6 +11,7 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream; import org.jcnc.snow.compiler.parser.context.TokenStream;
import org.jcnc.snow.compiler.parser.context.UnexpectedToken; import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.function.FunctionParser; import org.jcnc.snow.compiler.parser.function.FunctionParser;
import org.jcnc.snow.compiler.parser.statement.DeclarationStatementParser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -17,7 +19,7 @@ import java.util.List;
/** /**
* {@code ModuleParser} 负责解析源码中的模块结构是顶层结构解析器实现之一 * {@code ModuleParser} 负责解析源码中的模块结构是顶层结构解析器实现之一
* <p> * <p>
* 模块定义可包含多个导入import语句和函数定义function * 模块定义可包含多个导入import语句globals 全局声明和函数定义function
* 导入语句可在模块中任意位置出现且允许模块体中穿插任意数量的空行空行会被自动忽略不影响语法结构 * 导入语句可在模块中任意位置出现且允许模块体中穿插任意数量的空行空行会被自动忽略不影响语法结构
* </p> * </p>
* *
@ -26,6 +28,8 @@ import java.util.List;
* <pre> * <pre>
* module: mymod * module: mymod
* import ... * import ...
* globals:
* declare ...
* function ... * function ...
* ... * ...
* end module * end module
@ -40,7 +44,7 @@ public class ModuleParser implements TopLevelParser {
* 解析过程包括: * 解析过程包括:
* <ol> * <ol>
* <li>匹配模块声明起始 {@code module: IDENTIFIER}</li> * <li>匹配模块声明起始 {@code module: IDENTIFIER}</li>
* <li>收集模块体内所有 import function 语句允许穿插空行</li> * <li>收集模块体内所有 importglobals function 语句允许穿插空行</li>
* <li>匹配模块结束 {@code end module}</li> * <li>匹配模块结束 {@code end module}</li>
* </ol> * </ol>
* 若遇到未识别的语句将抛出 {@link UnexpectedToken} 异常定位错误位置和原因 * 若遇到未识别的语句将抛出 {@link UnexpectedToken} 异常定位错误位置和原因
@ -64,12 +68,15 @@ public class ModuleParser implements TopLevelParser {
ts.expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
List<ImportNode> imports = new ArrayList<>(); List<ImportNode> imports = new ArrayList<>();
List<DeclarationNode> globals = new ArrayList<>();
List<FunctionNode> functions = new ArrayList<>(); List<FunctionNode> functions = new ArrayList<>();
ImportParser importParser = new ImportParser(); ImportParser importParser = new ImportParser();
FunctionParser funcParser = new FunctionParser(); FunctionParser funcParser = new FunctionParser();
DeclarationStatementParser globalsParser = new DeclarationStatementParser();
while (true) { while (true) {
// 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) { if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next(); ts.next();
continue; continue;
@ -78,12 +85,33 @@ public class ModuleParser implements TopLevelParser {
break; break;
} }
String lex = ts.peek().getLexeme(); String lex = ts.peek().getLexeme();
if ("import".equals(lex)) { switch (lex) {
imports.addAll(importParser.parse(ctx)); case "import" -> imports.addAll(importParser.parse(ctx));
} else if ("function".equals(lex)) { case "function" -> functions.add(funcParser.parse(ctx));
functions.add(funcParser.parse(ctx)); case "globals" -> {
} else { ts.expect("globals");
throw new UnexpectedToken( ts.expect(":");
ts.expectType(TokenType.NEWLINE);
while (true) {
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
continue;
}
String innerLex = ts.peek().getLexeme();
if ("declare".equals(innerLex)) {
globals.add(globalsParser.parse(ctx));
} else if ("function".equals(innerLex) || "import".equals(innerLex) || "end".equals(innerLex)) {
break;
} else {
throw new UnexpectedToken(
"globals 区块中不支持的内容: " + innerLex,
ts.peek().getLine(),
ts.peek().getCol()
);
}
}
}
case null, default -> throw new UnexpectedToken(
"Unexpected token in module: " + lex, "Unexpected token in module: " + lex,
ts.peek().getLine(), ts.peek().getLine(),
ts.peek().getCol() ts.peek().getCol()
@ -94,6 +122,6 @@ public class ModuleParser implements TopLevelParser {
ts.expect("end"); ts.expect("end");
ts.expect("module"); ts.expect("module");
return new ModuleNode(name, imports, functions, new NodeContext(line, column, file)); return new ModuleNode(name, imports, globals, functions, new NodeContext(line, column, file));
} }
} }

View File

@ -0,0 +1,40 @@
package org.jcnc.snow.compiler.parser.statement;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.BreakNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
/**
* {@code BreakStatementParser} 用于解析 break 语句
* <p>
* break 语句的语法仅包含关键字本身随后以换行结束
* 语义立即终止当前最内层循环
* </p>
*/
public class BreakStatementParser implements StatementParser {
/**
* 解析 break 语句节点
* <p>
* 期望格式为'break' NEWLINE
* </p>
*
* @param ctx 解析上下文
* @return BreakNode AST 节点
*/
@Override
public BreakNode parse(ParserContext ctx) {
// 记录当前位置作为 NodeContext
int line = ctx.getTokens().peek().getLine();
int column = ctx.getTokens().peek().getCol();
String file = ctx.getSourceName();
// 消耗 'break'
ctx.getTokens().expect("break");
// 行结束
ctx.getTokens().expectType(TokenType.NEWLINE);
return new BreakNode(new NodeContext(line, column, file));
}
}

View File

@ -0,0 +1,40 @@
package org.jcnc.snow.compiler.parser.statement;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.ContinueNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
/**
* {@code ContinueStatementParser} 用于解析 continue 语句
* <p>
* continue 语句语法格式简单仅包含关键字本身随后以换行结束
* 语义跳过本次剩余循环体继续执行 step cond
* </p>
*/
public class ContinueStatementParser implements StatementParser {
/**
* 解析 continue 语句节点
* <p>
* 期望格式为'continue' NEWLINE
* </p>
*
* @param ctx 解析上下文
* @return ContinueNode AST 节点
*/
@Override
public ContinueNode parse(ParserContext ctx) {
// 记录当前位置作为 NodeContext
int line = ctx.getTokens().peek().getLine();
int column = ctx.getTokens().peek().getCol();
String file = ctx.getSourceName();
// 消耗 'continue'
ctx.getTokens().expect("continue");
// 行结束
ctx.getTokens().expectType(TokenType.NEWLINE);
return new ContinueNode(new NodeContext(line, column, file));
}
}

View File

@ -14,7 +14,7 @@ import java.util.*;
* 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串用于调试 * 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串用于调试
* 可视化或跨语言数据传输 * 可视化或跨语言数据传输
* <p> * <p>
* 支持的节点类型包括新增对 {@code BoolLiteralNode}{@code UnaryExpressionNode} 的完整支持: * 支持的节点类型包括:
* <ul> * <ul>
* <li>{@link ModuleNode}</li> * <li>{@link ModuleNode}</li>
* <li>{@link FunctionNode}</li> * <li>{@link FunctionNode}</li>
@ -83,7 +83,7 @@ public class ASTJsonSerializer {
return switch (n) { return switch (n) {
// 模块节点 // 模块节点
case ModuleNode( case ModuleNode(
String name, List<ImportNode> imports, List<FunctionNode> functions, NodeContext _ String name, List<ImportNode> imports, List<DeclarationNode> globals, List<FunctionNode> functions, NodeContext _
) -> { ) -> {
Map<String, Object> map = newNodeMap("Module"); Map<String, Object> map = newNodeMap("Module");
map.put("name", name); map.put("name", name);
@ -92,6 +92,15 @@ public class ASTJsonSerializer {
imps.add(Map.of("type", "Import", "module", imp.moduleName())); imps.add(Map.of("type", "Import", "module", imp.moduleName()));
} }
map.put("imports", imps); map.put("imports", imps);
List<Object> globs = new ArrayList<>(globals.size());
for (DeclarationNode d : globals) {
Map<String, Object> decl = newNodeMap("Declaration");
decl.put("name", d.getName());
decl.put("varType", d.getType());
decl.put("init", d.getInitializer().map(ASTJsonSerializer::exprToMap).orElse(null));
globs.add(decl);
}
map.put("globals", globs);
List<Object> funcs = new ArrayList<>(functions.size()); List<Object> funcs = new ArrayList<>(functions.size());
for (FunctionNode f : functions) { for (FunctionNode f : functions) {
funcs.add(nodeToMap(f)); funcs.add(nodeToMap(f));

View File

@ -5,50 +5,324 @@ import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context; import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo; 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.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
import static org.jcnc.snow.compiler.semantic.type.BuiltinType.*;
/** /**
* {@code NumberLiteralAnalyzer} 用于对数字字面量表达式进行语义分析并推断其精确类型 * {@code NumberLiteralAnalyzer}
* <p> *
* 类型判定逻辑如下: * <p>职责
* <ul>
* <li>对数字字面量表达式进行语义分析并推断其精确类型</li>
* <li>按照推断类型进行范围校验</li>
* <li>发生越界时给出智能的错误提示与合理建议</li>
* </ul>
*
* <p>类型推断规则</p>
* <ol> * <ol>
* <li>首先检查字面量末尾是否带有类型后缀不区分大小写: * <li>若字面量以类型后缀(b/s/l/f大小写均可)结尾则按后缀直接推断目标类型</li>
* <ul> * <ul>
* <li>{@code b}: byte 类型{@link BuiltinType#BYTE}</li> * <li>b byte</li>
* <li>{@code s}: short 类型{@link BuiltinType#SHORT}</li> * <li>s short</li>
* <li>{@code l}: long 类型{@link BuiltinType#LONG}</li> * <li>l long</li>
* <li>{@code f}: float 类型{@link BuiltinType#FLOAT}</li> * <li>f float</li>
* <li>{@code d}: double 类型{@link BuiltinType#DOUBLE}</li> * </ul>
* </ul> * <li>若无后缀且文本包含小数点或科学计数法('.' 'e/E')则推断为 double(浮点默认 double不支持 d/D 后缀)</li>
* </li> * <li>否则推断为 int</li>
* <li>若无后缀:
* <ul>
* <li>只要文本中包含 {@code '.'} {@code e/E}即判为 double 类型</li>
* <li>否则默认判为 int 类型</li>
* </ul>
* </li>
* </ol> * </ol>
* 本分析器不处理溢出非法格式等诊断仅做类型推断 *
* <p>智能错误提示策略</p>
* <ul>
* <li>整数
* <ul>
* <li>int 放不下但 long 能放下 直接建议 long</li>
* <li> long 也放不下 一次性提示超出 int/long 可表示范围</li>
* </ul>
* </li>
* <li>浮点
* <ul>
* <li>float 放不下但 double 能放下 直接建议 double(无需 d 后缀)</li>
* <li> double 也放不下 一次性提示超出 float/double 可表示范围</li>
* </ul>
* </li>
* </ul>
*/ */
public class NumberLiteralAnalyzer implements ExpressionAnalyzer<NumberLiteralNode> { public class NumberLiteralAnalyzer implements ExpressionAnalyzer<NumberLiteralNode> {
/** /**
* 对数字字面量进行语义分析推断其类型 * 根据字面量后缀和文本内容推断类型
* <p>
* 分析流程:
* <ol>
* <li>若字面量以后缀结尾直接按后缀映射类型</li>
* <li>否则若含有小数点或科学计数法标记则为 double否则为 int</li>
* </ol>
* *
* @param ctx 当前语义分析上下文可用于记录诊断信息等当前未使用 * @param hasSuffix 是否存在类型后缀( b/s/l/f 有效)
* @param mi 当前模块信息未使用 * @param suffix 后缀字符(已转小写)
* @param fn 当前函数节点未使用 * @param digits 去掉下划线与后缀后的数字主体(可能含 . e/E)
* @param locals 当前作用域的符号表未使用 * @return 推断类型(byte / short / int / long / float / double 之一)
* @param expr 数字字面量表达式节点 */
* @return 对应的 {@link BuiltinType} INTDOUBLE private static Type inferType(boolean hasSuffix, char suffix, String digits) {
if (hasSuffix) {
// 仅支持 b/s/l/f不支持 d/D(浮点默认 double)
return switch (suffix) {
case 'b' -> BYTE;
case 's' -> SHORT;
case 'l' -> BuiltinType.LONG;
case 'f' -> BuiltinType.FLOAT;
default -> INT; // 其他后缀当作无效 int 处理(如需严格可改为抛/非法后缀)
};
}
// 无后缀包含小数点或 e/E double否则 int
if (looksLikeFloat(digits)) {
return BuiltinType.DOUBLE; // 浮点默认 double
}
return INT;
}
/**
* 做范围校验发生越界时写入智能的错误与建议
*
* @param ctx 语义上下文(承载错误列表与日志)
* @param node 当前数字字面量节点
* @param inferred 推断类型
* @param digits 规整后的数字主体(去下划线去后缀"123.""123.0")
*/
private static void validateRange(Context ctx,
NumberLiteralNode node,
Type inferred,
String digits) {
try {
// 整数类型不允许出现浮点形式 //
if (inferred == BYTE) {
if (looksLikeFloat(digits)) throw new NumberFormatException(digits);
Byte.parseByte(digits);
} else if (inferred == SHORT) {
if (looksLikeFloat(digits)) throw new NumberFormatException(digits);
Short.parseShort(digits);
} else if (inferred == INT) {
if (looksLikeFloat(digits)) throw new NumberFormatException(digits);
Integer.parseInt(digits);
} else if (inferred == BuiltinType.LONG) {
if (looksLikeFloat(digits)) throw new NumberFormatException(digits);
Long.parseLong(digits);
}
// 浮点类型解析 + /下溢判断 //
else if (inferred == BuiltinType.FLOAT) {
float v = Float.parseFloat(digits);
// 上溢Infinity下溢解析为 0.0 但文本并非全零
if (Float.isInfinite(v) || (v == 0.0f && isTextualZero(digits))) {
throw new NumberFormatException("float overflow/underflow: " + digits);
}
} else if (inferred == BuiltinType.DOUBLE) {
double v = Double.parseDouble(digits);
if (Double.isInfinite(v) || (v == 0.0 && isTextualZero(digits))) {
throw new NumberFormatException("double overflow/underflow: " + digits);
}
}
} catch (NumberFormatException ex) {
// 智能的错误描述与建议(header 使用 digits避免带后缀)
String msg = getSmartSuggestionOneShot(digits, inferred);
ctx.getErrors().add(new SemanticError(node, msg));
ctx.log("错误: " + msg);
}
}
/**
* 生成智能的错误提示与建议
*
* <p>策略</p>
* <ul>
* <li>BYTE/SHORT/INT若能放进更大整型直接建议否则一次性提示已超出 int/long 范围</li>
* <li>LONG直接提示超出 long 可表示范围</li>
* <li>FLOAT double 能放下 建议 double否则一次性提示超出 float/double 可表示范围</li>
* <li>DOUBLE直接提示超出 double 可表示范围</li>
* </ul>
*
* @param digits 去后缀后的数字主体(用于 header 与建议示例)
* @param inferred 推断类型
* @return 完整错误消息(含建议)
*/
private static String getSmartSuggestionOneShot(String digits, Type inferred) {
String header;
String suggestion;
switch (inferred) {
case BYTE -> {
long v;
try {
v = Long.parseLong(digits);
} catch (NumberFormatException e) {
// long 都放不下智能
header = composeHeader(digits, "超出 byte/short/int/long 可表示范围。");
return header;
}
if (v >= Short.MIN_VALUE && v <= Short.MAX_VALUE) {
header = composeHeader(digits, "超出 byte 可表示范围。");
suggestion = "建议将变量类型声明为 short并在数字末尾添加 's'(如 " + digits + "s)。";
} else if (v >= Integer.MIN_VALUE && v <= Integer.MAX_VALUE) {
header = composeHeader(digits, "超出 byte 可表示范围。");
suggestion = "建议将变量类型声明为 int(如 " + digits + ")。";
} else {
header = composeHeader(digits, "超出 byte 可表示范围。");
suggestion = "建议将变量类型声明为 long并在数字末尾添加 'L'(如 " + digits + "L)。";
}
return appendSuggestion(header, suggestion);
}
case SHORT -> {
long v;
try {
v = Long.parseLong(digits);
} catch (NumberFormatException e) {
header = composeHeader(digits, "超出 short/int/long 可表示范围。");
return header;
}
if (v >= Integer.MIN_VALUE && v <= Integer.MAX_VALUE) {
header = composeHeader(digits, "超出 short 可表示范围。");
suggestion = "建议将变量类型声明为 int(如 " + digits + ")。";
} else {
header = composeHeader(digits, "超出 short 可表示范围。");
suggestion = "建议将变量类型声明为 long并在数字末尾添加 'L'(如 " + digits + "L)。";
}
return appendSuggestion(header, suggestion);
}
case INT -> {
try {
// 尝试解析为 long若成功说明能进 long
Long.parseLong(digits);
} catch (NumberFormatException e) {
// long 都放不下智能
header = composeHeader(digits, "超出 int/long 可表示范围。");
return header;
}
// 能进 long直接建议 long
header = composeHeader(digits, "超出 int 可表示范围。");
suggestion = "建议将变量类型声明为 long并在数字末尾添加 'L'(如 " + digits + "L)。";
return appendSuggestion(header, suggestion);
}
case LONG -> {
// 已明确处于 long 分支且越界智能
header = composeHeader(digits, "超出 long 可表示范围。");
return header;
}
case FLOAT -> {
// float 放不下尝试 double若能放下则直接建议 double否则智能提示 float/double 都不行
boolean fitsDouble = fitsDouble(digits);
if (fitsDouble) {
header = composeHeader(digits, "超出 float 可表示范围。");
suggestion = "建议将变量类型声明为 double(如 " + digits + ")。"; // double 默认无需 d 后缀
return appendSuggestion(header, suggestion);
} else {
header = composeHeader(digits, "超出 float/double 可表示范围。");
return header;
}
}
case DOUBLE -> {
header = composeHeader(digits, "超出 double 可表示范围。");
return header;
}
default -> {
header = composeHeader(digits, "超出 " + inferred + " 可表示范围。");
return header;
}
}
}
/**
* 生成越界错误头部统一使用数字主体而非原始 raw(避免带后缀引起误导)
*
* @param digits 去后缀后的数字主体
* @param tail 错误尾部描述(超出 int 可表示范围)
*/
private static String composeHeader(String digits, String tail) {
return "数值字面量越界: \"" + digits + "\" " + tail;
}
/**
* 在头部后拼接建议文本(若存在)
*
* @param header 错误头部
* @param suggestion 建议文本(可能为 null)
*/
private static String appendSuggestion(String header, String suggestion) {
return suggestion == null ? header : header + " " + suggestion;
}
/**
* 文本层面判断是否看起来是浮点字面量
* 只要包含 '.' 'e/E'即视为浮点
*/
private static boolean looksLikeFloat(String s) {
return s.indexOf('.') >= 0 || s.indexOf('e') >= 0 || s.indexOf('E') >= 0;
}
/**
* 文本判断是否为零(不解析纯文本)
* 在遇到 e/E 之前若出现任意 '1'..'9'视为非零否则视为零
* 用于识别文本非零但解析结果为 0.0的下溢场景
* <p>
*
* "0.0" true
* "000" true
* "1e-9999" false(e 前有 '1'若解析为 0.0 则视为下溢)
* "0e-9999" true
*/
private static boolean isTextualZero(String s) {
if (s == null || s.isEmpty()) return false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == 'e' || c == 'E') break; // 指数部分不参与是否为零的判断
if (c >= '1' && c <= '9') return true;
}
return false;
}
/**
* 判断 double 是否能正常表示(不溢出也非文本非零但解析为 0.0的下溢)
*
* @param digits 去后缀后的数字主体
*/
private static boolean fitsDouble(String digits) {
try {
double d = Double.parseDouble(digits);
return !Double.isInfinite(d) && !(d == 0.0 && isTextualZero(digits));
} catch (NumberFormatException e) {
return false;
}
}
/**
* 规整数字串
* 仅移除末尾的类型后缀( b/s/l/f大小写均可不含 d/D)
*
* @param s 原始字面量字符串
* @return 规整后的数字主体
*/
private static String normalizeDigits(String s) {
if (s == null) return "";
String t = s.trim();
if (t.isEmpty()) return t;
// 仅移除末尾的类型后缀(b/s/l/f大小写均可)
char last = t.charAt(t.length() - 1);
if ("bBsSfFlL".indexOf(last) >= 0) {
t = t.substring(0, t.length() - 1).trim();
}
return t;
}
/**
* 入口对数字字面量进行语义分析
* <p>
* 分步
* <ol>
* <li>读取原始文本 raw</li>
* <li>识别是否带后缀( b/s/l/f)</li>
* <li>规整数字主体 digits(去下划线去后缀补小数点零)</li>
* <li>按规则推断目标类型</li>
* <li>做范围校验越界时记录智能的错误与建议</li>
* <li>返回推断类型</li>
* </ol>
*/ */
@Override @Override
public Type analyze(Context ctx, public Type analyze(Context ctx,
@ -57,41 +331,30 @@ public class NumberLiteralAnalyzer implements ExpressionAnalyzer<NumberLiteralNo
SymbolTable locals, SymbolTable locals,
NumberLiteralNode expr) { NumberLiteralNode expr) {
// 获取字面量原始文本 "123", "3.14", "2f" // 1) 原始文本( "123", "3.14", "2f", "1_000_000L", "1e300")
String raw = expr.value(); String raw = expr.value();
if (raw == null || raw.isEmpty()) { if (raw == null || raw.isEmpty()) {
// 理论上不应为空兜底返回 int 类型 return INT; // 空文本回退为 int(按需可改为错误)
return BuiltinType.INT;
} }
// 获取最后一个字符判断是否为类型后缀b/s/l/f/d忽略大小写 // 2) 是否带后缀( b/s/l/f不支持 d/D)
char lastChar = raw.charAt(raw.length() - 1); char lastChar = raw.charAt(raw.length() - 1);
char suffix = Character.toLowerCase(lastChar); char suffix = Character.toLowerCase(lastChar);
boolean hasSuffix = switch (suffix) { boolean hasSuffix = switch (suffix) {
case 'b', 's', 'l', 'f', 'd' -> true; case 'b', 's', 'l', 'f' -> true;
default -> false; default -> false;
}; };
// 若有后缀 digits 为去除后缀的数字部分否则为原文本 // 3) 规整数字主体
String digits = hasSuffix ? raw.substring(0, raw.length() - 1) : raw; String digitsNormalized = normalizeDigits(raw);
// 1. 若有后缀直接返回对应类型 // 4) 推断类型
if (hasSuffix) { Type inferred = inferType(hasSuffix, suffix, digitsNormalized);
return switch (suffix) {
case 'b' -> BuiltinType.BYTE;
case 's' -> BuiltinType.SHORT;
case 'l' -> BuiltinType.LONG;
case 'f' -> BuiltinType.FLOAT;
case 'd' -> BuiltinType.DOUBLE;
default -> BuiltinType.INT; // 理论上不会到这里
};
}
// 2. 无后缀根据文本是否含小数点或科学计数法e/E判断类型 // 5) 范围校验(发生越界则收集智能的错误与建议)
if (digits.indexOf('.') >= 0 || digits.indexOf('e') >= 0 || digits.indexOf('E') >= 0) { validateRange(ctx, expr, inferred, digitsNormalized);
return BuiltinType.DOUBLE; // 有小数/科学计数默认 double 类型
}
return BuiltinType.INT; // 否则为纯整数默认 int 类型 return inferred;
} }
} }

View File

@ -0,0 +1,28 @@
package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.BreakNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
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.symbol.SymbolTable;
/**
* {@code BreakAnalyzer} break 语句的语义分析器
* <p>
* 目前不做额外检查是否位于循环体内的限制由 IR 构建阶段StatementBuilder
* 进行安全校验若出现在循环外会抛出清晰的错误
* 如需在语义阶段提前报错可在此处结合循环上下文进行校验
*/
public class BreakAnalyzer implements StatementAnalyzer<BreakNode> {
@Override
public void analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
BreakNode stmt) {
// no-op: break 本身不引入新符号或类型约束
// 在语义阶段校验是否处于循环内需要在 ctx SymbolTable 上增加上下文标记后在此检查
}
}

View File

@ -0,0 +1,42 @@
package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.ContinueNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
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.symbol.SymbolTable;
/**
* {@code ContinueAnalyzer} continue 语句的语义分析器
* <p>
* 当前实现不做任何额外检查continue 是否出现在有效的循环体内
* IR 构建阶段 StatementBuilder负责判定与错误提示
* </p>
* <p>
* 用于 AST {@link ContinueNode} 语句节点
* </p>
*/
public class ContinueAnalyzer implements StatementAnalyzer<ContinueNode> {
/**
* 分析 continue 语句节点的语义
* <p>
* 该方法为 no-op相关语义约束由后续阶段处理
* </p>
*
* @param ctx 语义分析上下文
* @param mi 当前模块信息
* @param fn 当前所在函数节点
* @param locals 当前作用域下的符号表
* @param stmt 需要分析的 continue 语句节点
*/
@Override
public void analyze(Context ctx,
ModuleInfo mi,
FunctionNode fn,
SymbolTable locals,
ContinueNode stmt) {
// no-op: continue 语句的合法性由 IR 构建阶段检查
}
}

View File

@ -12,7 +12,7 @@ import org.jcnc.snow.compiler.semantic.analyzers.statement.*;
* 本类为静态工具类不可实例化其唯一公开方法 {@link #registerAll(AnalyzerRegistry)} * 本类为静态工具类不可实例化其唯一公开方法 {@link #registerAll(AnalyzerRegistry)}
* 应在语义分析初始化阶段调用一次确保所有节点类型都能正确分发到对应分析器 * 应在语义分析初始化阶段调用一次确保所有节点类型都能正确分发到对应分析器
* <p> * <p>
* 注册内容包括: * 注册内容包括:
* <ul> * <ul>
* <li>所有标准语句节点如变量声明赋值条件循环返回等的分析器</li> * <li>所有标准语句节点如变量声明赋值条件循环返回等的分析器</li>
* <li>所有标准表达式节点如字面量标识符函数调用二元表达式等的分析器</li> * <li>所有标准表达式节点如字面量标识符函数调用二元表达式等的分析器</li>
@ -40,6 +40,8 @@ public final class AnalyzerRegistrar {
registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer()); registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer());
registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer()); registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer());
registry.registerExpressionAnalyzer(BoolLiteralNode.class, new BoolLiteralAnalyzer()); registry.registerExpressionAnalyzer(BoolLiteralNode.class, new BoolLiteralAnalyzer());
registry.registerStatementAnalyzer(BreakNode.class, new BreakAnalyzer());
registry.registerStatementAnalyzer(ContinueNode.class, new ContinueAnalyzer());
// 特殊处理: 表达式语句 "foo();"作为语句包装表达式 // 特殊处理: 表达式语句 "foo();"作为语句包装表达式
registry.registerStatementAnalyzer(ExpressionStatementNode.class, registry.registerStatementAnalyzer(ExpressionStatementNode.class,
@ -56,7 +58,7 @@ public final class AnalyzerRegistrar {
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer()); registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
// ---------- 注册一元表达式分析器 ---------- // ---------- 注册一元表达式分析器 ----------
registry.registerExpressionAnalyzer(UnaryExpressionNode.class,new UnaryExpressionAnalyzer()); registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());
// 对尚未实现的表达式类型使用兜底处理器 // 对尚未实现的表达式类型使用兜底处理器
registry.registerExpressionAnalyzer(MemberExpressionNode.class, registry.registerExpressionAnalyzer(MemberExpressionNode.class,

View File

@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.parser.ast.FunctionNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ModuleNode; import org.jcnc.snow.compiler.parser.ast.ModuleNode;
import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
import org.jcnc.snow.compiler.parser.ast.ReturnNode; import org.jcnc.snow.compiler.parser.ast.ReturnNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer; import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
@ -16,7 +17,7 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
* 它逐个遍历所有模块中的函数定义并对函数体中的每一条语句调用对应的语义分析器 * 它逐个遍历所有模块中的函数定义并对函数体中的每一条语句调用对应的语义分析器
* 执行类型检查作用域验证错误记录等任务 * 执行类型检查作用域验证错误记录等任务
* <p> * <p>
* 核心职责包括: * 核心职责包括:
* <ul> * <ul>
* <li>为每个函数构建局部符号表并注册函数参数为变量</li> * <li>为每个函数构建局部符号表并注册函数参数为变量</li>
* <li>分发函数体语句至相应的 {@link StatementAnalyzer}</li> * <li>分发函数体语句至相应的 {@link StatementAnalyzer}</li>
@ -39,7 +40,7 @@ public record FunctionChecker(Context ctx) {
/** /**
* 执行函数体检查流程 * 执行函数体检查流程
* <p> * <p>
* 对所有模块中的所有函数依次进行处理: * 对所有模块中的所有函数依次进行处理:
* <ol> * <ol>
* <li>查找模块对应的 {@link ModuleInfo}</li> * <li>查找模块对应的 {@link ModuleInfo}</li>
* <li>创建函数局部符号表 {@link SymbolTable}并注册所有参数变量</li> * <li>创建函数局部符号表 {@link SymbolTable}并注册所有参数变量</li>
@ -54,11 +55,18 @@ public record FunctionChecker(Context ctx) {
// 获取当前模块对应的语义信息 // 获取当前模块对应的语义信息
ModuleInfo mi = ctx.modules().get(mod.name()); ModuleInfo mi = ctx.modules().get(mod.name());
// 先构建全局符号表
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));
}
// 遍历模块中所有函数定义 // 遍历模块中所有函数定义
for (FunctionNode fn : mod.functions()) { for (FunctionNode fn : mod.functions()) {
// 构建函数局部作用域符号表设置父作用域为 null // 构建函数局部作用域符号表父作用域为 globalScope
SymbolTable locals = new SymbolTable(null); SymbolTable locals = new SymbolTable(globalScope);
// 将函数参数注册为局部变量 // 将函数参数注册为局部变量
fn.parameters().forEach(p -> fn.parameters().forEach(p ->

View File

@ -154,7 +154,7 @@ public final class CompileTask implements Task {
String arg = args[i]; String arg = args[i];
switch (arg) { switch (arg) {
case "run" -> runAfterCompile = true; case "run" -> runAfterCompile = true;
case "-debug", "--debug" -> SnowConfig.MODE = Mode.DEBUG; case "--debug" -> SnowConfig.MODE = Mode.DEBUG;
case "-o" -> { case "-o" -> {
if (i + 1 < args.length) outputName = args[++i]; if (i + 1 < args.length) outputName = args[++i];
else { else {

View File

@ -1,7 +1,11 @@
package org.jcnc.snow.vm.commands.flow.control; package org.jcnc.snow.vm.commands.flow.control;
import org.jcnc.snow.vm.interfaces.Command; import org.jcnc.snow.vm.interfaces.Command;
import org.jcnc.snow.vm.module.*; import org.jcnc.snow.vm.module.CallStack;
import org.jcnc.snow.vm.module.LocalVariableStore;
import org.jcnc.snow.vm.module.OperandStack;
import org.jcnc.snow.vm.module.StackFrame;
import org.jcnc.snow.vm.utils.LoggingUtils;
import static org.jcnc.snow.common.SnowConfig.print; import static org.jcnc.snow.common.SnowConfig.print;
@ -37,7 +41,7 @@ public class RetCommand implements Command {
/* ----- Root frame: do NOT pop, just end program ----- */ /* ----- Root frame: do NOT pop, just end program ----- */
if (topFrame.getReturnAddress() == PROGRAM_END) { if (topFrame.getReturnAddress() == PROGRAM_END) {
System.out.println("Return <root>"); LoggingUtils.logInfo("", "\nReturn <root>");
return PROGRAM_END; // VM main loop should break return PROGRAM_END; // VM main loop should break
} }