Compare commits
87 Commits
main
...
feature/ad
| Author | SHA1 | Date | |
|---|---|---|---|
| f20dc84bc5 | |||
| 784591b407 | |||
| b017f384ce | |||
| 1861d7fe53 | |||
| dc38b4e1a8 | |||
| ef956fb930 | |||
| ba436cb91c | |||
| 94b4bf94cf | |||
| 6dc651d305 | |||
| 5367fc39f6 | |||
| 4ac95dd5ef | |||
| 5f6751b3d4 | |||
| 6428ba5cbd | |||
| 808403e8e8 | |||
| 5c48f4038b | |||
| fefb44cda7 | |||
| 783be100e7 | |||
| 981a1eacdb | |||
| 9e322f3d87 | |||
| 04b43cbd5a | |||
| 0cb8abde80 | |||
| d258297609 | |||
| 426452f63f | |||
| 8ff103e105 | |||
| 8bc431091c | |||
| ce2b480e90 | |||
| 9f7628aded | |||
| bd37142a4b | |||
| f0e3bfe05c | |||
| c99614869f | |||
| 67d1949a25 | |||
| 5ec4f9ec6a | |||
| a57d666241 | |||
| 416f3fdeb1 | |||
| 419057c3c2 | |||
| b4a0bccb66 | |||
| 9ac8b6166a | |||
| f00d99d748 | |||
| 4b90f15371 | |||
| cbd8b24c4c | |||
| 1e832dc8bf | |||
| 513aeba2bc | |||
| cff45a9d17 | |||
| db720ea357 | |||
| 35fdb25d27 | |||
| 0c2a888e86 | |||
| 584e226051 | |||
| 586ede1cf0 | |||
| 17b078b6f3 | |||
| fea8e14245 | |||
| 49cb89c9f2 | |||
| 3942a22e67 | |||
| f03a44d2a5 | |||
| 1835b85c5c | |||
| 1ab4fb2e87 | |||
| 2abe056056 | |||
| a34f51dee1 | |||
| 8902748cde | |||
| 258d1909e6 | |||
| 1f7458be28 | |||
| 093e5a26b9 | |||
| 4b633bae70 | |||
| 8f59f91766 | |||
| 37c03cf248 | |||
| a8f1789fe0 | |||
| cdfcbf0d6f | |||
| b56824d935 | |||
| 2dd5e64c3a | |||
| 5b259a01d8 | |||
| 82578880ae | |||
| fb441c01af | |||
| 1a34e3e436 | |||
| 4a64659856 | |||
| 325ca67872 | |||
| 86a78bd1c6 | |||
| 50f4fbb82d | |||
| 3dd71ff742 | |||
| 8fce695e8f | |||
| 45a13fd15e | |||
| e758b7ad86 | |||
| df2fb3a7f4 | |||
| 35bf118abc | |||
| df4997600b | |||
| 306f6f91e1 | |||
| f75af0dc9b | |||
| 3583e9c67c | |||
| 7b6e9fa3f0 |
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo1 -o target/Demo1 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo1 -o target/Demo1" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo10 -o target/Demo10 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo10 -o target/Demo10" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo11 -o target/Demo11 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo11 -o target/Demo11" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo12 -o target/Demo12 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo12 -o target/Demo12" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Demo13" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo13 -o target/Demo13 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo13 -o target/Demo13" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<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" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo15 -o target/Demo15" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<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" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo16 -o target/Demo16 " />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<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" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo17 -o target/Demo17" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<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" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo18 -o target/Demo18" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<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" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo19 -o target/Demo19" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo2 -o target/Demo2 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo2 -o target/Demo2" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<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" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo20 -o target/Demo20" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<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" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo21 -o target/Demo21" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Demo23" 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/Demo23 -o target/Demo23 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo23 -o target/Demo23" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
10
.run/Demo26.run.xml
Normal file
10
.run/Demo26.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo26" 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/Demo26 -o target/Demo26" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
10
.run/Demo27.run.xml
Normal file
10
.run/Demo27.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo27" 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/Demo27 -o target/Demo27" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
10
.run/Demo28.run.xml
Normal file
10
.run/Demo28.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo28" 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/Demo28 -o target/Demo28" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
10
.run/Demo29.run.xml
Normal file
10
.run/Demo29.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo29" 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/Demo29 -o target/Demo29" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo3 -o target/Demo3 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo3 -o target/Demo3" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
10
.run/Demo30.run.xml
Normal file
10
.run/Demo30.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo30" 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/Demo30 -o target/Demo30" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
10
.run/Demo31.run.xml
Normal file
10
.run/Demo31.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo31" 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/Demo31 -o target/Demo31 " />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
10
.run/Demo32.run.xml
Normal file
10
.run/Demo32.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo32" 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/Demo32 -o target/Demo32" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo4 -o target/Demo4 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo4 -o target/Demo4" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo5 -o target/Demo5 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo5 -o target/Demo5" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo6 -o target/Demo6 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo6 -o target/Demo6" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo7 -o target/Demo7 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo7 -o target/Demo7" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo8 -o target/Demo8 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo8 -o target/Demo8" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo9 -o target/Demo9 --debug" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo9 -o target/Demo9" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@ -26,4 +26,33 @@
|
||||
<toRun name="Demo9" type="Application" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration default="false" name="测试" type="CompoundRunConfigurationType">
|
||||
<toRun name="Demo1" type="Application" />
|
||||
<toRun name="Demo10" type="Application" />
|
||||
<toRun name="Demo11" type="Application" />
|
||||
<toRun name="Demo12" 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="Demo18" type="Application" />
|
||||
<toRun name="Demo19" type="Application" />
|
||||
<toRun name="Demo2" type="Application" />
|
||||
<toRun name="Demo20" type="Application" />
|
||||
<toRun name="Demo21" type="Application" />
|
||||
<toRun name="Demo22" type="Application" />
|
||||
<toRun name="Demo23" type="Application" />
|
||||
<toRun name="Demo24" type="Application" />
|
||||
<toRun name="Demo25" type="Application" />
|
||||
<toRun name="Demo26" type="Application" />
|
||||
<toRun name="Demo28" type="Application" />
|
||||
<toRun name="Demo3" type="Application" />
|
||||
<toRun name="Demo4" type="Application" />
|
||||
<toRun name="Demo6" type="Application" />
|
||||
<toRun name="Demo7" type="Application" />
|
||||
<toRun name="Demo8" type="Application" />
|
||||
<toRun name="Demo9" type="Application" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
74
playground/Demo/Demo27/Test1.snow
Normal file
74
playground/Demo/Demo27/Test1.snow
Normal file
@ -0,0 +1,74 @@
|
||||
// 定义模块
|
||||
module: Main
|
||||
// 定义一个结构体 Counter
|
||||
struct: Counter
|
||||
fields:
|
||||
declare value: int
|
||||
|
||||
// 构造方法
|
||||
init:
|
||||
params:
|
||||
v: int
|
||||
body:
|
||||
this.value = v
|
||||
end body
|
||||
end init
|
||||
|
||||
// 方法:打印 value
|
||||
function: printValue
|
||||
returns: void
|
||||
body:
|
||||
os.print(this.value)
|
||||
end body
|
||||
end function
|
||||
|
||||
// 方法:打印 value + 100
|
||||
function: printSum
|
||||
returns: void
|
||||
body:
|
||||
os.print(this.value + 100)
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
|
||||
// 定义 AdvancedCounter,继承 Counter
|
||||
struct: AdvancedCounter extends Counter
|
||||
fields:
|
||||
declare offset: int
|
||||
|
||||
// 构造方法:调用父类构造(使用 init)
|
||||
init:
|
||||
params:
|
||||
v: int
|
||||
o: int
|
||||
body:
|
||||
super(v) // 调用父类构造方法
|
||||
this.offset = o
|
||||
end body
|
||||
end init
|
||||
|
||||
// 重写 printSum:打印 value + offset
|
||||
function: printSum
|
||||
returns: void
|
||||
body:
|
||||
os.print(this.value + this.offset)
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
|
||||
// 程序入口
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare c: Counter = new Counter(42)
|
||||
c.printValue() // 打印 42
|
||||
c.printSum() // 打印 142
|
||||
|
||||
declare ac: AdvancedCounter = new AdvancedCounter(42, 200)
|
||||
ac.printValue() // 打印 42 (继承自父类)
|
||||
ac.printSum() // 打印 242 (子类重写)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
135
playground/Demo/Demo27/Test2.snow
Normal file
135
playground/Demo/Demo27/Test2.snow
Normal file
@ -0,0 +1,135 @@
|
||||
// 演示 Snow OOP 四大要点:封装、继承、多态、重载
|
||||
module: Main
|
||||
// 封装:基类 Animal
|
||||
struct: Animal
|
||||
fields:
|
||||
declare name: string
|
||||
|
||||
init:
|
||||
params:
|
||||
n: string
|
||||
body:
|
||||
this.name = n
|
||||
end body
|
||||
end init
|
||||
|
||||
// 封装:提供只读访问器
|
||||
function: getName
|
||||
returns: string
|
||||
body:
|
||||
return this.name
|
||||
end body
|
||||
end function
|
||||
|
||||
// 多态:基类方法,默认虚
|
||||
function: speak
|
||||
returns: void
|
||||
body:
|
||||
os.print(this.name + " makes a sound.")
|
||||
end body
|
||||
end function
|
||||
|
||||
// 重载示例:同名 eat,不同参数
|
||||
function: eat
|
||||
params:
|
||||
food: string
|
||||
returns: void
|
||||
body:
|
||||
os.print(this.name + " eats " + food)
|
||||
end body
|
||||
end function
|
||||
|
||||
function: eat
|
||||
params:
|
||||
times: int
|
||||
returns: void
|
||||
body:
|
||||
os.print(this.name + " eats " + times + " times.")
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
|
||||
// 继承 + 多态:Dog
|
||||
struct: Dog extends Animal
|
||||
init:
|
||||
params:
|
||||
n: string
|
||||
body:
|
||||
super(n)
|
||||
end body
|
||||
end init
|
||||
|
||||
// 重写 speak
|
||||
function: speak
|
||||
returns: void
|
||||
body:
|
||||
os.print(this.name + " says: Woof!")
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
|
||||
// 继承 + 多态:Cat
|
||||
struct: Cat extends Animal
|
||||
init:
|
||||
params:
|
||||
n: string
|
||||
body:
|
||||
super(n)
|
||||
end body
|
||||
end init
|
||||
|
||||
// 重写 speak
|
||||
function: speak
|
||||
returns: void
|
||||
body:
|
||||
os.print(this.name + " says: Meow!")
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
|
||||
// 多态演示函数
|
||||
function: letAnimalSpeak
|
||||
params:
|
||||
a: Animal
|
||||
returns: void
|
||||
body:
|
||||
// 调用多态方法
|
||||
a.speak()
|
||||
end body
|
||||
end function
|
||||
|
||||
|
||||
// 程序入口
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
// 基类结构体
|
||||
declare a: Animal = new Animal("GenericAnimal")
|
||||
a.speak() // -> GenericAnimal makes a sound.
|
||||
a.eat("food") // -> GenericAnimal eats food
|
||||
a.eat(2) // -> GenericAnimal eats 2 times.
|
||||
|
||||
// 子结构体 Dog
|
||||
declare d: Dog = new Dog("Buddy")
|
||||
d.speak() // -> Buddy says: Woof!
|
||||
d.eat("bone") // 调用基类 eat(String)
|
||||
d.eat(3) // 调用基类 eat(int)
|
||||
|
||||
// 子类结构体 Cat
|
||||
declare c: Cat = new Cat("Kitty")
|
||||
c.speak() // -> Kitty says: Meow!
|
||||
c.eat("fish")
|
||||
c.eat(1)
|
||||
|
||||
// 多态:父类引用指向子类结构体
|
||||
declare poly: Animal = new Dog("Max")
|
||||
letAnimalSpeak(poly) // -> Max says: Woof!
|
||||
|
||||
poly = new Cat("Luna")
|
||||
letAnimalSpeak(poly) // -> Luna says: Meow!
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
54
playground/Demo/Demo28/Main.snow
Normal file
54
playground/Demo/Demo28/Main.snow
Normal file
@ -0,0 +1,54 @@
|
||||
module: Main
|
||||
import: os
|
||||
// Animal结构体
|
||||
struct: Animal
|
||||
// 字段
|
||||
fields:
|
||||
declare name: string
|
||||
declare age: int
|
||||
|
||||
// 构造函数
|
||||
init:
|
||||
params:
|
||||
n: string
|
||||
a: int
|
||||
body:
|
||||
this.name = n
|
||||
this.age = a
|
||||
end body
|
||||
end init
|
||||
|
||||
function: getAge
|
||||
returns: int
|
||||
body:
|
||||
// 获取字段 age
|
||||
return this.age
|
||||
end body
|
||||
end function
|
||||
|
||||
function: setAge
|
||||
params:
|
||||
declare a: int
|
||||
body:
|
||||
// 设置字段 age
|
||||
this.age = a
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
// 程序入口
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
// 实例化一个叫A的Animal,并且调用构造函数
|
||||
declare a: Animal = new Animal("GenericAnimal", 1)
|
||||
// 直接调用已导入模块内的静态函数
|
||||
a.setAge(2)
|
||||
os.println(a.getAge())
|
||||
a.setAge(3)
|
||||
os.println(a.getAge())
|
||||
|
||||
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
18
playground/Demo/Demo28/OS.snow
Normal file
18
playground/Demo/Demo28/OS.snow
Normal file
@ -0,0 +1,18 @@
|
||||
module: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT", i1)
|
||||
end body
|
||||
end function
|
||||
function: println
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINTLN", i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
65
playground/Demo/Demo29/Main.snow
Normal file
65
playground/Demo/Demo29/Main.snow
Normal file
@ -0,0 +1,65 @@
|
||||
module: Main
|
||||
import: os
|
||||
struct: Address
|
||||
fields:
|
||||
declare country: int
|
||||
declare city: int
|
||||
init:
|
||||
params:
|
||||
country: int
|
||||
city: int
|
||||
body:
|
||||
this.country = country
|
||||
this.city = city
|
||||
end body
|
||||
end init
|
||||
|
||||
function: getCity
|
||||
returns: int
|
||||
body:
|
||||
return this.city
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
struct: Person
|
||||
fields:
|
||||
declare name: int
|
||||
declare addr: Address
|
||||
init:
|
||||
params:
|
||||
name: int
|
||||
addr: Address
|
||||
body:
|
||||
this.name = name
|
||||
this.addr = addr
|
||||
end body
|
||||
end init
|
||||
|
||||
function: getName
|
||||
returns: int
|
||||
body:
|
||||
return this.name
|
||||
end body
|
||||
end function
|
||||
|
||||
function: getAddr
|
||||
returns: Address
|
||||
body:
|
||||
return this.addr
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
// 初始化 Person 和 Address 的实例
|
||||
declare p: Person = new Person(123, new Address(1, 2))
|
||||
os.println(p.getName()) // 打印 name
|
||||
os.println(p.getAddr().getCity()) // 打印 city
|
||||
|
||||
end body
|
||||
end function
|
||||
|
||||
end module
|
||||
18
playground/Demo/Demo29/OS.snow
Normal file
18
playground/Demo/Demo29/OS.snow
Normal file
@ -0,0 +1,18 @@
|
||||
module: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT", i1)
|
||||
end body
|
||||
end function
|
||||
function: println
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINTLN", i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
66
playground/Demo/Demo30/Main.snow
Normal file
66
playground/Demo/Demo30/Main.snow
Normal file
@ -0,0 +1,66 @@
|
||||
module: Main
|
||||
import: os
|
||||
struct: Address
|
||||
fields:
|
||||
declare country: int
|
||||
declare city: int
|
||||
init:
|
||||
params:
|
||||
country: int
|
||||
city: int
|
||||
body:
|
||||
this.country = country
|
||||
this.city = city
|
||||
end body
|
||||
end init
|
||||
|
||||
function: getCity
|
||||
returns: int
|
||||
body:
|
||||
return this.city
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
struct: Person
|
||||
fields:
|
||||
declare name: int
|
||||
declare addr: Address
|
||||
init:
|
||||
params:
|
||||
name: int
|
||||
addr: Address
|
||||
body:
|
||||
this.name = name
|
||||
this.addr = addr
|
||||
end body
|
||||
end init
|
||||
|
||||
function: getName
|
||||
returns: int
|
||||
body:
|
||||
return this.name
|
||||
end body
|
||||
end function
|
||||
|
||||
function: getAddr
|
||||
returns: Address
|
||||
body:
|
||||
return this.addr
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
// 初始化 Person 和 Address 的实例
|
||||
declare p: Person = new Person(123, new Address(1, 2))
|
||||
declare a: Person = p
|
||||
os.println(a.getName()) // -> 123
|
||||
os.println(a.getAddr().getCity()) // -> 2
|
||||
|
||||
end body
|
||||
end function
|
||||
|
||||
end module
|
||||
18
playground/Demo/Demo30/OS.snow
Normal file
18
playground/Demo/Demo30/OS.snow
Normal file
@ -0,0 +1,18 @@
|
||||
module: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT", i1)
|
||||
end body
|
||||
end function
|
||||
function: println
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINTLN", i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
56
playground/Demo/Demo31/Main.snow
Normal file
56
playground/Demo/Demo31/Main.snow
Normal file
@ -0,0 +1,56 @@
|
||||
module: Main
|
||||
import: os
|
||||
|
||||
// 父类 Person
|
||||
struct: Person
|
||||
fields:
|
||||
declare name: int
|
||||
init:
|
||||
params:
|
||||
name: int
|
||||
body:
|
||||
this.name = name
|
||||
end body
|
||||
end init
|
||||
|
||||
function: getName
|
||||
returns: int
|
||||
body:
|
||||
return this.name
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
// 子类 Student 继承 Person
|
||||
struct: Student extends Person
|
||||
fields:
|
||||
declare studentId: int
|
||||
init:
|
||||
params:
|
||||
name: int
|
||||
studentId: int
|
||||
body:
|
||||
super(name) // 调用父类构造函数
|
||||
this.studentId = studentId
|
||||
end body
|
||||
end init
|
||||
|
||||
function: getStudentId
|
||||
returns: int
|
||||
body:
|
||||
return this.studentId
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare s: Student = new Student(123, 1001)
|
||||
|
||||
// Student 拥有父类方法
|
||||
os.println(s.getName()) // 来自 Person → 123
|
||||
os.println(s.getStudentId()) // 来自 Student → 1001
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
18
playground/Demo/Demo31/OS.snow
Normal file
18
playground/Demo/Demo31/OS.snow
Normal file
@ -0,0 +1,18 @@
|
||||
module: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT", i1)
|
||||
end body
|
||||
end function
|
||||
function: println
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINTLN", i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
55
playground/Demo/Demo32/Main.snow
Normal file
55
playground/Demo/Demo32/Main.snow
Normal file
@ -0,0 +1,55 @@
|
||||
module: main
|
||||
import: os
|
||||
|
||||
struct: Student
|
||||
fields:
|
||||
declare name: int
|
||||
declare studentId: int
|
||||
|
||||
init:
|
||||
params:
|
||||
name: int
|
||||
studentId: int
|
||||
body:
|
||||
this.name = name
|
||||
this.studentId = studentId
|
||||
end body
|
||||
end init
|
||||
|
||||
init:
|
||||
params:
|
||||
body:
|
||||
this.name = 0
|
||||
this.studentId = 0
|
||||
end body
|
||||
end init
|
||||
|
||||
init:
|
||||
params:
|
||||
name: int
|
||||
body:
|
||||
this.name = name
|
||||
this.studentId = -1
|
||||
end body
|
||||
end init
|
||||
|
||||
function: getStudentId
|
||||
returns: int
|
||||
body:
|
||||
return this.studentId
|
||||
end body
|
||||
end function
|
||||
end struct
|
||||
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare a: Student = new Student(123, 1001)
|
||||
declare b: Student = new Student()
|
||||
declare c: Student = new Student(88)
|
||||
println(a.getStudentId())
|
||||
println(b.getStudentId())
|
||||
println(b.getStudentId())
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
18
playground/Demo/Demo32/OS.snow
Normal file
18
playground/Demo/Demo32/OS.snow
Normal file
@ -0,0 +1,18 @@
|
||||
module: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT", i1)
|
||||
end body
|
||||
end function
|
||||
function: println
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINTLN", i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,50 +1,66 @@
|
||||
package org.jcnc.snow.compiler.backend.builder;
|
||||
|
||||
import org.jcnc.snow.vm.engine.VMOpCode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* VMProgramBuilder 用于构建虚拟机(VM)的最终指令列表。
|
||||
* <p>
|
||||
* 主要职责:
|
||||
* {@code VMProgramBuilder} 负责后端阶段的 VM 指令序列组装及符号修补。
|
||||
* <ul>
|
||||
* <li>维护代码指令序列和符号地址表</li>
|
||||
* <li>支持跨函数、标签跳转的延后修补(Call/Branch Fixup)</li>
|
||||
* <li>支持虚拟机本地槽位类型的管理(如 I/F...)</li>
|
||||
* <li>管理函数与标签到指令地址的映射</li>
|
||||
* <li>支持 CALL 和分支指令的延迟回填(符号修补)</li>
|
||||
* <li>支持槽位类型标注(用于类型检查和后端优化,可选)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>符号修补机制:</b></p>
|
||||
* <ul>
|
||||
* <li>支持根据函数简名进行匹配,例如 Student.getName 可匹配到 Person.getName,实现方法复用和继承支持。</li>
|
||||
* <li>自动处理 super 调用的符号绑定,例如 Student.super 可自动绑定到 Person.__init__。</li>
|
||||
* <li>所有未解决的 CALL 或分支符号在 build() 阶段将抛出异常,便于调试和定位错误。</li>
|
||||
* </ul>
|
||||
*/
|
||||
|
||||
public final class VMProgramBuilder {
|
||||
|
||||
/**
|
||||
* 未知目标的 CALL 指令修补记录(待目标地址确定后修正)。
|
||||
* @param index CALL 指令在 code 列表中的位置
|
||||
* @param target 目标函数的全名
|
||||
* @param nArgs 参数个数
|
||||
* 未解析地址的占位符,便于后期批量修补
|
||||
*/
|
||||
private record CallFix(int index, String target, int nArgs) {}
|
||||
private static final String PLACEHOLDER = "-1";
|
||||
/**
|
||||
* VM 指令列表
|
||||
*/
|
||||
private final List<String> code = new ArrayList<>();
|
||||
/**
|
||||
* 槽位(寄存器)类型映射表(如 I/F...,用于类型检查或代码生成优化)
|
||||
*/
|
||||
private final Map<Integer, Character> slotType = new HashMap<>();
|
||||
/**
|
||||
* 符号(函数名/标签)到指令序号的映射表
|
||||
*/
|
||||
private final Map<String, Integer> addr = new HashMap<>();
|
||||
/**
|
||||
* 所有待修补的 CALL 指令集合
|
||||
*/
|
||||
private final List<CallFix> callFixes = new ArrayList<>();
|
||||
/**
|
||||
* 所有待修补的分支指令集合
|
||||
*/
|
||||
private final List<BranchFix> branchFixes = new ArrayList<>();
|
||||
/**
|
||||
* 当前代码指针(已生成指令的数量/下一个指令的位置)
|
||||
*/
|
||||
private int pc = 0;
|
||||
|
||||
/**
|
||||
* 未知目标的分支指令修补记录(待目标标签确定后修正)。
|
||||
* @param index 分支指令在 code 列表中的位置
|
||||
* @param label 跳转目标标签名
|
||||
* 获取符号名最后一段(即最后一个 '.' 之后的名字,若无 '.' 则为原名)。
|
||||
*
|
||||
* @param sym 符号全名
|
||||
* @return 简名
|
||||
*/
|
||||
private record BranchFix(int index, String label) {}
|
||||
|
||||
/** 未解析地址的占位符,便于后期批量修补 */
|
||||
private static final String PLACEHOLDER = "-1";
|
||||
|
||||
/** VM 指令列表 */
|
||||
private final List<String> code = new ArrayList<>();
|
||||
/** 槽位(寄存器)类型映射表(如 I/F...,用于类型检查或代码生成优化) */
|
||||
private final Map<Integer, Character> slotType = new HashMap<>();
|
||||
/** 符号(函数名/标签)到指令序号的映射表 */
|
||||
private final Map<String, Integer> addr = new HashMap<>();
|
||||
/** 所有待修补的 CALL 指令集合 */
|
||||
private final List<CallFix> callFixes = new ArrayList<>();
|
||||
/** 所有待修补的分支指令集合 */
|
||||
private final List<BranchFix> branchFixes = new ArrayList<>();
|
||||
/** 当前代码指针(已生成指令的数量/下一个指令的位置) */
|
||||
private int pc = 0;
|
||||
private static String lastSegment(String sym) {
|
||||
int p = sym.lastIndexOf('.');
|
||||
return (p >= 0 && p < sym.length() - 1) ? sym.substring(p + 1) : sym;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置槽位(局部变量/虚拟寄存器)的类型前缀。
|
||||
@ -56,6 +72,8 @@ public final class VMProgramBuilder {
|
||||
slotType.put(slot, prefix);
|
||||
}
|
||||
|
||||
// ============================== 槽位类型操作 ==============================
|
||||
|
||||
/**
|
||||
* 获取槽位的类型前缀,默认为 'I'(整数类型)。
|
||||
*
|
||||
@ -67,12 +85,9 @@ public final class VMProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记一个函数或标签的起始位置。
|
||||
* <p>
|
||||
* 1. 记录符号到当前 pc 的映射;
|
||||
* 2. 立即尝试修补之前所有针对该符号的延后调用和分支。
|
||||
* 声明一个函数/标签的起始地址,并尝试修补所有引用到此符号的 CALL/BRANCH。
|
||||
*
|
||||
* @param name 函数名或标签名(全限定名)
|
||||
* @param name 函数或标签全名(如 "Person.getName"、"loop.start")
|
||||
*/
|
||||
public void beginFunction(String name) {
|
||||
addr.put(name, pc);
|
||||
@ -80,17 +95,21 @@ public final class VMProgramBuilder {
|
||||
patchBranchFixes(name);
|
||||
}
|
||||
|
||||
/** 函数结尾的处理(占位,无需特殊处理)。 */
|
||||
public void endFunction() {}
|
||||
// =============函数/标签声明与指令生成=================
|
||||
|
||||
/**
|
||||
* 添加一条指令或标签到代码列表。
|
||||
* 函数结束接口,目前无具体实现,便于将来扩展。
|
||||
*/
|
||||
public void endFunction() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条 VM 指令或标签(末尾':'视为标签)。
|
||||
*
|
||||
* @param line 指令字符串或标签字符串(若以冒号结尾为标签)
|
||||
* @param line 指令或标签
|
||||
*/
|
||||
public void emit(String line) {
|
||||
if (line.endsWith(":")) {
|
||||
// 标签定义
|
||||
String label = line.substring(0, line.length() - 1);
|
||||
addr.put(label, pc);
|
||||
patchBranchFixes(label);
|
||||
@ -101,10 +120,10 @@ public final class VMProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条 CALL 指令,若目标未定义则延后修补。
|
||||
* 添加一条 CALL 指令。目标尚未声明时,使用占位符并登记延迟修补。
|
||||
*
|
||||
* @param target 目标函数全名
|
||||
* @param nArgs 参数个数
|
||||
* @param target 目标函数全名(IR 侧生成)
|
||||
* @param nArgs 实参个数
|
||||
*/
|
||||
public void emitCall(String target, int nArgs) {
|
||||
Integer a = resolve(target);
|
||||
@ -125,9 +144,9 @@ public final class VMProgramBuilder {
|
||||
public void emitBranch(String opcode, String label) {
|
||||
Integer a = resolve(label);
|
||||
if (a != null) {
|
||||
emit(opcode + ' ' + a);
|
||||
emit(opcode + " " + a);
|
||||
} else {
|
||||
emit(opcode + ' ' + PLACEHOLDER);
|
||||
emit(opcode + " " + PLACEHOLDER);
|
||||
branchFixes.add(new BranchFix(pc - 1, label));
|
||||
}
|
||||
}
|
||||
@ -143,8 +162,8 @@ public final class VMProgramBuilder {
|
||||
public List<String> build() {
|
||||
if (!callFixes.isEmpty() || !branchFixes.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"Unresolved symbols — CALL: " + callFixes +
|
||||
", BRANCH: " + branchFixes);
|
||||
"Unresolved symbols — CALL: " + callFixes + ", BRANCH: " + branchFixes
|
||||
);
|
||||
}
|
||||
return List.copyOf(code);
|
||||
}
|
||||
@ -160,17 +179,36 @@ public final class VMProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修补所有等待目标函数 name 的 CALL 指令。
|
||||
* <p>
|
||||
* 只支持全名精确修补,不做模糊查找或短名回退。
|
||||
*
|
||||
* @param name 目标函数全名
|
||||
* 修补所有延迟 CALL。
|
||||
* 匹配规则:
|
||||
* 1. 全限定名完全匹配
|
||||
* 2. super 调用支持多构造:target 以 .super 结尾,name 以 .__init__N 结尾且前缀匹配
|
||||
* 3. 简名匹配(含 __init__N)
|
||||
*/
|
||||
private void patchCallFixes(String name) {
|
||||
for (Iterator<CallFix> it = callFixes.iterator(); it.hasNext();) {
|
||||
String nameSimple = lastSegment(name);
|
||||
|
||||
for (Iterator<CallFix> it = callFixes.iterator(); it.hasNext(); ) {
|
||||
CallFix f = it.next();
|
||||
if (f.target.equals(name)) {
|
||||
code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs);
|
||||
|
||||
// 全限定名完全匹配
|
||||
boolean qualifiedMatch = f.target.equals(name);
|
||||
|
||||
// super 调用绑定:target 以 .super,name 以 .__init__N 且前缀匹配
|
||||
boolean superMatch = false;
|
||||
if (f.target.endsWith(".super") && name.contains(".__init__")) {
|
||||
String tStruct = f.target.substring(0, f.target.length() - 6); // 去掉 ".super"
|
||||
String nStruct = name.substring(0, name.indexOf(".__init__"));
|
||||
superMatch = tStruct.equals(nStruct);
|
||||
}
|
||||
|
||||
// 简名匹配(如 getName, __init__N 等)
|
||||
String targetSimple = lastSegment(f.target);
|
||||
boolean simpleMatch = targetSimple.equals(nameSimple);
|
||||
|
||||
if (qualifiedMatch || superMatch || simpleMatch) {
|
||||
int targetAddr = addr.get(name);
|
||||
code.set(f.index, VMOpCode.CALL + " " + targetAddr + " " + f.nArgs);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
@ -182,7 +220,7 @@ public final class VMProgramBuilder {
|
||||
* @param label 目标标签
|
||||
*/
|
||||
private void patchBranchFixes(String label) {
|
||||
for (Iterator<BranchFix> it = branchFixes.iterator(); it.hasNext();) {
|
||||
for (Iterator<BranchFix> it = branchFixes.iterator(); it.hasNext(); ) {
|
||||
BranchFix f = it.next();
|
||||
if (f.label.equals(label)) {
|
||||
String patched = code.get(f.index).replace(PLACEHOLDER, addr.get(label).toString());
|
||||
@ -191,4 +229,23 @@ public final class VMProgramBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 未知目标的 CALL 指令修补记录(待目标地址确定后修正)。
|
||||
*
|
||||
* @param index CALL 指令在 code 列表中的位置
|
||||
* @param target 目标函数的全名
|
||||
* @param nArgs 参数个数
|
||||
*/
|
||||
private record CallFix(int index, String target, int nArgs) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 未知目标的分支指令修补记录(待目标标签确定后修正)。
|
||||
*
|
||||
* @param index 分支指令在 code 列表中的位置
|
||||
* @param label 跳转目标标签名
|
||||
*/
|
||||
private record BranchFix(int index, String label) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,40 +16,62 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令。
|
||||
* CallGenerator 负责将 IR 层的 CallInstruction 转换为 VM 层的函数调用指令。
|
||||
* <p>
|
||||
* 支持 syscall、普通函数调用、一维/多维数组下标访问、以及字符串常量池绑定等功能。
|
||||
* </p>
|
||||
*
|
||||
* 功能包括:
|
||||
* <ul>
|
||||
* <li>syscall: 支持字符串常量与寄存器到子命令的绑定与解析</li>
|
||||
* <li>数组下标访问: 支持所有主流基础类型 “__index_b/s/i/l/f/d/r”</li>
|
||||
* <li>普通函数: 支持自动推断返回值类型与槽位类型</li>
|
||||
* <li>处理系统调用(syscall),支持字符串常量池</li>
|
||||
* <li>数组元素访问及赋值(__index / __setindex 系列)</li>
|
||||
* <li>普通函数调用,根据返回类型选择合适的 STORE 指令</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
|
||||
/**
|
||||
* 字符串常量池:用于绑定虚拟寄存器 id 到字符串值(供 syscall 子命令使用)。
|
||||
* <br>
|
||||
* 使用 ConcurrentHashMap 保证并发安全,所有 registerStringConst 的注册和读取都是线程安全的。
|
||||
* 字符串常量池,用于 syscall 子命令的字符串存储,
|
||||
* 键为虚拟寄存器 ID,值为字符串常量。
|
||||
*/
|
||||
private static final Map<Integer, String> STRING_CONST_POOL = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 注册一个字符串常量,绑定到虚拟寄存器 id。
|
||||
* 当前正在生成的函数名,用于错误上下文。
|
||||
*/
|
||||
private String fn;
|
||||
|
||||
/**
|
||||
* 注册字符串常量到常量池。
|
||||
*
|
||||
* @param regId 虚拟寄存器 id
|
||||
* @param value 字符串常量
|
||||
* @param regId 虚拟寄存器 ID
|
||||
* @param value 字符串常量值
|
||||
*/
|
||||
public static void registerStringConst(int regId, String value) {
|
||||
STRING_CONST_POOL.put(regId, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回本生成器支持的 IR 指令类型。
|
||||
* 将语言层类型名映射为 VM 指令的类型前缀字符。
|
||||
*
|
||||
* @return {@link CallInstruction} 类型的 Class 对象
|
||||
* @param name 类型名称,如 "int", "double", "string"
|
||||
* @return 对应的 VM 类型前缀('I','D','R' 等)
|
||||
*/
|
||||
private static char normalizeTypePrefix(String name) {
|
||||
if (name == null) return 'I';
|
||||
String n = name.toLowerCase(Locale.ROOT);
|
||||
return switch (n) {
|
||||
case "byte" -> 'B';
|
||||
case "short" -> 'S';
|
||||
case "int", "integer", "bool", "boolean" -> 'I';
|
||||
case "long" -> 'L';
|
||||
case "float" -> 'F';
|
||||
case "double" -> 'D';
|
||||
case "string" -> 'R'; // 字符串为引用类型
|
||||
case "void" -> 'V';
|
||||
default -> 'R'; // 结构体、自定义对象均视为引用类型
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回本生成器支持的 IR 指令类型,此处为 CallInstruction。
|
||||
*/
|
||||
@Override
|
||||
public Class<CallInstruction> supportedClass() {
|
||||
@ -57,12 +79,12 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成指定的调用指令,包括 syscall、数组下标、普通函数。
|
||||
* 核心生成方法,根据函数名分发到 syscall、数组操作或普通调用的处理逻辑。
|
||||
*
|
||||
* @param ins IR 层的调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位的映射表
|
||||
* @param currentFn 当前函数名
|
||||
* @param ins 待转换的 IR 调用指令
|
||||
* @param out VM 程序构建器,用于输出指令
|
||||
* @param slotMap IR 虚拟寄存器到 VM 本地槽位的映射
|
||||
* @param currentFn 当前所在的函数名,用于上下文传递
|
||||
*/
|
||||
@Override
|
||||
public void generate(CallInstruction ins,
|
||||
@ -71,13 +93,13 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
String currentFn) {
|
||||
String fn = ins.getFunctionName();
|
||||
|
||||
// 特殊处理 syscall 调用
|
||||
// 处理特殊的 syscall 调用
|
||||
if ("syscall".equals(fn) || fn.endsWith(".syscall")) {
|
||||
generateSyscall(ins, out, slotMap, fn);
|
||||
return;
|
||||
}
|
||||
|
||||
// 各种一维数组类型(byte/short/int/long/float/double/boolean)读取
|
||||
// 处理数组读取和写入的内置函数
|
||||
switch (fn) {
|
||||
case "__index_b" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'B');
|
||||
@ -107,7 +129,6 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'R');
|
||||
return;
|
||||
}
|
||||
|
||||
case "__setindex_b" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'B');
|
||||
return;
|
||||
@ -138,24 +159,17 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
}
|
||||
}
|
||||
|
||||
// 普通函数调用
|
||||
// 默认:普通函数调用
|
||||
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,则抛出异常
|
||||
* @param ins 调用 __setindex_* 的 IR 指令
|
||||
* @param out VM 指令构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param valType 元素值的类型前缀(如 'I','R' 等)
|
||||
*/
|
||||
private void generateSetIndexInstruction(CallInstruction ins,
|
||||
VMProgramBuilder out,
|
||||
@ -163,264 +177,189 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
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)
|
||||
);
|
||||
throw new IllegalStateException("[CallGenerator] __setindex_* 需要三个参数 (arr, idx, value),实际: " + args);
|
||||
}
|
||||
loadArgument(out, slotMap, args.get(0), 'R', ins.getFunctionName()); // 数组引用
|
||||
loadArgument(out, slotMap, args.get(1), 'I', ins.getFunctionName()); // 索引
|
||||
loadArgument(out, slotMap, args.get(2), valType, ins.getFunctionName()); // 值
|
||||
out.emit(VMOpCode.SYSCALL + " ARR_SET");
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载一个参数到栈,支持指定默认类型。如果未设置类型则采用默认类型。
|
||||
* 生成数组元素读取指令。
|
||||
*
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位的映射
|
||||
* @param arg 参数值(应为虚拟寄存器)
|
||||
* @param defaultType 默认类型
|
||||
* @param fn 当前函数名(用于错误提示)
|
||||
* @throws IllegalStateException 如果参数不是虚拟寄存器,或槽位未找到
|
||||
* @param ins 调用 __index_* 的 IR 指令
|
||||
* @param out VM 指令构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param retType 返回值类型前缀
|
||||
*/
|
||||
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) {
|
||||
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));
|
||||
throw new IllegalStateException("[CallGenerator] " + fn + " 需要两个参数 (arr, idx),实际: " + args);
|
||||
}
|
||||
// 加载数组参数(寄存器类型按 R/ref 处理,默认对象槽位)
|
||||
loadArgument(out, slotMap, args.get(0), 'R', fn);
|
||||
// 加载下标参数(寄存器类型按 I/int 处理)
|
||||
loadArgument(out, slotMap, args.get(1), 'I', fn);
|
||||
loadArgument(out, slotMap, args.get(0), 'R', fn); // 数组引用
|
||||
loadArgument(out, slotMap, args.get(1), 'I', fn); // 索引
|
||||
out.emit(VMOpCode.SYSCALL + " ARR_GET");
|
||||
|
||||
// 发出 ARR_GET 系统调用(元素访问由 VM 完成类型分派)
|
||||
out.emit(VMOpCode.SYSCALL + " " + "ARR_GET");
|
||||
|
||||
// 保存返回值到目标寄存器
|
||||
IRVirtualRegister dest = ins.getDest();
|
||||
if (dest == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] %s 需要有目标寄存器用于保存返回值".formatted(fn));
|
||||
throw new IllegalStateException("[CallGenerator] " + fn + " 必须有返回值寄存器");
|
||||
}
|
||||
Integer destSlot = slotMap.get(dest);
|
||||
if (destSlot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] %s 未找到目标寄存器的槽位映射 (dest: %s)".formatted(fn, dest));
|
||||
throw new IllegalStateException("[CallGenerator] " + fn + " 未找到目标槽位");
|
||||
}
|
||||
|
||||
// 按元素类型分派写入 VM 槽位
|
||||
// 根据类型选择 STORE 指令
|
||||
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');
|
||||
}
|
||||
case 'B' -> out.emit(OpHelper.opcode("B_STORE") + " " + destSlot);
|
||||
case 'S' -> out.emit(OpHelper.opcode("S_STORE") + " " + destSlot);
|
||||
case 'I' -> out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
|
||||
case 'L' -> out.emit(OpHelper.opcode("L_STORE") + " " + destSlot);
|
||||
case 'F' -> out.emit(OpHelper.opcode("F_STORE") + " " + destSlot);
|
||||
case 'D' -> out.emit(OpHelper.opcode("D_STORE") + " " + destSlot);
|
||||
default -> out.emit(OpHelper.opcode("R_STORE") + " " + destSlot);
|
||||
}
|
||||
out.setSlotType(destSlot, retType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成系统调用(syscall)指令。
|
||||
* 首个参数为子命令,支持常量或寄存器引用;其余参数均以引用形式加载。
|
||||
* 若存在返回寄存器,则将结果存为整数类型。
|
||||
*
|
||||
* @param ins syscall 对应的 IR 调用指令
|
||||
* @param out VM 指令构建器
|
||||
* @param slotMap 寄存器到槽位的映射
|
||||
* @param fn 函数名("syscall" 或以 ".syscall" 结尾)
|
||||
*/
|
||||
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 至少需要一个子命令");
|
||||
}
|
||||
String subcmd = resolveSyscallSubcmd(args.get(0), fn);
|
||||
for (int i = 1; i < args.size(); i++) {
|
||||
loadArgument(out, slotMap, args.get(i), 'R', fn);
|
||||
}
|
||||
out.emit(VMOpCode.SYSCALL + " " + subcmd);
|
||||
|
||||
IRVirtualRegister dest = ins.getDest();
|
||||
if (dest != null) {
|
||||
Integer slot = slotMap.get(dest);
|
||||
if (slot == null) throw new IllegalStateException("[CallGenerator] syscall 未找到目标槽位");
|
||||
out.emit(OpHelper.opcode("I_STORE") + " " + slot);
|
||||
out.setSlotType(slot, 'I');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 syscall 指令分支逻辑。
|
||||
* <ol>
|
||||
* <li>解析 syscall 子命令</li>
|
||||
* <li>压栈剩余参数</li>
|
||||
* <li>发出 SYSCALL 指令</li>
|
||||
* <li>若有返回值则保存至目标槽位</li>
|
||||
* </ol>
|
||||
* 解析 syscall 子命令字符串:
|
||||
* 若为 IRConstant,则直接取字符串值;
|
||||
* 若为 IRVirtualRegister,则从常量池中获取。
|
||||
* 最终返回大写子命令名称。
|
||||
*
|
||||
* @param ins 调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param fn 当前函数名
|
||||
* @param arg 第一个参数,常量或虚拟寄存器
|
||||
* @param fn 所属函数名,用于错误提示
|
||||
* @return 大写的子命令字符串
|
||||
* @throws IllegalStateException 参数类型或常量池缺失时抛出
|
||||
*/
|
||||
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));
|
||||
private String resolveSyscallSubcmd(IRValue arg, String fn) {
|
||||
if (arg instanceof IRConstant(Object value)) {
|
||||
if (value instanceof String s) {
|
||||
return s.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'I');
|
||||
throw new IllegalStateException("[CallGenerator] syscall 子命令必须是字符串 (函数: " + fn + ")");
|
||||
}
|
||||
if (arg instanceof IRVirtualRegister(int id)) {
|
||||
String s = STRING_CONST_POOL.get(id);
|
||||
if (s == null) throw new IllegalStateException("[CallGenerator] 未注册的 syscall 字符串常量");
|
||||
return s.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
throw new IllegalStateException("[CallGenerator] syscall 子命令参数错误: " + arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成普通函数调用指令:
|
||||
* <ol>
|
||||
* <li>推断返回值类型</li>
|
||||
* <li>压栈所有参数</li>
|
||||
* <li>生成 CALL 指令</li>
|
||||
* <li>保存返回值(若非 void)</li>
|
||||
* </ol>
|
||||
* 参数均以引用形式压栈,调用完成后,若返回类型非 void,
|
||||
* 则选择合适的 STORE 指令保存结果。
|
||||
*
|
||||
* @param ins 调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param out 指令构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param fn 当前函数名
|
||||
* @param fn 被调用函数名称
|
||||
*/
|
||||
private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
|
||||
// 1. 推断返回值类型(首字母大写,缺省为 'I')
|
||||
private void generateNormalCall(CallInstruction ins,
|
||||
VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
String fn) {
|
||||
String retTypeName = GlobalFunctionTable.getReturnType(fn);
|
||||
char retType = (retTypeName != null && !retTypeName.isEmpty()) ? Character.toUpperCase(retTypeName.charAt(0)) : 'I';
|
||||
char retType = normalizeTypePrefix(retTypeName);
|
||||
|
||||
// 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));
|
||||
throw new IllegalStateException("[CallGenerator] 普通调用缺少返回寄存器");
|
||||
}
|
||||
Integer destSlot = slotMap.get(dest);
|
||||
if (destSlot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 普通函数调用未找到目标寄存器的槽位映射 (function: %s, dest: %s)".formatted(fn, dest));
|
||||
Integer slot = slotMap.get(dest);
|
||||
if (slot == null) {
|
||||
throw new IllegalStateException("[CallGenerator] 未找到返回槽位");
|
||||
}
|
||||
out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, retType);
|
||||
out.emit(OpHelper.opcode(retType + "_STORE") + " " + slot);
|
||||
out.setSlotType(slot, retType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载参数:
|
||||
* 验证为虚拟寄存器后,获取槽位和类型,
|
||||
* 缺省时使用 defaultType,然后发出 LOAD 指令。
|
||||
*
|
||||
* @param out 指令构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param arg IR 参数值
|
||||
* @param defaultType 缺省类型前缀
|
||||
* @param fn 所属函数名,用于错误提示
|
||||
*/
|
||||
private void loadArgument(VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
IRValue arg,
|
||||
char defaultType,
|
||||
String fn) {
|
||||
this.fn = fn;
|
||||
if (!(arg instanceof IRVirtualRegister vr)) {
|
||||
throw new IllegalStateException("[CallGenerator] 参数必须是 IRVirtualRegister");
|
||||
}
|
||||
Integer slot = slotMap.get(vr);
|
||||
if (slot == null) {
|
||||
throw new IllegalStateException("[CallGenerator] 未找到参数槽位");
|
||||
}
|
||||
char t = out.getSlotType(slot);
|
||||
if (t == '\0' || (t == 'I' && defaultType != 'I')) {
|
||||
t = defaultType;
|
||||
}
|
||||
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回最近一次处理的函数名,可用于调试。
|
||||
*
|
||||
* @return 函数名字符串
|
||||
*/
|
||||
public String getFn() {
|
||||
return fn;
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,12 +43,7 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
// 布尔字面量,例如 true / false
|
||||
case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
|
||||
// 标识符(变量名),如 a、b
|
||||
case IdentifierNode id -> {
|
||||
// 查找当前作用域中的变量寄存器
|
||||
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
|
||||
if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
|
||||
yield reg;
|
||||
}
|
||||
case IdentifierNode id -> buildIdentifier(id);
|
||||
// 模块常量 / 全局变量,如 ModuleA.a
|
||||
case MemberExpressionNode mem -> buildMember(mem);
|
||||
// 二元表达式(如 a+b, x==y)
|
||||
@ -59,6 +54,7 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
case UnaryExpressionNode un -> buildUnary(un);
|
||||
case IndexExpressionNode idx -> buildIndex(idx);
|
||||
case ArrayLiteralNode arr -> buildArrayLiteral(arr);
|
||||
case NewExpressionNode n -> buildNew(n);
|
||||
// 默认分支:遇到未知表达式类型则直接抛异常
|
||||
default -> throw new IllegalStateException(
|
||||
"不支持的表达式类型: " + expr.getClass().getSimpleName());
|
||||
@ -66,76 +62,182 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 成员访问表达式构建
|
||||
* 构造标识符节点对应的 IR 虚拟寄存器。
|
||||
* <p>
|
||||
* 支持普通变量查找,以及结构体方法/构造器中的“字段回退”机制(即自动将裸标识符回退为 this.<id>)。
|
||||
*
|
||||
* @param mem 成员表达式节点
|
||||
* @return 存储结果的虚拟寄存器
|
||||
* @param id 标识符节点
|
||||
* @return 查找到的 IR 虚拟寄存器
|
||||
* @throws IllegalStateException 若标识符未定义且无法回退为字段
|
||||
*/
|
||||
private IRVirtualRegister buildMember(MemberExpressionNode mem) {
|
||||
if (!(mem.object() instanceof IdentifierNode id)) {
|
||||
throw new IllegalStateException("不支持的成员访问对象类型: "
|
||||
+ mem.object().getClass().getSimpleName());
|
||||
}
|
||||
String qualified = id.name() + "." + mem.member();
|
||||
private IRVirtualRegister buildIdentifier(IdentifierNode id) {
|
||||
// ====================== 普通变量查找 ======================
|
||||
// 1. 在当前作用域查找变量(可能是局部变量、形参、临时变量等)
|
||||
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
|
||||
if (reg != null) return reg;
|
||||
|
||||
/* 1) 尝试直接获取已有寄存器绑定 */
|
||||
IRVirtualRegister reg = ctx.getScope().lookup(qualified);
|
||||
if (reg != null) {
|
||||
return reg;
|
||||
// ====================== 字段回退机制 ======================
|
||||
// 2. 若未找到,则判断是否处于结构体方法或构造器中
|
||||
// 尝试将裸标识符自动视为 this.<id>,即访问当前结构体实例的成员字段
|
||||
IRVirtualRegister thisReg = ctx.getScope().lookup("this");
|
||||
String thisType = ctx.getScope().lookupType("this");
|
||||
if (thisReg != null && thisType != null) {
|
||||
// 生成成员表达式节点 this.<id>
|
||||
MemberExpressionNode asField = new MemberExpressionNode(
|
||||
new IdentifierNode("this", id.context()), // 构造 this 节点
|
||||
id.name(), // 字段名
|
||||
id.context()
|
||||
);
|
||||
// 递归构造成员访问(相当于构造 this.<id> 的 IR)
|
||||
return buildMember(asField);
|
||||
}
|
||||
|
||||
/* 2) 折叠为编译期常量:先查作用域,再查全局常量表 */
|
||||
Object v = ctx.getScope().getConstValue(qualified);
|
||||
if (v == null) {
|
||||
v = GlobalConstTable.get(qualified);
|
||||
}
|
||||
if (v != null) {
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(v)));
|
||||
return r;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("未定义的常量: " + qualified);
|
||||
// ====================== 标识符未定义异常 ======================
|
||||
// 3. 无法查找到变量且不能字段回退,抛出异常
|
||||
throw new IllegalStateException("未定义标识符: " + id.name());
|
||||
}
|
||||
|
||||
|
||||
/* ───────────────── 写入指定寄存器 ───────────────── */
|
||||
/**
|
||||
* 构建成员访问表达式的 IR 虚拟寄存器。
|
||||
* <p>
|
||||
* 支持两类成员访问:
|
||||
* <ul>
|
||||
* <li>模块常量访问(如 ModuleName.CONST)</li>
|
||||
* <li>结构体/对象字段访问(如 obj.field 或 this.field)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param mem 成员访问表达式节点(如 a.b 或 ModuleName.CONST)
|
||||
* @return 存储成员值的 IR 虚拟寄存器
|
||||
* @throws IllegalStateException 若找不到成员或无法解析类型
|
||||
*/
|
||||
private IRVirtualRegister buildMember(MemberExpressionNode mem) {
|
||||
// ===== 1. 处理模块常量 (ModuleName.member) =====
|
||||
// 检查成员访问的对象是否为一个标识符(即模块名)
|
||||
if (mem.object() instanceof IdentifierNode oid) {
|
||||
String mod = oid.name();
|
||||
// 查找是否存在该模块下的全局常量定义
|
||||
Object c = GlobalConstTable.get(mod + "." + mem.member());
|
||||
if (c != null) {
|
||||
// 若找到常量,分配新寄存器并生成 LoadConst 指令
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(c)));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 2. 结构体/对象字段访问 =====
|
||||
|
||||
// 1 递归构建成员对象(如 a.b,先获得 a 的寄存器)
|
||||
IRVirtualRegister objReg = build(mem.object());
|
||||
|
||||
// 2 尝试解析成员访问接收者(object)的类型
|
||||
String ownerType = null;
|
||||
if (mem.object() instanceof IdentifierNode oid) {
|
||||
// 如果对象是标识符,直接查询其类型(例如 a.x,a 的类型)
|
||||
ownerType = ctx.getScope().lookupType(oid.name());
|
||||
}
|
||||
if (ownerType == null || ownerType.isEmpty()) {
|
||||
// 兜底:如果访问 this.xxx,并且 this 有类型,则使用 this 的类型
|
||||
String thisType = ctx.getScope().lookupType("this");
|
||||
if (thisType != null) ownerType = thisType;
|
||||
}
|
||||
if (ownerType == null || ownerType.isEmpty()) {
|
||||
// 类型无法解析,抛出异常
|
||||
throw new IllegalStateException("无法解析成员访问接收者的类型");
|
||||
}
|
||||
|
||||
// 3 查找字段槽位下标:ownerType 的 mem.member() 字段的序号
|
||||
Integer fieldIndex = ctx.getScope().lookupFieldIndex(ownerType, mem.member());
|
||||
if (fieldIndex == null) {
|
||||
// 字段不存在,抛出异常
|
||||
throw new IllegalStateException("类型 " + ownerType + " 不存在字段: " + mem.member());
|
||||
}
|
||||
|
||||
// 4 生成读取字段的 IR 指令:CALL __index_r, objReg, const(fieldIndex)
|
||||
// 4.1 先将字段下标加载到寄存器
|
||||
IRVirtualRegister idxReg = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(
|
||||
idxReg,
|
||||
IRConstant.fromNumber(Integer.toString(fieldIndex))
|
||||
));
|
||||
|
||||
// 4.2 生成成员读取调用指令
|
||||
IRVirtualRegister out = ctx.newRegister();
|
||||
List<IRValue> args = new ArrayList<>();
|
||||
args.add(objReg); // 对象寄存器
|
||||
args.add(idxReg); // 字段下标寄存器
|
||||
ctx.addInstruction(new CallInstruction(out, "__index_r", args));
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将表达式节点 {@link ExpressionNode} 的结果写入指定的虚拟寄存器 {@code dest}。
|
||||
* 将表达式节点 {@link ExpressionNode} 的求值结果写入指定的目标虚拟寄存器 {@code dest}。
|
||||
* <p>
|
||||
* 按表达式类型分派处理,包括:
|
||||
* <ul>
|
||||
* <li>字面量(数字、字符串、布尔、数组):生成 loadConst 指令直接写入目标寄存器</li>
|
||||
* <li>变量标识符:查表获取源寄存器,并 move 到目标寄存器</li>
|
||||
* <li>二元表达式、下标、调用表达式:递归生成子表达式结果,并写入目标寄存器</li>
|
||||
* <li>其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器</li>
|
||||
* </ul>
|
||||
* 根据不同表达式类型,采取高效或递归方式生成中间代码,涵盖常量、变量、运算、数组、调用等。
|
||||
* </p>
|
||||
*
|
||||
* @param node 要求值的表达式节点
|
||||
* @param dest 结果目标虚拟寄存器
|
||||
* @throws IllegalStateException 若标识符未定义(如变量未声明时引用)
|
||||
* @param node 表达式节点(可为字面量、变量、数组、运算等)
|
||||
* @param dest 目标虚拟寄存器(写入结果)
|
||||
* @throws IllegalStateException 若变量标识符未定义
|
||||
*/
|
||||
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
|
||||
switch (node) {
|
||||
// 数字字面量:生成 loadConst 指令,将数值常量写入目标寄存器
|
||||
// ===================== 数字字面量 =====================
|
||||
// 如 42、3.14 等,直接生成 loadConst 指令,常量写入目标寄存器
|
||||
case NumberLiteralNode n -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
|
||||
|
||||
// 字符串字面量:生成 loadConst 指令,将字符串常量写入目标寄存器
|
||||
// ===================== 字符串字面量 =====================
|
||||
// 如 "hello",直接生成 loadConst 指令
|
||||
case StringLiteralNode s -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(s.value()));
|
||||
|
||||
// 布尔字面量:转换为 int 1/0,生成 loadConst 指令写入目标寄存器
|
||||
// ===================== 布尔字面量 =====================
|
||||
// 如 true/false,转换为 int 常量 1/0,生成 loadConst
|
||||
case BoolLiteralNode b -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
|
||||
|
||||
// 数组字面量:生成数组常量并写入目标寄存器
|
||||
// ===================== 数组字面量 =====================
|
||||
// 直接构造数组常量(只支持静态初始化),生成 loadConst
|
||||
case ArrayLiteralNode arr -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, buildArrayConstant(arr));
|
||||
|
||||
// 变量标识符:查表获得源寄存器,move 到目标寄存器
|
||||
// ===================== new 表达式(构造数组/对象) =====================
|
||||
// 生成空数组,然后按参数依次初始化每一项,使用 __setindex_r 填充目标寄存器
|
||||
case NewExpressionNode newExpr -> {
|
||||
// 1. 把空列表写入目标寄存器
|
||||
InstructionFactory.loadConstInto(ctx, dest, new IRConstant(java.util.List.of()));
|
||||
|
||||
// 2. 依次写入构造实参,同时缓存参数寄存器
|
||||
List<IRVirtualRegister> argRegs = new ArrayList<>();
|
||||
for (int i = 0; i < newExpr.arguments().size(); i++) {
|
||||
IRVirtualRegister argReg = build(newExpr.arguments().get(i));
|
||||
argRegs.add(argReg);
|
||||
|
||||
IRVirtualRegister idxReg = ctx.newTempRegister();
|
||||
InstructionFactory.loadConstInto(ctx, idxReg, new IRConstant(i));
|
||||
|
||||
ctx.addInstruction(new CallInstruction(
|
||||
null,
|
||||
"__setindex_r",
|
||||
List.of(dest, idxReg, argReg)));
|
||||
}
|
||||
|
||||
/* 3. 若确认为结构体,显式调用 <Struct>.__init__N 完成字段初始化 */
|
||||
if (IRBuilderScope.getStructLayout(newExpr.typeName()) != null) {
|
||||
String ctor = newExpr.typeName() + ".__init__" + argRegs.size();
|
||||
List<IRValue> ctorArgs = new ArrayList<>();
|
||||
ctorArgs.add(dest); // this
|
||||
ctorArgs.addAll(argRegs); // 实参
|
||||
ctx.addInstruction(new CallInstruction(null, ctor, ctorArgs));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===================== 变量标识符 =====================
|
||||
// 如 x,查找符号表,move 到目标寄存器。未定义时报错。
|
||||
case IdentifierNode id -> {
|
||||
IRVirtualRegister src = ctx.getScope().lookup(id.name());
|
||||
if (src == null)
|
||||
@ -143,22 +245,26 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
InstructionFactory.move(ctx, src, dest);
|
||||
}
|
||||
|
||||
// 二元表达式:递归生成左右子表达式,并将结果写入目标寄存器
|
||||
// ===================== 二元表达式(如 a + b) =====================
|
||||
// 递归生成左右操作数,并将运算结果写入目标寄存器
|
||||
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
|
||||
|
||||
// 下标表达式:递归生成索引结果,move 到目标寄存器
|
||||
// ===================== 下标访问(如 arr[1]) =====================
|
||||
// 先递归构造表达式,将索引结果 move 到目标寄存器
|
||||
case IndexExpressionNode idx -> {
|
||||
IRVirtualRegister tmp = buildIndex(idx);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
}
|
||||
|
||||
// 调用表达式:递归生成调用结果,move 到目标寄存器
|
||||
// ===================== 函数/方法调用(如 foo(a, b)) =====================
|
||||
// 递归生成调用,结果 move 到目标寄存器
|
||||
case CallExpressionNode call -> {
|
||||
IRVirtualRegister tmp = buildCall(call);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
}
|
||||
|
||||
// 其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器
|
||||
// ===================== 其它所有情况(兜底处理) =====================
|
||||
// 通用流程:先生成结果到临时寄存器,再 move 到目标寄存器
|
||||
default -> {
|
||||
IRVirtualRegister tmp = build(node);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
@ -315,46 +421,58 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @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; // 无法解析为数字
|
||||
switch (expr) {
|
||||
case null -> {
|
||||
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);
|
||||
// 数字字面量:尝试解析为 int 或 double
|
||||
case 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; // 无法解析为数字
|
||||
}
|
||||
}
|
||||
return List.copyOf(list);
|
||||
}
|
||||
|
||||
// 标识符:尝试查找作用域中的常量值
|
||||
if (expr instanceof IdentifierNode id) {
|
||||
Object v = null;
|
||||
try {
|
||||
v = ctx.getScope().getConstValue(id.name());
|
||||
} catch (Throwable ignored) {
|
||||
// 查不到常量或异常都视为无法折叠
|
||||
|
||||
// 字符串字面量:直接返回字符串
|
||||
case StringLiteralNode s -> {
|
||||
return s.value();
|
||||
}
|
||||
|
||||
// 布尔字面量:true 返回 1,false 返回 0
|
||||
case BoolLiteralNode b -> {
|
||||
return b.getValue() ? 1 : 0;
|
||||
}
|
||||
|
||||
// 数组字面量:递归折叠所有元素
|
||||
case 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);
|
||||
}
|
||||
|
||||
|
||||
// 标识符:尝试查找作用域中的常量值
|
||||
case IdentifierNode id -> {
|
||||
Object v = null;
|
||||
try {
|
||||
v = ctx.getScope().getConstValue(id.name());
|
||||
} catch (Throwable ignored) {
|
||||
// 查不到常量或异常都视为无法折叠
|
||||
}
|
||||
return v;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// 其它类型:不支持折叠,返回 null
|
||||
@ -397,47 +515,139 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
|
||||
|
||||
/**
|
||||
* 构建函数或方法调用表达式。
|
||||
* 构造 new 对象/数组创建表达式的 IR 虚拟寄存器(支持列表型构造)。
|
||||
* <p>
|
||||
* 支持普通函数调用(foo(a, b))与成员方法调用(obj.method(a, b))。
|
||||
* 语义说明:本方法用于生成“new 表达式”的中间代码流程,具体包括:
|
||||
* <ul>
|
||||
* <li>首先递归生成所有参数的虚拟寄存器列表。</li>
|
||||
* <li>根据 callee 类型区分成员访问或直接标识符调用,并规范化方法名(如加前缀)。</li>
|
||||
* <li>为返回值分配新寄存器,生成 Call 指令。</li>
|
||||
* <li>分配一个新的寄存器用于保存新对象/数组引用</li>
|
||||
* <li>将一个空列表常量写入该寄存器(后端 runtime 识别为可变列表/对象)</li>
|
||||
* <li>遍历所有构造参数,依次写入目标列表的 [0..n-1] 位置</li>
|
||||
* <li>最终返回该寄存器</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param call 函数/方法调用表达式节点
|
||||
* @return 存放调用结果的虚拟寄存器
|
||||
* @param node new 表达式节点,包含所有构造参数
|
||||
* @return 保存新创建对象/数组引用的目标寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildCall(CallExpressionNode call) {
|
||||
// 1. 递归生成所有参数的寄存器
|
||||
List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList();
|
||||
|
||||
// 2. 规范化被调用方法名(区分成员方法与普通函数)
|
||||
String callee = switch (call.callee()) {
|
||||
// 成员方法调用,如 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)
|
||||
yield current.substring(0, dot) + "." + id.name(); // 同命名空间内调用
|
||||
yield id.name(); // 全局函数调用
|
||||
}
|
||||
// 其它类型不支持
|
||||
default -> throw new IllegalStateException(
|
||||
"不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
};
|
||||
|
||||
// 3. 分配用于存放返回值的新寄存器,并生成 Call 指令
|
||||
private IRVirtualRegister buildNew(NewExpressionNode node) {
|
||||
// 1. 分配新的寄存器作为 new 表达式的结果
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
|
||||
|
||||
/* 1. 写入空列表 */
|
||||
InstructionFactory.loadConstInto(ctx, dest, new IRConstant(java.util.List.of()));
|
||||
|
||||
/* 2. 创建参数并写入列表,同时缓存参数寄存器 */
|
||||
List<IRVirtualRegister> argRegs = new ArrayList<>();
|
||||
for (int i = 0; i < node.arguments().size(); i++) {
|
||||
IRVirtualRegister argReg = build(node.arguments().get(i));
|
||||
argRegs.add(argReg);
|
||||
|
||||
IRVirtualRegister idxReg = ctx.newTempRegister();
|
||||
InstructionFactory.loadConstInto(ctx, idxReg, new IRConstant(i));
|
||||
|
||||
ctx.addInstruction(new CallInstruction(
|
||||
null, "__setindex_r",
|
||||
List.of(dest, idxReg, argReg)));
|
||||
}
|
||||
|
||||
/* 3. 若为结构体实例,调用构造器 <Struct>.__init__N */
|
||||
if (IRBuilderScope.getStructLayout(node.typeName()) != null) {
|
||||
String ctorName = node.typeName() + ".__init__" + argRegs.size();
|
||||
List<IRValue> ctorArgs = new ArrayList<>();
|
||||
ctorArgs.add(dest); // 隐式 this
|
||||
ctorArgs.addAll(argRegs); // 构造实参
|
||||
ctx.addInstruction(new CallInstruction(null, ctorName, ctorArgs));
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建函数或方法调用表达式的 IR 指令,并返回结果寄存器。
|
||||
* <p>
|
||||
* 支持三类调用:
|
||||
* <ul>
|
||||
* <li>普通函数调用(foo(a, b))</li>
|
||||
* <li>成员方法调用(obj.method(a, b) 或 ModuleName.func(a, b))</li>
|
||||
* <li>链式方法调用(obj.getAddr().getCity())</li>
|
||||
* </ul>
|
||||
* 核心流程:
|
||||
* <ol>
|
||||
* <li>递归生成所有参数的虚拟寄存器列表</li>
|
||||
* <li>根据 callee 类型区分结构体方法/模块静态函数/普通函数</li>
|
||||
* <li>规范化被调用方法名,并整理最终参数表</li>
|
||||
* <li>分配用于结果的新寄存器,生成 Call 指令</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param call 函数或方法调用表达式节点
|
||||
* @return 存放调用结果的目标虚拟寄存器
|
||||
* @throws IllegalStateException 被调用表达式类型不支持或参数异常
|
||||
*/
|
||||
private IRVirtualRegister buildCall(CallExpressionNode call) {
|
||||
// 1. 先递归生成“显式”参数的寄存器列表
|
||||
List<IRVirtualRegister> explicitRegs = new ArrayList<>();
|
||||
for (ExpressionNode a : call.arguments()) explicitRegs.add(build(a));
|
||||
|
||||
String callee; // 函数/方法的规范化名字
|
||||
List<IRValue> finalArgs = new ArrayList<>(); // 最终传递给 CALL 的参数表
|
||||
|
||||
// 2. 按 callee 类型分支
|
||||
switch (call.callee()) {
|
||||
case MemberExpressionNode m when m.object() instanceof IdentifierNode idObj -> {
|
||||
// ========= 情况 1: obj.method(...) 或 ModuleName.func(...) =========
|
||||
String recvName = idObj.name();
|
||||
String recvType = ctx.getScope().lookupType(recvName);
|
||||
|
||||
if (recvType == null || recvType.isEmpty()) {
|
||||
// 模块函数调用 —— "模块名.函数名"
|
||||
callee = recvName + "." + m.member();
|
||||
finalArgs.addAll(explicitRegs);
|
||||
} else {
|
||||
// 结构体实例方法调用 —— "类型名.方法名",且第一个参数为 this
|
||||
callee = recvType + "." + m.member();
|
||||
IRVirtualRegister thisReg = ctx.getScope().lookup(recvName);
|
||||
if (thisReg == null)
|
||||
throw new IllegalStateException("未定义标识符: " + recvName);
|
||||
finalArgs.add(thisReg); // 隐式 this
|
||||
finalArgs.addAll(explicitRegs); // 其它参数
|
||||
}
|
||||
}
|
||||
case MemberExpressionNode m -> {
|
||||
// ========= 情况 2: 通用成员调用 (支持链式调用) =========
|
||||
// 例如 p.getAddr().getCity()
|
||||
IRVirtualRegister objReg = build(m.object()); // 递归计算 object 表达式
|
||||
|
||||
callee = m.member(); // 直接用成员名
|
||||
|
||||
finalArgs.add(objReg); // 隐式 this
|
||||
|
||||
finalArgs.addAll(explicitRegs); // 其它参数
|
||||
}
|
||||
case IdentifierNode id -> {
|
||||
// ========= 情况 3: 普通函数调用 foo(...) =========
|
||||
String current = ctx.getFunction().name(); // 当前函数全名
|
||||
|
||||
int dot = current.lastIndexOf('.');
|
||||
if (dot >= 0 && !id.name().contains(".")) {
|
||||
// 自动补充模块名前缀
|
||||
callee = current.substring(0, dot) + "." + id.name();
|
||||
} else {
|
||||
callee = id.name();
|
||||
}
|
||||
finalArgs.addAll(explicitRegs);
|
||||
}
|
||||
default ->
|
||||
// ========= 其它不支持 =========
|
||||
throw new IllegalStateException(
|
||||
"不支持的被调用表达式: " + call.callee().getClass().getSimpleName());
|
||||
}
|
||||
|
||||
// 3. 分配目标寄存器,生成函数/方法调用指令
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
ctx.addInstruction(new CallInstruction(dest, callee, finalArgs));
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 二元表达式构建,结果存储到新寄存器。
|
||||
* <br>
|
||||
|
||||
@ -12,14 +12,15 @@ import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
/**
|
||||
* IR 函数构建器。
|
||||
* <p>
|
||||
* 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction,
|
||||
* 包含参数声明、返回类型推断、函数体语句转换等步骤。
|
||||
*
|
||||
* 将语法树中的 {@link FunctionNode} 转换为可执行的 {@link IRFunction},包含:
|
||||
* <ul>
|
||||
* <li>支持自动导入全局/跨模块常量,使跨模块常量引用(如 ModuleA.a)在 IR 阶段可用。</li>
|
||||
* <li>将函数形参声明为虚拟寄存器,并注册到作用域,便于后续指令生成。</li>
|
||||
* <li>根据返回类型设置表达式默认字面量类型,保证 IR 层类型一致性。</li>
|
||||
* <li>遍历并转换函数体语句为 IR 指令。</li>
|
||||
* <li>在全局函数表登记函数名与返回类型;</li>
|
||||
* <li>初始化 IR 容器与构建上下文(作用域、寄存器池等);</li>
|
||||
* <li>导入全局/跨模块常量,便于常量折叠与跨模块引用;</li>
|
||||
* <li>依据返回类型为表达式设置数字字面量默认后缀;</li>
|
||||
* <li>分配形参寄存器并注册到作用域与 IR 函数;</li>
|
||||
* <li>将函数体语句逐条转为 IR 指令;</li>
|
||||
* <li>在构建完成后清理默认字面量后缀,避免泄漏到其它函数。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class FunctionBuilder {
|
||||
@ -44,57 +45,56 @@ public class FunctionBuilder {
|
||||
* @return 构建得到的 IRFunction 对象
|
||||
*/
|
||||
public IRFunction build(FunctionNode functionNode) {
|
||||
|
||||
// 1. 在全局函数表注册函数名与返回类型
|
||||
// 方便其他阶段/模块调用、类型检查。
|
||||
// 1) 在全局函数表登记:名称 + 返回类型(返回类型可能为 null,例如构造函数 init)
|
||||
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
|
||||
|
||||
// 2. 初始化 IRFunction 实例与上下文对象
|
||||
// IRFunction: 表示该函数的中间代码容器
|
||||
// IRContext: 负责作用域、寄存器分配等编译上下文管理
|
||||
// 2) 初始化 IR 容器与上下文
|
||||
IRFunction irFunction = new IRFunction(functionNode.name());
|
||||
IRContext irContext = new IRContext(irFunction);
|
||||
|
||||
// 3. 自动导入所有全局/跨模块常量到当前作用域
|
||||
// 支持如 ModuleA.a 这样的常量访问/折叠(参见 ExpressionBuilder)
|
||||
// 3) 导入所有全局/跨模块常量到当前作用域(便于常量折叠和跨模块引用)
|
||||
GlobalConstTable.all().forEach((k, v) ->
|
||||
irContext.getScope().importExternalConst(k, v));
|
||||
|
||||
// 4. 根据函数返回类型设置默认类型后缀
|
||||
// 例如返回类型为 double 时, 字面量表达式自动用 d 后缀。
|
||||
char _returnSuffix = switch (functionNode.returnType().toLowerCase()) {
|
||||
// 4) 根据函数返回类型设置“数字字面量默认后缀”
|
||||
// - 关键修复:对 returnType 进行空值/空白保护,缺省视为 "void"
|
||||
String rt = functionNode.returnType();
|
||||
String rtLower = (rt == null || rt.trim().isEmpty()) ? "void" : rt.trim().toLowerCase();
|
||||
|
||||
// 根据返回类型决定默认字面量后缀
|
||||
// 仅在浮点/整型长短类型上设置;其它/void 情况不设置(使用 '\0' 表示不设置)
|
||||
char defaultSuffix = switch (rtLower) {
|
||||
case "double" -> 'd';
|
||||
case "float" -> 'f';
|
||||
case "long" -> 'l';
|
||||
case "short" -> 's';
|
||||
case "byte" -> 'b';
|
||||
default -> '\0'; // 其它类型不设默认后缀
|
||||
default -> '\0';
|
||||
};
|
||||
ExpressionUtils.setDefaultSuffix(_returnSuffix);
|
||||
ExpressionUtils.setDefaultSuffix(defaultSuffix);
|
||||
|
||||
try {
|
||||
// 5. 遍历函数参数列表
|
||||
// - 为每个参数分配一个新的虚拟寄存器
|
||||
// - 注册参数名、类型、寄存器到当前作用域
|
||||
// - 添加参数寄存器到 IRFunction(用于后续调用与指令生成)
|
||||
// 5) 处理形参:
|
||||
// - 为每个形参分配一个新的虚拟寄存器(从 IRContext 统一分配,保证作用域一致)
|
||||
// - 将 (参数名, 类型, 寄存器) 声明到当前作用域
|
||||
// - 将寄存器加入 IRFunction 的参数列表,便于后续调用/生成
|
||||
for (ParameterNode p : functionNode.parameters()) {
|
||||
IRVirtualRegister reg = irFunction.newRegister();
|
||||
IRVirtualRegister reg = irContext.newRegister(); // 使用上下文统一分配
|
||||
irContext.getScope().declare(p.name(), p.type(), reg);
|
||||
irFunction.addParameter(reg);
|
||||
}
|
||||
|
||||
// 6. 遍历函数体语句节点,转换为 IR 指令
|
||||
// StatementBuilder 负责将每条语句递归转换为 IR
|
||||
// 6) 构建函数体:将每条语句节点转换为 IR 指令序列
|
||||
StatementBuilder stmtBuilder = new StatementBuilder(irContext);
|
||||
for (StatementNode stmt : functionNode.body()) {
|
||||
stmtBuilder.build(stmt);
|
||||
}
|
||||
} finally {
|
||||
// 7. 清理默认类型后缀,防止影响后续其他函数的类型推断
|
||||
// 7) 清理默认后缀,防止影响后续函数的字面量推断
|
||||
ExpressionUtils.clearDefaultSuffix();
|
||||
}
|
||||
|
||||
// 8. 返回构建完成的 IRFunction
|
||||
// 8) 返回构建完成的 IRFunction
|
||||
return irFunction;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.ir.builder;
|
||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -16,6 +17,7 @@ import java.util.Map;
|
||||
* <li>支持变量的类型信息记录与查询</li>
|
||||
* <li>支持变量的编译期常量值记录与查询(便于常量折叠等优化)</li>
|
||||
* <li>支持跨模块全局常量(如 ModuleA.a)查找</li>
|
||||
* <li>维护结构体字段布局(全局共享):字段名 → 槽位下标,用于对象字段读写</li>
|
||||
* </ul>
|
||||
*/
|
||||
final class IRBuilderScope {
|
||||
@ -28,14 +30,23 @@ final class IRBuilderScope {
|
||||
private final Map<String, Object> varConstValues = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 额外:存放跨模块导入的全局常量
|
||||
* key 形如 "ModuleA.a" value 为其常量值
|
||||
* 存放跨模块导入的全局常量(如 ModuleA.a)。
|
||||
* 键为 "ModuleA.a",值为常量值。
|
||||
*/
|
||||
private final Map<String, Object> externalConsts = new HashMap<>();
|
||||
|
||||
/** 当前作用域所绑定的 IRFunction 实例 */
|
||||
/**
|
||||
* 结构体字段布局的全局静态表:
|
||||
* 结构体名 → (字段名 → 槽位下标)。
|
||||
* 静态表设计,确保所有作用域、所有 IR 构建都能访问同一套布局。
|
||||
*/
|
||||
private static final Map<String, Map<String, Integer>> STRUCT_LAYOUTS = new HashMap<>();
|
||||
|
||||
/** 当前作用域所绑定的 IRFunction 实例,用于变量分配新寄存器等。 */
|
||||
private IRFunction fn;
|
||||
|
||||
// ---------------- 作用域与变量 ----------------
|
||||
|
||||
/**
|
||||
* 绑定当前作用域到指定 IRFunction。
|
||||
*
|
||||
@ -55,7 +66,7 @@ final class IRBuilderScope {
|
||||
IRVirtualRegister reg = fn.newRegister();
|
||||
vars.put(name, reg);
|
||||
varTypes.put(name, type);
|
||||
varConstValues.remove(name);
|
||||
varConstValues.remove(name); // 声明新变量即清除原常量绑定
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,7 +79,7 @@ final class IRBuilderScope {
|
||||
void declare(String name, String type, IRVirtualRegister reg) {
|
||||
vars.put(name, reg);
|
||||
varTypes.put(name, type);
|
||||
varConstValues.remove(name);
|
||||
varConstValues.remove(name); // 重复声明也会清除常量绑定
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,16 +115,18 @@ final class IRBuilderScope {
|
||||
/**
|
||||
* 获取变量名到类型名映射的不可变副本。
|
||||
*
|
||||
* @return 变量名→类型名映射的只读视图
|
||||
* @return 变量名→类型名映射的只读视图(用于调试/全局分析)
|
||||
*/
|
||||
Map<String, String> getVarTypes() {
|
||||
return Map.copyOf(varTypes);
|
||||
return Collections.unmodifiableMap(varTypes);
|
||||
}
|
||||
|
||||
// ---------------- 编译期常量相关接口 ----------------
|
||||
|
||||
/**
|
||||
* 设置变量的编译期常量值(本地变量)。
|
||||
* <p>
|
||||
* 便于 IR 生成时做常量折叠等优化,value 传 null 则清除绑定。
|
||||
*
|
||||
* @param name 变量名称
|
||||
* @param value 常量值(null 表示清除)
|
||||
@ -124,9 +137,9 @@ final class IRBuilderScope {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取变量的编译期常量值(本地变量或导入的外部常量)。
|
||||
* <br>
|
||||
* 优先查找本地常量,未命中再查外部(如 "ModuleA.a")。
|
||||
* 获取变量的编译期常量值(优先本地,再查跨模块导入)。
|
||||
* <p>
|
||||
* 常用于优化与折叠,支持 "Module.a" 这类跨模块全局常量查找。
|
||||
*
|
||||
* @param name 变量名称或"模块名.常量名"
|
||||
* @return 编译期常量值,或 null
|
||||
@ -134,7 +147,7 @@ final class IRBuilderScope {
|
||||
Object getConstValue(String name) {
|
||||
Object v = varConstValues.get(name);
|
||||
if (v != null) return v;
|
||||
// 支持跨模块常量/全局变量
|
||||
// 支持跨模块常量/全局变量(如 "ModuleA.a")
|
||||
return externalConsts.get(name);
|
||||
}
|
||||
|
||||
@ -158,4 +171,60 @@ final class IRBuilderScope {
|
||||
void importExternalConst(String qualifiedName, Object value) {
|
||||
externalConsts.put(qualifiedName, value);
|
||||
}
|
||||
|
||||
// ---------------- 结构体字段布局(全局静态) ----------------
|
||||
|
||||
/**
|
||||
* 全局注册结构体的字段布局映射(字段名 -> 槽位下标)。
|
||||
* 一般在语义分析/IR 前期由类型系统收集后调用。
|
||||
*
|
||||
* @param structName 结构体名(建议为简单名,如 "Animal";如有模块前缀也可)
|
||||
* @param fieldToIndex 字段名到下标的映射(下标从 0 递增)
|
||||
*/
|
||||
static void registerStructLayout(String structName, Map<String, Integer> fieldToIndex) {
|
||||
if (structName == null || fieldToIndex == null) return;
|
||||
// 覆盖式注册:方便增量/重复编译时刷新
|
||||
STRUCT_LAYOUTS.put(structName, new HashMap<>(fieldToIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询字段槽位下标。
|
||||
* 支持“模块.结构体”及简单名两种写法自动兼容。
|
||||
*
|
||||
* @param structName 结构体名("Module.Struct" 或 "Struct")
|
||||
* @param fieldName 字段名
|
||||
* @return 槽位下标;若未知返回 null
|
||||
*/
|
||||
Integer lookupFieldIndex(String structName, String fieldName) {
|
||||
// 先按原样查
|
||||
Map<String, Integer> layout = STRUCT_LAYOUTS.get(structName);
|
||||
// 兼容 “模块.结构体” 的写法:若没命中,退化为简单名再查
|
||||
if (layout == null && structName != null) {
|
||||
int dot = structName.lastIndexOf('.');
|
||||
if (dot >= 0 && dot + 1 < structName.length()) {
|
||||
String simple = structName.substring(dot + 1);
|
||||
layout = STRUCT_LAYOUTS.get(simple);
|
||||
}
|
||||
}
|
||||
if (layout == null) return null;
|
||||
return layout.get(fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取某结构体的完整字段布局(返回只读 Map)。
|
||||
* 支持“模块.结构体”及简单名两种写法。
|
||||
*
|
||||
* @param structName 结构体名
|
||||
* @return 字段名到下标映射的只读视图,或 null
|
||||
*/
|
||||
static Map<String, Integer> getStructLayout(String structName) {
|
||||
Map<String, Integer> layout = STRUCT_LAYOUTS.get(structName);
|
||||
if (layout == null && structName != null) {
|
||||
int dot = structName.lastIndexOf('.');
|
||||
if (dot >= 0 && dot + 1 < structName.length()) {
|
||||
layout = STRUCT_LAYOUTS.get(structName.substring(dot + 1));
|
||||
}
|
||||
}
|
||||
return layout == null ? null : Collections.unmodifiableMap(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
|
||||
|
||||
/**
|
||||
* IRContext 类负责封装当前正在构建的 IRFunction 实例
|
||||
* {@code IRContext} 类负责封装当前正在构建的 IRFunction 实例
|
||||
* 以及与之配套的作用域管理(IRBuilderScope),
|
||||
* 并简化虚拟寄存器分配与 IR 指令添加操作。
|
||||
*
|
||||
@ -16,104 +16,92 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
* <li>管理变量名与虚拟寄存器的映射关系;</li>
|
||||
* <li>分配新的虚拟寄存器实例;</li>
|
||||
* <li>将生成的 IRInstruction 自动添加到 IRFunction 中;</li>
|
||||
* <li>支持声明阶段临时类型记录(如变量声明时的类型推断/校验);</li>
|
||||
* </ul>
|
||||
* <b>注:</b> 该类一般不直接暴露给最终用户,仅供 IR 构建器内部流程调用。
|
||||
*/
|
||||
public class IRContext {
|
||||
|
||||
/* 生成唯一标签用 */
|
||||
/** label 自动生成计数器,用于保证唯一性 */
|
||||
private int labelCounter = 0;
|
||||
/**
|
||||
* 当前正在构建的 IRFunction 对象,所有指令将添加至此
|
||||
*/
|
||||
|
||||
/** 当前正在构建的 IRFunction 对象,所有 IR 指令均将添加至此 */
|
||||
private final IRFunction function;
|
||||
|
||||
/**
|
||||
* 用于管理当前函数作用域内变量与虚拟寄存器的映射
|
||||
*/
|
||||
/** 用于管理当前函数作用域内变量与虚拟寄存器、类型等的映射关系 */
|
||||
private final IRBuilderScope scope;
|
||||
|
||||
/**
|
||||
* 当前声明变量的类型,不在声明变量时为空
|
||||
*/
|
||||
/** 当前 declare 编译阶段变量类型,不在变量声明流程时为 null */
|
||||
private String varType;
|
||||
|
||||
/**
|
||||
* 构造一个新的 IRContext,并将指定的 IRFunction 与作用域关联。
|
||||
* 构造一个新的 IRContext,并将指定的 IRFunction 与作用域管理器关联。
|
||||
*
|
||||
* @param function 要构建的 IRFunction 实例
|
||||
* @param function 要构建的 IRFunction 实例(不可为 null)
|
||||
*/
|
||||
public IRContext(IRFunction function) {
|
||||
this.function = function;
|
||||
this.scope = new IRBuilderScope();
|
||||
// 关联作用域与 IRFunction,以便在声明变量时申请寄存器
|
||||
// 作用域需知晓当前 IRFunction,以便为变量分配寄存器
|
||||
this.scope.attachFunction(function);
|
||||
this.varType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前正在构建的 IRFunction 对象。
|
||||
*
|
||||
* @return 当前 IRFunction 实例
|
||||
*/
|
||||
/** 获取当前正在构建的 IRFunction 对象。 */
|
||||
public IRFunction getFunction() {
|
||||
return function;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前函数的变量与寄存器映射作用域。
|
||||
*
|
||||
* <p>包内可见: 仅限 builder 包内部使用。
|
||||
*
|
||||
* @return IRBuilderScope 实例
|
||||
* <p>包内可见,仅限 builder 包内部调用,避免被外部滥用。</p>
|
||||
*/
|
||||
IRBuilderScope getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为当前函数分配一个新的虚拟寄存器。
|
||||
*
|
||||
* @return 分配到的 IRVirtualRegister 对象
|
||||
*/
|
||||
/** 为当前函数分配一个新的虚拟寄存器。 */
|
||||
public IRVirtualRegister newRegister() {
|
||||
return function.newRegister();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定的 IRInstruction 添加到当前 IRFunction 的指令列表中。
|
||||
*
|
||||
* @param instr 要添加的 IRInstruction 实例
|
||||
* 兼容用法:分配一个“临时虚拟寄存器”。
|
||||
* <p>
|
||||
* 目前直接委托给 {@link #newRegister()},便于老代码兼容与简单用法。
|
||||
* 若将来有命名/临时寄存器区分的需要,可在此扩展实现。
|
||||
* </p>
|
||||
*/
|
||||
public IRVirtualRegister newTempRegister() {
|
||||
return newRegister();
|
||||
}
|
||||
|
||||
/** 将指定的 IRInstruction 添加到当前 IRFunction 的指令列表中。 */
|
||||
public void addInstruction(IRInstruction instr) {
|
||||
function.add(instr);
|
||||
}
|
||||
|
||||
/** 生成一个形如 L0 / L1 ... 的唯一标签名 */
|
||||
/**
|
||||
* 生成一个唯一标签名,如 L0、L1、L2...
|
||||
* <p>常用于条件跳转、分支合流等 IR 控制流构建场景。</p>
|
||||
*
|
||||
* @return 形如 "L0", "L1" 等的唯一字符串标签
|
||||
*/
|
||||
public String newLabel() {
|
||||
return "L" + (labelCounter++);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 declare 编译阶段变量类型
|
||||
*
|
||||
* @return 当前 declare 的变量类型
|
||||
*/
|
||||
/** 获取当前 declare 编译阶段变量类型(声明流程中临时记录) */
|
||||
public String getVarType() {
|
||||
return varType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前 declare 编译阶段变量类型
|
||||
*
|
||||
*/
|
||||
/** 设置当前 declare 编译阶段变量类型(一般在变量声明时赋值) */
|
||||
public void setVarType(String type) {
|
||||
this.varType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除当前 declare 编译阶段变量类型
|
||||
*
|
||||
*/
|
||||
/** 清除当前 declare 编译阶段变量类型(声明流程结束时调用) */
|
||||
public void clearVarType() {
|
||||
this.varType = null;
|
||||
}
|
||||
|
||||
@ -9,62 +9,209 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* IRProgramBuilder 负责将 AST 顶层节点转换为可执行的 {@link IRProgram}。
|
||||
* IRProgramBuilder 负责将 AST 顶层节点(如模块、函数、语句等)转换为可执行的 {@link IRProgram}。
|
||||
*
|
||||
* <p>
|
||||
* 主要工作内容包括:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>预扫描所有模块,将 <code>declare const</code> 常量登记到全局常量表,支持跨模块常量折叠。</li>
|
||||
* <li>对模块内的函数加上模块前缀,保证命名唯一,并将本模块全局声明注入到函数体前部。</li>
|
||||
* <li>预扫描并注册所有 struct 的字段布局(字段→下标),供 IR 阶段对象字段读写使用。</li>
|
||||
* <li>对模块内的普通函数加上模块前缀(ModuleName.func),保证命名唯一,并将本模块全局声明注入到函数体前部。</li>
|
||||
* <li>将 struct 的 init 与 methods 降级为普通 IR 函数并注册为 <code>StructName.__init__</code>、<code>StructName.method</code>;同时在其参数列表首位插入隐式 <code>this: StructName</code>。</li>
|
||||
* <li>将独立顶层语句自动包装为特殊的 "_start" 函数(脚本模式支持)。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* 该类为不可变工具类,仅包含静态行为,不持有状态。
|
||||
* </p>
|
||||
*/
|
||||
public final class IRProgramBuilder {
|
||||
|
||||
/**
|
||||
* 将解析生成的 AST 根节点列表转换为 IRProgram。
|
||||
*
|
||||
* @param roots 含 ModuleNode、FunctionNode 或 StatementNode 的顶层 AST 根节点列表
|
||||
* @return 包含所有转换后 IRFunction 的 IRProgram 对象
|
||||
* @param roots 顶层 AST 根节点列表(ModuleNode / FunctionNode / StatementNode)
|
||||
* @return 构建好的 IRProgram
|
||||
* @throws IllegalStateException 遇到不支持的顶层节点类型时抛出
|
||||
*
|
||||
* <p>主流程:</p>
|
||||
* <ol>
|
||||
* <li>登记全局常量(便于后续常量折叠)。</li>
|
||||
* <li>注册所有 struct 的字段布局(为成员读写和 this.xx 做准备)。</li>
|
||||
* <li>遍历根节点:模块 → 先降级并注册 struct 的构造/方法,再处理模块函数;顶层函数直接构建;顶层语句打包为 "_start" 构建。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public IRProgram buildProgram(List<Node> roots) {
|
||||
// 预先收集并登记全部模块常量到全局常量表
|
||||
// 1. 先登记全局常量,便于后续常量折叠
|
||||
preloadGlobals(roots);
|
||||
// 2. 注册所有结构体的字段布局(为成员访问做准备)
|
||||
preloadStructLayouts(roots);
|
||||
|
||||
// 创建 IR 程序对象
|
||||
IRProgram irProgram = new IRProgram();
|
||||
// 3. 遍历并处理所有顶层节点
|
||||
for (Node node : roots) {
|
||||
switch (node) {
|
||||
case ModuleNode moduleNode -> {
|
||||
// 处理模块节点:遍历其中所有函数,统一用“模块名.函数名”作为全限定名,避免命名冲突
|
||||
for (FunctionNode f : moduleNode.functions()) {
|
||||
irProgram.add(buildFunctionWithGlobals(moduleNode, f));
|
||||
// 3.1 先降级并注册本模块所有 struct 的构造/方法(struct 方法降级)
|
||||
if (moduleNode.structs() != null) {
|
||||
for (StructNode structNode : moduleNode.structs()) {
|
||||
lowerAndRegisterStruct(structNode, irProgram);
|
||||
}
|
||||
}
|
||||
// 3.2 再处理模块里的普通函数,模块内函数名全限定,注入全局声明
|
||||
if (moduleNode.functions() != null) {
|
||||
for (FunctionNode f : moduleNode.functions()) {
|
||||
irProgram.add(buildFunctionWithGlobals(moduleNode, f));
|
||||
}
|
||||
}
|
||||
}
|
||||
case FunctionNode functionNode ->
|
||||
// 处理顶层函数节点:直接构建为 IRFunction 并加入
|
||||
// 3.3 处理顶层函数节点:直接构建为 IRFunction 并加入
|
||||
irProgram.add(buildFunction(functionNode));
|
||||
case StatementNode statementNode ->
|
||||
// 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
|
||||
// 3.4 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
|
||||
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
|
||||
default ->
|
||||
// 遇到未知类型节点,抛出异常
|
||||
// 3.5 遇到未知类型节点,抛出异常
|
||||
throw new IllegalStateException("Unsupported top-level node: " + node);
|
||||
}
|
||||
}
|
||||
return irProgram;
|
||||
}
|
||||
|
||||
// ===================== 全局常量收集 =====================
|
||||
// ===================== 预扫描:注册结构体字段布局 =====================
|
||||
|
||||
/**
|
||||
* 扫描所有模块节点,将其中声明的 const 全局变量(compile-time 常量)
|
||||
* 为每个结构体注册字段布局(字段名 → 槽位索引),并在有继承时将父类布局复制并续接。
|
||||
*
|
||||
* <p>规则:</p>
|
||||
* <ol>
|
||||
* <li>若存在父类:先取到父类布局(如果能找到),将其按顺序复制到当前布局中;起始索引 = 父类字段数。</li>
|
||||
* <li>再将当前结构体声明的字段按声明顺序追加;如果字段名与父类重复,跳过以避免覆盖。</li>
|
||||
* <li>最后将布局以 <code>StructName</code> 为键注册到 {@link IRBuilderScope} 的全局布局表。</li>
|
||||
* </ol>
|
||||
* @param roots AST 顶层节点列表,包含模块/结构体信息
|
||||
*/
|
||||
private void preloadStructLayouts(List<Node> roots) {
|
||||
for (Node n : roots) {
|
||||
if (!(n instanceof ModuleNode mod)) continue;
|
||||
if (mod.structs() == null) continue;
|
||||
|
||||
for (StructNode s : mod.structs()) {
|
||||
List<DeclarationNode> fields = s.fields();
|
||||
if (fields == null) continue;
|
||||
|
||||
Map<String, Integer> layout = new LinkedHashMap<>();
|
||||
int idx = 0;
|
||||
|
||||
// 1. 若有父类,先复制父类布局,并将索引起点置为父类字段数
|
||||
String parentName = s.parent();
|
||||
if (parentName != null && !parentName.isBlank()) {
|
||||
// 约定:IRBuilderScope 内部维护一个“结构体名 → 字段布局”的全局表
|
||||
// 这里假定提供静态查询方法 getStructLayout(String)
|
||||
Map<String, Integer> parentLayout = IRBuilderScope.getStructLayout(parentName);
|
||||
if (parentLayout != null && !parentLayout.isEmpty()) {
|
||||
layout.putAll(parentLayout);
|
||||
idx = parentLayout.size();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 续接当前结构体声明的字段;若与父类同名,跳过(避免覆盖父类槽位)
|
||||
for (DeclarationNode d : fields) {
|
||||
String name = d.getName();
|
||||
if (!layout.containsKey(name)) {
|
||||
layout.put(name, idx++);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 注册:如 Person → {name:0};Student(extends Person) → {name:0, studentId:1}
|
||||
IRBuilderScope.registerStructLayout(s.name(), layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== Struct 降级:方法/构造 → 普通函数 =====================
|
||||
|
||||
/**
|
||||
* 将一个 Struct 的所有构造函数(inits)和方法(methods)降级为普通 Function,并注册进 IRProgram:
|
||||
* <ul>
|
||||
* <li>构造函数:StructName.__init__N(this:StructName, ...),N为参数个数</li>
|
||||
* <li>方法:StructName.method(this:StructName, ...)</li>
|
||||
* </ul>
|
||||
* <p>降级规则:</p>
|
||||
* <ol>
|
||||
* <li>构造/方法函数名前缀加上结构体名(便于唯一定位)。</li>
|
||||
* <li>参数列表最前添加隐式 this:StructName 参数。</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param structNode 当前结构体节点
|
||||
* @param out 注册到的 IRProgram
|
||||
*/
|
||||
private void lowerAndRegisterStruct(StructNode structNode, IRProgram out) {
|
||||
String structName = structNode.name();
|
||||
|
||||
// 1. 多构造函数:降级为 StructName.__init__N
|
||||
if (structNode.inits() != null) {
|
||||
for (FunctionNode initFn : structNode.inits()) {
|
||||
String loweredName = structName + ".__init__" + initFn.parameters().size();
|
||||
FunctionNode loweredInit = lowerStructCallable(
|
||||
initFn,
|
||||
loweredName,
|
||||
structName
|
||||
);
|
||||
out.add(buildFunction(loweredInit));
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 降级处理所有普通方法
|
||||
if (structNode.methods() != null) {
|
||||
for (FunctionNode m : structNode.methods()) {
|
||||
FunctionNode loweredMethod = lowerStructCallable(
|
||||
m,
|
||||
structName + "." + m.name(),
|
||||
structName
|
||||
);
|
||||
out.add(buildFunction(loweredMethod));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个带隐式 this:StructName 参数的函数节点副本,并重命名为 loweredName。
|
||||
*
|
||||
* @param original 原始 FunctionNode
|
||||
* @param loweredName 降级后的新函数名(如 StructName.method)
|
||||
* @param structName 结构体名,用于 this 参数类型
|
||||
* @return 重新包装后的函数节点
|
||||
*/
|
||||
private FunctionNode lowerStructCallable(FunctionNode original, String loweredName, String structName) {
|
||||
// 在参数列表首位插入隐式 this 参数(类型为结构体名)
|
||||
List<ParameterNode> newParams = new ArrayList<>(original.parameters().size() + 1);
|
||||
newParams.add(new ParameterNode("this", structName, original.context()));
|
||||
newParams.addAll(original.parameters());
|
||||
|
||||
return new FunctionNode(
|
||||
loweredName,
|
||||
newParams,
|
||||
original.returnType(),
|
||||
original.body(),
|
||||
original.context()
|
||||
);
|
||||
}
|
||||
|
||||
// ===================== 预扫描:全局常量收集 =====================
|
||||
|
||||
/**
|
||||
* 扫描所有模块节点,将其中声明的 const 全局变量(即编译期常量)
|
||||
* 以 "模块名.常量名" 形式注册到全局常量表。
|
||||
* 支持跨模块常量折叠,用于后续 IR 生成过程中的常量折叠优化。
|
||||
* <p>
|
||||
* 支持跨模块常量折叠,便于 IR 生成时做常量传播与优化。
|
||||
* </p>
|
||||
*
|
||||
* @param roots AST 顶层节点列表
|
||||
*/
|
||||
@ -74,7 +221,7 @@ public final class IRProgramBuilder {
|
||||
String moduleName = mod.name();
|
||||
if (mod.globals() == null) continue;
|
||||
for (DeclarationNode decl : mod.globals()) {
|
||||
// 只处理 compile-time 的 const 常量,并要求有初始值
|
||||
// 只处理带初始值的 const 常量(编译期常量),忽略 run-time/无初始值
|
||||
if (!decl.isConst() || decl.getInitializer().isEmpty()) continue;
|
||||
ExpressionNode init = decl.getInitializer().get();
|
||||
Object value = evalLiteral(init);
|
||||
@ -88,24 +235,28 @@ public final class IRProgramBuilder {
|
||||
|
||||
/**
|
||||
* 字面量提取与类型折叠工具。
|
||||
* <p>
|
||||
* 用于将表达式节点还原为 Java 原生类型(int、long、double、String等),仅支持直接字面量。
|
||||
* 不支持复杂表达式、非常量等情况,无法静态折叠则返回 null。
|
||||
*
|
||||
* @param expr 要计算的表达式节点
|
||||
* @return 提取到的原生常量值,不支持的情况返回 null
|
||||
* @param expr 要计算的表达式节点(要求是字面量)
|
||||
* @return 提取到的原生常量值;若不支持则返回 null
|
||||
*/
|
||||
private Object evalLiteral(ExpressionNode expr) {
|
||||
return switch (expr) {
|
||||
case NumberLiteralNode num -> {
|
||||
// 数字字面量:支持下划线、类型后缀(如 123_456L)
|
||||
String raw = num.value();
|
||||
String s = raw.replace("_", "");
|
||||
char last = Character.toLowerCase(s.charAt(s.length() - 1));
|
||||
// 处理类型后缀:如 123l/123d/123f
|
||||
String core = switch (last) {
|
||||
case 'b', 's', 'l', 'f', 'd' -> s.substring(0, s.length() - 1);
|
||||
default -> s;
|
||||
};
|
||||
try {
|
||||
// 支持浮点数、科学计数法
|
||||
if (core.contains(".") || core.contains("e") || core.contains("E")) {
|
||||
// 浮点数处理
|
||||
yield Double.parseDouble(core);
|
||||
}
|
||||
long lv = Long.parseLong(core);
|
||||
@ -119,9 +270,9 @@ public final class IRProgramBuilder {
|
||||
yield null;
|
||||
}
|
||||
}
|
||||
case StringLiteralNode str -> str.value();
|
||||
case BoolLiteralNode b -> b.getValue() ? 1 : 0;
|
||||
default -> null;
|
||||
case StringLiteralNode str -> str.value(); // 字符串字面量直接返回
|
||||
case BoolLiteralNode b -> b.getValue() ? 1 : 0; // 布尔常量转为 1/0
|
||||
default -> null; // 其他情况不支持常量折叠
|
||||
};
|
||||
}
|
||||
|
||||
@ -129,21 +280,23 @@ public final class IRProgramBuilder {
|
||||
|
||||
/**
|
||||
* 构建带有模块全局声明“注入”的函数,并将函数名加上模块前缀,保证模块内函数名唯一。
|
||||
* 如果模块有全局声明,则这些声明会被插入到函数体前部(**会过滤掉与参数同名的全局声明**)。
|
||||
* <p>
|
||||
* 如果模块有全局声明,则这些声明会被插入到函数体前部(<b>会过滤掉与参数同名的全局声明</b>,防止变量遮蔽)。
|
||||
* </p>
|
||||
*
|
||||
* @param moduleNode 所属模块节点
|
||||
* @param functionNode 待构建的函数节点
|
||||
* @return 包含全局声明的 IRFunction
|
||||
*/
|
||||
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
|
||||
// 拼接模块名和函数名,生成全限定名
|
||||
// 1. 拼接模块名和函数名,生成全限定名
|
||||
String qualifiedName = moduleNode.name() + "." + functionNode.name();
|
||||
// 2. 若无全局声明,直接重命名构建
|
||||
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
|
||||
// 无全局声明,直接重命名构建
|
||||
return buildFunction(renameFunction(functionNode, qualifiedName));
|
||||
}
|
||||
|
||||
// ------- 过滤与参数重名的全局声明 -------
|
||||
// 3. 过滤掉与参数重名的全局声明(优先参数作用域,避免变量遮蔽)
|
||||
Set<String> paramNames = new HashSet<>();
|
||||
for (ParameterNode p : functionNode.parameters()) {
|
||||
paramNames.add(p.name());
|
||||
@ -156,12 +309,12 @@ public final class IRProgramBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 若无可插入的全局声明,直接重命名构建
|
||||
if (filteredGlobals.isEmpty()) {
|
||||
// 过滤后已无可插入的全局声明
|
||||
return buildFunction(renameFunction(functionNode, qualifiedName));
|
||||
}
|
||||
|
||||
// 合并全局声明与函数体,前插全局声明
|
||||
// 5. 合并全局声明与函数体,前插全局声明
|
||||
List<StatementNode> newBody = new ArrayList<>(filteredGlobals.size() + functionNode.body().size());
|
||||
newBody.addAll(filteredGlobals);
|
||||
newBody.addAll(functionNode.body());
|
||||
@ -197,6 +350,8 @@ public final class IRProgramBuilder {
|
||||
*
|
||||
* @param functionNode 待构建的 FunctionNode
|
||||
* @return 构建后的 IRFunction
|
||||
*
|
||||
* <p>本方法仅作中转,直接委托给 FunctionBuilder。</p>
|
||||
*/
|
||||
private IRFunction buildFunction(FunctionNode functionNode) {
|
||||
return new FunctionBuilder().build(functionNode);
|
||||
@ -215,6 +370,7 @@ public final class IRProgramBuilder {
|
||||
List.of(),
|
||||
"void",
|
||||
List.of(stmt),
|
||||
// 用(-1,-1,"")占位,避免依赖真实位置信息
|
||||
new NodeContext(-1, -1, "")
|
||||
);
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ public class InstructionFactory {
|
||||
|
||||
/**
|
||||
* 生成“值拷贝”语义(src → dest)。
|
||||
* 若类型无法推断,默认采用 int 方案(ADD_I32, src+0)。
|
||||
* 通过在 IR 中构造 “src + 0” 的形式,触发 Peephole 优化折叠成 MOV。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param src 源寄存器
|
||||
@ -114,46 +114,50 @@ public class InstructionFactory {
|
||||
if (src == dest) {
|
||||
return;
|
||||
}
|
||||
String varType = ctx.getVarType(); // 需要 IRContext 提供
|
||||
char suffix = '\0';
|
||||
String varType = ctx.getVarType();
|
||||
IROpCode op;
|
||||
IRConstant zeroConst;
|
||||
|
||||
if (varType != null) {
|
||||
switch (varType) {
|
||||
case "byte" -> suffix = 'b';
|
||||
case "short" -> suffix = 's';
|
||||
case "int" -> suffix = 'i';
|
||||
case "long" -> suffix = 'l';
|
||||
case "float" -> suffix = 'f';
|
||||
case "double" -> suffix = 'd';
|
||||
case "byte" -> {
|
||||
op = IROpCode.ADD_B8;
|
||||
zeroConst = new IRConstant((byte) 0);
|
||||
}
|
||||
case "short" -> {
|
||||
op = IROpCode.ADD_S16;
|
||||
zeroConst = new IRConstant((short) 0);
|
||||
}
|
||||
case "int" -> {
|
||||
op = IROpCode.ADD_I32;
|
||||
zeroConst = new IRConstant(0);
|
||||
}
|
||||
case "long" -> {
|
||||
op = IROpCode.ADD_L64;
|
||||
zeroConst = new IRConstant(0L);
|
||||
}
|
||||
case "float" -> {
|
||||
op = IROpCode.ADD_F32;
|
||||
zeroConst = new IRConstant(0.0f);
|
||||
}
|
||||
case "double" -> {
|
||||
op = IROpCode.ADD_D64;
|
||||
zeroConst = new IRConstant(0.0);
|
||||
}
|
||||
default -> {
|
||||
// 引用类型 / 结构体等统一走 int 路径
|
||||
op = IROpCode.ADD_I32;
|
||||
zeroConst = new IRConstant(0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 无法推断类型时,退化为 int 方案
|
||||
op = IROpCode.ADD_I32;
|
||||
zeroConst = new IRConstant(0);
|
||||
}
|
||||
IRVirtualRegister zero;
|
||||
IROpCode op = switch (suffix) {
|
||||
case 'd' -> {
|
||||
zero = loadConst(ctx, 0.0);
|
||||
yield IROpCode.ADD_D64;
|
||||
}
|
||||
case 'f' -> {
|
||||
zero = loadConst(ctx, 0.0f);
|
||||
yield IROpCode.ADD_F32;
|
||||
}
|
||||
case 'l' -> {
|
||||
zero = loadConst(ctx, 0L);
|
||||
yield IROpCode.ADD_L64;
|
||||
}
|
||||
case 's' -> {
|
||||
zero = loadConst(ctx, 0);
|
||||
yield IROpCode.ADD_S16;
|
||||
}
|
||||
case 'b' -> {
|
||||
zero = loadConst(ctx, 0);
|
||||
yield IROpCode.ADD_B8;
|
||||
}
|
||||
default -> {
|
||||
zero = loadConst(ctx, 0);
|
||||
yield IROpCode.ADD_I32;
|
||||
}
|
||||
};
|
||||
ctx.addInstruction(new BinaryOperationInstruction(op, dest, src, zero));
|
||||
|
||||
// 注意:这里传入的是立即数 zeroConst,而不是寄存器
|
||||
ctx.addInstruction(new BinaryOperationInstruction(op, dest, src, zeroConst));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -24,9 +24,6 @@ import java.util.ArrayDeque;
|
||||
* <li>负责控制流跳转(分支、循环)的标签分配与维护。</li>
|
||||
* <li>在变量赋值和声明时自动常量折叠和登记。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author [你的名字]
|
||||
* @since 1.0
|
||||
*/
|
||||
public class StatementBuilder {
|
||||
|
||||
@ -84,8 +81,58 @@ public class StatementBuilder {
|
||||
expr.build(exp);
|
||||
return;
|
||||
}
|
||||
|
||||
// ===== 赋值语句 =====
|
||||
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) {
|
||||
// 1) 优先当作“已存在的局部/参数变量”赋值(保持原有行为)
|
||||
final String type = ctx.getScope().lookupType(var);
|
||||
IRVirtualRegister localReg = ctx.getScope().lookup(var);
|
||||
if (localReg != null) {
|
||||
ctx.setVarType(type);
|
||||
expr.buildInto(rhs, localReg);
|
||||
|
||||
// 赋值时尝试记录/清除常量
|
||||
try {
|
||||
Object constVal = tryFoldConst(rhs);
|
||||
if (constVal != null) ctx.getScope().setConstValue(var, constVal);
|
||||
else ctx.getScope().clearConstValue(var);
|
||||
} catch (Throwable ignored) {}
|
||||
ctx.clearVarType();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) 若不是局部变量:尝试将“裸标识符”视为 this.<field> 的结构体字段赋值
|
||||
IRVirtualRegister thisReg = ctx.getScope().lookup("this");
|
||||
String thisType = ctx.getScope().lookupType("this");
|
||||
if (thisReg != null && thisType != null) {
|
||||
java.util.Map<String, Integer> layout = ctx.getScope().getStructLayout(thisType);
|
||||
if (layout == null) {
|
||||
// 兼容 "Module.Struct" 与简单名 "Struct"
|
||||
int dot = thisType.lastIndexOf('.');
|
||||
if (dot >= 0 && dot + 1 < thisType.length()) {
|
||||
layout = ctx.getScope().getStructLayout(thisType.substring(dot + 1));
|
||||
}
|
||||
}
|
||||
Integer idx = (layout != null) ? layout.get(var) : null;
|
||||
|
||||
if (idx != null) {
|
||||
// 生成右值
|
||||
IRVirtualRegister valReg = expr.build(rhs);
|
||||
// 字段槽位下标常量
|
||||
IRVirtualRegister idxReg = InstructionFactory.loadConst(ctx, idx);
|
||||
// 用 __setindex_r 写入字段(与构造写入/读取对齐)
|
||||
java.util.List<org.jcnc.snow.compiler.ir.core.IRValue> argv =
|
||||
java.util.List.of(thisReg, idxReg, valReg);
|
||||
ctx.addInstruction(new org.jcnc.snow.compiler.ir.instruction.CallInstruction(
|
||||
null, "__setindex_r", argv));
|
||||
|
||||
// 字段写入后清理该标识符的常量绑定(避免误当作本地常量)
|
||||
try { ctx.getScope().clearConstValue(var); } catch (Throwable ignored) {}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 否则退回为“声明/绑定一个新的局部变量并赋值”(与原逻辑一致)
|
||||
ctx.setVarType(type);
|
||||
IRVirtualRegister target = getOrDeclareRegister(var, type);
|
||||
expr.buildInto(rhs, target);
|
||||
@ -97,8 +144,7 @@ public class StatementBuilder {
|
||||
ctx.getScope().setConstValue(var, constVal);
|
||||
else
|
||||
ctx.getScope().clearConstValue(var);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
ctx.clearVarType();
|
||||
return;
|
||||
@ -163,11 +209,9 @@ public class StatementBuilder {
|
||||
ctx.setVarType(decl.getType());
|
||||
|
||||
// 2. 为当前声明的变量分配一个全新的虚拟寄存器
|
||||
// 这样可以保证该变量和初始值表达式中的变量物理上独立,不会发生别名/串扰
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
|
||||
// 3. 将初始值表达式的计算结果写入新分配的寄存器
|
||||
// 即使初始值是某个已存在变量(如 outer_i),这里是值的拷贝
|
||||
expr.buildInto(decl.getInitializer().get(), dest);
|
||||
|
||||
// 声明赋初值时登记常量
|
||||
@ -177,8 +221,7 @@ public class StatementBuilder {
|
||||
ctx.getScope().setConstValue(decl.getName(), constVal);
|
||||
else
|
||||
ctx.getScope().clearConstValue(decl.getName());
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
ctx.clearVarType();
|
||||
ctx.getScope().declare(decl.getName(), decl.getType(), dest);
|
||||
@ -188,18 +231,18 @@ public class StatementBuilder {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (stmt instanceof ReturnNode ret) {
|
||||
// return 语句
|
||||
if (ret.getExpression().isPresent()) {
|
||||
// return 带返回值
|
||||
IRVirtualRegister r = expr.build(ret.getExpression().get());
|
||||
InstructionFactory.ret(ctx, r);
|
||||
} else {
|
||||
// return 无返回值
|
||||
InstructionFactory.retVoid(ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (stmt instanceof BreakNode) {
|
||||
// break 语句:跳转到当前最近一层循环的结束标签
|
||||
if (breakTargets.isEmpty()) {
|
||||
@ -208,6 +251,7 @@ public class StatementBuilder {
|
||||
InstructionFactory.jmp(ctx, breakTargets.peek());
|
||||
return;
|
||||
}
|
||||
|
||||
if (stmt instanceof ContinueNode) {
|
||||
// continue 语句:跳转到当前最近一层循环的 step 起始标签
|
||||
if (continueTargets.isEmpty()) {
|
||||
@ -216,10 +260,12 @@ public class StatementBuilder {
|
||||
InstructionFactory.jmp(ctx, continueTargets.peek());
|
||||
return;
|
||||
}
|
||||
|
||||
// 不支持的语句类型
|
||||
throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取变量名对应的寄存器,如果尚未声明则新声明一个并返回。
|
||||
*
|
||||
|
||||
@ -3,29 +3,99 @@ package org.jcnc.snow.compiler.ir.value;
|
||||
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||
|
||||
/**
|
||||
* IRConstant —— 表示中间表示(IR)系统中的常量值。
|
||||
* {@code IRConstant} —— 表示中间表示(IR)系统中的常量值节点。
|
||||
* <p>
|
||||
* 常量用于表示在编译期间已知的不可变值,例如字面整数、浮点数、布尔值或字符串。
|
||||
* 与 {@link IRVirtualRegister} 不同,常量不需要通过寄存器存储,
|
||||
* 可直接作为 IR 指令的操作数使用。
|
||||
* <p>
|
||||
* 典型应用:
|
||||
* - 加载常量指令: v1 = CONST 42
|
||||
* - 计算表达式: v2 = ADD v1, 100
|
||||
* 常量用于表示在编译期间已知的不可变值,例如字面整数、浮点数、布尔值或字符串等。
|
||||
* 与 {@link IRVirtualRegister} 不同,IRConstant 不需要通过寄存器存储,
|
||||
* 可直接作为 IR 指令的操作数(立即数/常量池引用等)。
|
||||
*/
|
||||
public record IRConstant(Object value) implements IRValue {
|
||||
|
||||
/**
|
||||
* 将常量值转换为字符串,用于打印 IR 指令或调试输出。
|
||||
* <p>
|
||||
* 例如:
|
||||
* - 整数常量: 42
|
||||
* - 字符串常量: "hello"
|
||||
* 通用工厂方法:将任意 Java 对象包装为 IRConstant。
|
||||
*
|
||||
* @return 常量的字符串表示
|
||||
* @param v 任意对象
|
||||
* @return IRConstant 封装后的常量
|
||||
*/
|
||||
public static IRConstant fromObject(Object v) {
|
||||
return new IRConstant(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字字面量工厂:接受源码字面量字符串,解析为 int/long/double 等 Java 数值类型。
|
||||
* <p>
|
||||
* 支持下划线分隔(如 "1_000_000")、类型后缀(如 'l','d','f' 等),并自动转换为
|
||||
* <ul>
|
||||
* <li>double:带小数点或科学计数法</li>
|
||||
* <li>long/byte/short/int:依类型后缀或值范围判定</li>
|
||||
* <li>解析失败时兜底为字符串原文,保证编译不中断</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param literal 字面量字符串(可能含类型后缀、下划线分隔)
|
||||
* @return IRConstant 包装的数字常量
|
||||
*/
|
||||
public static IRConstant fromNumber(String literal) {
|
||||
if (literal == null) return new IRConstant(null);
|
||||
String s = literal.replace("_", ""); // 去除下划线分隔符
|
||||
// 检查并处理类型后缀(b/s/l/f/d)
|
||||
char last = Character.toLowerCase(s.charAt(s.length() - 1));
|
||||
String core = switch (last) {
|
||||
case 'b', 's', 'l', 'f', 'd' -> s.substring(0, s.length() - 1);
|
||||
default -> s;
|
||||
};
|
||||
try {
|
||||
// 浮点:含小数点或科学计数法
|
||||
if (core.contains(".") || core.contains("e") || core.contains("E")) {
|
||||
return new IRConstant(Double.parseDouble(core));
|
||||
}
|
||||
long lv = Long.parseLong(core); // 整型
|
||||
return switch (last) {
|
||||
case 'b' -> new IRConstant((byte) lv); // 字节型
|
||||
case 's' -> new IRConstant((short) lv); // 短整型
|
||||
case 'l' -> new IRConstant(lv); // 长整型
|
||||
default -> new IRConstant((int) lv); // 默认 int
|
||||
};
|
||||
} catch (NumberFormatException e) {
|
||||
// 解析失败时,回退为原始字符串,避免编译中断
|
||||
return new IRConstant(core);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串字面量工厂。
|
||||
*
|
||||
* @param s 字符串内容
|
||||
* @return IRConstant 封装的字符串常量
|
||||
*/
|
||||
public static IRConstant fromString(String s) {
|
||||
return new IRConstant(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 布尔字面量工厂。
|
||||
*
|
||||
* @param v 布尔值
|
||||
* @return IRConstant 封装的布尔常量
|
||||
*/
|
||||
public static IRConstant fromBoolean(boolean v) {
|
||||
return new IRConstant(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试友好的字符串表示。
|
||||
* <ul>
|
||||
* <li>字符串常量输出带引号,转义引号和反斜杠</li>
|
||||
* <li>null 输出为 "null"</li>
|
||||
* <li>其它类型调用 toString()</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
if (value == null) return "null";
|
||||
if (value instanceof String s) {
|
||||
String escaped = s.replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
return "\"" + escaped + "\"";
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
package org.jcnc.snow.compiler.lexer.core;
|
||||
|
||||
import org.jcnc.snow.common.SnowConfig;
|
||||
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
|
||||
import org.jcnc.snow.compiler.lexer.scanners.*;
|
||||
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
import org.jcnc.snow.compiler.lexer.utils.TokenPrinter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@ -54,6 +56,7 @@ public class LexerEngine {
|
||||
/* 2. 后置整体校验 */
|
||||
validateTokens();
|
||||
/* 3. 打印 token */
|
||||
// TODO: TokenPrinter
|
||||
// if (SnowConfig.isDebug()) {
|
||||
// TokenPrinter.print(tokens);
|
||||
// }
|
||||
|
||||
@ -28,7 +28,9 @@ public class TokenFactory {
|
||||
private static final Set<String> KEYWORDS = Set.of
|
||||
("module", "function", "params", "returns", "body", "end",
|
||||
"if", "then", "else", "loop", "declare", "return", "import", "init",
|
||||
"cond", "step", "globals", "break", "continue", "const");
|
||||
"cond", "step", "globals", "break", "continue", "const",
|
||||
"new", "extends"
|
||||
);
|
||||
|
||||
/**
|
||||
* 内置类型名称集合,如 int、string 等。
|
||||
|
||||
@ -7,44 +7,62 @@ import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* 表示模块定义的 AST 节点。
|
||||
* 一个模块通常由模块名、导入语句列表和函数定义列表组成。
|
||||
* {@code ModuleNode}
|
||||
* <p>
|
||||
* 抽象语法树(AST)顶层节点 —— 模块定义。
|
||||
* <ul>
|
||||
* <li>代表一个完整的源码模块/文件(如 main, math 等)。</li>
|
||||
* <li>包含模块名、导入列表、全局变量、结构体和函数定义等全部模块级内容。</li>
|
||||
* <li>是整个编译流程的入口节点。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param name 模块名称。
|
||||
* @param imports 模块导入列表,每个导入是一个 {@link ImportNode}。
|
||||
* @param functions 模块中的函数列表,每个函数是一个 {@link FunctionNode}。
|
||||
* @param context 节点上下文信息(包含行号、列号等)
|
||||
* @param name 模块名称
|
||||
* @param imports 导入模块列表
|
||||
* @param globals 全局变量/常量声明
|
||||
* @param structs 结构体定义列表
|
||||
* @param functions 函数定义列表
|
||||
* @param context 源代码位置信息
|
||||
*/
|
||||
public record ModuleNode(
|
||||
String name,
|
||||
List<ImportNode> imports,
|
||||
List<DeclarationNode> globals,
|
||||
List<StructNode> structs,
|
||||
List<FunctionNode> functions,
|
||||
NodeContext context
|
||||
) implements Node {
|
||||
|
||||
/**
|
||||
* 返回模块节点的字符串表示形式,包含模块名、导入模块列表和函数列表。
|
||||
* 返回模块节点的简要字符串表示(用于日志、调试)。
|
||||
* 列出模块名、导入、全局变量、结构体、函数等简明内容。
|
||||
*
|
||||
* @return 模块的简洁字符串表示,用于调试和日志输出。
|
||||
* @return 字符串形式,如
|
||||
* Module(name=main, imports=[math], globals=[int x], structs=[Foo], functions=[bar])
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringJoiner importJoiner = new StringJoiner(", ");
|
||||
for (ImportNode imp : imports) {
|
||||
importJoiner.add(imp.moduleName());
|
||||
}
|
||||
StringJoiner globalJoiner = new StringJoiner(", ");
|
||||
for (DeclarationNode g : globals) {
|
||||
globalJoiner.add(g.getType() + " " + g.getName());
|
||||
}
|
||||
StringJoiner funcJoiner = new StringJoiner(", ");
|
||||
for (FunctionNode fn : functions) {
|
||||
funcJoiner.add(fn.name());
|
||||
}
|
||||
return "Module(name=" + name
|
||||
+ ", imports=[" + importJoiner + "]"
|
||||
+ ", globals=[" + globalJoiner + "]"
|
||||
+ ", functions=[" + funcJoiner + "])";
|
||||
// 1) 导入模块列表字符串
|
||||
StringJoiner impJ = new StringJoiner(", ");
|
||||
imports.forEach(i -> impJ.add(i.moduleName()));
|
||||
|
||||
// 2) 全局变量/常量列表字符串
|
||||
StringJoiner globJ = new StringJoiner(", ");
|
||||
globals.forEach(g -> globJ.add(g.getType() + " " + g.getName()));
|
||||
|
||||
// 3) 结构体名列表字符串
|
||||
StringJoiner structJ = new StringJoiner(", ");
|
||||
structs.forEach(s -> structJ.add(s.name()));
|
||||
|
||||
// 4) 函数名列表字符串
|
||||
StringJoiner funcJ = new StringJoiner(", ");
|
||||
functions.forEach(f -> funcJ.add(f.name()));
|
||||
|
||||
// 5) 综合输出
|
||||
return "Module(name=" + name +
|
||||
", imports=[" + impJ + "]" +
|
||||
", globals=[" + globJ + "]" +
|
||||
", structs=[" + structJ + "]" +
|
||||
", functions=[" + funcJ + "])";
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
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 NewExpressionNode}
|
||||
* <p>
|
||||
* 抽象语法树(AST)节点 —— 对象创建表达式。
|
||||
* <pre>new TypeName(arg1, arg2, ...)</pre>
|
||||
*
|
||||
* <p>
|
||||
* 语义:在源码中通过 <b>new</b> 关键字实例化某个类型,
|
||||
* 实参列表为构造函数参数。表达式类型为对应结构体类型。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 字段说明:
|
||||
* <ul>
|
||||
* <li>{@code typeName} —— 创建对象的目标类型名(如 "Person"、"MyStruct")。</li>
|
||||
* <li>{@code arguments} —— 构造函数参数表达式列表(可为空)。</li>
|
||||
* <li>{@code ctx} —— 源码位置上下文(行列信息,便于报错和调试定位)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param typeName 目标类型名
|
||||
* @param arguments 构造参数表达式列表
|
||||
* @param ctx 源代码位置信息
|
||||
*/
|
||||
public record NewExpressionNode(
|
||||
String typeName,
|
||||
List<ExpressionNode> arguments,
|
||||
NodeContext ctx
|
||||
) implements ExpressionNode {
|
||||
|
||||
/**
|
||||
* 获取本节点的源代码位置信息(行号、列号等)。
|
||||
* <p>
|
||||
* 实现 ExpressionNode 接口的抽象方法,便于统一错误处理与调试。
|
||||
* </p>
|
||||
*
|
||||
* @return 当前节点的源代码上下文信息
|
||||
*/
|
||||
@Override
|
||||
public NodeContext context() {
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package org.jcnc.snow.compiler.parser.ast;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* {@code StructNode}
|
||||
* <p>
|
||||
* AST 节点 —— 结构体定义(struct)。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 描述一个结构体类型,包括字段、可选构造函数、方法列表等。
|
||||
* 结构体可声明零个或多个字段,可选构造函数(init),
|
||||
* 以及零个或多个方法。
|
||||
*
|
||||
* @param name 结构体名称
|
||||
* @param parent 父类名称(无继承时为 {@code null})
|
||||
* @param fields 字段声明列表
|
||||
* @param inits 构造函数(可为 null)
|
||||
* @param methods 方法列表
|
||||
* @param context 源码位置信息
|
||||
*/
|
||||
public record StructNode(
|
||||
String name,
|
||||
String parent,
|
||||
List<DeclarationNode> fields,
|
||||
List<FunctionNode> inits,
|
||||
List<FunctionNode> methods,
|
||||
NodeContext context
|
||||
) implements Node {
|
||||
|
||||
/**
|
||||
* 输出结构体节点的简明字符串表示,便于调试和日志查看。
|
||||
*
|
||||
* @return 结构体节点的简要信息,包括名称、字段、构造函数、方法
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", "StructNode[", "]")
|
||||
.add("name=" + name)
|
||||
.add("parent=" + parent)
|
||||
.add("fields=" + fields.size())
|
||||
.add("ctors=" + (inits == null ? 0 : inits.size()))
|
||||
.add("methods=" + methods.size())
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
package org.jcnc.snow.compiler.parser.ast.base;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ImportNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.StructNode;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* {@code ModuleNode} 表示一个源文件或逻辑模块的 AST 根节点。
|
||||
*
|
||||
* <p>
|
||||
* 用于描述一个完整模块的全部语法内容,包括模块名、导入依赖、全局变量、结构体定义、函数定义等。
|
||||
* 作为语法树的顶层节点,供后续语义分析与 IR 生成统一处理。
|
||||
* </p>
|
||||
*
|
||||
* <p><b>字段说明:</b></p>
|
||||
* <ul>
|
||||
* <li>{@code name} —— 模块名称,通常对应文件名或逻辑包名,要求唯一。</li>
|
||||
* <li>{@code imports} —— import 导入列表,描述本模块依赖的其它模块名。</li>
|
||||
* <li>{@code globals} —— 全局变量与常量声明列表,作用域为整个模块。</li>
|
||||
* <li>{@code structs} —— 结构体定义列表(支持零个或多个结构体)。</li>
|
||||
* <li>{@code functions} —— 函数定义列表(支持零个或多个函数)。</li>
|
||||
* <li>{@code context} —— 源代码位置信息,用于报错定位与调试。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* 该类为 Java record,天然不可变。构造参数即为字段名,调用时自动生成 getter 方法。
|
||||
* </p>
|
||||
*
|
||||
* @param name 模块名称(如 "main")
|
||||
* @param imports 导入依赖列表(ImportNode)
|
||||
* @param globals 全局变量声明列表(DeclarationNode)
|
||||
* @param structs 结构体定义列表(StructNode)
|
||||
* @param functions 函数定义列表(FunctionNode)
|
||||
* @param context 源代码位置信息(NodeContext)
|
||||
*/
|
||||
public record ModuleNode(
|
||||
String name,
|
||||
List<ImportNode> imports,
|
||||
List<DeclarationNode> globals,
|
||||
List<StructNode> structs,
|
||||
List<FunctionNode> functions,
|
||||
NodeContext context
|
||||
) implements Node {
|
||||
|
||||
/**
|
||||
* 重写 toString 方法,便于调试和日志输出。
|
||||
* 展示模块名、导入、全局变量、结构体、函数的简要列表。
|
||||
*
|
||||
* @return 字符串形式,格式如:
|
||||
* Module(name=main, imports=[foo, bar], globals=[int x, ...], structs=[A, B], functions=[f, g])
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
// 构造 import 字符串
|
||||
StringJoiner impJ = new StringJoiner(", ");
|
||||
imports.forEach(i -> impJ.add(i.moduleName()));
|
||||
|
||||
// 构造全局变量声明字符串(类型+名称)
|
||||
StringJoiner globJ = new StringJoiner(", ");
|
||||
globals.forEach(g -> globJ.add(g.getType() + " " + g.getName()));
|
||||
|
||||
// 构造结构体名列表
|
||||
StringJoiner structJ = new StringJoiner(", ");
|
||||
structs.forEach(s -> structJ.add(s.name()));
|
||||
|
||||
// 构造函数名列表
|
||||
StringJoiner funcJ = new StringJoiner(", ");
|
||||
functions.forEach(f -> funcJ.add(f.name()));
|
||||
|
||||
// 综合输出
|
||||
return "Module(name=" + name +
|
||||
", imports=[" + impJ + "]" +
|
||||
", globals=[" + globJ + "]" +
|
||||
", structs=[" + structJ + "]" +
|
||||
", functions=[" + funcJ + "])";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
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.NewExpressionNode;
|
||||
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.context.UnexpectedToken;
|
||||
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code NewObjectParselet}
|
||||
* <p>
|
||||
* 解析对象创建表达式 <code>new TypeName(arg1, arg2, ...)</code> 的前缀解析器实现。
|
||||
* <ul>
|
||||
* <li>用于 Pratt 解析框架,实现 new 关键字前缀的自定义解析逻辑。</li>
|
||||
* <li>支持类型名为内置类型(TokenType.TYPE)或用户结构体名(TokenType.IDENTIFIER)。</li>
|
||||
* <li>支持可变参数列表,参数为任意表达式。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class NewObjectParselet implements PrefixParselet {
|
||||
|
||||
/**
|
||||
* 解析 new 对象创建表达式,并生成 AST 新建节点。
|
||||
*
|
||||
* @param ctx 语法分析上下文,包含 TokenStream 及源文件名等信息
|
||||
* @param token 当前读取到的 new 关键字 Token
|
||||
* @return 表达式节点(NewExpressionNode)
|
||||
*
|
||||
* <p>
|
||||
* 解析流程:
|
||||
* <ol>
|
||||
* <li>读取类型名(支持内建类型或结构体名);</li>
|
||||
* <li>读取参数列表,参数为表达式,用逗号分隔;</li>
|
||||
* <li>封装为 NewExpressionNode,记录源码位置信息。</li>
|
||||
* </ol>
|
||||
* 出错时会抛出 UnexpectedToken 异常。
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public ExpressionNode parse(ParserContext ctx, Token token) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
// ==========================
|
||||
// 1) 解析类型名
|
||||
// ==========================
|
||||
// 类型名只能为内建类型(TYPE)或用户结构体名(IDENTIFIER)
|
||||
if (ts.peek().getType() != TokenType.TYPE && ts.peek().getType() != TokenType.IDENTIFIER) {
|
||||
var t = ts.peek();
|
||||
throw new UnexpectedToken(
|
||||
"期望的标记类型为 TYPE 或 IDENTIFIER,但实际得到的是 " +
|
||||
t.getType() + " ('" + t.getLexeme() + "')",
|
||||
t.getLine(), t.getCol()
|
||||
);
|
||||
}
|
||||
String typeName = ts.next().getLexeme();
|
||||
|
||||
// ==========================
|
||||
// 2) 解析构造参数列表
|
||||
// ==========================
|
||||
ts.expect("("); // 必须有左括号
|
||||
|
||||
List<ExpressionNode> args = new ArrayList<>();
|
||||
if (!ts.match(")")) { // 非空参数列表
|
||||
// 连续处理逗号分隔的多个参数
|
||||
do {
|
||||
args.add(new PrattExpressionParser().parse(ctx));
|
||||
} while (ts.match(","));
|
||||
ts.expect(")"); // 结尾必须为右括号
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// 3) 封装为 AST 节点并返回
|
||||
// ==========================
|
||||
NodeContext nc = new NodeContext(token.getLine(), token.getCol(), ctx.getSourceName());
|
||||
return new NewExpressionNode(typeName, args, nc);
|
||||
}
|
||||
}
|
||||
@ -13,62 +13,62 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@code PrattExpressionParser} 基于 Pratt 算法的表达式解析器实现。
|
||||
* {@code PrattExpressionParser}
|
||||
* <p>
|
||||
* 该类通过前缀(PrefixParselet)和中缀(InfixParselet)解析器注册表,
|
||||
* 支持灵活扩展的表达式语法,包括字面量、变量、函数调用、成员访问和各种运算符表达式。
|
||||
* </p>
|
||||
* <p>
|
||||
* 运算符优先级通过枚举控制,结合递归解析实现高效的优先级处理和语法结构解析。
|
||||
* 未注册的语法类型或运算符会统一抛出 {@link UnsupportedFeature} 异常。
|
||||
* 基于 Pratt 算法的表达式解析器(经典“运算符优先级”递归解析框架)。
|
||||
* <ul>
|
||||
* <li>支持注册前缀和中缀解析器,灵活组合表达式语法。</li>
|
||||
* <li>支持字面量、变量、函数调用、成员访问、对象创建、各类一元/二元/多元运算。</li>
|
||||
* <li>可快速扩展新语法(只需注册新的 Parselet 即可)。</li>
|
||||
* <li>出错时统一抛出 {@link UnsupportedFeature},便于调试和错误提示。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class PrattExpressionParser implements ExpressionParser {
|
||||
|
||||
/**
|
||||
* 前缀解析器注册表(按 Token 类型名或词素索引)。
|
||||
* <p>
|
||||
* 用于存储所有支持的前缀表达式解析器,例如字面量、变量、分组、数组、一元运算等。
|
||||
* 支持通过 TokenType 的名称和特定词素(如 "(", "[")两种方式索引。
|
||||
* </p>
|
||||
* 前缀解析器注册表(通过 Token 类型名或词素作为索引)。
|
||||
* <ul>
|
||||
* <li>如数字字面量、标识符、字符串、布尔值、new、分组、数组、一元运算等。</li>
|
||||
* <li>支持同时用 TokenType 名称和具体词素(如 "("、"-")注册。</li>
|
||||
* </ul>
|
||||
*/
|
||||
private static final Map<String, PrefixParselet> prefixes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 中缀解析器注册表(按运算符词素索引)。
|
||||
* <p>
|
||||
* 用于存储所有支持的中缀表达式解析器,如二元运算、函数调用、下标、成员访问等。
|
||||
* 仅通过词素索引(如 "+", "-", "(", "[" 等)。
|
||||
* </p>
|
||||
* 中缀解析器注册表(通过运算符词素索引)。
|
||||
* <ul>
|
||||
* <li>如 + - * / % 及比较、逻辑、函数调用、下标、成员访问等。</li>
|
||||
* <li>仅词素索引(如 "+"、"-"、"("、".")</li>
|
||||
* </ul>
|
||||
*/
|
||||
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.STRING_LITERAL.name(), new StringLiteralParselet());
|
||||
// 注册布尔字面量解析
|
||||
prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
|
||||
// ----------------- 前缀解析器注册 -----------------
|
||||
// 各种字面量/标识符
|
||||
prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
|
||||
prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
|
||||
prefixes.put(TokenType.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(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());
|
||||
|
||||
// -------- 中缀解析器注册 --------
|
||||
// 注册常见二元运算符(加减乘除、取模)
|
||||
// 对象创建 new TypeName(args...)
|
||||
prefixes.put("new", new NewObjectParselet());
|
||||
|
||||
// ----------------- 中缀解析器注册 -----------------
|
||||
// 常见二元算数运算符
|
||||
infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
|
||||
@ -79,25 +79,25 @@ public class PrattExpressionParser implements ExpressionParser {
|
||||
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());
|
||||
infixes.put("||", new BinaryOperatorParselet(Precedence.OR, true));
|
||||
// 函数调用、数组下标、成员访问
|
||||
infixes.put("(", new CallParselet());
|
||||
infixes.put("[", new IndexParselet());
|
||||
infixes.put(".", new MemberParselet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析任意表达式的统一入口。
|
||||
* 统一表达式解析入口,自动以最低优先级递归解析整个表达式。
|
||||
* <p>
|
||||
* 该方法将以最低优先级启动表达式递归解析,能够自动适配和处理多层嵌套或复杂组合表达式。
|
||||
* 能解析嵌套、复合等所有合法表达式结构。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前解析上下文对象(持有 token 流等信息)
|
||||
* @param ctx 当前解析上下文对象(含 token 流等信息)
|
||||
* @return 解析得到的表达式 AST 节点对象
|
||||
*/
|
||||
@Override
|
||||
@ -106,50 +106,42 @@ public class PrattExpressionParser implements ExpressionParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定优先级解析表达式(Pratt 算法核心)。
|
||||
* Pratt 算法主递归循环:按给定优先级递归解析表达式。
|
||||
* <p>
|
||||
* 1. 先取当前 token,查找对应的前缀解析器进行初始解析,构建表达式左侧(如字面量、变量等)。
|
||||
* 2. 然后循环检测是否有更高优先级的中缀操作符,
|
||||
* 若有则递归处理右侧表达式并组合为新的表达式节点。
|
||||
* </p>
|
||||
* <p>
|
||||
* 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常。
|
||||
* 实现按优先级吸收中缀操作符(如连续算术、链式调用、组合表达式等)。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 解析上下文
|
||||
* @param prec 当前运算符优先级(用于控制递归层级)
|
||||
* @return 解析构建好的表达式节点
|
||||
* @throws UnsupportedFeature 遇到未注册的解析器时抛出
|
||||
* @param prec 当前已绑定优先级
|
||||
* @return 已解析的表达式节点
|
||||
*/
|
||||
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
|
||||
// 取下一个 token 作为本轮前缀表达式起始
|
||||
// 1) 消耗一个 token 作为前缀起点
|
||||
Token token = ctx.getTokens().next();
|
||||
|
||||
// 查找前缀解析器(先按类型名,再按词素)
|
||||
PrefixParselet prefix = prefixes.get(token.getType().name());
|
||||
// 2) 查找前缀解析器(优先按词素,再按 TokenType)
|
||||
PrefixParselet prefix = prefixes.get(token.getLexeme());
|
||||
if (prefix == null) {
|
||||
prefix = prefixes.get(token.getLexeme());
|
||||
prefix = prefixes.get(token.getType().name());
|
||||
}
|
||||
if (prefix == null) {
|
||||
// 未找到前缀解析器则报错
|
||||
// 未注册前缀解析器,直接报错
|
||||
throw new UnsupportedFeature(
|
||||
"没有为该 Token 类型注册前缀解析器: " + token.getType(),
|
||||
"没有为该 Token 注册前缀解析器: " + token.getLexeme() + " / " + token.getType(),
|
||||
token.getLine(),
|
||||
token.getCol()
|
||||
);
|
||||
}
|
||||
|
||||
// 执行前缀解析,获得左侧表达式
|
||||
// 3) 前缀解析得到左侧表达式
|
||||
ExpressionNode left = prefix.parse(ctx, token);
|
||||
|
||||
// 不断尝试查找优先级更高的中缀运算符,递归处理表达式链
|
||||
while (!ctx.getTokens().isAtEnd()
|
||||
&& prec.ordinal() < nextPrecedence(ctx)) {
|
||||
// 查看下一个 token 词素,查找中缀解析器
|
||||
// 4) 主循环:不断吸收更高优先级的中缀操作,直到优先级不再提升
|
||||
while (!ctx.getTokens().isAtEnd() && prec.ordinal() < nextPrecedence(ctx)) {
|
||||
String lex = ctx.getTokens().peek().getLexeme();
|
||||
InfixParselet infix = infixes.get(lex);
|
||||
if (infix == null) {
|
||||
// 若未注册中缀解析器,则直接抛异常(常见于语法错误)
|
||||
// nextPrecedence > prec 时一般已注册中缀解析器
|
||||
Token t = ctx.getTokens().peek();
|
||||
throw new UnsupportedFeature(
|
||||
"没有为该运算符注册中缀解析器: '" + lex + "'",
|
||||
@ -157,21 +149,21 @@ public class PrattExpressionParser implements ExpressionParser {
|
||||
t.getCol()
|
||||
);
|
||||
}
|
||||
// 使用中缀解析器处理表达式组合
|
||||
// 递归组合更高优先级的中缀表达式
|
||||
left = infix.parse(ctx, left);
|
||||
}
|
||||
// 返回本层递归已解析的表达式节点
|
||||
// 5) 返回本层解析完成的表达式节点
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个 token 词素对应的中缀运算符优先级(Pratt 算法关键)。
|
||||
* 获取下一个 token 对应的中缀运算符优先级(Pratt 算法关键)。
|
||||
* <p>
|
||||
* 用于决定当前是否需要递归处理更高优先级的中缀操作。
|
||||
* 若无注册的中缀解析器,则返回 -1。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前解析上下文
|
||||
* @return 下一个中缀运算符的优先级序号;若无注册解析器则返回 -1
|
||||
* @param ctx 解析上下文
|
||||
* @return 下一个运算符优先级序号(无则-1)
|
||||
*/
|
||||
private int nextPrecedence(ParserContext ctx) {
|
||||
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());
|
||||
|
||||
@ -62,10 +62,38 @@ public class ASTPrinter {
|
||||
for (ImportNode imp : m.imports()) {
|
||||
System.out.println(pad + " import " + imp.moduleName());
|
||||
}
|
||||
for (StructNode s : m.structs()) {
|
||||
print(s, indent + 1);
|
||||
}
|
||||
for (FunctionNode fn : m.functions()) {
|
||||
print(fn, indent + 1);
|
||||
}
|
||||
}
|
||||
|
||||
case StructNode s -> {
|
||||
System.out.print(pad + "struct " + s.name());
|
||||
if (s.parent() != null && !s.parent().isEmpty()) {
|
||||
System.out.print(" extends " + s.parent());
|
||||
}
|
||||
System.out.println();
|
||||
for (DeclarationNode f : s.fields()) {
|
||||
print(f, indent + 1);
|
||||
}
|
||||
// 打印所有构造函数 inits
|
||||
if (s.inits() != null && !s.inits().isEmpty()) {
|
||||
for (FunctionNode ctor : s.inits()) {
|
||||
System.out.println(pad + " [init]");
|
||||
print(ctor, indent + 2);
|
||||
}
|
||||
}
|
||||
// 打印所有方法
|
||||
if (s.methods() != null && !s.methods().isEmpty()) {
|
||||
for (FunctionNode m : s.methods()) {
|
||||
print(m, indent + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case FunctionNode(
|
||||
String name, List<ParameterNode> parameters, String returnType, List<StatementNode> body,
|
||||
NodeContext _
|
||||
|
||||
@ -2,15 +2,16 @@ package org.jcnc.snow.compiler.parser.function;
|
||||
|
||||
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ParameterNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
|
||||
import org.jcnc.snow.compiler.parser.context.ParseException;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
|
||||
import org.jcnc.snow.compiler.parser.factory.StatementParserFactory;
|
||||
import org.jcnc.snow.compiler.parser.utils.FlexibleSectionParser;
|
||||
import org.jcnc.snow.compiler.parser.utils.FlexibleSectionParser.SectionDefinition;
|
||||
@ -22,15 +23,13 @@ import java.util.*;
|
||||
* 实现 {@link TopLevelParser} 接口,用于将源代码中的函数块解析为抽象语法树(AST)中的 {@link FunctionNode}。
|
||||
*
|
||||
* <p>
|
||||
* 本类使用 {@link FlexibleSectionParser} 机制,按照语义区块结构对函数进行模块化解析,支持以下部分:
|
||||
* </p>
|
||||
*
|
||||
* 使用 {@link FlexibleSectionParser} 机制,按照语义区块结构对函数进行模块化解析,支持以下部分:
|
||||
* <ul>
|
||||
* <li>函数头(关键字 {@code function:} 与函数名)</li>
|
||||
* <li>参数列表(params 区块)</li>
|
||||
* <li>返回类型(returns 区块)</li>
|
||||
* <li>函数体(body 区块)</li>
|
||||
* <li>函数结束(关键字 {@code end function})</li>
|
||||
* <li>函数头(关键字 {@code function:} 与函数名)</li>
|
||||
* <li>参数列表(params 区块)</li>
|
||||
* <li>返回类型(returns 区块)</li>
|
||||
* <li>函数体(body 区块)</li>
|
||||
* <li>函数结束(关键字 {@code end function})</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
@ -47,76 +46,80 @@ public class FunctionParser implements TopLevelParser {
|
||||
* 顶层语法解析入口。
|
||||
*
|
||||
* <p>
|
||||
* 该方法负责完整解析函数定义,包括其所有组成部分,并构建对应的 {@link FunctionNode}。
|
||||
* 解析并生成函数的 AST。
|
||||
* 该方法从源代码中获取 {@link TokenStream},并按照函数定义的不同区块解析,最终生成 {@link FunctionNode}。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等作用域信息。
|
||||
* @return 构建完成的 {@link FunctionNode} 抽象语法树节点。
|
||||
* @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等信息
|
||||
* @return 构建完成的 {@link FunctionNode} 对象
|
||||
*/
|
||||
@Override
|
||||
public FunctionNode parse(ParserContext ctx) {
|
||||
// 获取当前解析上下文中的 token 流
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
// 记录当前 token 的行号、列号和源文件名,用于错误报告或生成节点上下文
|
||||
int line = ctx.getTokens().peek().getLine();
|
||||
int column = ctx.getTokens().peek().getCol();
|
||||
int line = ts.peek().getLine();
|
||||
int column = ts.peek().getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
// 解析函数头(例如可能是 `function` 关键字等)
|
||||
// 解析函数头 (function:)
|
||||
parseFunctionHeader(ts);
|
||||
|
||||
// 解析函数名
|
||||
// 函数名
|
||||
String functionName = parseFunctionName(ts);
|
||||
|
||||
// 用于存放解析出来的参数列表
|
||||
List<ParameterNode> parameters = new ArrayList<>();
|
||||
// 用于存放返回类型,这里用数组是为了在闭包中修改其值
|
||||
String[] returnType = new String[1];
|
||||
// 用于存放函数体语句列表
|
||||
String[] returnType = new String[1]; // 使用数组模拟可变引用
|
||||
List<StatementNode> body = new ArrayList<>();
|
||||
|
||||
// 获取函数可以包含的可选节(如参数、返回类型、主体等)的定义映射
|
||||
// 定义函数可选区块规则 (params, returns, body)
|
||||
Map<String, SectionDefinition> sections = getSectionDefinitions(parameters, returnType, body);
|
||||
|
||||
// 调用通用的多节解析器,实际根据 sections 中注册的规则解析各部分内容
|
||||
// 解析这些区块
|
||||
FlexibleSectionParser.parse(ctx, ts, sections);
|
||||
|
||||
// 如果函数体为空且返回类型为 void,自动补充一个空的 return 语句
|
||||
if (body.isEmpty() && returnType[0].equals("void")) {
|
||||
// 如果函数体为空且返回 void,补充一个空 return
|
||||
if (body.isEmpty() && "void".equals(returnType[0])) {
|
||||
body.add(new ReturnNode(null, new NodeContext(line, column, file)));
|
||||
}
|
||||
|
||||
// 检查参数名称是否重复
|
||||
Set<String> set = new HashSet<>();
|
||||
parameters.forEach((node) -> {
|
||||
final String name = node.name();
|
||||
if (set.contains(name)) {
|
||||
// 如果参数名重复,抛出带具体行列信息的解析异常
|
||||
throw new ParseException(String.format("参数 `%s` 重定义", name),
|
||||
node.context().line(), node.context().column());
|
||||
// 检查参数名是否重复
|
||||
Set<String> seen = new HashSet<>();
|
||||
for (ParameterNode node : parameters) {
|
||||
if (!seen.add(node.name())) {
|
||||
throw new ParseException(
|
||||
String.format("参数 `%s` 重定义", node.name()),
|
||||
node.context().line(),
|
||||
node.context().column());
|
||||
}
|
||||
set.add(name);
|
||||
});
|
||||
}
|
||||
|
||||
// 解析函数的尾部(例如右大括号或者 end 标志)
|
||||
// 解析函数结尾
|
||||
parseFunctionFooter(ts);
|
||||
|
||||
// 返回完整的函数节点,包含函数名、参数、返回类型、函数体以及源位置信息
|
||||
return new FunctionNode(functionName, parameters, returnType[0], body, new NodeContext(line, column, file));
|
||||
return new FunctionNode(functionName, parameters, returnType[0], body,
|
||||
new NodeContext(line, column, file));
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
/* -------------------------- Section Definitions ----------------------- */
|
||||
/* ====================================================================== */
|
||||
|
||||
/**
|
||||
* 构造函数定义中各区块的解析规则(params、returns、body)。
|
||||
* 定义函数的各个语义区块及其解析规则,包括参数列表、返回类型及函数体。
|
||||
*
|
||||
* <p>
|
||||
* 每个 {@link SectionDefinition} 包含两个部分: 区块起始判断器(基于关键字)与具体的解析逻辑。
|
||||
* 此方法将定义如下的三个区块:
|
||||
* <ul>
|
||||
* <li>"params" 区块,解析参数列表。</li>
|
||||
* <li>"returns" 区块,解析返回类型。</li>
|
||||
* <li>"body" 区块,解析函数体。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param params 参数节点收集容器,解析结果将存入此列表。
|
||||
* @param returnType 返回类型容器,以单元素数组方式模拟引用传递。
|
||||
* @param body 函数体语句节点列表容器。
|
||||
* @return 区块关键字到解析定义的映射表。
|
||||
* @param params 存储函数参数列表的集合
|
||||
* @param returnType 存储函数返回类型的字符串数组
|
||||
* @param body 存储函数体的语句列表
|
||||
* @return 各个区块的解析定义
|
||||
*/
|
||||
private Map<String, SectionDefinition> getSectionDefinitions(
|
||||
List<ParameterNode> params,
|
||||
@ -142,8 +145,12 @@ public class FunctionParser implements TopLevelParser {
|
||||
return map;
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
/* ---------------------------- 函数头/尾 ------------------------------- */
|
||||
/* ====================================================================== */
|
||||
|
||||
/**
|
||||
* 解析函数头部标识符 {@code function:},并跳过其后多余注释与空行。
|
||||
* 解析函数头部分,期望的格式为 `function:`。
|
||||
*
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
*/
|
||||
@ -158,7 +165,7 @@ public class FunctionParser implements TopLevelParser {
|
||||
* 解析函数名称(标识符)并跳过换行。
|
||||
*
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
* @return 函数名字符串。
|
||||
* @return 函数名称字符串。
|
||||
*/
|
||||
private String parseFunctionName(TokenStream ts) {
|
||||
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||
@ -167,7 +174,7 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析函数结束标记 {@code end function}。
|
||||
* 解析函数结束标记 `end function`。
|
||||
*
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
*/
|
||||
@ -177,18 +184,14 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析函数参数列表。
|
||||
*
|
||||
* <p>
|
||||
* 支持声明后附加注释,格式示例:
|
||||
* 解析函数参数列表,支持附加注释,格式为:
|
||||
* <pre>
|
||||
* params:
|
||||
* declare x: int // 说明文字
|
||||
* declare y: float
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等作用域信息。
|
||||
* @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等信息。
|
||||
* @return 所有参数节点的列表。
|
||||
*/
|
||||
private List<ParameterNode> parseParameters(ParserContext ctx) {
|
||||
@ -208,19 +211,28 @@ public class FunctionParser implements TopLevelParser {
|
||||
continue;
|
||||
}
|
||||
String lex = ts.peek().getLexeme();
|
||||
if (lex.equals("returns") || lex.equals("body") || lex.equals("end")) {
|
||||
break;
|
||||
}
|
||||
if (lex.equals("returns") || lex.equals("body") || lex.equals("end")) break;
|
||||
|
||||
// 获取当前 token 的行号、列号和文件名
|
||||
int line = ctx.getTokens().peek().getLine();
|
||||
int column = ctx.getTokens().peek().getCol();
|
||||
int line = ts.peek().getLine();
|
||||
int column = ts.peek().getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
ts.expect("declare");
|
||||
String pname = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||
ts.expect(":");
|
||||
String ptype = ts.expectType(TokenType.TYPE).getLexeme();
|
||||
|
||||
// 既接受 TYPE 也接受 IDENTIFIER
|
||||
String ptype;
|
||||
if (ts.peek().getType() == TokenType.TYPE || ts.peek().getType() == TokenType.IDENTIFIER) {
|
||||
ptype = ts.next().getLexeme();
|
||||
} else {
|
||||
var t = ts.peek();
|
||||
throw new UnexpectedToken(
|
||||
"期望 TYPE 或 IDENTIFIER,但实际得到 " +
|
||||
t.getType() + " ('" + t.getLexeme() + "')",
|
||||
t.getLine(), t.getCol());
|
||||
}
|
||||
|
||||
skipComments(ts);
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
list.add(new ParameterNode(pname, ptype, new NodeContext(line, column, file)));
|
||||
@ -229,20 +241,27 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析返回类型声明。
|
||||
*
|
||||
* <p>
|
||||
* 格式为 {@code returns: TYPE},支持前置或行尾注释。
|
||||
* </p>
|
||||
* 解析返回类型声明,格式为 `returns: TYPE`,支持注释。
|
||||
*
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
* @return 返回类型名称字符串。
|
||||
* @return 返回类型字符串。
|
||||
*/
|
||||
private String parseReturnType(TokenStream ts) {
|
||||
ts.expect("returns");
|
||||
ts.expect(":");
|
||||
skipComments(ts);
|
||||
Token typeToken = ts.expectType(TokenType.TYPE);
|
||||
|
||||
Token typeToken;
|
||||
if (ts.peek().getType() == TokenType.TYPE || ts.peek().getType() == TokenType.IDENTIFIER) {
|
||||
typeToken = ts.next();
|
||||
} else {
|
||||
var t = ts.peek();
|
||||
throw new UnexpectedToken(
|
||||
"期望 TYPE 或 IDENTIFIER,但实际得到 " +
|
||||
t.getType() + " ('" + t.getLexeme() + "')",
|
||||
t.getLine(), t.getCol());
|
||||
}
|
||||
|
||||
String rtype = typeToken.getLexeme();
|
||||
skipComments(ts);
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
@ -251,14 +270,10 @@ public class FunctionParser implements TopLevelParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析函数体区块,直到遇到 {@code end body}。
|
||||
*
|
||||
* <p>
|
||||
* 每一行由对应的语句解析器处理,可嵌套控制结构、返回语句、表达式等。
|
||||
* </p>
|
||||
* 解析函数体区块,直到遇到 `end body`。
|
||||
*
|
||||
* @param ctx 当前解析上下文。
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
* @return 所有函数体语句节点的列表。
|
||||
*/
|
||||
private List<StatementNode> parseFunctionBody(ParserContext ctx, TokenStream ts) {
|
||||
@ -275,9 +290,8 @@ public class FunctionParser implements TopLevelParser {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
if ("end".equals(ts.peek().getLexeme())) {
|
||||
break;
|
||||
}
|
||||
if ("end".equals(ts.peek().getLexeme())) break;
|
||||
|
||||
stmts.add(StatementParserFactory.get(ts.peek().getLexeme()).parse(ctx));
|
||||
}
|
||||
ts.expect("end");
|
||||
@ -293,9 +307,7 @@ public class FunctionParser implements TopLevelParser {
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
*/
|
||||
private void skipComments(TokenStream ts) {
|
||||
while (ts.peek().getType() == TokenType.COMMENT) {
|
||||
ts.next();
|
||||
}
|
||||
while (ts.peek().getType() == TokenType.COMMENT) ts.next();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -304,8 +316,6 @@ public class FunctionParser implements TopLevelParser {
|
||||
* @param ts 当前使用的 {@link TokenStream}。
|
||||
*/
|
||||
private void skipNewlines(TokenStream ts) {
|
||||
while (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
}
|
||||
while (ts.peek().getType() == TokenType.NEWLINE) ts.next();
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,7 @@
|
||||
package org.jcnc.snow.compiler.parser.module;
|
||||
|
||||
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.ImportNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.*;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
@ -12,47 +9,33 @@ import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
|
||||
import org.jcnc.snow.compiler.parser.function.FunctionParser;
|
||||
import org.jcnc.snow.compiler.parser.statement.DeclarationStatementParser;
|
||||
import org.jcnc.snow.compiler.parser.struct.StructParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code ModuleParser} 负责解析源码中的模块结构,是顶层结构解析器实现之一。
|
||||
* {@code ModuleParser}
|
||||
* <p>
|
||||
* 模块定义可包含多个导入(import)语句、globals 全局声明和函数定义(function),
|
||||
* 导入语句可在模块中任意位置出现,且允许模块体中穿插任意数量的空行(空行会被自动忽略,不影响语法结构)。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 典型模块语法结构:
|
||||
* <pre>
|
||||
* module: mymod
|
||||
* import ...
|
||||
* globals:
|
||||
* declare ...
|
||||
* function ...
|
||||
* ...
|
||||
* end module
|
||||
* </pre>
|
||||
* 顶层结构解析器:负责解析整个源码模块(module ... end module)。
|
||||
* <ul>
|
||||
* <li>支持模块声明、导入(import)、全局变量(globals)、结构体(struct)、函数(function)等顶层语法。</li>
|
||||
* <li>允许模块体中出现任意数量的空行(自动跳过),顺序自由。</li>
|
||||
* <li>遇到非法顶层语句或区块会抛出 {@link UnexpectedToken},提示具体位置和原因。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class ModuleParser implements TopLevelParser {
|
||||
|
||||
/**
|
||||
* 解析一个模块定义块,返回完整的 {@link ModuleNode} 语法树节点。
|
||||
* 解析一个完整的模块定义块,返回 AST {@link ModuleNode}。
|
||||
* <p>
|
||||
* 解析过程包括:
|
||||
* <ol>
|
||||
* <li>匹配模块声明起始 {@code module: IDENTIFIER}。</li>
|
||||
* <li>收集模块体内所有 import、globals 和 function 语句,允许穿插空行。</li>
|
||||
* <li>匹配模块结束 {@code end module}。</li>
|
||||
* </ol>
|
||||
* 若遇到未识别的语句,将抛出 {@link UnexpectedToken} 异常,定位错误位置和原因。
|
||||
* 支持空行,允许导入、全局、结构体、函数等多种区块混排。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 当前解析上下文(包含词法流等状态)
|
||||
* @return 解析得到的 {@link ModuleNode} 实例
|
||||
* @throws UnexpectedToken 当模块体中出现未识别的顶层语句时抛出
|
||||
* @param ctx 解析上下文(包含 TokenStream、文件名等信息)
|
||||
* @return 解析生成的 ModuleNode
|
||||
* @throws UnexpectedToken 当模块体中遇到不支持的顶层语句时抛出
|
||||
*/
|
||||
@Override
|
||||
public ModuleNode parse(ParserContext ctx) {
|
||||
@ -62,37 +45,53 @@ public class ModuleParser implements TopLevelParser {
|
||||
int column = ts.peek().getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
// 1) 解析模块声明头部
|
||||
ts.expect("module");
|
||||
ts.expect(":");
|
||||
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
|
||||
// 2) 初始化各类节点容器
|
||||
List<StructNode> structs = new ArrayList<>();
|
||||
List<ImportNode> imports = new ArrayList<>();
|
||||
List<DeclarationNode> globals = new ArrayList<>();
|
||||
List<FunctionNode> functions = new ArrayList<>();
|
||||
|
||||
// 3) 各子区块的专用解析器
|
||||
StructParser structParser = new StructParser();
|
||||
ImportParser importParser = new ImportParser();
|
||||
FunctionParser funcParser = new FunctionParser();
|
||||
DeclarationStatementParser globalsParser = new DeclarationStatementParser();
|
||||
|
||||
// 4) 进入主循环,直到 end module
|
||||
while (true) {
|
||||
// 跳过空行
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
// 到达模块结尾
|
||||
if ("end".equals(ts.peek().getLexeme())) {
|
||||
break;
|
||||
}
|
||||
String lex = ts.peek().getLexeme();
|
||||
switch (lex) {
|
||||
// 解析 import 语句(可多次出现,支持 import 多个模块)
|
||||
case "import" -> imports.addAll(importParser.parse(ctx));
|
||||
|
||||
// 解析 struct 结构体定义块
|
||||
case "struct" -> structs.add(structParser.parse(ctx));
|
||||
|
||||
// 解析 function 顶层函数定义
|
||||
case "function" -> functions.add(funcParser.parse(ctx));
|
||||
|
||||
// 解析全局变量声明区块
|
||||
case "globals" -> {
|
||||
ts.expect("globals");
|
||||
ts.expect(":");
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
while (true) {
|
||||
// 跳过空行
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
@ -100,9 +99,13 @@ public class ModuleParser implements TopLevelParser {
|
||||
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)) {
|
||||
}
|
||||
// 下一个 function/import/end 开头则结束 globals 区块
|
||||
else if ("function".equals(innerLex) || "import".equals(innerLex) || "end".equals(innerLex)) {
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
// 其余标记为非法内容,抛出异常
|
||||
else {
|
||||
throw new UnexpectedToken(
|
||||
"globals 区块中不支持的内容: " + innerLex,
|
||||
ts.peek().getLine(),
|
||||
@ -111,6 +114,7 @@ public class ModuleParser implements TopLevelParser {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 未知或非法顶层内容
|
||||
case null, default -> throw new UnexpectedToken(
|
||||
"Unexpected token in module: " + lex,
|
||||
ts.peek().getLine(),
|
||||
@ -119,9 +123,11 @@ public class ModuleParser implements TopLevelParser {
|
||||
}
|
||||
}
|
||||
|
||||
// 5) 匹配模块结尾 "end module"
|
||||
ts.expect("end");
|
||||
ts.expect("module");
|
||||
|
||||
return new ModuleNode(name, imports, globals, functions, new NodeContext(line, column, file));
|
||||
// 6) 构造并返回 ModuleNode
|
||||
return new ModuleNode(name, imports, globals, structs, functions, new NodeContext(line, column, file));
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,75 +10,96 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||
/**
|
||||
* {@code DeclarationStatementParser} 负责解析变量声明语句节点。
|
||||
* <p>
|
||||
* 支持以下两种语法结构:
|
||||
* <pre>{@code
|
||||
* declare myVar:Integer
|
||||
* declare myVar:Integer = 42 + 3
|
||||
* }</pre>
|
||||
* 解析器能够识别多维数组类型(如 {@code int[]}, {@code string[][]}),并支持可选初始化表达式。
|
||||
* <p>
|
||||
* 每个声明语句均要求以换行符结尾,语法不合法时会抛出异常。
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>支持类型标识符与自定义结构体名</li>
|
||||
* <li>支持多维数组类型,如 <code>int[][]</code></li>
|
||||
* <li>支持带初始值的声明</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class DeclarationStatementParser implements StatementParser {
|
||||
|
||||
/**
|
||||
* 解析一条 {@code declare} 语句,生成对应的抽象语法树节点 {@link DeclarationNode}。
|
||||
* <p>
|
||||
* 支持类型标注和可选初始化表达式。类型部分自动拼接数组维度(如 int[][])。
|
||||
* </p>
|
||||
* 解析变量或常量声明语句。
|
||||
*
|
||||
* @param ctx 当前语法解析上下文(包含词法流、错误信息等)
|
||||
* @return {@link DeclarationNode} 表示声明语句结构
|
||||
* @throws RuntimeException 语法不合法时抛出
|
||||
* @param ctx 语法分析上下文,提供词法单元流与其他辅助功能
|
||||
* @return 解析得到的声明节点 {@link DeclarationNode}
|
||||
* @throws org.jcnc.snow.compiler.parser.context.UnexpectedToken 若语法不合法则抛出异常
|
||||
*/
|
||||
@Override
|
||||
public DeclarationNode parse(ParserContext ctx) {
|
||||
// 便捷引用词法 token 流
|
||||
var tokens = ctx.getTokens();
|
||||
var tokens = ctx.getTokens(); // 获取词法单元流
|
||||
|
||||
// 获取当前 token 的行号、列号和文件名
|
||||
int line = tokens.peek().getLine();
|
||||
// 记录声明语句在源码中的位置信息(行、列、文件名)
|
||||
int line = tokens.peek().getLine();
|
||||
int column = tokens.peek().getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
// 声明语句必须以 "declare" 开头
|
||||
tokens.expect("declare");
|
||||
|
||||
// 是否声明为常量
|
||||
boolean isConst = tokens.match("const");
|
||||
|
||||
// 获取变量名称(标识符)
|
||||
String name = tokens
|
||||
.expectType(TokenType.IDENTIFIER)
|
||||
.getLexeme();
|
||||
|
||||
// 类型标注的冒号分隔符
|
||||
tokens.expect(":");
|
||||
|
||||
// 获取变量类型(类型标识符)
|
||||
StringBuilder type = new StringBuilder(
|
||||
tokens
|
||||
.expectType(TokenType.TYPE)
|
||||
.getLexeme()
|
||||
);
|
||||
|
||||
// 消费多维数组类型后缀 "[]"
|
||||
while (tokens.match("[")) {
|
||||
tokens.expectType(TokenType.RBRACKET); // 必须配对
|
||||
type.append("[]"); // 类型名称拼接 [],如 int[][] 等
|
||||
// 判断并消费声明关键字 declare 或 const
|
||||
boolean isConst = false;
|
||||
String first = tokens.peek().getLexeme();
|
||||
if ("declare".equals(first)) {
|
||||
tokens.next(); // 消费 declare
|
||||
// declare 后可选 const,用于声明常量
|
||||
if ("const".equals(tokens.peek().getLexeme())) {
|
||||
isConst = true;
|
||||
tokens.next(); // 消费 const
|
||||
}
|
||||
} else if ("const".equals(first)) {
|
||||
// 支持 const 开头的声明写法
|
||||
isConst = true;
|
||||
tokens.next(); // 消费 const
|
||||
} else {
|
||||
// 不符合语法规则,抛出异常
|
||||
throw new org.jcnc.snow.compiler.parser.context.UnexpectedToken(
|
||||
"声明应以 'declare' 或 'declare const' 开始,而不是 '" + first + "'",
|
||||
tokens.peek().getLine(), tokens.peek().getCol());
|
||||
}
|
||||
|
||||
// 可选初始化表达式(=号右侧)
|
||||
// 获取变量名(标识符)
|
||||
String name = tokens.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||
|
||||
// 检查并消费冒号 “:”
|
||||
tokens.expect(":");
|
||||
|
||||
// 解析变量类型(类型标识符或自定义结构体名)
|
||||
StringBuilder type = new StringBuilder();
|
||||
if (tokens.peek().getType() == TokenType.TYPE || tokens.peek().getType() == TokenType.IDENTIFIER) {
|
||||
// 类型可以是基础类型或结构体名
|
||||
type.append(tokens.next().getLexeme());
|
||||
} else {
|
||||
// 类型不是合法的 Token,抛出异常
|
||||
var t = tokens.peek();
|
||||
throw new org.jcnc.snow.compiler.parser.context.UnexpectedToken(
|
||||
"期望的标记类型为 TYPE 或 IDENTIFIER,但实际得到的是 "
|
||||
+ t.getType() + " ('" + t.getLexeme() + "')",
|
||||
t.getLine(), t.getCol()
|
||||
);
|
||||
}
|
||||
|
||||
// 处理多维数组类型后缀(支持 int[][] 等类型)
|
||||
while (tokens.match("[")) {
|
||||
// 消费左中括号 '[' 后必须跟右中括号 ']'
|
||||
tokens.expectType(TokenType.RBRACKET); // 消费 ']'
|
||||
type.append("[]"); // 追加数组后缀
|
||||
}
|
||||
|
||||
// 可选的初始化表达式(如 = 10)
|
||||
ExpressionNode init = null;
|
||||
if (tokens.match("=")) {
|
||||
// 使用 Pratt 解析器解析表达式,获得初始化表达式节点
|
||||
init = new PrattExpressionParser().parse(ctx);
|
||||
}
|
||||
|
||||
// 声明语句必须以换行符结尾
|
||||
// 声明语句必须以换行符 NEWLINE 结尾
|
||||
tokens.expectType(TokenType.NEWLINE);
|
||||
|
||||
// 返回构建好的声明语法树节点
|
||||
return new DeclarationNode(name, type.toString(), isConst, init, new NodeContext(line, column, file));
|
||||
// 组装声明节点并返回
|
||||
return new DeclarationNode(
|
||||
name, // 变量/常量名
|
||||
type.toString(), // 类型字符串
|
||||
isConst, // 是否常量
|
||||
init, // 初始化表达式节点(可为 null)
|
||||
new NodeContext(line, column, file) // 源码位置信息
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,13 +18,13 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||
* <pre>{@code
|
||||
* x = 1 + 2 // 赋值语句
|
||||
* doSomething() // 一般表达式语句
|
||||
* this.name = n // 将 this.name 赋值语法糖为对 name 的赋值
|
||||
* }</pre>
|
||||
* <ul>
|
||||
* <li>以标识符开头且后接 {@code =} 时,解析为 {@link AssignmentNode}。</li>
|
||||
* <li>否则视为普通表达式,解析为 {@link ExpressionStatementNode}。</li>
|
||||
* <li>所有表达式语句必须以换行符({@code NEWLINE})结尾。</li>
|
||||
* </ul>
|
||||
* 若语句起始为关键字或空行,将直接抛出异常,防止非法语法进入表达式解析流程。
|
||||
*
|
||||
* - 以标识符开头且后接 '=' 时,解析为 {@link AssignmentNode}。
|
||||
* - 否则先解析为一般表达式;若后续遇到 '=',则回退为“<expr> = <expr>”赋值语句。
|
||||
* - 所有表达式语句必须以换行符(NEWLINE)结尾。
|
||||
* <p>
|
||||
*/
|
||||
public class ExpressionStatementParser implements StatementParser {
|
||||
|
||||
@ -33,49 +33,81 @@ public class ExpressionStatementParser implements StatementParser {
|
||||
*
|
||||
* @param ctx 当前解析上下文,提供词法流与环境信息
|
||||
* @return {@link AssignmentNode} 或 {@link ExpressionStatementNode} 语法节点
|
||||
* @throws UnexpectedToken 若遇到非法起始(关键字、空行等)
|
||||
* @throws UnexpectedToken 若遇到非法起始(关键字 'end' 等)
|
||||
*/
|
||||
@Override
|
||||
public StatementNode parse(ParserContext ctx) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) {
|
||||
// ----------- 起始 token 合法性检查(放宽以支持 this 开头)-----------
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
// 空行不应进入表达式解析,直接抛出异常
|
||||
throw new UnexpectedToken(
|
||||
"无法解析以关键字开头的表达式: " + ts.peek().getLexeme(),
|
||||
"无法解析以空行开头的表达式",
|
||||
ts.peek().getLine(),
|
||||
ts.peek().getCol()
|
||||
);
|
||||
}
|
||||
if (ts.peek().getType() == TokenType.KEYWORD) {
|
||||
String kw = ts.peek().getLexeme();
|
||||
// 仅允许 this 作为表达式起始;其它关键字(如 end/if/else 等)仍禁止
|
||||
if (!"this".equals(kw)) {
|
||||
throw new UnexpectedToken(
|
||||
"无法解析以关键字开头的表达式: " + kw,
|
||||
ts.peek().getLine(),
|
||||
ts.peek().getCol()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
int line = ts.peek().getLine();
|
||||
int column = ts.peek().getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
// 简单形式: IDENTIFIER = expr
|
||||
// ------------- 简单形式: IDENTIFIER = expr -------------
|
||||
// 快速路径:如 "a = ...",直接识别为赋值语句,无需完整表达式树回退
|
||||
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
|
||||
String varName = ts.next().getLexeme();
|
||||
ts.expect("=");
|
||||
ExpressionNode value = new PrattExpressionParser().parse(ctx);
|
||||
String varName = ts.next().getLexeme(); // 消费 IDENTIFIER
|
||||
ts.expect("="); // 消费 '='
|
||||
ExpressionNode value = new PrattExpressionParser().parse(ctx); // 解析右侧表达式
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
// 返回简单变量赋值节点
|
||||
return new AssignmentNode(varName, value, new NodeContext(line, column, file));
|
||||
}
|
||||
|
||||
// 尝试解析更通用的左值形式(支持下标): <expr> = <expr>
|
||||
// ------------- 通用形式: <expr> [= <expr>] -------------
|
||||
// 先解析潜在“左值表达式”或普通表达式
|
||||
ExpressionNode lhs = new PrattExpressionParser().parse(ctx);
|
||||
|
||||
// 若遇到等号,则尝试回退为赋值语句(兼容更复杂的左值表达式)
|
||||
if ("=".equals(ts.peek().getLexeme())) {
|
||||
ts.next(); // consume '='
|
||||
ExpressionNode rhs = new PrattExpressionParser().parse(ctx);
|
||||
ts.next(); // 消费 '='
|
||||
ExpressionNode rhs = new PrattExpressionParser().parse(ctx); // 解析右值表达式
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
// 根据左值类型构造具体赋值节点
|
||||
|
||||
// 根据左值 AST 类型,生成不同赋值节点
|
||||
if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode id) {
|
||||
// 变量名赋值:a = rhs
|
||||
return new AssignmentNode(id.name(), rhs, new NodeContext(line, column, file));
|
||||
|
||||
} else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IndexExpressionNode idx) {
|
||||
// 下标赋值:a[i] = rhs
|
||||
return new org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode(idx, rhs, new NodeContext(line, column, file));
|
||||
|
||||
} else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.MemberExpressionNode mem
|
||||
&& mem.object() instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode oid
|
||||
&& "this".equals(oid.name())) {
|
||||
// 支持:this.field = rhs
|
||||
// 语法糖:降级为对当前作用域同名变量的赋值,相当于 "field = rhs"
|
||||
return new AssignmentNode(mem.member(), rhs, new NodeContext(line, column, file));
|
||||
|
||||
} else {
|
||||
// 其它成员赋值(如 a.b = ...)不支持,报错
|
||||
throw new UnexpectedToken("不支持的赋值左值类型: " + lhs.getClass().getSimpleName(), line, column);
|
||||
}
|
||||
}
|
||||
// 普通表达式语句
|
||||
|
||||
// 不是赋值,则当作普通表达式语句处理
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
return new ExpressionStatementNode(lhs, new NodeContext(line, column, file));
|
||||
}
|
||||
|
||||
@ -0,0 +1,293 @@
|
||||
package org.jcnc.snow.compiler.parser.struct;
|
||||
|
||||
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.ParameterNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.StructNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
|
||||
import org.jcnc.snow.compiler.parser.factory.StatementParserFactory;
|
||||
import org.jcnc.snow.compiler.parser.function.FunctionParser;
|
||||
import org.jcnc.snow.compiler.parser.statement.DeclarationStatementParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code StructParser}
|
||||
* <p>
|
||||
* 解析 <code>struct ... end struct</code> 结构体声明块的顶层语法解析器。
|
||||
* <ul>
|
||||
* <li>支持解析结构体字段、构造函数(init)、结构体方法列表。</li>
|
||||
* <li>按顺序支持 <b>fields/init/function</b> 三种结构体内部块。</li>
|
||||
* <li>语法出错时抛出 {@link UnexpectedToken} 异常,便于错误定位。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class StructParser implements TopLevelParser {
|
||||
|
||||
/**
|
||||
* 解析结构体声明块,并返回 AST 节点 {@link StructNode}。
|
||||
*
|
||||
* <p>
|
||||
* 该方法解析结构体声明的头部,结构体内部的字段块(fields)、构造函数块(init)、方法块(function)以及结束标记(end struct)。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 解析上下文,包含 TokenStream 和符号表等信息。
|
||||
* @return 解析得到的 {@link StructNode},包含结构体的名称、字段、构造函数、方法等信息。
|
||||
*/
|
||||
@Override
|
||||
public StructNode parse(ParserContext ctx) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
int line = ts.peek().getLine();
|
||||
int col = ts.peek().getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
/* -------- 解析头部 -------- */
|
||||
ts.expect("struct");
|
||||
ts.expect(":");
|
||||
String structName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||
|
||||
// 解析可选 extends
|
||||
String parentName = null;
|
||||
if ("extends".equals(ts.peek().getLexeme())) {
|
||||
ts.expect("extends");
|
||||
parentName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||
}
|
||||
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
|
||||
/* -------- 初始化容器 -------- */
|
||||
List<DeclarationNode> fields = new ArrayList<>();
|
||||
List<FunctionNode> inits = new ArrayList<>();
|
||||
List<FunctionNode> methods = new ArrayList<>();
|
||||
|
||||
DeclarationStatementParser declParser = new DeclarationStatementParser();
|
||||
FunctionParser funcParser = new FunctionParser();
|
||||
|
||||
/* -------- 主循环:依次解析 struct 块内部字段、构造、方法 -------- */
|
||||
while (true) {
|
||||
/* 跳过空行 */
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
String lex = ts.peek().getLexeme();
|
||||
switch (lex) {
|
||||
/* ---------- fields 块 ---------- */
|
||||
case "fields" -> {
|
||||
ts.expect("fields");
|
||||
ts.expect(":");
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
// 字段块不强制 'end fields',遇到非 declare 则退出
|
||||
while (true) {
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
if ("declare".equals(ts.peek().getLexeme())) {
|
||||
// 字段声明使用通用 DeclarationStatementParser(其已支持 TYPE/IDENTIFIER 作为类型)
|
||||
fields.add(declParser.parse(ctx));
|
||||
} else {
|
||||
// 非 declare 开头则结束 fields
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- 构造函数 init ---------- */
|
||||
case "init" -> {
|
||||
FunctionNode ctor = parseInit(ctx, structName);
|
||||
|
||||
// 按参数个数去重
|
||||
for (FunctionNode ex : inits) {
|
||||
if (ex.parameters().size() == ctor.parameters().size()) {
|
||||
throw new UnexpectedToken(
|
||||
"重复定义 init 构造函数 (参数数量冲突)",
|
||||
ts.peek().getLine(), ts.peek().getCol());
|
||||
}
|
||||
}
|
||||
inits.add(ctor);
|
||||
}
|
||||
|
||||
/* ---------- 普通方法 function ---------- */
|
||||
case "function" -> methods.add(funcParser.parse(ctx));
|
||||
|
||||
/* ---------- struct 结束 ---------- */
|
||||
case "end" -> {
|
||||
ts.expect("end");
|
||||
ts.expect("struct");
|
||||
return new StructNode(structName, parentName,
|
||||
fields, inits, methods,
|
||||
new NodeContext(line, col, file));
|
||||
}
|
||||
|
||||
/* ---------- 非法内容 ---------- */
|
||||
default -> throw new UnexpectedToken(
|
||||
"struct 块内不支持的标记: " + lex,
|
||||
ts.peek().getLine(), ts.peek().getCol());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
/* -------------------------- 构造函数 init -------------------------- */
|
||||
/* ====================================================================== */
|
||||
|
||||
/**
|
||||
* 解析结构体构造函数 init 块,返回 FunctionNode。
|
||||
* <p>
|
||||
* 允许包含 params 和 body 两部分,顺序不限;以 "end init" 结束。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 解析上下文
|
||||
* @param structName 结构体名称,用于构造函数唯一命名
|
||||
* @return 表示构造函数的 FunctionNode
|
||||
*/
|
||||
private FunctionNode parseInit(ParserContext ctx, String structName) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
int line = ts.peek().getLine();
|
||||
int col = ts.peek().getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
ts.expect("init");
|
||||
ts.expect(":");
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
|
||||
/* -------- 初始化参数和方法体容器 -------- */
|
||||
List<ParameterNode> params = new ArrayList<>();
|
||||
List<StatementNode> body = new ArrayList<>();
|
||||
|
||||
// 主循环:支持 params/body 两块,顺序不限
|
||||
while (true) {
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
String lex = ts.peek().getLexeme();
|
||||
switch (lex) {
|
||||
case "params" -> params.addAll(parseParams(ctx));
|
||||
case "body" -> body.addAll(parseBody(ctx));
|
||||
case "end" -> {
|
||||
ts.expect("end");
|
||||
ts.expect("init");
|
||||
// 构造唯一命名的 FunctionNode
|
||||
return new FunctionNode(
|
||||
structName + ".__init__", // 唯一命名
|
||||
params,
|
||||
"void",
|
||||
body,
|
||||
new NodeContext(line, col, file));
|
||||
}
|
||||
default -> throw new UnexpectedToken(
|
||||
"init 块内不支持的标记: " + lex,
|
||||
ts.peek().getLine(), ts.peek().getCol());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- params: 参数块解析 ---------------- */
|
||||
|
||||
/**
|
||||
* 解析 params 块,返回参数列表。
|
||||
* <p>
|
||||
* 且在类型位置同时接受内建类型({@link TokenType#TYPE})与自定义标识符({@link TokenType#IDENTIFIER})。
|
||||
*
|
||||
* @param ctx 解析上下文
|
||||
* @return 解析得到的参数节点列表
|
||||
*/
|
||||
private List<ParameterNode> parseParams(ParserContext ctx) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
ts.expect("params");
|
||||
ts.expect(":");
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
|
||||
List<ParameterNode> list = new ArrayList<>();
|
||||
while (true) {
|
||||
// 跳过空行
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
// 碰到 body / end / returns 等其他小节,说明 params 结束
|
||||
String lookaheadLex = ts.peek().getLexeme();
|
||||
if ("body".equals(lookaheadLex) || "end".equals(lookaheadLex) || "returns".equals(lookaheadLex)) {
|
||||
break;
|
||||
}
|
||||
|
||||
int line = ts.peek().getLine();
|
||||
int col = ts.peek().getCol();
|
||||
|
||||
// 支持两种前缀:有 declare / 无 declare
|
||||
boolean hasDeclare = "declare".equals(ts.peek().getLexeme());
|
||||
if (hasDeclare) {
|
||||
ts.expect("declare");
|
||||
}
|
||||
|
||||
// 参数名
|
||||
String pName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
|
||||
ts.expect(":");
|
||||
|
||||
// 参数类型:既可为 TYPE(内置),也可为 IDENTIFIER(自定义)
|
||||
String pType;
|
||||
if (ts.peek().getType() == TokenType.TYPE || ts.peek().getType() == TokenType.IDENTIFIER) {
|
||||
pType = ts.next().getLexeme();
|
||||
} else {
|
||||
var t = ts.peek();
|
||||
throw new UnexpectedToken(
|
||||
"期望的标记类型为 TYPE 或 IDENTIFIER,但实际得到的是 " +
|
||||
t.getType() + " ('" + t.getLexeme() + "')",
|
||||
t.getLine(), t.getCol());
|
||||
}
|
||||
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
list.add(new ParameterNode(pName, pType, new NodeContext(line, col, ctx.getSourceName())));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/* ---------------- body: 方法体块解析 ---------------- */
|
||||
|
||||
/**
|
||||
* 解析 body 块,返回语句节点列表。
|
||||
*
|
||||
* @param ctx 解析上下文
|
||||
* @return 解析得到的方法体语句列表
|
||||
*/
|
||||
private List<StatementNode> parseBody(ParserContext ctx) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
ts.expect("body");
|
||||
ts.expect(":");
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
|
||||
List<StatementNode> body = new ArrayList<>();
|
||||
|
||||
// 循环读取每一条语句,直到 end body
|
||||
while (true) {
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
}
|
||||
if ("end".equals(ts.peek().getLexeme())) break; // body 块结束
|
||||
|
||||
var parser = StatementParserFactory.get(ts.peek().getLexeme());
|
||||
body.add(parser.parse(ctx));
|
||||
}
|
||||
|
||||
ts.expect("end");
|
||||
ts.expect("body");
|
||||
return body;
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,10 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@code ASTJsonSerializer} 是抽象语法树(AST)序列化工具类。
|
||||
@ -83,7 +86,8 @@ public class ASTJsonSerializer {
|
||||
return switch (n) {
|
||||
// 模块节点
|
||||
case ModuleNode(
|
||||
String name, List<ImportNode> imports, List<DeclarationNode> globals, List<FunctionNode> functions, NodeContext _
|
||||
String name, List<ImportNode> imports, List<DeclarationNode> globals, List<StructNode> structs,
|
||||
List<FunctionNode> functions, NodeContext _
|
||||
) -> {
|
||||
Map<String, Object> map = newNodeMap("Module");
|
||||
map.put("name", name);
|
||||
@ -105,9 +109,47 @@ public class ASTJsonSerializer {
|
||||
for (FunctionNode f : functions) {
|
||||
funcs.add(nodeToMap(f));
|
||||
}
|
||||
List<Object> lStructs = new ArrayList<>();
|
||||
structs.forEach(s -> lStructs.add(nodeToMap(s)));
|
||||
map.put("structs", lStructs);
|
||||
map.put("functions", funcs);
|
||||
yield map;
|
||||
}
|
||||
|
||||
// Struct 节点(多构造支持)
|
||||
case StructNode(
|
||||
String name, String parent, List<DeclarationNode> fields, List<FunctionNode> inits,
|
||||
List<FunctionNode> methods, NodeContext _
|
||||
) -> {
|
||||
Map<String, Object> map = newNodeMap("Struct");
|
||||
map.put("name", name);
|
||||
map.put("parent", parent);
|
||||
|
||||
List<Object> lFields = new ArrayList<>();
|
||||
fields.forEach(d -> lFields.add(Map.of(
|
||||
"type", "Field",
|
||||
"name", d.getName(),
|
||||
"varType", d.getType())));
|
||||
map.put("fields", lFields);
|
||||
|
||||
// 多构造函数序列化为 inits 数组
|
||||
List<Object> lInits = new ArrayList<>();
|
||||
if (inits != null) {
|
||||
for (FunctionNode ctor : inits) {
|
||||
lInits.add(nodeToMap(ctor));
|
||||
}
|
||||
}
|
||||
map.put("inits", lInits);
|
||||
|
||||
List<Object> lMethods = new ArrayList<>();
|
||||
if (methods != null) {
|
||||
for (FunctionNode f : methods) lMethods.add(nodeToMap(f));
|
||||
}
|
||||
map.put("methods", lMethods);
|
||||
yield map;
|
||||
}
|
||||
|
||||
|
||||
// 函数定义节点
|
||||
case FunctionNode f -> {
|
||||
Map<String, Object> map = newNodeMap("Function");
|
||||
@ -189,13 +231,12 @@ public class ASTJsonSerializer {
|
||||
private static Object exprToMap(ExpressionNode expr) {
|
||||
return switch (expr) {
|
||||
// 二元表达式
|
||||
case BinaryExpressionNode(
|
||||
ExpressionNode left, String operator, ExpressionNode right, NodeContext _
|
||||
) -> exprMap("BinaryExpression",
|
||||
"left", exprToMap(left),
|
||||
"operator", operator,
|
||||
"right", exprToMap(right)
|
||||
);
|
||||
case BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right, NodeContext _) ->
|
||||
exprMap("BinaryExpression",
|
||||
"left", exprToMap(left),
|
||||
"operator", operator,
|
||||
"right", exprToMap(right)
|
||||
);
|
||||
// 一元表达式
|
||||
case UnaryExpressionNode(String operator, ExpressionNode operand, NodeContext _) ->
|
||||
exprMap("UnaryExpression",
|
||||
|
||||
@ -1,44 +1,74 @@
|
||||
package org.jcnc.snow.compiler.semantic.analyzers.expression;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.*;
|
||||
import org.jcnc.snow.compiler.parser.ast.CallExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.MemberExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
import org.jcnc.snow.compiler.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.Symbol;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.FunctionType;
|
||||
import org.jcnc.snow.compiler.semantic.type.StructType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code CallExpressionAnalyzer} 是函数调用表达式的语义分析器。
|
||||
* <p>
|
||||
* 它负责处理类似 {@code callee(arg1, arg2, ...)} 形式的调用表达式,执行如下操作:
|
||||
* {@code CallExpressionAnalyzer} 是函数调用表达式 ({@link CallExpressionNode}) 的语义分析器。
|
||||
*
|
||||
* <p>它负责处理所有形式的调用表达式(如 {@code callee(arg1, arg2, ...)}),并执行如下操作:
|
||||
* <ul>
|
||||
* <li>识别调用目标(支持模块成员函数调用和当前模块函数调用,也支持自动在所有已导入模块中查找唯一同名函数);</li>
|
||||
* <li>根据被调用函数的参数签名检查实参数量和类型的兼容性;</li>
|
||||
* <li>支持数值参数的宽化转换(如 int → double);</li>
|
||||
* <li>支持数值到字符串的隐式转换(自动视为调用 {@code to_string});</li>
|
||||
* <li>在发生类型不匹配、未导入模块或函数未定义等情况下记录语义错误。</li>
|
||||
* <li>新增:以"_"开头的函数名只允许在本模块访问,禁止跨模块访问。</li>
|
||||
* <li>识别调用目标:支持三种调用方式
|
||||
* <ol>
|
||||
* <li>模块函数调用: {@code module.func(...)} </li>
|
||||
* <li>结构体实例方法调用: {@code instance.method(...)} </li>
|
||||
* <li>普通函数调用(当前模块或导入模块): {@code func(...)} </li>
|
||||
* </ol>
|
||||
* </li>
|
||||
* <li>在函数解析时遵循如下规则:
|
||||
* <ul>
|
||||
* <li>若是模块调用,必须确认模块已导入。</li>
|
||||
* <li>若是结构体实例调用,需先解析左侧表达式类型并确认方法存在。</li>
|
||||
* <li>若是普通函数调用,优先在当前模块中查找,若未找到,则尝试唯一导入模块解析。</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>参数检查与类型推断:
|
||||
* <ul>
|
||||
* <li>检查实参与形参数量是否一致。</li>
|
||||
* <li>检查类型兼容性,支持数值宽化转换 (int → double)。</li>
|
||||
* <li>支持数值到字符串的隐式转换(自动视为调用 {@code to_string})。</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>错误处理:
|
||||
* <ul>
|
||||
* <li>函数/方法未定义时记录 {@link SemanticError}。</li>
|
||||
* <li>访问未导入的模块时报错。</li>
|
||||
* <li>跨模块访问私有函数(以 "_" 开头)时报错。</li>
|
||||
* <li>参数数量或类型不匹配时报错。</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>最终返回函数的返回类型;若分析过程中存在错误,返回 {@link BuiltinType#INT} 作为默认回退类型。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>此分析器是编译器语义分析阶段的重要组成部分,确保调用表达式在类型系统和模块作用域中合法。</p>
|
||||
*/
|
||||
public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> {
|
||||
|
||||
/**
|
||||
* 分析函数调用表达式并推断其类型。
|
||||
* 分析函数调用表达式,推断返回类型并执行语义检查。
|
||||
*
|
||||
* @param ctx 当前语义分析上下文,提供日志、错误记录、模块访问等功能。
|
||||
* @param mi 当前模块信息,用于函数查找及模块依赖判断。
|
||||
* @param fn 当前分析的函数节点。
|
||||
* @param locals 局部符号表,用于变量查找。
|
||||
* @param fn 当前正在分析的函数节点(函数作用域)。
|
||||
* @param locals 局部符号表,用于变量和结构体实例查找。
|
||||
* @param call 待分析的函数调用表达式节点。
|
||||
* @return 表达式的返回类型。如果存在语义错误,默认返回 {@code BuiltinType.INT}。
|
||||
* @return 调用表达式的返回类型;若存在语义错误,返回 {@link BuiltinType#INT}。
|
||||
*/
|
||||
@Override
|
||||
public Type analyze(Context ctx,
|
||||
@ -46,88 +76,148 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
|
||||
FunctionNode fn,
|
||||
SymbolTable locals,
|
||||
CallExpressionNode call) {
|
||||
ctx.log("检查函数调用: " + call.callee());
|
||||
ctx.log("检查函数/方法调用: " + call.callee());
|
||||
|
||||
ModuleInfo target = mi; // 默认目标模块为当前模块
|
||||
String functionName;
|
||||
ExpressionNode callee = call.callee();
|
||||
ExpressionNode callee = call.callee(); // 被调用的表达式(函数名或成员访问)
|
||||
ModuleInfo targetModule = mi; // 初始假设目标模块为当前模块
|
||||
String functionName; // 被调用函数的名称
|
||||
FunctionType funcType; // 被调用函数的类型签名
|
||||
|
||||
// 支持模块调用形式: ModuleName.FunctionName(...)
|
||||
if (callee instanceof MemberExpressionNode(var obj, String member, NodeContext _)
|
||||
&& obj instanceof IdentifierNode(String mod, NodeContext _)) {
|
||||
// 验证模块是否存在并已导入
|
||||
if (!ctx.getModules().containsKey(mod)
|
||||
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"未知或未导入模块: " + mod));
|
||||
ctx.log("错误: 未导入模块 " + mod);
|
||||
return BuiltinType.INT;
|
||||
// ========== 支持三种调用形式 ==========
|
||||
// 1. 模块.函数(...)
|
||||
// 2. 结构体实例.方法(...)
|
||||
// 3. 普通函数(...)
|
||||
|
||||
if (callee instanceof MemberExpressionNode memberExpr) {
|
||||
// -------- 情况1 & 情况2: 成员调用表达式 --------
|
||||
ExpressionNode left = memberExpr.object();
|
||||
String member = memberExpr.member();
|
||||
|
||||
if (left instanceof IdentifierNode idNode) {
|
||||
// 左侧是标识符(可能是模块名或结构体变量)
|
||||
String leftName = idNode.name();
|
||||
|
||||
// (1) 模块.函数调用
|
||||
if (ctx.getModules().containsKey(leftName) &&
|
||||
(mi.getImports().contains(leftName) || mi.getName().equals(leftName))) {
|
||||
targetModule = ctx.getModules().get(leftName);
|
||||
functionName = member;
|
||||
funcType = targetModule.getFunctions().get(functionName);
|
||||
} else {
|
||||
// (2) 结构体实例.方法调用
|
||||
Symbol sym = locals.resolve(leftName);
|
||||
if (sym != null && sym.type() instanceof StructType structType) {
|
||||
funcType = structType.getMethods().get(member);
|
||||
functionName = member;
|
||||
if (funcType == null) {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"结构体方法未定义: " + structType + "." + member));
|
||||
ctx.log("错误: 结构体方法未定义 " + structType + "." + member);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
} else {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"未知或未导入模块: " + leftName));
|
||||
ctx.log("错误: 未导入模块或未声明变量 " + leftName);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// (2 扩展) 任意表达式.方法调用
|
||||
Type leftType = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(left)
|
||||
.analyze(ctx, mi, fn, locals, left);
|
||||
|
||||
if (leftType instanceof StructType structType) {
|
||||
funcType = structType.getMethods().get(member);
|
||||
functionName = member;
|
||||
if (funcType == null) {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"结构体方法未定义: " + structType + "." + member));
|
||||
ctx.log("错误: 结构体方法未定义 " + structType + "." + member);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
} else {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"不支持的成员调用对象类型: " + leftType));
|
||||
ctx.log("错误: 不支持的成员调用对象类型 " + leftType);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
}
|
||||
target = ctx.getModules().get(mod);
|
||||
functionName = member;
|
||||
}
|
||||
// 简单函数名形式: func(...)
|
||||
else if (callee instanceof IdentifierNode(String name, NodeContext _)) {
|
||||
functionName = name;
|
||||
}
|
||||
// 不支持的 callee 形式
|
||||
else {
|
||||
} else if (callee instanceof IdentifierNode idNode) {
|
||||
// -------- 情况3: 普通函数调用 --------
|
||||
functionName = idNode.name();
|
||||
funcType = mi.getFunctions().get(functionName); // 优先在当前模块查找
|
||||
|
||||
// 若当前模块未定义,则尝试在导入模块中唯一解析
|
||||
if (funcType == null) {
|
||||
ModuleInfo unique = null;
|
||||
for (String imp : mi.getImports()) {
|
||||
ModuleInfo mod = ctx.getModules().get(imp);
|
||||
if (mod != null && mod.getFunctions().containsKey(functionName)) {
|
||||
if (unique != null) {
|
||||
unique = null;
|
||||
break;
|
||||
} // 冲突: 不唯一
|
||||
unique = mod;
|
||||
}
|
||||
}
|
||||
if (unique != null) {
|
||||
targetModule = unique;
|
||||
funcType = unique.getFunctions().get(functionName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 不支持的调用方式 (如直接 lambda/表达式调用)
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"不支持的调用方式: " + callee));
|
||||
ctx.log("错误: 不支持的调用方式 " + callee);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// 私有函数访问控制
|
||||
// -------------------------
|
||||
// 如果函数名以"_"开头,且不是在本模块调用,则报错
|
||||
if (functionName.startsWith("_") && !target.getName().equals(mi.getName())) {
|
||||
// -------- 访问控制检查 --------
|
||||
if (functionName != null && funcType != null &&
|
||||
functionName.startsWith("_") && !targetModule.getName().equals(mi.getName())) {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"无法访问模块私有函数: " + target.getName() + "." + functionName
|
||||
"无法访问模块私有函数: " + targetModule.getName() + "." + functionName
|
||||
+ "(下划线开头的函数只允许在定义模块内访问)"));
|
||||
ctx.log("错误: 试图跨模块访问私有函数 " + target.getName() + "." + functionName);
|
||||
ctx.log("错误: 试图跨模块访问私有函数 " + targetModule.getName() + "." + functionName);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
|
||||
// 查找目标函数签名(先在当前模块/指定模块查找)
|
||||
FunctionType ft = target.getFunctions().get(functionName);
|
||||
|
||||
// 未找到则报错
|
||||
if (ft == null) {
|
||||
// -------- 函数是否存在 --------
|
||||
if (funcType == null) {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"函数未定义: " + functionName));
|
||||
ctx.log("错误: 函数未定义 " + functionName);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
|
||||
// 分析所有实参并获取类型
|
||||
// -------- 分析实参类型 --------
|
||||
List<Type> args = new ArrayList<>();
|
||||
for (ExpressionNode arg : call.arguments()) {
|
||||
args.add(ctx.getRegistry().getExpressionAnalyzer(arg)
|
||||
.analyze(ctx, mi, fn, locals, arg));
|
||||
}
|
||||
|
||||
// 参数数量检查
|
||||
if (args.size() != ft.paramTypes().size()) {
|
||||
// -------- 参数数量检查 --------
|
||||
if (args.size() != funcType.paramTypes().size()) {
|
||||
ctx.getErrors().add(new SemanticError(call,
|
||||
"参数数量不匹配: 期望 " + ft.paramTypes().size()
|
||||
"参数数量不匹配: 期望 " + funcType.paramTypes().size()
|
||||
+ " 个, 实际 " + args.size() + " 个"));
|
||||
ctx.log("错误: 参数数量不匹配: 期望 "
|
||||
+ ft.paramTypes().size() + ", 实际 " + args.size());
|
||||
|
||||
+ funcType.paramTypes().size() + ", 实际 " + args.size());
|
||||
} else {
|
||||
// 参数类型检查与转换支持
|
||||
// -------- 参数类型检查与转换 --------
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
Type expected = ft.paramTypes().get(i);
|
||||
Type actual = args.get(i);
|
||||
Type expected = funcType.paramTypes().get(i);
|
||||
Type actual = args.get(i);
|
||||
|
||||
// 完全兼容或数值宽化转换
|
||||
boolean ok = expected.isCompatible(actual)
|
||||
|| (expected.isNumeric() && actual.isNumeric()
|
||||
&& Type.widen(actual, expected) == expected);
|
||||
|
||||
// 支持将数值自动转换为字符串
|
||||
// 特殊情况:数值自动转 string
|
||||
if (!ok && expected == BuiltinType.STRING && actual.isNumeric()) {
|
||||
ctx.log(String.format(
|
||||
"隐式将参数 %d 的数值类型 %s 转换为 string (to_string)",
|
||||
@ -136,7 +226,6 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// 类型不匹配,记录语义错误
|
||||
if (!ok) {
|
||||
ctx.getErrors().add(new SemanticError(call,
|
||||
String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
|
||||
@ -147,8 +236,8 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
|
||||
}
|
||||
}
|
||||
|
||||
// 返回函数的返回类型作为整个调用表达式的类型
|
||||
ctx.log("函数调用类型: 返回 " + ft.returnType());
|
||||
return ft.returnType();
|
||||
// -------- 返回类型 --------
|
||||
ctx.log("函数调用类型: 返回 " + funcType.returnType());
|
||||
return funcType.returnType();
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,39 +11,49 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.Symbol;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.FunctionType;
|
||||
import org.jcnc.snow.compiler.semantic.type.StructType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
/**
|
||||
* {@code MemberExpressionAnalyzer} 用于分析模块成员访问表达式的类型和语义。
|
||||
* {@code MemberExpressionAnalyzer}
|
||||
*
|
||||
* <p>
|
||||
* 当前实现支持 <code>ModuleName.constOrVar</code> 形式的跨模块常量/全局变量访问,
|
||||
* 能根据目标模块的全局符号表,返回准确的类型信息,完全支持跨模块类型推断。
|
||||
* <br>
|
||||
* 对于非模块成员的访问(如对象.属性、多级 a.b.c),暂不支持,遇到时将报告语义错误。
|
||||
* 负责成员访问表达式的语义分析与类型推断。
|
||||
* 典型形如 <code>a.b</code>、<code>this.x</code>、<code>Module.member</code>。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>核心特性:</b>
|
||||
* <ul>
|
||||
* <li>校验模块是否存在、是否已导入(或自身);</li>
|
||||
* <li>跨模块访问目标模块的全局符号表,查找指定成员符号及其类型;</li>
|
||||
* <li>若成员不存在,报告“模块成员未定义”语义错误;</li>
|
||||
* <li>暂不支持更复杂的对象成员访问,遇到将报“不支持的成员访问对象类型”错误。</li>
|
||||
* </ul>
|
||||
* 支持三类成员访问方式:
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li><b>当前实例字段语法糖</b>:如 <code>this.x</code>,等价于访问当前作用域下名为 <code>x</code> 的变量或字段。</li>
|
||||
* <li><b>跨模块成员访问</b>:如 <code>ModuleName.member</code>,常用于引用其他模块的全局变量、常量、函数等。</li>
|
||||
* <li><b>结构体实例成员/方法访问</b>:如 <code>a.b</code> 或 <code>a.method</code>,a 为结构体变量,b 为字段或方法。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class MemberExpressionAnalyzer implements ExpressionAnalyzer<MemberExpressionNode> {
|
||||
|
||||
/**
|
||||
* 语义分析模块成员访问表达式。
|
||||
* 分析成员表达式,并返回其类型(如字段类型、方法类型等)。
|
||||
*
|
||||
* @param ctx 全局语义分析上下文,持有所有模块及错误记录
|
||||
* @param mi 当前模块信息(用于判断导入关系)
|
||||
* @param ctx 全局语义分析上下文
|
||||
* @param mi 当前模块信息
|
||||
* @param fn 当前函数节点
|
||||
* @param locals 当前局部符号表
|
||||
* @param expr 当前要分析的成员表达式(如 ModuleA.a)
|
||||
* @return 成员表达式的类型;出错时类型降级为 int,并记录语义错误
|
||||
* @param locals 局部符号表(当前作用域的变量、形参等)
|
||||
* @param expr 当前成员访问表达式(形如 obj.member)
|
||||
* @return 该表达式的推断类型,如失败则返回 {@link BuiltinType#INT}
|
||||
*
|
||||
* <p>
|
||||
* 主要处理流程分为四大分支:
|
||||
* <ol>
|
||||
* <li>this.x 语法糖(字段或变量访问)</li>
|
||||
* <li>ModuleName.member 跨模块成员访问</li>
|
||||
* <li>结构体实例的成员/方法访问</li>
|
||||
* <li>其它对象成员(不支持)</li>
|
||||
* </ol>
|
||||
* 出错时会注册语义错误,返回 int 作为降级类型。
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public Type analyze(Context ctx,
|
||||
@ -54,41 +64,117 @@ public class MemberExpressionAnalyzer implements ExpressionAnalyzer<MemberExpres
|
||||
|
||||
ctx.log("检查成员访问: " + expr);
|
||||
|
||||
// -------- 仅支持 ModuleName.member 形式 --------
|
||||
if (expr.object() instanceof IdentifierNode(String mod, NodeContext _)) {
|
||||
// =====================================================================
|
||||
// 1) 处理 this.x 语法糖
|
||||
// =====================================================================
|
||||
// 匹配左侧为 IdentifierNode,且为 "this"
|
||||
if (expr.object() instanceof IdentifierNode oid) {
|
||||
String objName = oid.name();
|
||||
|
||||
// 1. 校验模块存在且已导入或为本模块自身
|
||||
if (!ctx.getModules().containsKey(mod)
|
||||
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) {
|
||||
if ("this".equals(objName)) {
|
||||
// 优先在当前局部符号表查找字段或变量(如结构体字段/参数/局部变量等)
|
||||
if (locals != null) {
|
||||
Symbol localSym = locals.resolve(expr.member());
|
||||
if (localSym != null) {
|
||||
return localSym.type();
|
||||
}
|
||||
}
|
||||
// 再查全局符号表(当前模块全局声明)
|
||||
SymbolTable globals = mi != null ? mi.getGlobals() : null;
|
||||
if (globals != null) {
|
||||
Symbol globalSym = globals.resolve(expr.member());
|
||||
if (globalSym != null) {
|
||||
return globalSym.type();
|
||||
}
|
||||
}
|
||||
// 两处都查不到则报错
|
||||
ctx.getErrors().add(new SemanticError(expr,
|
||||
"未知或未导入模块: " + mod));
|
||||
ctx.log("错误: 未导入模块 " + mod);
|
||||
"未定义的字段或变量: " + expr.member()));
|
||||
ctx.log("错误: 未定义的字段或变量 this." + expr.member());
|
||||
// 降级为 int 类型
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 查找目标模块的全局符号表,精确返回成员类型
|
||||
ModuleInfo target = ctx.getModules().get(mod);
|
||||
SymbolTable globals = target.getGlobals();
|
||||
if (globals != null) {
|
||||
Symbol sym = globals.resolve(expr.member());
|
||||
if (sym != null) {
|
||||
return sym.type(); // 找到成员,返回其真实类型
|
||||
// =====================================================================
|
||||
// 2) 处理 ModuleName.member 跨模块访问
|
||||
// =====================================================================
|
||||
// 左侧为 IdentifierNode,尝试解析为模块名
|
||||
if (expr.object() instanceof IdentifierNode(String mod, NodeContext _)) {
|
||||
// 是否为已知模块
|
||||
boolean moduleExists = ctx.getModules().containsKey(mod);
|
||||
// 是否导入了该模块或就是本模块(允许自引用)
|
||||
boolean importedOrSelf = mi != null && (mi.getName().equals(mod) || mi.getImports().contains(mod));
|
||||
if (moduleExists && importedOrSelf) {
|
||||
ModuleInfo target = ctx.getModules().get(mod);
|
||||
// 1) 先查模块内函数(支持模块.函数名引用)
|
||||
FunctionType ft = target.getFunctions().get(expr.member());
|
||||
if (ft != null) return ft;
|
||||
|
||||
// 2) 再查模块全局符号表(全局常量/变量)
|
||||
SymbolTable globals = target != null ? target.getGlobals() : null;
|
||||
if (globals != null) {
|
||||
Symbol sym = globals.resolve(expr.member());
|
||||
if (sym != null) {
|
||||
return sym.type();
|
||||
}
|
||||
}
|
||||
// 以上均未找到则报错
|
||||
ctx.getErrors().add(new SemanticError(expr,
|
||||
"模块成员未定义: " + mod + "." + expr.member()));
|
||||
ctx.log("错误: 模块成员未定义 " + mod + "." + expr.member());
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
// 左侧不是模块名,可能是变量名(结构体实例),进入结构体成员分支
|
||||
if (locals != null) {
|
||||
Symbol sym = locals.resolve(mod);
|
||||
if (sym != null && sym.type() instanceof StructType st) {
|
||||
// 结构体字段访问
|
||||
Type t = st.getFields().get(expr.member());
|
||||
if (t != null) return t;
|
||||
// 结构体方法访问(返回方法类型,用于引用/分派)
|
||||
FunctionType ft = st.getMethods().get(expr.member());
|
||||
if (ft != null) return ft;
|
||||
// 字段和方法都没有,报错
|
||||
ctx.getErrors().add(new SemanticError(expr,
|
||||
"结构体成员未定义: " + st + "." + expr.member()));
|
||||
ctx.log("错误: 结构体成员未定义 " + st + "." + expr.member());
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 成员不存在,记录语义错误并类型降级
|
||||
ctx.getErrors().add(new SemanticError(expr,
|
||||
"模块成员未定义: " + mod + "." + expr.member()));
|
||||
ctx.log("错误: 模块成员未定义 " + mod + "." + expr.member());
|
||||
// 非模块非变量均未找到,报错
|
||||
ctx.getErrors().add(new SemanticError(expr, "未知或未导入模块: " + mod));
|
||||
ctx.log("错误: 未导入模块或未声明变量 " + mod);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
|
||||
// -------- 其它对象成员(如 a.b.c)暂不支持 --------
|
||||
// =====================================================================
|
||||
// 3) 支持通用结构体成员访问:a.b 或任意表达式.b
|
||||
// =====================================================================
|
||||
// 动态推断左侧表达式类型(如结构体类型等)
|
||||
Type leftType = ctx.getRegistry().getExpressionAnalyzer(expr.object())
|
||||
.analyze(ctx, mi, fn, locals, expr.object());
|
||||
|
||||
if (leftType instanceof StructType st) {
|
||||
// 字段访问
|
||||
Type t = st.getFields().get(expr.member());
|
||||
if (t != null) return t;
|
||||
// 方法访问(返回方法类型,支持引用/分派)
|
||||
FunctionType ft = st.getMethods().get(expr.member());
|
||||
if (ft != null) return ft;
|
||||
// 均未找到,报错
|
||||
ctx.getErrors().add(new SemanticError(expr,
|
||||
"结构体成员未定义: " + st + "." + expr.member()));
|
||||
ctx.log("错误: 结构体成员未定义 " + st + "." + expr.member());
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// 4) 其它对象成员(如 xx.yy 且 xx 非结构体)暂不支持
|
||||
// =====================================================================
|
||||
ctx.getErrors().add(new SemanticError(expr,
|
||||
"不支持的成员访问对象类型: "
|
||||
+ expr.object().getClass().getSimpleName()));
|
||||
ctx.log("错误: 不支持的成员访问对象类型 "
|
||||
+ expr.object().getClass().getSimpleName());
|
||||
"不支持的成员访问对象类型: " + expr.object().getClass().getSimpleName()));
|
||||
ctx.log("错误: 不支持的成员访问对象类型 " + expr.object().getClass().getSimpleName());
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,116 @@
|
||||
package org.jcnc.snow.compiler.semantic.analyzers.expression;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.NewExpressionNode;
|
||||
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.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.FunctionType;
|
||||
import org.jcnc.snow.compiler.semantic.type.StructType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code NewExpressionAnalyzer}
|
||||
* <p>
|
||||
* 负责“对象创建表达式”<code>new T(args...)</code>的语义分析与类型推断。
|
||||
* <ul>
|
||||
* <li>分析 new 关键字创建结构体对象的表达式。</li>
|
||||
* <li>校验目标类型是否为结构体,参数数量与类型是否匹配构造函数。</li>
|
||||
* <li>出错时会收集详细的语义错误信息。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* <b>表达式类型:</b> 返回被 new 的结构体类型 <code>T</code>,如失败则降级为 int。
|
||||
* </p>
|
||||
*/
|
||||
public class NewExpressionAnalyzer implements ExpressionAnalyzer<NewExpressionNode> {
|
||||
|
||||
/**
|
||||
* 语义分析:推断 new 表达式的类型、参数检查、构造函数合法性校验。
|
||||
*
|
||||
* @param ctx 全局语义分析上下文
|
||||
* @param mi 当前模块信息
|
||||
* @param fn 当前所在函数节点
|
||||
* @param locals 局部符号表(当前作用域变量/形参)
|
||||
* @param expr new 表达式节点
|
||||
* @return 分析推断得到的类型;如出错则降级为 int 类型
|
||||
*/
|
||||
@Override
|
||||
public Type analyze(Context ctx,
|
||||
ModuleInfo mi,
|
||||
FunctionNode fn,
|
||||
SymbolTable locals,
|
||||
NewExpressionNode expr) {
|
||||
|
||||
final String typeName = expr.typeName();
|
||||
|
||||
// 1. 解析目标类型(通过语义上下文统一入口,支持别名、跨模块等情况)
|
||||
Type parsed = ctx.parseType(typeName);
|
||||
if (parsed == null) {
|
||||
// 类型不存在,报错并降级
|
||||
ctx.errors().add(new SemanticError(expr, "未知类型: " + typeName));
|
||||
return BuiltinType.INT; // 兜底,避免连锁报错
|
||||
}
|
||||
if (!(parsed instanceof StructType st)) {
|
||||
// 非结构体类型不能用 new
|
||||
ctx.errors().add(new SemanticError(expr, "只有结构体类型才能使用 new: " + parsed));
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
|
||||
// 2. 分析所有实参的类型
|
||||
List<Type> argTypes = new ArrayList<>();
|
||||
for (ExpressionNode a : expr.arguments()) {
|
||||
Type at = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(a)
|
||||
.analyze(ctx, mi, fn, locals, a);
|
||||
if (at == null) {
|
||||
// 兜底处理,防止后续 NPE
|
||||
at = BuiltinType.INT;
|
||||
}
|
||||
argTypes.add(at);
|
||||
}
|
||||
|
||||
// 3. 选择并检查结构体构造函数(init)——支持重载:按“参数个数”匹配
|
||||
FunctionType ctor = st.getConstructor(argTypes.size());
|
||||
|
||||
if (ctor == null) {
|
||||
// 若该结构体完全未声明任何 init,且调用为 0 实参,则允许隐式默认构造(保留旧行为)
|
||||
if (st.getConstructors().isEmpty() && argTypes.isEmpty()) {
|
||||
return st;
|
||||
}
|
||||
// 否则报错:未找到对应形参个数的构造函数
|
||||
ctx.errors().add(new SemanticError(expr,
|
||||
"未找到参数个数为 " + argTypes.size() + " 的构造函数"));
|
||||
return st;
|
||||
}
|
||||
|
||||
// 3.1. 构造函数参数类型兼容性检查(包括数值类型宽化)
|
||||
List<Type> expectedParams = ctor.paramTypes();
|
||||
for (int i = 0; i < expectedParams.size(); i++) {
|
||||
Type expected = expectedParams.get(i); // 构造函数声明的参数类型
|
||||
Type actual = argTypes.get(i); // 实际传入的实参类型
|
||||
|
||||
boolean compatible = expected.isCompatible(actual); // 直接类型兼容
|
||||
boolean widenOK = expected.isNumeric()
|
||||
&& actual.isNumeric()
|
||||
&& expected.equals(Type.widen(actual, expected)); // 支持数值类型自动宽化
|
||||
|
||||
if (!compatible && !widenOK) {
|
||||
// 实参类型不兼容也无法宽化,报错
|
||||
ctx.errors().add(new SemanticError(expr,
|
||||
String.format("构造函数参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
|
||||
i, expected, actual)));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. new 表达式的类型就是结构体类型
|
||||
return st;
|
||||
}
|
||||
}
|
||||
@ -56,17 +56,11 @@ public final class AnalyzerRegistrar {
|
||||
registry.registerExpressionAnalyzer(IdentifierNode.class, new IdentifierAnalyzer());
|
||||
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.registerExpressionAnalyzer(ArrayLiteralNode.class, new ArrayLiteralAnalyzer());
|
||||
registry.registerExpressionAnalyzer(IndexExpressionNode.class, new IndexExpressionAnalyzer());
|
||||
registry.registerStatementAnalyzer(IndexAssignmentNode.class, new IndexAssignmentAnalyzer());
|
||||
|
||||
|
||||
// ---------- 注册一元表达式分析器 ----------
|
||||
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());
|
||||
|
||||
// ---------- 成员访问表达式 ----------
|
||||
registry.registerExpressionAnalyzer(MemberExpressionNode.class,
|
||||
new MemberExpressionAnalyzer());
|
||||
registry.registerExpressionAnalyzer(NewExpressionNode.class, new NewExpressionAnalyzer());
|
||||
registry.registerExpressionAnalyzer(MemberExpressionNode.class, new MemberExpressionAnalyzer());
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,47 +9,61 @@ 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>收集和存储语义分析阶段产生的所有错误;</li>
|
||||
* <li>根据配置输出调试日志;</li>
|
||||
* <li>调度各类语义分析器执行;</li>
|
||||
* <li>类型解析(包括内建类型、数组类型、结构体等自定义类型)。</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 提供便捷的 getter 方法和类型解析工具方法。
|
||||
* </p>
|
||||
* 该类为所有分析器提供便捷的访问、查找和工具方法。
|
||||
*/
|
||||
public class Context {
|
||||
/**
|
||||
* 模块表:模块名 → {@link ModuleInfo},用于模块查找与跨模块引用。
|
||||
* 所有模块信息表。
|
||||
* <p>
|
||||
* 键为模块名,值为 {@link ModuleInfo} 实例。用于支持跨模块类型/结构体引用。
|
||||
*/
|
||||
private final Map<String, ModuleInfo> modules;
|
||||
|
||||
/**
|
||||
* 错误列表:语义分析过程中收集的所有 {@link SemanticError}。
|
||||
* 语义分析期间收集的所有错误对象。
|
||||
* <p>
|
||||
* 每当发现语义错误时,将 {@link SemanticError} 添加到该列表。
|
||||
*/
|
||||
private final List<SemanticError> errors;
|
||||
|
||||
/**
|
||||
* 日志开关:若为 true,将启用 {@link #log(String)} 输出日志信息。
|
||||
* 日志输出开关。
|
||||
* <p>
|
||||
* 为 true 时,调用 {@link #log(String)} 会输出日志信息,用于调试。
|
||||
*/
|
||||
private final boolean verbose;
|
||||
|
||||
/**
|
||||
* 语义分析器注册表:用于按节点类型动态调度分析器。
|
||||
* 语义分析器注册表。
|
||||
* <p>
|
||||
* 用于根据语法树节点类型动态分派对应的分析器。
|
||||
*/
|
||||
private final AnalyzerRegistry registry;
|
||||
|
||||
/**
|
||||
* 构造语义分析上下文对象。
|
||||
* 当前正在处理的模块名。
|
||||
* <p>
|
||||
* 主要用于类型解析时限定结构体查找的作用域。
|
||||
*/
|
||||
private String currentModuleName;
|
||||
|
||||
/**
|
||||
* 构造一个新的语义分析上下文实例。
|
||||
*
|
||||
* @param modules 已注册的模块信息集合
|
||||
* @param errors 错误收集器,分析器将所有语义错误写入此列表
|
||||
* @param verbose 是否启用调试日志输出
|
||||
* @param registry 分析器注册表,提供类型到分析器的映射与调度能力
|
||||
* @param modules 所有已加载模块的映射表
|
||||
* @param errors 用于收集错误的列表
|
||||
* @param verbose 是否输出日志
|
||||
* @param registry 分析器注册表
|
||||
*/
|
||||
public Context(Map<String, ModuleInfo> modules,
|
||||
List<SemanticError> errors,
|
||||
@ -61,102 +75,137 @@ public class Context {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
// ------------------ 模块信息 ------------------
|
||||
// ==== Getter 方法(基本访问器) ====
|
||||
|
||||
/**
|
||||
* 获取所有模块信息映射表。
|
||||
*
|
||||
* @return 模块名到模块信息({@link ModuleInfo})的映射表
|
||||
* 获取所有模块信息表(模块名 → ModuleInfo)。
|
||||
*/
|
||||
public Map<String, ModuleInfo> modules() {
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有模块信息表(与 {@link #modules()} 等价)。
|
||||
*/
|
||||
public Map<String, ModuleInfo> getModules() {
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块信息 getter(快捷方式)。
|
||||
*
|
||||
* @return 模块名到模块信息({@link ModuleInfo})的映射表
|
||||
* 获取语义分析期间收集的所有错误。
|
||||
*/
|
||||
public Map<String, ModuleInfo> modules() {
|
||||
return modules;
|
||||
public List<SemanticError> errors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
// ------------------ 错误收集 ------------------
|
||||
|
||||
/**
|
||||
* 获取语义分析过程中记录的所有错误。
|
||||
*
|
||||
* @return 错误列表
|
||||
* 获取语义分析期间收集的所有错误(与 {@link #errors()} 等价)。
|
||||
*/
|
||||
public List<SemanticError> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误列表 getter(快捷方式)。
|
||||
*
|
||||
* @return 错误列表
|
||||
*/
|
||||
public List<SemanticError> errors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
// ------------------ 分析器注册表 ------------------
|
||||
|
||||
/**
|
||||
* 获取分析器注册表,用于分发语句与表达式分析器。
|
||||
*
|
||||
* @return {@link AnalyzerRegistry} 实例
|
||||
* 获取语义分析器注册表。
|
||||
*/
|
||||
public AnalyzerRegistry getRegistry() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册表 getter(快捷方式)。
|
||||
* 输出语义分析日志信息(受 verbose 控制)。
|
||||
*
|
||||
* @return {@link AnalyzerRegistry} 实例
|
||||
*/
|
||||
public AnalyzerRegistry registry() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
// ------------------ 日志输出 ------------------
|
||||
|
||||
/**
|
||||
* 打印日志信息,仅当 {@code verbose} 为 true 时生效。
|
||||
*
|
||||
* @param msg 日志内容
|
||||
* @param msg 日志消息内容
|
||||
*/
|
||||
public void log(String msg) {
|
||||
if (verbose) {
|
||||
System.out.println("[SemanticAnalyzer] " + msg);
|
||||
}
|
||||
if (verbose) System.out.println("[semantic] " + msg);
|
||||
}
|
||||
|
||||
// ------------------ 工具函数 ------------------
|
||||
// ==== 当前模块管理 ====
|
||||
|
||||
/**
|
||||
* 将类型名称字符串解析为对应的类型实例(支持多维数组后缀)。
|
||||
* <p>
|
||||
* 例如,"int" → int 类型,"int[][]" → 二维整型数组类型。
|
||||
* </p>
|
||||
* 获取当前正在分析的模块名。
|
||||
*
|
||||
* @param name 类型名称(支持 "[]" 数组后缀)
|
||||
* @return 对应的 {@link Type} 实例,若无法识别返回 null
|
||||
* @return 当前模块名,可能为 null
|
||||
*/
|
||||
public Type parseType(String name) {
|
||||
int dims = 0;
|
||||
public String getCurrentModule() {
|
||||
return currentModuleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前正在分析的模块名。
|
||||
* <p>
|
||||
* 用于限定结构体查找的优先作用域。
|
||||
*
|
||||
* @param moduleName 当前模块名
|
||||
*/
|
||||
public void setCurrentModule(String moduleName) {
|
||||
this.currentModuleName = moduleName;
|
||||
}
|
||||
|
||||
// ==== 类型解析工具 ====
|
||||
|
||||
/**
|
||||
* 解析类型字符串为 {@link Type} 实例。
|
||||
* <p>
|
||||
* 支持内建类型、数组类型(带 "[]" 后缀)、用户自定义结构体类型。
|
||||
* 类型解析的查找顺序为:<br>
|
||||
* 1. 内建类型;<br>
|
||||
* 2. 当前模块定义的结构体类型;<br>
|
||||
* 3. 当前模块导入模块中的结构体类型;<br>
|
||||
* 4. 全局所有模块的结构体类型。
|
||||
*
|
||||
* @param typeName 类型名称字符串,如 "int"、"Foo"、"Bar[][]"
|
||||
* @return 解析出的 {@link Type} 实例,若找不到则返回 null
|
||||
*/
|
||||
public Type parseType(String typeName) {
|
||||
if (typeName == null) return null; // 处理空输入
|
||||
|
||||
String name = typeName; // 剥离数组后缀前的基本类型名
|
||||
int dims = 0; // 记录数组维度
|
||||
// 支持多维数组:如 "int[][]" -> dims=2
|
||||
while (name.endsWith("[]")) {
|
||||
name = name.substring(0, name.length() - 2);
|
||||
dims++;
|
||||
}
|
||||
|
||||
// 1) 优先查找内建类型
|
||||
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);
|
||||
|
||||
// 2) 如果不是内建类型,则尝试查找结构体类型
|
||||
if (base == null) {
|
||||
// 2.1 当前模块下的结构体
|
||||
if (currentModuleName != null && modules.containsKey(currentModuleName)) {
|
||||
ModuleInfo mi = modules.get(currentModuleName);
|
||||
if (mi.getStructs().containsKey(name)) {
|
||||
base = mi.getStructs().get(name);
|
||||
} else {
|
||||
// 2.2 当前模块导入的模块中的结构体
|
||||
for (String imp : mi.getImports()) {
|
||||
ModuleInfo im = modules.get(imp);
|
||||
if (im != null && im.getStructs().containsKey(name)) {
|
||||
base = im.getStructs().get(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2.3 全局所有模块的结构体,作为兜底
|
||||
if (base == null) {
|
||||
for (ModuleInfo im : modules.values()) {
|
||||
if (im.getStructs().containsKey(name)) {
|
||||
base = im.getStructs().get(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (base == null) return null; // 所有路径均未找到
|
||||
|
||||
// 包装数组类型:根据数组维度递归封装
|
||||
Type t = base;
|
||||
for (int i = 0; i < dims; i++) t = new ArrayType(t);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,123 +4,110 @@ import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
|
||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.Symbol;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code FunctionChecker} 是 Snow 编译器语义分析阶段用于检查所有函数体合法性的总控调度器。
|
||||
* {@code FunctionChecker} 负责对所有模块的函数体进行两遍扫描式的语义检查。
|
||||
* <p>
|
||||
* <b>设计核心:</b>采用“两遍扫描”方案,彻底解决跨模块全局变量/常量类型推断和引用依赖问题:
|
||||
* 检查流程为:
|
||||
* <ol>
|
||||
* <li>第一遍:为每个模块构建全局符号表,注册所有模块级变量与常量声明(并检查重复/未知类型)。</li>
|
||||
* <li>第二遍:在所有全局符号表准备好后,依次分析各模块下所有函数的参数和函数体语句。</li>
|
||||
* </ol>
|
||||
* 检查要点:
|
||||
* <ul>
|
||||
* <li><b>第一遍</b>:为所有模块预先构建并注册其全局符号表(globals),保证跨模块引用时可见。</li>
|
||||
* <li><b>第二遍</b>:在全局符号表全部就绪后,依次分析所有模块的函数体,实现局部作用域、类型推断、语义校验等任务。</li>
|
||||
* <li>类型未知或变量/常量重复声明时,均收集为语义错误。</li>
|
||||
* <li>所有函数参数注册为局部变量。</li>
|
||||
* <li>所有函数体语句分派到对应 StatementAnalyzer 实例做分析。</li>
|
||||
* <li>非 void 返回类型的函数,必须有至少一条 return。</li>
|
||||
* </ul>
|
||||
* <b>功能职责:</b>
|
||||
* <ul>
|
||||
* <li>遍历所有模块,先建立 globals,再遍历并检查所有函数体语句。</li>
|
||||
* <li>为每个函数体构建完整符号表,并注册参数变量。</li>
|
||||
* <li>分发每条语句到对应 {@link StatementAnalyzer} 进行类型检查和错误校验。</li>
|
||||
* <li>自动检查非 void 函数 return 完备性。</li>
|
||||
* <li>记录所有语义错误,便于前端高亮和诊断。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ctx 全局语义分析上下文,持有模块信息、符号表、错误收集等资源
|
||||
*/
|
||||
public record FunctionChecker(Context ctx) {
|
||||
|
||||
/**
|
||||
* 主入口:对所有模块的所有函数体进行语义检查(两遍扫描实现)。
|
||||
* <p>
|
||||
* <b>第一遍</b>:为每个模块提前构建全局符号表(包含本模块所有全局变量和常量),
|
||||
* 并注册到 {@link ModuleInfo},确保跨模块引用时所有全局符号都已可用。
|
||||
* <br>
|
||||
* <b>第二遍</b>:遍历所有模块的所有函数,对每个函数体:
|
||||
* <ul>
|
||||
* <li>构建局部作用域,父作用域为对应模块的 globals;</li>
|
||||
* <li>注册参数变量;</li>
|
||||
* <li>依次分发每条语句到对应 {@link StatementAnalyzer},进行类型和语义检查;</li>
|
||||
* <li>自动校验非 void 函数 return 完备性;</li>
|
||||
* <li>将所有发现的问题统一记录到 {@link SemanticError} 列表。</li>
|
||||
* </ul>
|
||||
* 对传入的所有模块做函数体的两遍扫描式语义检查。
|
||||
*
|
||||
* @param mods 所有模块的 AST 根节点集合
|
||||
* @param mods 所有待分析的模块 AST 节点集合
|
||||
*/
|
||||
public void check(Iterable<ModuleNode> mods) {
|
||||
List<ModuleNode> moduleList = new ArrayList<>();
|
||||
// ---------- 第1遍:收集所有全局符号表 ----------
|
||||
|
||||
// ---------- 第一遍:构建并注册各模块全局符号表 ----------
|
||||
for (ModuleNode mod : mods) {
|
||||
ctx.setCurrentModule(mod.name()); // 标记当前模块
|
||||
moduleList.add(mod);
|
||||
|
||||
// 获取当前模块的元信息
|
||||
ModuleInfo mi = ctx.modules().get(mod.name());
|
||||
// 创建本模块全局作用域(无父作用域)
|
||||
SymbolTable globalScope = new SymbolTable(null);
|
||||
SymbolTable globalScope = new SymbolTable(null); // 模块级全局作用域
|
||||
|
||||
// 注册所有全局变量/常量到符号表
|
||||
// 处理所有全局变量/常量声明
|
||||
for (DeclarationNode g : mod.globals()) {
|
||||
var t = ctx.parseType(g.getType());
|
||||
SymbolKind k = g.isConst() ? SymbolKind.CONSTANT : SymbolKind.VARIABLE;
|
||||
Type t = ctx.parseType(g.getType()); // 解析声明类型
|
||||
if (t == null) {
|
||||
// 类型未知,记录错误,兜底为 int 类型,避免后续 NullPointer
|
||||
ctx.errors().add(new SemanticError(g, "未知类型: " + g.getType()));
|
||||
t = BuiltinType.INT;
|
||||
}
|
||||
SymbolKind kind = g.isConst() ? SymbolKind.CONSTANT : SymbolKind.VARIABLE;
|
||||
String dupType = g.isConst() ? "常量" : "变量";
|
||||
// 检查重复声明
|
||||
if (!globalScope.define(new Symbol(g.getName(), t, k))) {
|
||||
ctx.errors().add(new SemanticError(
|
||||
g,
|
||||
dupType + "重复声明: " + g.getName()
|
||||
));
|
||||
// 注册符号表(防止重名)
|
||||
if (!globalScope.define(new Symbol(g.getName(), t, kind))) {
|
||||
ctx.errors().add(new SemanticError(g, dupType + "重复声明: " + g.getName()));
|
||||
}
|
||||
}
|
||||
// 注册到模块信息,供跨模块引用
|
||||
// 将全局符号表挂载到模块信息对象
|
||||
mi.setGlobals(globalScope);
|
||||
}
|
||||
|
||||
// ---------- 第2遍:遍历所有函数,分析函数体 ----------
|
||||
// ---------- 第二遍:遍历各模块函数并分析函数体 ----------
|
||||
for (ModuleNode mod : moduleList) {
|
||||
ctx.setCurrentModule(mod.name());
|
||||
ModuleInfo mi = ctx.modules().get(mod.name());
|
||||
SymbolTable globalScope = mi.getGlobals();
|
||||
SymbolTable globalScope = mi.getGlobals(); // 全局作用域
|
||||
|
||||
for (FunctionNode fn : mod.functions()) {
|
||||
// 构建函数局部作用域,父作用域为 globalScope
|
||||
// 构建函数的局部作用域(父作用域为模块全局)
|
||||
SymbolTable locals = new SymbolTable(globalScope);
|
||||
|
||||
// 注册函数参数为局部变量
|
||||
fn.parameters().forEach(p ->
|
||||
locals.define(new Symbol(
|
||||
p.name(),
|
||||
ctx.parseType(p.type()),
|
||||
SymbolKind.VARIABLE
|
||||
))
|
||||
);
|
||||
// 注册所有函数参数到局部作用域,类型未知时兜底为 int
|
||||
fn.parameters().forEach(p -> {
|
||||
Type t = ctx.parseType(p.type());
|
||||
if (t == null) {
|
||||
ctx.errors().add(new SemanticError(p, "未知类型: " + p.type()));
|
||||
t = BuiltinType.INT;
|
||||
}
|
||||
locals.define(new Symbol(p.name(), t, SymbolKind.VARIABLE));
|
||||
});
|
||||
|
||||
// 分析函数体内每条语句
|
||||
for (var stmt : fn.body()) {
|
||||
var analyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
|
||||
// 分析函数体所有语句
|
||||
for (StatementNode stmt : fn.body()) {
|
||||
StatementAnalyzer<StatementNode> analyzer =
|
||||
ctx.getRegistry().getStatementAnalyzer(stmt);
|
||||
if (analyzer != null) {
|
||||
// 传递语义分析器“实例”,避免类型擦除/反射调用
|
||||
analyzer.analyze(ctx, mi, fn, locals, stmt);
|
||||
} else {
|
||||
ctx.errors().add(new SemanticError(
|
||||
stmt,
|
||||
"不支持的语句类型: " + stmt
|
||||
));
|
||||
// 语句类型未支持,收集错误
|
||||
ctx.errors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt));
|
||||
}
|
||||
}
|
||||
|
||||
// 检查非 void 函数是否至少包含一条 return 语句
|
||||
var returnType = ctx.parseType(fn.returnType());
|
||||
if (returnType != BuiltinType.VOID) {
|
||||
boolean hasReturn = fn.body().stream()
|
||||
.anyMatch(stmtNode -> stmtNode instanceof ReturnNode);
|
||||
// 非 void 函数,要求必须含至少一条 return 语句
|
||||
Type ret = ctx.parseType(fn.returnType());
|
||||
if (ret != null && ret != BuiltinType.VOID) {
|
||||
boolean hasReturn = fn.body().stream().anyMatch(s -> s instanceof ReturnNode);
|
||||
if (!hasReturn) {
|
||||
ctx.errors().add(new SemanticError(
|
||||
fn,
|
||||
"非 void 函数必须包含至少一条 return 语句"
|
||||
));
|
||||
ctx.errors().add(new SemanticError(fn, "非 void 函数必须包含至少一条 return 语句"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.semantic.core;
|
||||
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.type.FunctionType;
|
||||
import org.jcnc.snow.compiler.semantic.type.StructType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -9,106 +10,155 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@code ModuleInfo} 表示单个模块在语义分析阶段的元信息封装。
|
||||
* <p>
|
||||
* 用于在分析期间管理模块间依赖、函数签名查找、全局符号表等关键任务。
|
||||
* 每个模块对应一个唯一的 {@code ModuleInfo} 实例,贯穿整个语义分析流程。
|
||||
* {@code ModuleInfo} 表示单个模块在语义分析阶段的所有元信息封装。
|
||||
*
|
||||
* <p><b>包含信息:</b>
|
||||
* <p>
|
||||
* 每个模块对应一个唯一的 ModuleInfo 实例,在语义分析期间用于管理和查询:
|
||||
* <ul>
|
||||
* <li>模块名称(全局唯一标识);</li>
|
||||
* <li>该模块导入的其他模块名集合(跨模块引用支持);</li>
|
||||
* <li>该模块中定义的所有函数签名 {@code Map<String, FunctionType>};</li>
|
||||
* <li>模块级全局符号表 {@link SymbolTable}(常量 / 全局变量,支持跨模块类型推断)。</li>
|
||||
* <li>本模块导入的其他模块名集合(跨模块依赖支持);</li>
|
||||
* <li>该模块中所有函数签名(用于查找/重名检测/类型推断);</li>
|
||||
* <li>模块级全局符号表(常量、全局变量,支持类型推断和常量折叠);</li>
|
||||
* <li>本模块定义的结构体类型(struct)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p><b>典型用途:</b>
|
||||
* <p><b>主要用途:</b>
|
||||
* <ul>
|
||||
* <li>用于函数签名类型查找、重名检测、跨模块引用校验等;</li>
|
||||
* <li>全局符号表为类型检查与后端 IR 常量折叠等模块级分析提供支撑。</li>
|
||||
* <li>跨模块语义分析、符号查找、类型推断、常量查找、函数/类型重名校验等。</li>
|
||||
* <li>所有对该模块的引用、类型检查、全局变量访问等均依赖本类提供的索引信息。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class ModuleInfo {
|
||||
|
||||
/**
|
||||
* 模块名称,作为全局唯一标识
|
||||
* 模块名称,作为全局唯一标识符。
|
||||
* 不可为空。通常为文件名或逻辑模块名。
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* 该模块显式导入的模块名集合(用于跨模块访问符号)
|
||||
* 该模块显式导入的模块名集合。
|
||||
* <p>
|
||||
* 仅存储本模块 import 的模块名(如 import foo;),
|
||||
* 用于限制跨模块访问符号时的合法性校验。
|
||||
* </p>
|
||||
* <p>
|
||||
* 注意:集合为内部引用,允许外部直接添加或删除导入关系。
|
||||
* </p>
|
||||
*/
|
||||
private final Set<String> imports = new HashSet<>();
|
||||
|
||||
/**
|
||||
* 该模块中定义的函数名 → 函数类型映射
|
||||
* 该模块中声明的所有函数签名映射。
|
||||
* <p>
|
||||
* 键为函数名,值为函数类型。
|
||||
* 支持跨模块函数引用、重名检测和类型推断。
|
||||
* </p>
|
||||
*/
|
||||
private final Map<String, FunctionType> functions = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 模块级全局符号表(常量 / 全局变量)
|
||||
* 该模块中声明的所有结构体类型(struct)。
|
||||
* <p>
|
||||
* 键为结构体名称,值为结构体类型。
|
||||
* 用于跨模块/跨作用域结构体查找与类型检查。
|
||||
* </p>
|
||||
*/
|
||||
private final Map<String, StructType> structs = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 模块级全局符号表。
|
||||
* <p>
|
||||
* 记录本模块声明的所有常量和全局变量。
|
||||
* 支持类型推断、跨模块常量折叠、全局符号查找。
|
||||
* 由 FunctionChecker 阶段注入。
|
||||
* </p>
|
||||
*/
|
||||
private SymbolTable globals;
|
||||
|
||||
/**
|
||||
* 构造模块信息对象。
|
||||
* 构造函数:根据模块名初始化模块元信息。
|
||||
*
|
||||
* @param name 模块名称,必须唯一且不可为空
|
||||
* @param name 模块名称,要求全局唯一,且不可为空。
|
||||
*/
|
||||
public ModuleInfo(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块名称。
|
||||
* 获取当前模块的全局唯一名称。
|
||||
*
|
||||
* @return 当前模块的唯一名称
|
||||
* @return 模块名字符串
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该模块导入的模块名称集合。
|
||||
* <p>
|
||||
* 返回集合为内部数据的直接引用,调用方可通过 {@code add}/{@code remove} 方法动态维护导入信息。
|
||||
* 获取本模块导入的所有模块名称集合。
|
||||
*
|
||||
* @return 可变集合,包含所有导入模块名
|
||||
* <p>
|
||||
* 注意:返回为内部集合引用,调用方可直接对集合进行 add/remove 操作维护导入依赖关系。
|
||||
* </p>
|
||||
*
|
||||
* @return 可变集合,包含所有导入模块名(如 "foo", "bar")
|
||||
*/
|
||||
public Set<String> getImports() {
|
||||
return imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块中已声明的函数签名表。
|
||||
* <p>
|
||||
* 映射键为函数名,值为对应的 {@link FunctionType}。
|
||||
* 返回对象为内部引用,可用于添加、修改或删除函数定义。
|
||||
* 获取模块内所有函数签名表。
|
||||
*
|
||||
* @return 模块内函数定义映射表
|
||||
* <p>
|
||||
* 键为函数名,值为函数类型。
|
||||
* 返回对象为内部映射,可用于动态添加/修改/删除函数定义。
|
||||
* </p>
|
||||
*
|
||||
* @return 函数名 → 函数类型映射表
|
||||
*/
|
||||
public Map<String, FunctionType> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块的全局符号表(包含常量与全局变量)。
|
||||
* <p>
|
||||
* 该符号表由语义分析的 FunctionChecker 阶段构建完成并注入。
|
||||
* 提供跨模块类型检查、常量折叠等能力。
|
||||
* 获取模块定义的所有结构体类型。
|
||||
*
|
||||
* @return 当前模块的全局符号表
|
||||
* <p>
|
||||
* 键为结构体名,值为结构体类型描述。
|
||||
* </p>
|
||||
*
|
||||
* @return 结构体名 → 结构体类型映射
|
||||
*/
|
||||
public Map<String, StructType> getStructs() {
|
||||
return structs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前模块的全局符号表(包含常量与全局变量)。
|
||||
*
|
||||
* <p>
|
||||
* 该符号表通常由 FunctionChecker 阶段在全局扫描时构建并注入。
|
||||
* 提供后续类型检查、常量折叠、跨模块全局符号访问等能力。
|
||||
* </p>
|
||||
*
|
||||
* @return 本模块全局符号表
|
||||
*/
|
||||
public SymbolTable getGlobals() {
|
||||
return globals;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置模块的全局符号表。
|
||||
* <p>
|
||||
* 仅应由 FunctionChecker 在语义分析全局扫描阶段调用。
|
||||
* 设置模块的全局符号表(仅限 FunctionChecker 构建时注入)。
|
||||
*
|
||||
* @param globals 全局符号表实例
|
||||
* <p>
|
||||
* 通常仅语义分析的全局阶段由框架内部调用,
|
||||
* 不建议外部用户主动修改此符号表。
|
||||
* </p>
|
||||
*
|
||||
* @param globals 新的全局符号表实例
|
||||
*/
|
||||
public void setGlobals(SymbolTable globals) {
|
||||
this.globals = globals;
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
package org.jcnc.snow.compiler.semantic.core;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ImportNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ParameterNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.*;
|
||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.FunctionType;
|
||||
import org.jcnc.snow.compiler.semantic.type.StructType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -14,71 +12,118 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* {@code SignatureRegistrar} 负责函数签名登记与导入语义检查。
|
||||
* {@code SignatureRegistrar}
|
||||
* <p>
|
||||
* 在语义分析初期阶段,它遍历每个模块,完成以下任务:
|
||||
* <ul>
|
||||
* <li>验证所有 {@link ImportNode} 导入的模块是否存在于全局模块表 {@link Context#modules()} 中;</li>
|
||||
* <li>将每个 {@link FunctionNode} 的函数签名(参数类型和返回类型)注册到对应 {@link ModuleInfo} 中;</li>
|
||||
* <li>在参数或返回类型无法识别时,记录 {@link SemanticError},并进行容错降级。</li>
|
||||
* </ul>
|
||||
* 本组件作为语义分析的准备阶段,为后续函数体检查提供函数类型上下文。
|
||||
* 语义分析准备阶段:负责函数签名登记、结构体签名登记与 import 校验。
|
||||
*
|
||||
* @param ctx 全局语义分析上下文,提供模块、类型、错误管理等功能
|
||||
* <ul>
|
||||
* <li>验证每个模块声明的 import 模块在全局模块表 {@link Context#modules()} 是否存在。</li>
|
||||
* <li>将每个函数、结构体方法、构造函数的类型签名登记到 {@link ModuleInfo},便于后续类型推断。</li>
|
||||
* <li>支持 {@code extends} 单继承:子类会继承父类的字段与方法。</li>
|
||||
* <li>若参数或返回类型无法解析,则报错并降级为 int 或 void,保证语义分析流程健壮。</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 作为语义分析前置流程,为后续函数体和表达式分析提供类型环境。
|
||||
*/
|
||||
public record SignatureRegistrar(Context ctx) {
|
||||
|
||||
/**
|
||||
* 构造函数签名注册器。
|
||||
* 遍历所有模块,注册函数/方法/结构体签名,校验 import 合法性。
|
||||
*
|
||||
* @param ctx 当前语义分析上下文
|
||||
* @param modules 需要分析的所有模块列表(AST 顶层节点)
|
||||
*/
|
||||
public SignatureRegistrar {
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历模块并注册函数签名,同时校验导入模块的合法性。
|
||||
*
|
||||
* @param mods 所有模块的语法树节点集合
|
||||
*/
|
||||
public void register(Iterable<ModuleNode> mods) {
|
||||
for (ModuleNode mod : mods) {
|
||||
public void register(Iterable<ModuleNode> modules) {
|
||||
for (ModuleNode mod : modules) {
|
||||
ctx.setCurrentModule(mod.name()); // 切换上下文到当前模块
|
||||
ModuleInfo mi = ctx.modules().get(mod.name());
|
||||
|
||||
// ---------- 1. 模块导入检查 ----------
|
||||
// ========== 1) 校验 imports ==========
|
||||
for (ImportNode imp : mod.imports()) {
|
||||
if (!ctx.modules().containsKey(imp.moduleName())) {
|
||||
ctx.errors().add(new SemanticError(
|
||||
imp,
|
||||
"未知模块: " + imp.moduleName()
|
||||
));
|
||||
// 导入的模块在全局表中不存在,报错
|
||||
ctx.errors().add(new SemanticError(imp, "未知模块: " + imp.moduleName()));
|
||||
} else {
|
||||
// 添加到本模块导入集合
|
||||
mi.getImports().add(imp.moduleName());
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 2. 函数签名注册 ----------
|
||||
for (FunctionNode fn : mod.functions()) {
|
||||
List<Type> params = new ArrayList<>();
|
||||
// ========== 2) 结构体签名登记 ==========
|
||||
for (StructNode stn : mod.structs()) {
|
||||
// 构造结构体类型对象,唯一标识为 (模块名, 结构体名)
|
||||
StructType st = new StructType(mod.name(), stn.name());
|
||||
mi.getStructs().put(stn.name(), st);
|
||||
|
||||
// 参数类型解析
|
||||
for (ParameterNode p : fn.parameters()) {
|
||||
Type t = Optional.ofNullable(ctx.parseType(p.type()))
|
||||
.orElseGet(() -> {
|
||||
ctx.errors().add(new SemanticError(
|
||||
p,
|
||||
"未知类型: " + p.type()
|
||||
));
|
||||
return BuiltinType.INT; // 容错降级
|
||||
});
|
||||
params.add(t);
|
||||
// --- 2.1 多个构造函数 init(重载,按参数个数区分) ---
|
||||
if (stn.inits() != null) {
|
||||
for (FunctionNode initFn : stn.inits()) {
|
||||
List<Type> ptypes = new ArrayList<>();
|
||||
for (ParameterNode p : initFn.parameters()) {
|
||||
// 解析参数类型,不存在则报错降级为 int
|
||||
Type t = ctx.parseType(p.type());
|
||||
if (t == null) {
|
||||
ctx.errors().add(new SemanticError(p, "未知类型: " + p.type()));
|
||||
t = BuiltinType.INT;
|
||||
}
|
||||
ptypes.add(t);
|
||||
}
|
||||
// 构造函数返回类型固定为 void
|
||||
st.addConstructor(new FunctionType(ptypes, BuiltinType.VOID));
|
||||
}
|
||||
}
|
||||
|
||||
// 返回类型解析(默认降级为 void)
|
||||
// --- 2.2 结构体方法签名 ---
|
||||
for (FunctionNode fn : stn.methods()) {
|
||||
List<Type> ptypes = new ArrayList<>();
|
||||
for (ParameterNode p : fn.parameters()) {
|
||||
Type t = ctx.parseType(p.type());
|
||||
if (t == null) {
|
||||
ctx.errors().add(new SemanticError(p, "未知类型: " + p.type()));
|
||||
t = BuiltinType.INT;
|
||||
}
|
||||
ptypes.add(t);
|
||||
}
|
||||
Type ret = Optional.ofNullable(ctx.parseType(fn.returnType()))
|
||||
.orElse(BuiltinType.VOID);
|
||||
st.getMethods().put(fn.name(), new FunctionType(ptypes, ret));
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 2.3 继承处理 ==========
|
||||
for (StructNode stn : mod.structs()) {
|
||||
if (stn.parent() != null) {
|
||||
StructType child = mi.getStructs().get(stn.name());
|
||||
StructType parent = mi.getStructs().get(stn.parent());
|
||||
|
||||
if (parent == null) {
|
||||
// 父类不存在,报语义错误
|
||||
ctx.errors().add(new SemanticError(stn,
|
||||
"未知父类: " + stn.parent()));
|
||||
} else {
|
||||
// 将父类字段拷贝到子类(若子类未定义同名字段)
|
||||
parent.getFields().forEach(
|
||||
(k, v) -> child.getFields().putIfAbsent(k, v));
|
||||
// 将父类方法拷贝到子类(若子类未覆盖)
|
||||
parent.getMethods().forEach(
|
||||
(k, v) -> child.getMethods().putIfAbsent(k, v));
|
||||
// 构造函数不继承,由子类自行声明
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 3) 模块级函数签名登记 ==========
|
||||
for (FunctionNode fn : mod.functions()) {
|
||||
List<Type> params = new ArrayList<>();
|
||||
for (ParameterNode p : fn.parameters()) {
|
||||
Type t = ctx.parseType(p.type());
|
||||
if (t == null) {
|
||||
ctx.errors().add(new SemanticError(p, "未知类型: " + p.type()));
|
||||
t = BuiltinType.INT;
|
||||
}
|
||||
params.add(t);
|
||||
}
|
||||
Type ret = Optional.ofNullable(ctx.parseType(fn.returnType()))
|
||||
.orElse(BuiltinType.VOID);
|
||||
|
||||
// 注册函数签名
|
||||
mi.getFunctions().put(fn.name(), new FunctionType(params, ret));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,186 @@
|
||||
package org.jcnc.snow.compiler.semantic.type;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@code StructType}
|
||||
* <p>
|
||||
* 表示用户自定义结构体类型,在类型系统中用于描述结构体实例的静态类型信息。
|
||||
* <ul>
|
||||
* <li>由 <b>(moduleName, structName)</b> 唯一标识(支持跨模块同名 struct)。</li>
|
||||
* <li>包含字段定义、方法签名、构造函数等全部类型元信息。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class StructType implements Type {
|
||||
/**
|
||||
* 所属模块名称,用于唯一标识、支持跨模块同名结构体
|
||||
*/
|
||||
private final String moduleName;
|
||||
/**
|
||||
* 结构体类型名称(如 "Person")
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* 形参个数 → 构造函数签名
|
||||
*/
|
||||
private final Map<Integer, FunctionType> constructors = new HashMap<>();
|
||||
/**
|
||||
* 字段定义表:字段名 -> 字段类型
|
||||
*/
|
||||
private final Map<String, Type> fields = new HashMap<>();
|
||||
/**
|
||||
* 方法签名表:方法名 -> 函数类型
|
||||
*/
|
||||
private final Map<String, FunctionType> methods = new HashMap<>();
|
||||
/**
|
||||
* 构造函数(init)的函数类型;可为 null(表示无参默认构造)
|
||||
*/
|
||||
private FunctionType constructor;
|
||||
|
||||
/**
|
||||
* 构造函数:创建结构体类型描述。
|
||||
*
|
||||
* @param moduleName 所属模块名(不可为空)
|
||||
* @param name 结构体名称(不可为空)
|
||||
*/
|
||||
public StructType(String moduleName, String name) {
|
||||
this.moduleName = moduleName;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取结构体所属模块名。
|
||||
*
|
||||
* @return 模块名称
|
||||
*/
|
||||
public String moduleName() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加构造函数签名。
|
||||
*
|
||||
* @param ctor 构造函数的 {@link FunctionType} 实例
|
||||
*/
|
||||
public void addConstructor(FunctionType ctor) {
|
||||
constructors.put(ctor.paramTypes().size(), ctor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据参数个数获取对应构造函数。
|
||||
*
|
||||
* @param argc 参数个数
|
||||
* @return 对应的构造函数签名,找不到则返回 null
|
||||
*/
|
||||
public FunctionType getConstructor(int argc) {
|
||||
return constructors.get(argc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有构造函数签名(不可变视图)。
|
||||
*
|
||||
* @return 形参个数 → 构造函数签名的映射
|
||||
*/
|
||||
public Map<Integer, FunctionType> getConstructors() {
|
||||
return Map.copyOf(constructors);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取结构体名称。
|
||||
*
|
||||
* @return 结构体类型名
|
||||
*/
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认构造函数签名。
|
||||
*
|
||||
* @return 默认构造函数(可能为 null,表示无参构造)
|
||||
*/
|
||||
public FunctionType getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认构造函数签名。
|
||||
*
|
||||
* @param ctor 构造函数类型
|
||||
*/
|
||||
public void setConstructor(FunctionType ctor) {
|
||||
this.constructor = ctor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有字段定义(字段名 → 字段类型)。
|
||||
*
|
||||
* @return 字段定义表
|
||||
*/
|
||||
public Map<String, Type> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有方法签名(方法名 → 函数类型)。
|
||||
*
|
||||
* @return 方法签名表
|
||||
*/
|
||||
public Map<String, FunctionType> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断类型兼容性。
|
||||
* <ul>
|
||||
* <li>仅模块名与结构体名都相等才视为兼容。</li>
|
||||
* <li>跨模块同名 struct 不兼容。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param other 另一个类型
|
||||
* @return 类型兼容返回 true,否则 false
|
||||
*/
|
||||
@Override
|
||||
public boolean isCompatible(Type other) {
|
||||
if (!(other instanceof StructType s)) return false;
|
||||
return Objects.equals(this.name, s.name)
|
||||
&& Objects.equals(this.moduleName, s.moduleName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断类型相等:模块名+结构体名全等。
|
||||
*
|
||||
* @param o 另一个对象
|
||||
* @return 相等返回 true,否则 false
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof StructType s)) return false;
|
||||
return Objects.equals(name, s.name) && Objects.equals(moduleName, s.moduleName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 哈希值定义,和 equals 保持一致(用于 Map/Set 索引)。
|
||||
*
|
||||
* @return 哈希码
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(moduleName, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串表示:返回结构体名(调试用)。
|
||||
*
|
||||
* @return 结构体名字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@ -31,71 +31,61 @@ import java.util.*;
|
||||
import static org.jcnc.snow.common.SnowConfig.print;
|
||||
|
||||
/**
|
||||
* CLI 任务: 编译 .snow 源文件为 VM 字节码(.water 文件)。
|
||||
* CompileTask: 将 .snow 源文件编译为 VM 字节码文件 (.water) 的 CLI 任务实现。
|
||||
* <p>
|
||||
* 支持单文件、多文件和目录递归编译,并可在编译后立即运行虚拟机。<br>
|
||||
* 命令行参数支持 run、-o、-d 及直接指定源文件。
|
||||
* </p>
|
||||
*
|
||||
* 用法示例:
|
||||
* <pre>
|
||||
* 用法示例:
|
||||
* $ snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow ...]
|
||||
* snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow ...]
|
||||
* </pre>
|
||||
* 支持编译完成后立即运行生成的 VM。
|
||||
*/
|
||||
public final class CompileTask implements Task {
|
||||
/**
|
||||
* 项目信息
|
||||
*/
|
||||
private final Project project;
|
||||
/**
|
||||
* 原始命令行参数
|
||||
*/
|
||||
private final String[] args;
|
||||
public record CompileTask(Project project, String[] args) implements Task {
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 1. 构造方法和成员变量 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* 创建一个编译任务。
|
||||
* 使用默认空参数数组的构造方法。
|
||||
*
|
||||
* @param project 项目信息对象
|
||||
* @param args 命令行参数数组
|
||||
*/
|
||||
public CompileTask(Project project, String[] args) {
|
||||
this.project = project;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个不带参数的编译任务。
|
||||
*
|
||||
* @param project 项目信息对象
|
||||
* @param project 当前项目对象
|
||||
*/
|
||||
public CompileTask(Project project) {
|
||||
this(project, new String[0]);
|
||||
}
|
||||
|
||||
/* ---------------------- 调试输出工具 ---------------------- */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 2. 工具方法 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* 推断 .water 输出文件名。
|
||||
* 推断输出 .water 文件的路径。
|
||||
* <p>
|
||||
* 规则:
|
||||
* <ul>
|
||||
* <li>如果指定 -o,直接使用该名称。</li>
|
||||
* <li>目录编译时,取目录名。</li>
|
||||
* <li>单文件编译时,取文件名去掉 .snow 后缀。</li>
|
||||
* <li>否则默认 "program"。</li>
|
||||
* <li>若指定了 outName,则以 outName 为基名</li>
|
||||
* <li>否则若指定了目录,则以目录名为基名</li>
|
||||
* <li>否则若只有一个源文件,则以该文件名(去 .snow 后缀)为基名</li>
|
||||
* <li>否则默认为 "program"</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param sources 源文件路径列表
|
||||
* @param outName 输出文件名(如有指定,否则为 null)
|
||||
* @param dir 源码目录(如有指定,否则为 null)
|
||||
* @return 推断出的输出文件路径(.water 文件)
|
||||
* @param sources 源文件列表
|
||||
* @param outName 用户指定的输出文件基名
|
||||
* @param dir 用户指定的源目录
|
||||
* @return 推断得到的 .water 文件路径
|
||||
*/
|
||||
private static Path deriveOutputPath(List<Path> sources, String outName, Path dir) {
|
||||
private static Path deriveOutputPath(List<Path> sources,
|
||||
String outName,
|
||||
Path dir) {
|
||||
String base;
|
||||
if (outName != null) {
|
||||
base = outName;
|
||||
} else if (dir != null) {
|
||||
base = dir.getFileName().toString();
|
||||
} else if (sources.size() == 1) {
|
||||
base = sources.getFirst().getFileName().toString()
|
||||
base = sources.getFirst()
|
||||
.getFileName()
|
||||
.toString()
|
||||
.replaceFirst("\\.snow$", "");
|
||||
} else {
|
||||
base = "program";
|
||||
@ -104,15 +94,16 @@ public final class CompileTask implements Task {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 main 函数调整至函数列表首位,确保程序入口为 PC=0。
|
||||
* 将 main 或 *.main 函数调整到列表首位,确保程序入口的 PC = 0。
|
||||
*
|
||||
* @param in 原始 IRProgram
|
||||
* @return 调整入口后的 IRProgram
|
||||
* @return 调整入口顺序后的 IRProgram
|
||||
*/
|
||||
private static IRProgram reorderForEntry(IRProgram in) {
|
||||
List<IRFunction> ordered = new ArrayList<>(in.functions());
|
||||
for (int i = 0; i < ordered.size(); i++) {
|
||||
if ("main".equals(ordered.get(i).name())) {
|
||||
String fn = ordered.get(i).name();
|
||||
if ("main".equals(fn) || fn.endsWith(".main")) {
|
||||
Collections.swap(ordered, 0, i);
|
||||
break;
|
||||
}
|
||||
@ -122,10 +113,14 @@ public final class CompileTask implements Task {
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 3. 任务执行入口 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* 执行编译任务。该方法会解析参数并调用 {@link #execute(String[])} 进行实际编译流程。
|
||||
* 执行任务入口,调用 execute 并处理异常。
|
||||
*
|
||||
* @throws Exception 执行过程中出现任意异常时抛出
|
||||
* @throws Exception 若执行过程出现异常
|
||||
*/
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
@ -144,15 +139,15 @@ public final class CompileTask implements Task {
|
||||
* @throws Exception 编译或写入过程中出现异常时抛出
|
||||
*/
|
||||
public int execute(String[] args) throws Exception {
|
||||
// ---------------- 解析命令行参数 ----------------
|
||||
|
||||
/* ---------------- 3.1 解析命令行参数 ---------------- */
|
||||
boolean runAfterCompile = false;
|
||||
String outputName = null;
|
||||
Path dir = null;
|
||||
List<Path> sources = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
switch (arg) {
|
||||
switch (args[i]) {
|
||||
case "run" -> runAfterCompile = true;
|
||||
case "--debug" -> SnowConfig.MODE = Mode.DEBUG;
|
||||
case "-o" -> {
|
||||
@ -172,10 +167,10 @@ public final class CompileTask implements Task {
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
if (arg.endsWith(".snow")) {
|
||||
sources.add(Path.of(arg));
|
||||
if (args[i].endsWith(".snow")) {
|
||||
sources.add(Path.of(args[i]));
|
||||
} else {
|
||||
System.err.println("Unknown option or file: " + arg);
|
||||
System.err.println("Unknown option or file: " + args[i]);
|
||||
new CompileCommand().printUsage();
|
||||
return 1;
|
||||
}
|
||||
@ -183,7 +178,7 @@ public final class CompileTask implements Task {
|
||||
}
|
||||
}
|
||||
|
||||
// --------- 如果指定了目录则递归收集所有 *.snow ---------
|
||||
/* ---------------- 3.2 递归收集目录中的 .snow 文件 ---------------- */
|
||||
if (dir != null) {
|
||||
if (!Files.isDirectory(dir)) {
|
||||
System.err.println("Not a directory: " + dir);
|
||||
@ -191,7 +186,7 @@ public final class CompileTask implements Task {
|
||||
}
|
||||
try (var stream = Files.walk(dir)) {
|
||||
stream.filter(p -> p.toString().endsWith(".snow"))
|
||||
.sorted() // 确保稳定顺序
|
||||
.sorted()
|
||||
.forEach(sources::add);
|
||||
}
|
||||
}
|
||||
@ -200,87 +195,73 @@ public final class CompileTask implements Task {
|
||||
System.err.println("No .snow source files found.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 多文件但未指定 -o 且非目录编译 —— 提示必须指定输出名
|
||||
if (sources.size() > 1 && outputName == null && dir == null) {
|
||||
System.err.println("Please specify output name using -o <name>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ---------------- 1. 词法/语法分析,并打印源代码 ----------------
|
||||
/* ---------------- 4. 词法和语法分析 ---------------- */
|
||||
List<Node> allAst = new ArrayList<>();
|
||||
|
||||
print("## 编译器输出");
|
||||
print("### Snow 源代码");
|
||||
|
||||
for (Path p : sources) {
|
||||
if (!Files.exists(p)) {
|
||||
System.err.println("File not found: " + p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
String code = Files.readString(p, StandardCharsets.UTF_8);
|
||||
|
||||
// 打印源码
|
||||
print("#### " + p.getFileName());
|
||||
for (Path src : sources) {
|
||||
String code = Files.readString(src, StandardCharsets.UTF_8);
|
||||
print("#### " + src.getFileName());
|
||||
print(code);
|
||||
|
||||
// 词法、语法分析
|
||||
LexerEngine lexer = new LexerEngine(code, p.toString());
|
||||
// 若词法阶段存在错误,立即终止编译,避免进入后续的语法及语义分析
|
||||
if (!lexer.getErrors().isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
LexerEngine lex = new LexerEngine(code, src.toString());
|
||||
if (!lex.getErrors().isEmpty()) return 1;
|
||||
|
||||
ParserContext ctx = new ParserContext(lexer.getAllTokens(), p.toString());
|
||||
ParserContext ctx = new ParserContext(lex.getAllTokens(), src.toString());
|
||||
allAst.addAll(new ParserEngine(ctx).parse());
|
||||
}
|
||||
|
||||
// ---------------- 2. 语义分析 ----------------
|
||||
/* ---------------- 5. 语义分析 ---------------- */
|
||||
SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false);
|
||||
|
||||
// ---------------- 3. AST → IR,并将 main 函数移动至首位 ----------------
|
||||
/* ---------------- 6. AST 转 IR,并调整入口 ---------------- */
|
||||
IRProgram program = new IRProgramBuilder().buildProgram(allAst);
|
||||
program = reorderForEntry(program);
|
||||
program = reorderForEntry(program); // 确保 main 在首位
|
||||
|
||||
// 打印 AST 和 IR
|
||||
print("### AST");
|
||||
if (SnowConfig.isDebug()) ASTPrinter.printJson(allAst);
|
||||
// print("### AST");
|
||||
// if (SnowConfig.isDebug()) ASTPrinter.printJson(allAst);
|
||||
|
||||
print("### IR");
|
||||
print(program.toString());
|
||||
|
||||
// ---------------- 4. IR → VM 指令 ----------------
|
||||
/* ---------------- 7. IR 转 VM 指令 ---------------- */
|
||||
VMProgramBuilder builder = new VMProgramBuilder();
|
||||
List<InstructionGenerator<? extends IRInstruction>> generators =
|
||||
List<InstructionGenerator<? extends IRInstruction>> gens =
|
||||
InstructionGeneratorProvider.defaultGenerators();
|
||||
|
||||
for (IRFunction fn : program.functions()) {
|
||||
Map<IRVirtualRegister, Integer> slotMap =
|
||||
new RegisterAllocator().allocate(fn);
|
||||
new VMCodeGenerator(slotMap, builder, generators).generate(fn);
|
||||
new VMCodeGenerator(slotMap, builder, gens).generate(fn);
|
||||
}
|
||||
List<String> finalCode = builder.build();
|
||||
List<String> vmCode = builder.build();
|
||||
|
||||
print("### VM code");
|
||||
if (SnowConfig.isDebug()) {
|
||||
for (int i = 0; i < finalCode.size(); i++) {
|
||||
String[] parts = finalCode.get(i).split(" ");
|
||||
for (int i = 0; i < vmCode.size(); i++) {
|
||||
String[] parts = vmCode.get(i).split(" ");
|
||||
String name = OpHelper.opcodeName(parts[0]);
|
||||
parts = Arrays.copyOfRange(parts, 1, parts.length);
|
||||
print("%04d: %-10s %s%n", i, name, String.join(" ", parts));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- 5. 写出 .water 文件 ----------------
|
||||
Path outputFile = deriveOutputPath(sources, outputName, dir);
|
||||
Files.write(outputFile, finalCode, StandardCharsets.UTF_8);
|
||||
print("Written to " + outputFile.toAbsolutePath());
|
||||
/* ---------------- 8. 写出 .water 文件 ---------------- */
|
||||
Path outFile = deriveOutputPath(sources, outputName, dir);
|
||||
Files.write(outFile, vmCode, StandardCharsets.UTF_8);
|
||||
print("Written to " + outFile.toAbsolutePath());
|
||||
|
||||
// ---------------- 6. 立即运行 VM ----------------
|
||||
/* ---------------- 9. 可选运行 VM ---------------- */
|
||||
if (runAfterCompile) {
|
||||
print("\n=== Launching VM ===");
|
||||
VMLauncher.main(new String[]{outputFile.toString()});
|
||||
VMLauncher.main(new String[]{outFile.toString()});
|
||||
print("\n=== VM exited ===");
|
||||
}
|
||||
|
||||
|
||||
@ -35,9 +35,6 @@ import java.util.List;
|
||||
* R_PUSH [1, 2, 3] // pushes ArrayList {1, 2, 3}
|
||||
* R_PUSH [1, [2, 3], 4] // pushes nested arrays as mutable lists
|
||||
* </pre>
|
||||
*
|
||||
* @author (your name or org)
|
||||
* @since 1.0
|
||||
*/
|
||||
public class RPushCommand implements Command {
|
||||
|
||||
|
||||
@ -400,9 +400,20 @@ public class SyscallCommand implements Command {
|
||||
// 必须是可变 List
|
||||
@SuppressWarnings("unchecked")
|
||||
java.util.List<Object> mlist = (java.util.List<Object>) list;
|
||||
if (idx < 0 || idx >= mlist.size())
|
||||
if (idx < 0) {
|
||||
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + mlist.size() + ")");
|
||||
mlist.set(idx, value);
|
||||
}
|
||||
// 允许自动扩容:当 idx == size 时等价于 append;当 idx > size 时以 null 填充到 idx-1,再 set(idx, value)
|
||||
if (idx >= mlist.size()) {
|
||||
// 以 null 填充直到 size == idx
|
||||
while (mlist.size() < idx) {
|
||||
mlist.add(null);
|
||||
}
|
||||
// 此时 size == idx,直接追加
|
||||
mlist.add(value);
|
||||
} else {
|
||||
mlist.set(idx, value);
|
||||
}
|
||||
} else if (arrObj != null && arrObj.getClass().isArray()) {
|
||||
int len = java.lang.reflect.Array.getLength(arrObj);
|
||||
if (idx < 0 || idx >= len)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user