feat: Initialize project skeleton, including basic directories and configuration files
This commit is contained in:
commit
0c2cb0357d
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
.idea
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
|
/.idea/
|
||||||
|
/Snow.tar
|
||||||
|
/src/main/java/org/jcnc/snow/compiler/ir.tar
|
||||||
7
.run/Run.run.xml
Normal file
7
.run/Run.run.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Run" type="CompoundRunConfigurationType">
|
||||||
|
<toRun name="build_project2tar.ps1" type="PowerShellRunType" />
|
||||||
|
<toRun name="SnowCompiler" type="Application" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
33
.run/SnowCompiler.run.xml
Normal file
33
.run/SnowCompiler.run.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="SnowCompiler" type="Application" factoryName="Application" activateToolWindowBeforeRun="false" nameIsGenerated="true">
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" value="23" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||||
|
<module name="SCompiler" />
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="test" />
|
||||||
|
<extension name="coverage">
|
||||||
|
<pattern>
|
||||||
|
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||||
|
<option name="ENABLED" value="true" />
|
||||||
|
</pattern>
|
||||||
|
</extension>
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
<configuration default="false" name="SnowCompiler" type="Application" factoryName="Application" activateToolWindowBeforeRun="false" nameIsGenerated="true">
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||||
|
<module name="Snow" />
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="-d playground" />
|
||||||
|
<extension name="coverage">
|
||||||
|
<pattern>
|
||||||
|
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||||
|
<option name="ENABLED" value="true" />
|
||||||
|
</pattern>
|
||||||
|
</extension>
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
10
.run/build_project2tar.ps1.run.xml
Normal file
10
.run/build_project2tar.ps1.run.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="build_project2tar.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build_project2tar.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
|
||||||
|
<envs />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration default="false" name="build_project2tar.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build_project2tar.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
|
||||||
|
<envs />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
26
build/build_project2tar.ps1
Normal file
26
build/build_project2tar.ps1
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
$tarName = "Snow.tar"
|
||||||
|
|
||||||
|
# 脚本当前目录(build文件夹)
|
||||||
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
|
# 上一级目录(snow根目录)
|
||||||
|
$parentDir = Split-Path -Parent $scriptDir
|
||||||
|
|
||||||
|
# tar包的完整路径
|
||||||
|
$tarPath = Join-Path $parentDir $tarName
|
||||||
|
|
||||||
|
# 如果存在旧tar包,删除
|
||||||
|
if (Test-Path $tarPath) {
|
||||||
|
Remove-Item $tarPath
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "正在创建新的 $tarName 到 $parentDir ..."
|
||||||
|
|
||||||
|
# 打包:先切换到 org\jcnc 目录下再压缩 snow 文件夹
|
||||||
|
& tar -cf $tarPath -C "$scriptDir\..\src\main\java\org\jcnc" snow
|
||||||
|
|
||||||
|
if (Test-Path $tarPath) {
|
||||||
|
Write-Output "✅ 成功创建 $tarName"
|
||||||
|
} else {
|
||||||
|
Write-Output "❌ 创建失败,请检查 tar 命令"
|
||||||
|
}
|
||||||
5
build/readme.md
Normal file
5
build/readme.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
使用 build——project2tar.ps1 需要在管理员权限下的 PowerShell 输入下面的内容
|
||||||
|
|
||||||
|
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
||||||
|
|
||||||
|
Tip:RemoteSigned 表示:本地创建的脚本可以运行,从互联网下载的脚本必须有签名。
|
||||||
34
lib/math/factorial/factorial.snow
Normal file
34
lib/math/factorial/factorial.snow
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
module: Math
|
||||||
|
function: main
|
||||||
|
parameter:
|
||||||
|
return_type: int
|
||||||
|
body:
|
||||||
|
Math.factorial(6)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end body
|
||||||
|
end function
|
||||||
|
|
||||||
|
|
||||||
|
function: factorial
|
||||||
|
parameter:
|
||||||
|
declare n:int
|
||||||
|
return_type: int
|
||||||
|
body:
|
||||||
|
declare num1:int = 1
|
||||||
|
loop:
|
||||||
|
initializer:
|
||||||
|
declare counter:int = 1
|
||||||
|
condition:
|
||||||
|
counter <= n
|
||||||
|
update:
|
||||||
|
counter = counter + 1
|
||||||
|
body:
|
||||||
|
num1 = num1 * counter
|
||||||
|
end body
|
||||||
|
end loop
|
||||||
|
return num1
|
||||||
|
end body
|
||||||
|
end function
|
||||||
|
|
||||||
|
end module
|
||||||
18
lib/test/Dead_loop.snow
Normal file
18
lib/test/Dead_loop.snow
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
function: main
|
||||||
|
parameter:
|
||||||
|
return_type: int
|
||||||
|
body:
|
||||||
|
loop:
|
||||||
|
initializer:
|
||||||
|
declare i:int = 0
|
||||||
|
condition:
|
||||||
|
1 == 1
|
||||||
|
update:
|
||||||
|
i = i + 1
|
||||||
|
body:
|
||||||
|
|
||||||
|
end body
|
||||||
|
end loop
|
||||||
|
return 0
|
||||||
|
end body
|
||||||
|
end function
|
||||||
22
lib/test/opcode.snow
Normal file
22
lib/test/opcode.snow
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
module: Math
|
||||||
|
function: factorial
|
||||||
|
parameter:
|
||||||
|
declare n:int
|
||||||
|
return_type: int
|
||||||
|
body:
|
||||||
|
declare num1:int = 1
|
||||||
|
loop:
|
||||||
|
initializer:
|
||||||
|
declare counter:int = 1
|
||||||
|
condition:
|
||||||
|
counter <= n
|
||||||
|
update:
|
||||||
|
counter = counter + 1
|
||||||
|
body:
|
||||||
|
num1 = num1 * counter
|
||||||
|
end body
|
||||||
|
end loop
|
||||||
|
return num1
|
||||||
|
end body
|
||||||
|
end function
|
||||||
|
end module
|
||||||
13
playground/main.snow
Normal file
13
playground/main.snow
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module: Main
|
||||||
|
import:Math
|
||||||
|
function: main
|
||||||
|
parameter:
|
||||||
|
return_type: int
|
||||||
|
body:
|
||||||
|
Math.factorial(6L,1L)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end body
|
||||||
|
end function
|
||||||
|
|
||||||
|
end module
|
||||||
11
playground/test.snow
Normal file
11
playground/test.snow
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module: Math
|
||||||
|
function: factorial
|
||||||
|
parameter:
|
||||||
|
declare n1: long
|
||||||
|
declare n2: long
|
||||||
|
return_type: long
|
||||||
|
body:
|
||||||
|
return n1+n2
|
||||||
|
end body
|
||||||
|
end function
|
||||||
|
end module
|
||||||
72
pom.xml
Normal file
72
pom.xml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.jcnc.snow</groupId>
|
||||||
|
<artifactId>Snow</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>23</maven.compiler.source>
|
||||||
|
<maven.compiler.target>23</maven.compiler.target>
|
||||||
|
<native.maven.plugin.version>0.10.5</native.maven.plugin.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>native</id>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.graalvm.buildtools</groupId>
|
||||||
|
<artifactId>native-maven-plugin</artifactId>
|
||||||
|
<version>${native.maven.plugin.version}</version>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>build-native</id>
|
||||||
|
<goals>
|
||||||
|
<goal>compile-no-fork</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>package</phase>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>test-native</id>
|
||||||
|
<goals>
|
||||||
|
<goal>test</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>test</phase>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.12.1</version>
|
||||||
|
<configuration>
|
||||||
|
<fork>true</fork>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>org.jcnc.snow.compiler.cli.SnowCompiler</mainClass>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
</project>
|
||||||
6
src/main/java/module-info.java
Normal file
6
src/main/java/module-info.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module org.jcnc.snow.compiler {
|
||||||
|
requires java.desktop;
|
||||||
|
requires java.logging;
|
||||||
|
exports org.jcnc.snow.compiler.ir.core;
|
||||||
|
exports org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.alloc;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线性扫描寄存器分配器
|
||||||
|
* <p>
|
||||||
|
* 本类为 IR(中间表示)中的虚拟寄存器分配物理槽号,通常用于后端生成目标代码时确定
|
||||||
|
* 各虚拟寄存器实际对应的物理寄存器或栈槽号。采用简单的线性扫描分配策略。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 分配过程如下:
|
||||||
|
* <ol>
|
||||||
|
* <li>优先为函数参数分配槽号,从 0 开始,按参数顺序递增。</li>
|
||||||
|
* <li>遍历函数体的每条指令,为尚未分配的目标寄存器及其操作数分配新的槽号。</li>
|
||||||
|
* <li>保证每个虚拟寄存器在函数作用域内分配唯一且连续的槽号。</li>
|
||||||
|
* </ol>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public final class RegisterAllocator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 虚拟寄存器到槽号的分配映射表。
|
||||||
|
* <p>
|
||||||
|
* 键:虚拟寄存器 {@link IRVirtualRegister};
|
||||||
|
* 值:对应分配的槽编号 {@link Integer}。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private final Map<IRVirtualRegister, Integer> map = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为指定 IR 函数分配所有虚拟寄存器的槽号。
|
||||||
|
* <p>
|
||||||
|
* 分配顺序说明:
|
||||||
|
* <ol>
|
||||||
|
* <li>首先为所有参数分配槽号。</li>
|
||||||
|
* <li>然后线性遍历函数体,为每个指令涉及的虚拟寄存器(目标或操作数)分配槽号,
|
||||||
|
* 遇到未分配的寄存器立即分配下一个可用槽号。</li>
|
||||||
|
* </ol>
|
||||||
|
* 返回的映射不可变,防止外部修改。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param fn 需要进行寄存器分配的 IR 函数对象
|
||||||
|
* @return 一个不可变映射,记录所有虚拟寄存器到槽号的分配关系
|
||||||
|
*/
|
||||||
|
public Map<IRVirtualRegister, Integer> allocate(IRFunction fn) {
|
||||||
|
int next = 0; // 下一个可分配的槽编号
|
||||||
|
|
||||||
|
// 1. 优先为所有参数分配槽号(顺序与参数列表一致)
|
||||||
|
for (IRVirtualRegister param : fn.parameters()) {
|
||||||
|
map.put(param, next++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 线性扫描整个函数体,依次为指令涉及的虚拟寄存器分配槽号
|
||||||
|
for (IRInstruction inst : fn.body()) {
|
||||||
|
// 2.1 若指令有目标寄存器且尚未分配,则分配新槽号
|
||||||
|
if (inst.dest() != null && !map.containsKey(inst.dest())) {
|
||||||
|
map.put(inst.dest(), next++);
|
||||||
|
}
|
||||||
|
// 2.2 遍历所有操作数,若为虚拟寄存器且尚未分配,则分配新槽号
|
||||||
|
for (IRValue operand : inst.operands()) {
|
||||||
|
if (operand instanceof IRVirtualRegister vr && !map.containsKey(vr)) {
|
||||||
|
map.put(vr, next++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 返回不可变映射,防止外部代码误修改分配结果
|
||||||
|
return Map.copyOf(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.builder;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 虚拟机代码生成器(VMCodeGenerator)
|
||||||
|
* <p>
|
||||||
|
* 本类作为指令生成器调度中心,不负责任何具体 IR 指令到 VM 指令的转换实现,
|
||||||
|
* 仅负责根据指令类型分发到对应的 {@link InstructionGenerator} 子类完成实际生成。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 工作流程简述:
|
||||||
|
* <ol>
|
||||||
|
* <li>接收一组已注册的 IR 指令生成器,并建立类型到生成器的映射表。</li>
|
||||||
|
* <li>遍历 IR 函数体的每条指令,根据类型找到对应的生成器,调用其 generate 方法生成 VM 指令。</li>
|
||||||
|
* <li>生成流程以函数为单位(beginFunction/endFunction)。</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
public final class VMCodeGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指令类型到生成器的注册表(调度表)。
|
||||||
|
* <p>
|
||||||
|
* 键:IR 指令类型(Class对象),
|
||||||
|
* 值:对应的指令生成器实例。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private final Map<Class<? extends IRInstruction>, InstructionGenerator<? extends IRInstruction>> registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 虚拟寄存器到槽号的映射表,由 RegisterAllocator 负责生成。
|
||||||
|
*/
|
||||||
|
private final Map<IRVirtualRegister, Integer> slotMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 虚拟机程序构建器,用于输出 VM 指令。
|
||||||
|
*/
|
||||||
|
private final VMProgramBuilder out;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前处理的函数名,用于部分指令生成逻辑(如主函数判断等)。
|
||||||
|
*/
|
||||||
|
private String currentFn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法
|
||||||
|
*
|
||||||
|
* @param slotMap 虚拟寄存器到槽号的映射
|
||||||
|
* @param out 虚拟机程序构建器
|
||||||
|
* @param generators 各类 IR 指令生成器集合,需预先构建
|
||||||
|
*/
|
||||||
|
public VMCodeGenerator(Map<IRVirtualRegister, Integer> slotMap,
|
||||||
|
VMProgramBuilder out,
|
||||||
|
List<InstructionGenerator<? extends IRInstruction>> generators) {
|
||||||
|
this.slotMap = slotMap;
|
||||||
|
this.out = out;
|
||||||
|
// 按类型注册各 IR 指令生成器,建立不可变类型-生成器映射表
|
||||||
|
this.registry = generators.stream()
|
||||||
|
.collect(Collectors.toUnmodifiableMap(InstructionGenerator::supportedClass, g -> g));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为一个 IR 函数生成虚拟机指令
|
||||||
|
*
|
||||||
|
* @param fn 待生成的 IR 函数对象
|
||||||
|
* @throws IllegalStateException 若遇到不支持的 IR 指令类型
|
||||||
|
*/
|
||||||
|
public void generate(IRFunction fn) {
|
||||||
|
this.currentFn = fn.name();
|
||||||
|
out.beginFunction(currentFn); // 输出函数起始
|
||||||
|
for (IRInstruction ins : fn.body()) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
// 取得与当前 IR 指令类型匹配的生成器(泛型强转消除类型警告)
|
||||||
|
InstructionGenerator<IRInstruction> gen =
|
||||||
|
(InstructionGenerator<IRInstruction>) registry.get(ins.getClass());
|
||||||
|
if (gen == null) {
|
||||||
|
throw new IllegalStateException("Unsupported IR: " + ins);
|
||||||
|
}
|
||||||
|
// 通过多态分发到实际生成器
|
||||||
|
gen.generate(ins, out, slotMap, currentFn);
|
||||||
|
}
|
||||||
|
out.endFunction(); // 输出函数结束
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,195 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.builder;
|
||||||
|
|
||||||
|
import org.jcnc.snow.vm.engine.VMOpCode;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VMProgramBuilder:构建线性 VM 程序(即按顺序存放所有 VM 指令)。
|
||||||
|
* <p>
|
||||||
|
* 本类用于编译器后端,将所有生成的 VM 指令(包括分支和调用指令)统一存储、管理。
|
||||||
|
* 支持符号(如函数入口、标签地址)的延迟解析与回填(fix-up)机制,
|
||||||
|
* 可在目标尚未定义时提前生成分支或调用指令,定义后自动修正。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 常用于处理跨函数、跨标签的 CALL/JUMP 等复杂控制流,确保最终生成的 VM 指令地址一致正确。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public final class VMProgramBuilder {
|
||||||
|
/** 未解析目标的 CALL 指令信息(待修补) */
|
||||||
|
private record CallFix(int index, String target, int nArgs) {}
|
||||||
|
|
||||||
|
/** 未解析目标的分支指令(JUMP/IC_* 等待修补) */
|
||||||
|
private record BranchFix(int index, String label) {}
|
||||||
|
|
||||||
|
/** 占位符:用于表示尚未确定的符号地址 */
|
||||||
|
private static final String PLACEHOLDER = "-1";
|
||||||
|
|
||||||
|
/** 按顺序存放的 VM 指令文本 */
|
||||||
|
private final List<String> code = new ArrayList<>();
|
||||||
|
|
||||||
|
// 虚拟机槽位编号到数据类型前缀的映射(如 0 -> 'I', 1 -> 'D' 等)
|
||||||
|
private final Map<Integer, Character> slotType = new HashMap<>();
|
||||||
|
|
||||||
|
/** 符号(如函数名、标签名)到其首地址(即指令序号/偏移量)的映射表
|
||||||
|
* 主要用于跳转和调用,定位具体的代码位置 */
|
||||||
|
private final Map<String, Integer> addr = new HashMap<>();
|
||||||
|
|
||||||
|
/** 所有待回填(fix-up)的 CALL 调用指令记录
|
||||||
|
* 由于被调用目标地址在编译时可能尚未确定,需要先记录,最终统一回填 */
|
||||||
|
private final List<CallFix> callFixes = new ArrayList<>();
|
||||||
|
|
||||||
|
/** 所有待回填(fix-up)的分支跳转指令记录
|
||||||
|
* 与 CALL 类似,分支指令的目标地址也可能需要编译后期再补充 */
|
||||||
|
private final List<BranchFix> branchFixes = new ArrayList<>();
|
||||||
|
|
||||||
|
/** 程序计数器(Program Counter),表示下一个生成指令将插入的位置 */
|
||||||
|
private int pc = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置某个槽位对应的数据类型前缀
|
||||||
|
* @param slot 槽位编号
|
||||||
|
* @param prefix 类型前缀(如 'I' 表示 int,'D' 表示 double 等)
|
||||||
|
*/
|
||||||
|
public void setSlotType(int slot, char prefix) {
|
||||||
|
slotType.put(slot, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某个槽位对应的数据类型前缀
|
||||||
|
* 若未指定则返回默认类型 'I'(int)
|
||||||
|
* @param slot 槽位编号
|
||||||
|
* @return 类型前缀(如 'I', 'D' 等)
|
||||||
|
*/
|
||||||
|
public char getSlotType(int slot) {
|
||||||
|
return slotType.getOrDefault(slot, 'I');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记函数入口或标签,并尝试修补所有等候该符号的指令。
|
||||||
|
* @param name 符号名(函数名/标签名)
|
||||||
|
*/
|
||||||
|
public void beginFunction(String name) {
|
||||||
|
addr.put(name, pc); // 记录当前地址为入口
|
||||||
|
patchCallFixes(name); // 修补所有待该符号的 CALL
|
||||||
|
patchBranchFixes(name); // 修补所有待该符号的分支
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束函数(当前实现为空,方便 API 统一)。
|
||||||
|
*/
|
||||||
|
public void endFunction() { /* no-op */ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入一条 VM 指令或标签。
|
||||||
|
* <ul>
|
||||||
|
* <li>如果以冒号结尾,视为标签,仅登记其地址,不写入指令流,也不递增 pc。</li>
|
||||||
|
* <li>否则写入实际指令,并自增 pc。</li>
|
||||||
|
* </ul>
|
||||||
|
* @param line 一行 VM 指令文本或标签名(结尾有冒号)
|
||||||
|
*/
|
||||||
|
public void emit(String line) {
|
||||||
|
if (line.endsWith(":")) { // 是标签定义行
|
||||||
|
String label = line.substring(0, line.length() - 1);
|
||||||
|
addr.put(label, pc); // 记录标签地址
|
||||||
|
patchBranchFixes(label); // 修补所有以该标签为目标的分支指令
|
||||||
|
return; // 标签行不写入 code,不递增 pc
|
||||||
|
}
|
||||||
|
code.add(line); // 普通指令写入 code
|
||||||
|
pc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 CALL 指令。
|
||||||
|
* 支持延迟修补:若目标已知,直接写入地址;否则写入占位并登记 fix-up。
|
||||||
|
* @param target 目标函数名
|
||||||
|
* @param nArgs 参数个数
|
||||||
|
*/
|
||||||
|
public void emitCall(String target, int nArgs) {
|
||||||
|
Integer a = resolve(target);
|
||||||
|
if (a != null) {
|
||||||
|
emit(VMOpCode.CALL + " " + a + " " + nArgs);
|
||||||
|
} else {
|
||||||
|
emit(VMOpCode.CALL + " " + PLACEHOLDER + " " + nArgs);
|
||||||
|
callFixes.add(new CallFix(pc - 1, target, nArgs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成分支(JUMP 或 IC_*)指令。
|
||||||
|
* 支持延迟修补机制。
|
||||||
|
* @param opcode 指令名
|
||||||
|
* @param label 目标标签名
|
||||||
|
*/
|
||||||
|
public void emitBranch(String opcode, String label) {
|
||||||
|
Integer a = resolve(label);
|
||||||
|
if (a != null) {
|
||||||
|
emit(opcode + ' ' + a);
|
||||||
|
} else {
|
||||||
|
emit(opcode + ' ' + PLACEHOLDER);
|
||||||
|
branchFixes.add(new BranchFix(pc - 1, label));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建最终 VM 代码文本列表。
|
||||||
|
* <ul>
|
||||||
|
* <li>若存在未解析符号(CALL 或分支),则抛出异常。</li>
|
||||||
|
* <li>否则返回不可变指令流。</li>
|
||||||
|
* </ul>
|
||||||
|
* @return 完整 VM 指令流
|
||||||
|
* @throws IllegalStateException 若有未修补的符号引用
|
||||||
|
*/
|
||||||
|
public List<String> build() {
|
||||||
|
if (!callFixes.isEmpty() || !branchFixes.isEmpty()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Unresolved symbols — CALL: " + callFixes +
|
||||||
|
", BRANCH: " + branchFixes);
|
||||||
|
}
|
||||||
|
return List.copyOf(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析符号地址。若全限定名找不到则降级尝试简单名。
|
||||||
|
* @param sym 符号名
|
||||||
|
* @return 地址或 null(未定义)
|
||||||
|
*/
|
||||||
|
private Integer resolve(String sym) {
|
||||||
|
Integer a = addr.get(sym);
|
||||||
|
if (a == null && sym.contains(".")) {
|
||||||
|
a = addr.get(sym.substring(sym.lastIndexOf('.') + 1));
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修补所有以 name 为目标的 CALL 占位符。
|
||||||
|
* @param name 新定义的函数名
|
||||||
|
*/
|
||||||
|
private void patchCallFixes(String name) {
|
||||||
|
for (Iterator<CallFix> it = callFixes.iterator(); it.hasNext();) {
|
||||||
|
CallFix f = it.next();
|
||||||
|
// 目标函数名完全匹配或后缀匹配(兼容全限定名)
|
||||||
|
if (f.target.equals(name) || f.target.endsWith("." + name)) {
|
||||||
|
code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs);
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修补所有以 label 为目标的分支占位符。
|
||||||
|
* @param label 新定义的标签名
|
||||||
|
*/
|
||||||
|
private void patchBranchFixes(String label) {
|
||||||
|
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());
|
||||||
|
code.set(f.index, patched);
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用指令生成器接口
|
||||||
|
* <p>
|
||||||
|
* 本接口规定了所有 IR 指令生成器(翻译器)必须实现的方法,负责将特定类型的 IR 指令
|
||||||
|
* 翻译为虚拟机(VM)指令。每个具体的指令生成器都需要指定其支持的 IR 指令类型,并实现翻译生成的方法。
|
||||||
|
*
|
||||||
|
* @param <T> 指令生成器所支持的 IRInstruction 子类型
|
||||||
|
*/
|
||||||
|
public interface InstructionGenerator<T extends IRInstruction> {
|
||||||
|
/**
|
||||||
|
* 获取当前生成器支持的 IR 指令类型。
|
||||||
|
*
|
||||||
|
* @return 当前生成器支持的 IRInstruction 类对象
|
||||||
|
*/
|
||||||
|
Class<T> supportedClass();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将一条 IR 指令翻译为对应的 VM 指令序列。
|
||||||
|
*
|
||||||
|
* @param ins 当前待翻译的 IR 指令
|
||||||
|
* @param out 虚拟机程序构建器,用于输出 VM 指令
|
||||||
|
* @param slotMap 虚拟寄存器与实际槽号的映射关系
|
||||||
|
* @param currentFn 当前函数名称(用于作用域或调试等)
|
||||||
|
*/
|
||||||
|
void generate(T ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String currentFn);
|
||||||
|
}
|
||||||
56
src/main/java/org/jcnc/snow/compiler/backend/doc/README.md
Normal file
56
src/main/java/org/jcnc/snow/compiler/backend/doc/README.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Snow Compiler - 后端模块
|
||||||
|
|
||||||
|
> Snow 编译器的后端模块 —— 负责将中间表示(IR)翻译、优化并生成虚拟机指令序列(VM)
|
||||||
|
|
||||||
|
## 项目简介
|
||||||
|
|
||||||
|
**后端模块(Backend)** 是 [Snow 编译器]() 的核心组成部分之一,承接中间表示(IR)与运行时虚拟机(VM)之间的桥梁。
|
||||||
|
它主要完成以下工作:
|
||||||
|
|
||||||
|
1. **寄存器分配**:将 IR 中的虚拟寄存器映射为物理槽号或栈槽;
|
||||||
|
2. **指令生成与调度**:为每条 IR 指令分配对应的 VM 指令生成器,并负责调用它们输出指令文本;
|
||||||
|
3. **程序构建**:管理函数入口、标签定义、延迟修补(fix-up)CALL/JUMP 等控制流指令地址;
|
||||||
|
4. **操作码与常量映射**:提供 IR 操作码到 VM 操作码的映射工具,以及根据常量类型选择 PUSH/STORE 指令。
|
||||||
|
|
||||||
|
后端模块设计注重可扩展性:各类 IR → VM 的转换逻辑被拆分到不同的 `*Generator` 类中,新增指令只需注册新的生成器;寄存器分配和程序构建也各司其职,职责清晰,便于维护和测试。
|
||||||
|
|
||||||
|
## 核心功能
|
||||||
|
|
||||||
|
* **线性扫描寄存器分配**:`RegisterAllocator` 按参数优先、指令顺序为 IR 虚拟寄存器分配连续槽号。
|
||||||
|
* **指令生成器体系**:`InstructionGenerator<T>` 接口 + 多个 `*Generator` 实现,支持二元运算、跳转、调用、标签、常量加载、一元运算、返回等指令。
|
||||||
|
* **生成器调度**:`VMCodeGenerator` 建立类型到生成器的映射,遍历 IR 函数体并分发调用,实现多态化指令生成。
|
||||||
|
* **程序构建与延迟修补**:`VMProgramBuilder` 维护指令流、符号地址表及待修补列表,实现对 `CALL`/`JUMP` 等指令的延迟地址填充。
|
||||||
|
* **操作码与常量映射**:`IROpCodeMapper` 提供 IR 操作码到 VM 指令名的静态映射;`OpHelper` 基于反射获取 VMOpCode 常量,并根据值类型(I/L/F…)选择合适的 PUSH/STORE 操作码。
|
||||||
|
|
||||||
|
## 模块结构
|
||||||
|
|
||||||
|
```
|
||||||
|
backend/
|
||||||
|
├── core/
|
||||||
|
│ └── InstructionGenerator.java // 通用指令生成器接口
|
||||||
|
├── generator/
|
||||||
|
│ ├── BinaryOpGenerator.java // 二元运算
|
||||||
|
│ ├── UnaryOpGenerator.java // 一元运算
|
||||||
|
│ ├── LoadConstGenerator.java // 常量加载
|
||||||
|
│ ├── CallGenerator.java // 函数调用
|
||||||
|
│ ├── ReturnGenerator.java // 函数返回
|
||||||
|
│ ├── CmpJumpGenerator.java // 条件跳转
|
||||||
|
│ ├── JumpGenerator.java // 无条件跳转
|
||||||
|
│ └── LabelGenerator.java // 标签定义
|
||||||
|
├── alloc/
|
||||||
|
│ └── RegisterAllocator.java // 线性扫描寄存器分配
|
||||||
|
├── builder/
|
||||||
|
│ ├── VMCodeGenerator.java // 指令生成调度
|
||||||
|
│ └── VMProgramBuilder.java // VM 程序构建与 fix-up
|
||||||
|
└── util/
|
||||||
|
├── IROpCodeMapper.java // IR → VM 操作码映射
|
||||||
|
└── OpHelper.java // opcode 反射 & 常量指令辅助
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发环境
|
||||||
|
|
||||||
|
* JDK 23 或更高版本
|
||||||
|
* Maven 构建管理
|
||||||
|
* 推荐 IDE:IntelliJ IDEA
|
||||||
|
|
||||||
|
---
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.generator;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.backend.util.OpHelper;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.BinaryOperationInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二元运算指令生成器
|
||||||
|
* 支持二元运算指令的自动类型提升。
|
||||||
|
* <p>类型提升优先级为:D > F > L > I > S > B</p>
|
||||||
|
*/
|
||||||
|
public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationInstruction> {
|
||||||
|
|
||||||
|
/* ---------- 类型优先级工具 ---------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回类型前缀的优先级数值。数值越大,类型“越宽”。
|
||||||
|
* D: 6, F: 5, L: 4, I: 3, S: 2, B: 1
|
||||||
|
*/
|
||||||
|
private static int rank(char p) {
|
||||||
|
return switch (p) {
|
||||||
|
case 'D' -> 6;
|
||||||
|
case 'F' -> 5;
|
||||||
|
case 'L' -> 4;
|
||||||
|
case 'I' -> 3;
|
||||||
|
case 'S' -> 2;
|
||||||
|
case 'B' -> 1;
|
||||||
|
default -> 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回a和b中优先级更高的类型前缀(即类型提升结果)。
|
||||||
|
*/
|
||||||
|
private static char promote(char a, char b) {
|
||||||
|
return rank(a) >= rank(b) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型前缀转字符串,方便拼接。
|
||||||
|
*/
|
||||||
|
private static String str(char p) {
|
||||||
|
return String.valueOf(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 类型转换指令工具 ---------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据源类型和目标类型前缀,返回相应的类型转换指令助记符。
|
||||||
|
*
|
||||||
|
* @param from 源类型前缀
|
||||||
|
* @param to 目标类型前缀
|
||||||
|
* @return 转换指令字符串,如 "I2L" 或 "F2D",若无需转换则返回null
|
||||||
|
*/
|
||||||
|
private static String convert(char from, char to) {
|
||||||
|
if (from == to) return null; // 类型一致,无需转换
|
||||||
|
return switch ("" + from + to) {
|
||||||
|
case "IL" -> "I2L";
|
||||||
|
case "ID" -> "I2D";
|
||||||
|
case "IF" -> "I2F";
|
||||||
|
case "LI" -> "L2I";
|
||||||
|
case "LD" -> "L2D";
|
||||||
|
case "LF" -> "L2F";
|
||||||
|
case "FI" -> "F2I";
|
||||||
|
case "FL" -> "F2L";
|
||||||
|
case "FD" -> "F2D";
|
||||||
|
case "DI" -> "D2I";
|
||||||
|
case "DL" -> "D2L";
|
||||||
|
case "DF" -> "D2F";
|
||||||
|
case "SI" -> "S2I";
|
||||||
|
case "BI" -> "B2I";
|
||||||
|
default -> null; // 其它组合暂未用到
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回本生成器支持的指令类型,即 BinaryOperationInstruction。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<BinaryOperationInstruction> supportedClass() {
|
||||||
|
return BinaryOperationInstruction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成二元运算的虚拟机指令,实现自动类型提升和必要的类型转换。
|
||||||
|
*
|
||||||
|
* @param ins 当前二元运算IR指令
|
||||||
|
* @param out 虚拟机程序构建器
|
||||||
|
* @param slotMap IR虚拟寄存器到实际槽位编号的映射
|
||||||
|
* @param currentFn 当前函数名
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generate(BinaryOperationInstruction ins,
|
||||||
|
VMProgramBuilder out,
|
||||||
|
Map<IRVirtualRegister, Integer> slotMap,
|
||||||
|
String currentFn) {
|
||||||
|
|
||||||
|
/* ------- 1. 获取左右操作数的槽位编号和类型 ------- */
|
||||||
|
int lSlot = slotMap.get((IRVirtualRegister) ins.operands().get(0)); // 左操作数槽位
|
||||||
|
int rSlot = slotMap.get((IRVirtualRegister) ins.operands().get(1)); // 右操作数槽位
|
||||||
|
int dSlot = slotMap.get(ins.dest()); // 目标槽位
|
||||||
|
char lType = out.getSlotType(lSlot); // 左操作数类型前缀
|
||||||
|
char rType = out.getSlotType(rSlot); // 右操作数类型前缀
|
||||||
|
|
||||||
|
// 类型提升,确定本次二元运算的目标类型(优先级较高的那一个)
|
||||||
|
char tType = promote(lType, rType);
|
||||||
|
String tPref = str(tType); // 用于拼接指令字符串
|
||||||
|
|
||||||
|
/* ------- 2. 加载左操作数,并自动进行类型转换(如有必要) ------- */
|
||||||
|
out.emit(OpHelper.opcode(str(lType) + "_LOAD") + " " + lSlot); // LOAD指令
|
||||||
|
String cvt = convert(lType, tType); // 如需类型提升
|
||||||
|
if (cvt != null) out.emit(OpHelper.opcode(cvt)); // 插入类型转换指令
|
||||||
|
|
||||||
|
/* ------- 3. 加载右操作数,并自动进行类型转换(如有必要) ------- */
|
||||||
|
out.emit(OpHelper.opcode(str(rType) + "_LOAD") + " " + rSlot); // LOAD指令
|
||||||
|
cvt = convert(rType, tType); // 如需类型提升
|
||||||
|
if (cvt != null) out.emit(OpHelper.opcode(cvt)); // 插入类型转换指令
|
||||||
|
|
||||||
|
/* ------- 4. 生成具体的二元运算指令 ------- */
|
||||||
|
// 获取IR指令中的操作名(如ADD、SUB、MUL等,去掉结尾的"_"后缀)
|
||||||
|
String opName = ins.op().name().split("_")[0];
|
||||||
|
// 例如生成 "I_ADD", "D_MUL" 等虚拟机指令
|
||||||
|
out.emit(OpHelper.opcode(tPref + "_" + opName));
|
||||||
|
|
||||||
|
/* ------- 5. 结果存入目标槽位,并更新槽位类型 ------- */
|
||||||
|
out.emit(OpHelper.opcode(tPref + "_STORE") + " " + dSlot);
|
||||||
|
out.setSlotType(dSlot, tType); // 记录运算结果的类型前缀,便于后续指令正确处理
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.generator;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.backend.util.OpHelper;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 函数调用指令生成器。
|
||||||
|
* <p>
|
||||||
|
* 该类实现了函数调用(CallInstruction)的指令翻译逻辑,
|
||||||
|
* 负责将 IR 层的函数调用转换为虚拟机可执行的低级指令。
|
||||||
|
*/
|
||||||
|
public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回本指令生成器支持的 IR 指令类型(CallInstruction)。
|
||||||
|
*
|
||||||
|
* @return 指令类型的 Class 对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<CallInstruction> supportedClass() {
|
||||||
|
return CallInstruction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成函数调用相关的虚拟机指令。
|
||||||
|
* <p>
|
||||||
|
* 步骤如下:
|
||||||
|
* <ol>
|
||||||
|
* <li>预测返回值类型(采用首个实参槽的类型作为近似)</li>
|
||||||
|
* <li>为每个参数根据实际类型发出加载指令</li>
|
||||||
|
* <li>生成 CALL 调用指令</li>
|
||||||
|
* <li>将返回值存储到目标槽,并记录类型信息</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param ins 待翻译的 CallInstruction 指令对象
|
||||||
|
* @param out 指令输出与类型槽管理器
|
||||||
|
* @param slotMap IR 寄存器到槽号的映射
|
||||||
|
* @param currentFn 当前函数名(未用,可用于递归/闭包等复杂场景)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generate(CallInstruction ins,
|
||||||
|
VMProgramBuilder out,
|
||||||
|
Map<IRVirtualRegister, Integer> slotMap,
|
||||||
|
String currentFn) {
|
||||||
|
|
||||||
|
// —— 1. 预测返回值类型(用首个实参槽类型作为近似推断) ——
|
||||||
|
char retType = 'I'; // 默认整型
|
||||||
|
if (!ins.getArguments().isEmpty()) {
|
||||||
|
int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst());
|
||||||
|
retType = out.getSlotType(firstSlot); // 获取槽位实际类型
|
||||||
|
if (retType == '\0') retType = 'I'; // 默认整型
|
||||||
|
}
|
||||||
|
|
||||||
|
// —— 2. 按真实类型加载每个参数到虚拟机操作栈 ——
|
||||||
|
for (var arg : ins.getArguments()) {
|
||||||
|
int slotId = slotMap.get((IRVirtualRegister) arg); // 获取参数槽号
|
||||||
|
char t = out.getSlotType(slotId); // 获取参数类型
|
||||||
|
if (t == '\0') t = 'I'; // 类型未知时默认整型
|
||||||
|
// 生成类型相关的加载指令,如 I_LOAD、F_LOAD 等
|
||||||
|
out.emit(OpHelper.opcode(String.valueOf(t) + "_LOAD") + " " + slotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// —— 3. 生成 CALL 调用指令 ——
|
||||||
|
out.emitCall(ins.getFunctionName(), ins.getArguments().size());
|
||||||
|
|
||||||
|
// —— 4. 将返回值存入目标槽,并记录槽的类型 ——
|
||||||
|
int destSlot = slotMap.get(ins.getDest()); // 目标寄存器槽
|
||||||
|
out.emit(OpHelper.opcode(String.valueOf(retType) + "_STORE") + " " + destSlot);
|
||||||
|
out.setSlotType(destSlot, retType); // 标记返回值类型
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.generator;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.util.IROpCodeMapper;
|
||||||
|
import org.jcnc.snow.compiler.backend.util.OpHelper;
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.IRCompareJumpInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件比较跳转指令生成器
|
||||||
|
* <p>
|
||||||
|
* 该类实现了 {@link InstructionGenerator} 接口,用于将 IR 中的条件比较跳转指令
|
||||||
|
* 转换为虚拟机可执行的指令序列。主要流程是先将比较操作数加载到虚拟机栈中,生成比较操作码,
|
||||||
|
* 并发出跳转到目标标签的指令。
|
||||||
|
*/
|
||||||
|
public class CmpJumpGenerator implements InstructionGenerator<IRCompareJumpInstruction> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该生成器所支持的指令类型。
|
||||||
|
*
|
||||||
|
* @return {@link IRCompareJumpInstruction} 的类对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<IRCompareJumpInstruction> supportedClass() {
|
||||||
|
return IRCompareJumpInstruction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成条件比较跳转相关的虚拟机指令。
|
||||||
|
*
|
||||||
|
* @param ins 需要生成的条件比较跳转中间指令(IR)
|
||||||
|
* @param out 虚拟机程序构建器,用于输出生成的指令
|
||||||
|
* @param slotMap 虚拟寄存器到实际槽(slot)编号的映射表
|
||||||
|
* @param currentFn 当前处理的函数名(可用于调试或作用域标识)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generate(IRCompareJumpInstruction ins,
|
||||||
|
VMProgramBuilder out,
|
||||||
|
Map<IRVirtualRegister, Integer> slotMap,
|
||||||
|
String currentFn) {
|
||||||
|
// 获取左操作数所在的寄存器槽编号
|
||||||
|
int leftSlot = slotMap.get(ins.left());
|
||||||
|
// 获取右操作数所在的寄存器槽编号
|
||||||
|
int rightSlot = slotMap.get(ins.right());
|
||||||
|
// 加载左操作数到虚拟机栈
|
||||||
|
out.emit(OpHelper.opcode("I_LOAD") + " " + leftSlot);
|
||||||
|
// 加载右操作数到虚拟机栈
|
||||||
|
out.emit(OpHelper.opcode("I_LOAD") + " " + rightSlot);
|
||||||
|
// 获取与当前比较操作对应的虚拟机操作码
|
||||||
|
String cmpOp = IROpCodeMapper.toVMOp(ins.op());
|
||||||
|
// 生成分支跳转指令,如果比较成立则跳转到目标标签
|
||||||
|
out.emitBranch(OpHelper.opcode(cmpOp), ins.label());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.generator;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.util.OpHelper;
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.IRJumpInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无条件跳转指令生成器
|
||||||
|
* <p>
|
||||||
|
* 该类实现了 {@link InstructionGenerator} 接口,用于将 IR 中的无条件跳转指令
|
||||||
|
* (即跳转到指定标签)翻译为虚拟机指令。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class JumpGenerator implements InstructionGenerator<IRJumpInstruction> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回本生成器所支持的 IR 指令类型。
|
||||||
|
*
|
||||||
|
* @return {@link IRJumpInstruction} 的类对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<IRJumpInstruction> supportedClass() {
|
||||||
|
return IRJumpInstruction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成对应的虚拟机跳转指令。
|
||||||
|
*
|
||||||
|
* @param ins 当前 IR 跳转指令
|
||||||
|
* @param out 虚拟机程序构建器,用于输出指令
|
||||||
|
* @param slotMap 虚拟寄存器与槽号的映射表(本跳转指令未用到)
|
||||||
|
* @param currentFn 当前函数名称(便于调试或作用域标识)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generate(IRJumpInstruction ins,
|
||||||
|
VMProgramBuilder out,
|
||||||
|
Map<IRVirtualRegister, Integer> slotMap,
|
||||||
|
String currentFn) {
|
||||||
|
// 生成无条件跳转到指定标签的虚拟机指令
|
||||||
|
out.emitBranch(OpHelper.opcode("JUMP"), ins.label());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.generator;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.IRLabelInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签指令生成器
|
||||||
|
* <p>
|
||||||
|
* 本类实现了 {@link InstructionGenerator} 接口,用于将 IR 层的标签指令翻译为
|
||||||
|
* 虚拟机(VM)中的标签标记。标签一般用于跳转或分支目的地的定位。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class LabelGenerator implements InstructionGenerator<IRLabelInstruction> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回本生成器所支持的 IR 指令类型。
|
||||||
|
*
|
||||||
|
* @return {@link IRLabelInstruction} 的类对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<IRLabelInstruction> supportedClass() {
|
||||||
|
return IRLabelInstruction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成对应的虚拟机标签指令。
|
||||||
|
*
|
||||||
|
* @param ins 当前 IR 标签指令
|
||||||
|
* @param out 虚拟机程序构建器,用于输出指令
|
||||||
|
* @param slotMap 虚拟寄存器与槽号的映射表(标签指令未使用此参数)
|
||||||
|
* @param currentFn 当前函数名称(用于调试或作用域标识,可选)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generate(IRLabelInstruction ins,
|
||||||
|
VMProgramBuilder out,
|
||||||
|
Map<IRVirtualRegister, Integer> slotMap,
|
||||||
|
String currentFn) {
|
||||||
|
// 生成标签(如 "label_name:"),用于虚拟机指令流中的位置标记
|
||||||
|
out.emit(ins.name() + ":");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.generator;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.backend.util.OpHelper;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 常量加载指令生成器
|
||||||
|
* 该类用于生成将常量加载到虚拟机寄存器的指令,包括 PUSH 常量值和 STORE 到指定槽位,
|
||||||
|
* 并为每个槽位设置正确的类型前缀(如 'I', 'L', 'F' 等)。
|
||||||
|
*/
|
||||||
|
public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruction> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回本生成器支持的指令类型,即 LoadConstInstruction。
|
||||||
|
*
|
||||||
|
* @return 支持的指令类型的 Class 对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<LoadConstInstruction> supportedClass() {
|
||||||
|
return LoadConstInstruction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成一条常量加载指令的目标虚拟机代码。
|
||||||
|
*
|
||||||
|
* @param ins 当前要生成的 LoadConstInstruction 指令
|
||||||
|
* @param out VMProgramBuilder,用于输出生成的虚拟机指令
|
||||||
|
* @param slotMap IR 虚拟寄存器到实际槽位编号的映射表
|
||||||
|
* @param currentFn 当前函数名(如有需要可使用)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generate(LoadConstInstruction ins,
|
||||||
|
VMProgramBuilder out,
|
||||||
|
Map<IRVirtualRegister, Integer> slotMap,
|
||||||
|
String currentFn) {
|
||||||
|
// 1. 获取常量值(第一个操作数必为常量)
|
||||||
|
IRConstant constant = (IRConstant) ins.operands().getFirst();
|
||||||
|
Object value = constant.value();
|
||||||
|
|
||||||
|
// 2. 生成 PUSH 指令,将常量值推入操作数栈
|
||||||
|
// 通过 OpHelper 辅助方法获取合适的数据类型前缀
|
||||||
|
String pushOp = OpHelper.pushOpcodeFor(value);
|
||||||
|
out.emit(pushOp + " " + value);
|
||||||
|
|
||||||
|
// 3. 生成 STORE 指令,将栈顶的值存入对应槽位(寄存器)
|
||||||
|
// 同样通过 OpHelper 获取对应类型的 STORE 指令
|
||||||
|
String storeOp = OpHelper.storeOpcodeFor(value);
|
||||||
|
// 获取目标虚拟寄存器对应的槽位编号
|
||||||
|
int slot = slotMap.get(ins.dest());
|
||||||
|
out.emit(storeOp + " " + slot);
|
||||||
|
|
||||||
|
// 4. 根据常量的 Java 类型,为槽位设置正确的前缀字符
|
||||||
|
// 这样在后续类型检查/运行时可用,常见前缀如 'I', 'L', 'F', 'D', 'S', 'B'
|
||||||
|
char prefix = switch (value) {
|
||||||
|
case Integer _ -> 'I'; // 整型
|
||||||
|
case Long _ -> 'L'; // 长整型
|
||||||
|
case Short _ -> 'S'; // 短整型
|
||||||
|
case Byte _ -> 'B'; // 字节型
|
||||||
|
case Double _ -> 'D'; // 双精度浮点型
|
||||||
|
case Float _ -> 'F'; // 单精度浮点型
|
||||||
|
case null, default ->
|
||||||
|
throw new IllegalStateException("Unknown const type: " + (value != null ? value.getClass() : null));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 写入槽位类型映射表
|
||||||
|
out.setSlotType(slot, prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.generator;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.backend.util.OpHelper;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.ReturnInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回指令生成器
|
||||||
|
* <p>
|
||||||
|
* 本类实现了 {@link InstructionGenerator} 接口,用于将 IR 中的函数返回指令翻译为
|
||||||
|
* 虚拟机可执行的返回(RET/HALT)相关指令。支持有返回值和无返回值两种情况。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class ReturnGenerator implements InstructionGenerator<ReturnInstruction> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该生成器支持的 IR 指令类型。
|
||||||
|
*
|
||||||
|
* @return {@link ReturnInstruction} 的类对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<ReturnInstruction> supportedClass() {
|
||||||
|
return ReturnInstruction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成对应的虚拟机返回指令。
|
||||||
|
*
|
||||||
|
* @param ins 当前 IR 返回指令
|
||||||
|
* @param out 虚拟机程序构建器,用于输出 VM 指令
|
||||||
|
* @param slotMap 虚拟寄存器到槽号的映射表
|
||||||
|
* @param currentFn 当前函数名称(用于判断是否为主函数 main)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generate(ReturnInstruction ins,
|
||||||
|
VMProgramBuilder out,
|
||||||
|
Map<IRVirtualRegister, Integer> slotMap,
|
||||||
|
String currentFn) {
|
||||||
|
// 若存在返回值,先将返回值加载到虚拟机栈顶
|
||||||
|
if (ins.value() != null) {
|
||||||
|
int slotId = slotMap.get(ins.value());
|
||||||
|
// 根据之前记录的槽类型前缀(I/L/S/B/D/F)选 LOAD
|
||||||
|
char prefix = out.getSlotType(slotId);
|
||||||
|
String loadOp = prefix + "_LOAD";
|
||||||
|
out.emit(OpHelper.opcode(loadOp) + " " + slotId);
|
||||||
|
}
|
||||||
|
// 主函数返回使用 HALT,普通函数返回使用 RET
|
||||||
|
String code = "main".equals(currentFn) ? "HALT" : "RET";
|
||||||
|
out.emit(OpHelper.opcode(code));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.generator;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.util.IROpCodeMapper;
|
||||||
|
import org.jcnc.snow.compiler.backend.util.OpHelper;
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一元运算指令生成器
|
||||||
|
* <p>
|
||||||
|
* 本类实现了 {@link InstructionGenerator} 接口,用于将 IR 中的一元运算指令
|
||||||
|
* (如取负等)翻译为虚拟机(VM)可执行的指令序列。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class UnaryOpGenerator implements InstructionGenerator<UnaryOperationInstruction> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回本生成器所支持的 IR 指令类型。
|
||||||
|
*
|
||||||
|
* @return {@link UnaryOperationInstruction} 的类对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<UnaryOperationInstruction> supportedClass() {
|
||||||
|
return UnaryOperationInstruction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成一元运算相关的虚拟机指令。
|
||||||
|
*
|
||||||
|
* @param ins 当前 IR 一元运算指令
|
||||||
|
* @param out 虚拟机程序构建器,用于输出指令
|
||||||
|
* @param slotMap 虚拟寄存器与槽号的映射表
|
||||||
|
* @param currentFn 当前函数名称(用于作用域或调试,可选)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generate(UnaryOperationInstruction ins,
|
||||||
|
VMProgramBuilder out,
|
||||||
|
Map<IRVirtualRegister, Integer> slotMap,
|
||||||
|
String currentFn) {
|
||||||
|
// 获取操作数所在槽号
|
||||||
|
int slotId = slotMap.get((IRVirtualRegister) ins.operands().getFirst());
|
||||||
|
// 加载操作数到虚拟机栈顶
|
||||||
|
out.emit(OpHelper.opcode("I_LOAD") + " " + slotId);
|
||||||
|
// 生成对应的一元运算操作码(如取负等)
|
||||||
|
out.emit(OpHelper.opcode(IROpCodeMapper.toVMOp(ins.op())));
|
||||||
|
// 将结果存储到目标寄存器槽
|
||||||
|
out.emit(OpHelper.opcode("I_STORE") + " " + slotMap.get(ins.dest()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.util;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IR 操作码与虚拟机指令名映射工具类。
|
||||||
|
* <p>
|
||||||
|
* 本类用于将 IR 层的操作码({@link IROpCode})映射为目标虚拟机的指令名({@code String})。
|
||||||
|
* 该工具类在编译器后端阶段提供指令名转换功能,不处理参数或操作数,仅负责纯映射。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 若需扩展新的操作码或 VM 指令,应在本类中统一维护映射关系。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public final class IROpCodeMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IR 操作码到 VM 指令名的静态映射表。
|
||||||
|
* <ul>
|
||||||
|
* <li>键:IR 操作码({@link IROpCode} 枚举项)</li>
|
||||||
|
* <li>值:对应虚拟机指令名(字符串)</li>
|
||||||
|
* </ul>
|
||||||
|
* 使用 {@link EnumMap},查找和存储高效。
|
||||||
|
*/
|
||||||
|
private static final Map<IROpCode, String> opcodeMap = new EnumMap<>(IROpCode.class);
|
||||||
|
|
||||||
|
// 静态代码块,初始化所有 IR 操作码到 VM 指令名的映射关系
|
||||||
|
static {
|
||||||
|
opcodeMap.put(IROpCode.CONV_I32_TO_F32, "I2F");
|
||||||
|
opcodeMap.put(IROpCode.CONV_I32_TO_D64, "I2D");
|
||||||
|
|
||||||
|
opcodeMap.put(IROpCode.CONV_F32_TO_I32, "F2I");
|
||||||
|
opcodeMap.put(IROpCode.CONV_D64_TO_I32, "D2I");
|
||||||
|
|
||||||
|
opcodeMap.put(IROpCode.CONV_F32_TO_D64, "F2D");
|
||||||
|
opcodeMap.put(IROpCode.CONV_D64_TO_F32, "D2F");
|
||||||
|
|
||||||
|
// 整形8位算术运算映射
|
||||||
|
opcodeMap.put(IROpCode.ADD_B8, "B_ADD");
|
||||||
|
opcodeMap.put(IROpCode.SUB_B8, "B_SUB");
|
||||||
|
opcodeMap.put(IROpCode.MUL_B8, "B_MUL");
|
||||||
|
opcodeMap.put(IROpCode.DIV_B8, "B_DIV");
|
||||||
|
opcodeMap.put(IROpCode.NEG_B8, "B_NEG");
|
||||||
|
|
||||||
|
// 整形16位算术运算映射
|
||||||
|
opcodeMap.put(IROpCode.ADD_S16, "S_ADD");
|
||||||
|
opcodeMap.put(IROpCode.SUB_S16, "S_SUB");
|
||||||
|
opcodeMap.put(IROpCode.MUL_S16, "S_MUL");
|
||||||
|
opcodeMap.put(IROpCode.DIV_S16, "S_DIV");
|
||||||
|
opcodeMap.put(IROpCode.NEG_S16, "S_NEG");
|
||||||
|
|
||||||
|
// 整形32位算术运算映射
|
||||||
|
opcodeMap.put(IROpCode.ADD_I32, "I_ADD");
|
||||||
|
opcodeMap.put(IROpCode.SUB_I32, "I_SUB");
|
||||||
|
opcodeMap.put(IROpCode.MUL_I32, "I_MUL");
|
||||||
|
opcodeMap.put(IROpCode.DIV_I32, "I_DIV");
|
||||||
|
opcodeMap.put(IROpCode.NEG_I32, "I_NEG");
|
||||||
|
|
||||||
|
// 整形64位算术运算映射
|
||||||
|
opcodeMap.put(IROpCode.ADD_L64, "L_ADD");
|
||||||
|
opcodeMap.put(IROpCode.SUB_L64, "L_SUB");
|
||||||
|
opcodeMap.put(IROpCode.MUL_L64, "L_MUL");
|
||||||
|
opcodeMap.put(IROpCode.DIV_L64, "L_DIV");
|
||||||
|
opcodeMap.put(IROpCode.NEG_L64, "L_NEG");
|
||||||
|
|
||||||
|
// --- 32-bit floating point ---
|
||||||
|
|
||||||
|
opcodeMap.put(IROpCode.ADD_F32, "F_ADD");
|
||||||
|
opcodeMap.put(IROpCode.SUB_F32, "F_SUB");
|
||||||
|
opcodeMap.put(IROpCode.MUL_F32, "F_MUL");
|
||||||
|
opcodeMap.put(IROpCode.DIV_F32, "F_DIV");
|
||||||
|
opcodeMap.put(IROpCode.NEG_F32, "F_NEG");
|
||||||
|
|
||||||
|
// --- 64-bit floating point ---
|
||||||
|
opcodeMap.put(IROpCode.ADD_D64, "D_ADD");
|
||||||
|
opcodeMap.put(IROpCode.SUB_D64, "D_SUB");
|
||||||
|
opcodeMap.put(IROpCode.MUL_D64, "D_MUL");
|
||||||
|
opcodeMap.put(IROpCode.DIV_D64, "D_DIV");
|
||||||
|
opcodeMap.put(IROpCode.NEG_D64, "D_NEG");
|
||||||
|
|
||||||
|
// 比较运算映射
|
||||||
|
opcodeMap.put(IROpCode.CMP_EQ, "IC_EQ"); // 相等
|
||||||
|
opcodeMap.put(IROpCode.CMP_NE, "IC_NE"); // 不等
|
||||||
|
opcodeMap.put(IROpCode.CMP_LT, "IC_L"); // 小于
|
||||||
|
opcodeMap.put(IROpCode.CMP_GT, "IC_G"); // 大于
|
||||||
|
opcodeMap.put(IROpCode.CMP_LE, "IC_LE"); // 小于等于
|
||||||
|
opcodeMap.put(IROpCode.CMP_GE, "IC_GE"); // 大于等于
|
||||||
|
|
||||||
|
// 加载与存储
|
||||||
|
opcodeMap.put(IROpCode.LOAD, "I_LOAD"); // 加载
|
||||||
|
opcodeMap.put(IROpCode.STORE, "I_STORE"); // 存储
|
||||||
|
opcodeMap.put(IROpCode.CONST, "I_PUSH"); // 常量入栈
|
||||||
|
|
||||||
|
// 跳转与标签
|
||||||
|
opcodeMap.put(IROpCode.JUMP, "JMP"); // 无条件跳转
|
||||||
|
opcodeMap.put(IROpCode.LABEL, "LABEL"); // 标签
|
||||||
|
|
||||||
|
// 函数相关
|
||||||
|
opcodeMap.put(IROpCode.CALL, "CALL"); // 调用
|
||||||
|
opcodeMap.put(IROpCode.RET, "RET"); // 返回
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具类私有构造,禁止实例化。
|
||||||
|
*/
|
||||||
|
private IROpCodeMapper() {
|
||||||
|
// 禁止实例化
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定 IR 操作码获取对应的虚拟机指令名。
|
||||||
|
*
|
||||||
|
* @param irOp 需转换的 IR 操作码({@link IROpCode} 枚举值)
|
||||||
|
* @return 对应的虚拟机指令名(字符串)
|
||||||
|
* @throws IllegalArgumentException 若 {@code irOp} 未定义映射关系,抛出异常
|
||||||
|
*/
|
||||||
|
public static String toVMOp(IROpCode irOp) {
|
||||||
|
String vmCode = opcodeMap.get(irOp);
|
||||||
|
if (vmCode == null) {
|
||||||
|
throw new IllegalArgumentException("未映射的 IR 操作码: " + irOp);
|
||||||
|
}
|
||||||
|
return vmCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
168
src/main/java/org/jcnc/snow/compiler/backend/util/OpHelper.java
Normal file
168
src/main/java/org/jcnc/snow/compiler/backend/util/OpHelper.java
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package org.jcnc.snow.compiler.backend.util;
|
||||||
|
|
||||||
|
import org.jcnc.snow.vm.engine.VMOpCode;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opcode 帮助类
|
||||||
|
* <p>
|
||||||
|
* 通过 <strong>静态不可变 Map</strong> 保存指令名到 opcode(以字符串表示)的映射表。
|
||||||
|
* <p>
|
||||||
|
* 本文件由脚本根据 {@link VMOpCode} 中实际存在的 <code>public static</code> 字段自动生成,
|
||||||
|
* 保证与指令集保持同步,避免手写出错。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public final class OpHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指令名 → opcode 字符串 的静态映射表
|
||||||
|
*/
|
||||||
|
private static final Map<String, String> OPCODE_MAP;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("I_ADD", Integer.toString(VMOpCode.I_ADD));
|
||||||
|
map.put("I_SUB", Integer.toString(VMOpCode.I_SUB));
|
||||||
|
map.put("I_MUL", Integer.toString(VMOpCode.I_MUL));
|
||||||
|
map.put("I_DIV", Integer.toString(VMOpCode.I_DIV));
|
||||||
|
map.put("I_MOD", Integer.toString(VMOpCode.I_MOD));
|
||||||
|
map.put("I_INC", Integer.toString(VMOpCode.I_INC));
|
||||||
|
map.put("I_NEG", Integer.toString(VMOpCode.I_NEG));
|
||||||
|
map.put("L_ADD", Integer.toString(VMOpCode.L_ADD));
|
||||||
|
map.put("L_SUB", Integer.toString(VMOpCode.L_SUB));
|
||||||
|
map.put("L_MUL", Integer.toString(VMOpCode.L_MUL));
|
||||||
|
map.put("L_DIV", Integer.toString(VMOpCode.L_DIV));
|
||||||
|
map.put("L_MOD", Integer.toString(VMOpCode.L_MOD));
|
||||||
|
map.put("L_INC", Integer.toString(VMOpCode.L_INC));
|
||||||
|
map.put("L_NEG", Integer.toString(VMOpCode.L_NEG));
|
||||||
|
map.put("S_ADD", Integer.toString(VMOpCode.S_ADD));
|
||||||
|
map.put("S_SUB", Integer.toString(VMOpCode.S_SUB));
|
||||||
|
map.put("S_MUL", Integer.toString(VMOpCode.S_MUL));
|
||||||
|
map.put("S_DIV", Integer.toString(VMOpCode.S_DIV));
|
||||||
|
map.put("S_MOD", Integer.toString(VMOpCode.S_MOD));
|
||||||
|
map.put("S_INC", Integer.toString(VMOpCode.S_INC));
|
||||||
|
map.put("S_NEG", Integer.toString(VMOpCode.S_NEG));
|
||||||
|
map.put("B_ADD", Integer.toString(VMOpCode.B_ADD));
|
||||||
|
map.put("B_SUB", Integer.toString(VMOpCode.B_SUB));
|
||||||
|
map.put("B_MUL", Integer.toString(VMOpCode.B_MUL));
|
||||||
|
map.put("B_DIV", Integer.toString(VMOpCode.B_DIV));
|
||||||
|
map.put("B_MOD", Integer.toString(VMOpCode.B_MOD));
|
||||||
|
map.put("B_INC", Integer.toString(VMOpCode.B_INC));
|
||||||
|
map.put("B_NEG", Integer.toString(VMOpCode.B_NEG));
|
||||||
|
map.put("D_ADD", Integer.toString(VMOpCode.D_ADD));
|
||||||
|
map.put("D_SUB", Integer.toString(VMOpCode.D_SUB));
|
||||||
|
map.put("D_MUL", Integer.toString(VMOpCode.D_MUL));
|
||||||
|
map.put("D_DIV", Integer.toString(VMOpCode.D_DIV));
|
||||||
|
map.put("D_MOD", Integer.toString(VMOpCode.D_MOD));
|
||||||
|
map.put("D_NEG", Integer.toString(VMOpCode.D_NEG));
|
||||||
|
map.put("F_ADD", Integer.toString(VMOpCode.F_ADD));
|
||||||
|
map.put("F_SUB", Integer.toString(VMOpCode.F_SUB));
|
||||||
|
map.put("F_MUL", Integer.toString(VMOpCode.F_MUL));
|
||||||
|
map.put("F_DIV", Integer.toString(VMOpCode.F_DIV));
|
||||||
|
map.put("F_MOD", Integer.toString(VMOpCode.F_MOD));
|
||||||
|
map.put("F_NEG", Integer.toString(VMOpCode.F_NEG));
|
||||||
|
map.put("D_INC", Integer.toString(VMOpCode.D_INC));
|
||||||
|
map.put("F_INC", Integer.toString(VMOpCode.F_INC));
|
||||||
|
map.put("I2L", Integer.toString(VMOpCode.I2L));
|
||||||
|
map.put("I2S", Integer.toString(VMOpCode.I2S));
|
||||||
|
map.put("I2B", Integer.toString(VMOpCode.I2B));
|
||||||
|
map.put("I2D", Integer.toString(VMOpCode.I2D));
|
||||||
|
map.put("I2F", Integer.toString(VMOpCode.I2F));
|
||||||
|
map.put("L2I", Integer.toString(VMOpCode.L2I));
|
||||||
|
map.put("L2D", Integer.toString(VMOpCode.L2D));
|
||||||
|
map.put("L2F", Integer.toString(VMOpCode.L2F));
|
||||||
|
map.put("F2I", Integer.toString(VMOpCode.F2I));
|
||||||
|
map.put("F2L", Integer.toString(VMOpCode.F2L));
|
||||||
|
map.put("F2D", Integer.toString(VMOpCode.F2D));
|
||||||
|
map.put("D2I", Integer.toString(VMOpCode.D2I));
|
||||||
|
map.put("D2L", Integer.toString(VMOpCode.D2L));
|
||||||
|
map.put("D2F", Integer.toString(VMOpCode.D2F));
|
||||||
|
map.put("S2I", Integer.toString(VMOpCode.S2I));
|
||||||
|
map.put("B2I", Integer.toString(VMOpCode.B2I));
|
||||||
|
map.put("I_AND", Integer.toString(VMOpCode.I_AND));
|
||||||
|
map.put("I_OR", Integer.toString(VMOpCode.I_OR));
|
||||||
|
map.put("I_XOR", Integer.toString(VMOpCode.I_XOR));
|
||||||
|
map.put("L_AND", Integer.toString(VMOpCode.L_AND));
|
||||||
|
map.put("L_OR", Integer.toString(VMOpCode.L_OR));
|
||||||
|
map.put("L_XOR", Integer.toString(VMOpCode.L_XOR));
|
||||||
|
map.put("JUMP", Integer.toString(VMOpCode.JUMP));
|
||||||
|
map.put("IC_E", Integer.toString(VMOpCode.IC_E));
|
||||||
|
map.put("IC_NE", Integer.toString(VMOpCode.IC_NE));
|
||||||
|
map.put("IC_G", Integer.toString(VMOpCode.IC_G));
|
||||||
|
map.put("IC_GE", Integer.toString(VMOpCode.IC_GE));
|
||||||
|
map.put("IC_L", Integer.toString(VMOpCode.IC_L));
|
||||||
|
map.put("IC_LE", Integer.toString(VMOpCode.IC_LE));
|
||||||
|
map.put("I_PUSH", Integer.toString(VMOpCode.I_PUSH));
|
||||||
|
map.put("L_PUSH", Integer.toString(VMOpCode.L_PUSH));
|
||||||
|
map.put("S_PUSH", Integer.toString(VMOpCode.S_PUSH));
|
||||||
|
map.put("B_PUSH", Integer.toString(VMOpCode.B_PUSH));
|
||||||
|
map.put("D_PUSH", Integer.toString(VMOpCode.D_PUSH));
|
||||||
|
map.put("F_PUSH", Integer.toString(VMOpCode.F_PUSH));
|
||||||
|
map.put("POP", Integer.toString(VMOpCode.POP));
|
||||||
|
map.put("DUP", Integer.toString(VMOpCode.DUP));
|
||||||
|
map.put("SWAP", Integer.toString(VMOpCode.SWAP));
|
||||||
|
map.put("I_STORE", Integer.toString(VMOpCode.I_STORE));
|
||||||
|
map.put("L_STORE", Integer.toString(VMOpCode.L_STORE));
|
||||||
|
map.put("S_STORE", Integer.toString(VMOpCode.S_STORE));
|
||||||
|
map.put("B_STORE", Integer.toString(VMOpCode.B_STORE));
|
||||||
|
map.put("D_STORE", Integer.toString(VMOpCode.D_STORE));
|
||||||
|
map.put("F_STORE", Integer.toString(VMOpCode.F_STORE));
|
||||||
|
map.put("I_LOAD", Integer.toString(VMOpCode.I_LOAD));
|
||||||
|
map.put("L_LOAD", Integer.toString(VMOpCode.L_LOAD));
|
||||||
|
map.put("S_LOAD", Integer.toString(VMOpCode.S_LOAD));
|
||||||
|
map.put("B_LOAD", Integer.toString(VMOpCode.B_LOAD));
|
||||||
|
map.put("D_LOAD", Integer.toString(VMOpCode.D_LOAD));
|
||||||
|
map.put("F_LOAD", Integer.toString(VMOpCode.F_LOAD));
|
||||||
|
map.put("MOV", Integer.toString(VMOpCode.MOV));
|
||||||
|
map.put("CALL", Integer.toString(VMOpCode.CALL));
|
||||||
|
map.put("RET", Integer.toString(VMOpCode.RET));
|
||||||
|
map.put("HALT", Integer.toString(VMOpCode.HALT));
|
||||||
|
OPCODE_MAP = Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私有构造器,禁止实例化
|
||||||
|
*/
|
||||||
|
private OpHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指令名获取 opcode 字符串。
|
||||||
|
*
|
||||||
|
* @param name 指令名(如 "I_LOAD"、"I_PUSH")
|
||||||
|
* @return opcode 字符串
|
||||||
|
* @throws IllegalStateException 若名称未知
|
||||||
|
*/
|
||||||
|
public static String opcode(String name) {
|
||||||
|
String code = OPCODE_MAP.get(name);
|
||||||
|
if (code == null) {
|
||||||
|
throw new IllegalStateException("Unknown opcode: " + name);
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// region 辅助方法 – 根据常量类型推导 PUSH / STORE 指令名
|
||||||
|
|
||||||
|
public static String pushOpcodeFor(Object v) {
|
||||||
|
return opcode(typePrefix(v) + "_PUSH");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String storeOpcodeFor(Object v) {
|
||||||
|
return opcode(typePrefix(v) + "_STORE");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String typePrefix(Object v) {
|
||||||
|
if (v instanceof Integer) return "I";
|
||||||
|
if (v instanceof Long) return "L";
|
||||||
|
if (v instanceof Short) return "S";
|
||||||
|
if (v instanceof Byte) return "B";
|
||||||
|
if (v instanceof Double) return "D";
|
||||||
|
if (v instanceof Float) return "F";
|
||||||
|
throw new IllegalStateException("Unknown const type: " + v.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
}
|
||||||
149
src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java
Normal file
149
src/main/java/org/jcnc/snow/compiler/cli/SnowCompiler.java
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package org.jcnc.snow.compiler.cli;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.backend.alloc.RegisterAllocator;
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMCodeGenerator;
|
||||||
|
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||||
|
import org.jcnc.snow.compiler.backend.generator.*;
|
||||||
|
import org.jcnc.snow.compiler.ir.builder.IRProgramBuilder;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRProgram;
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerEngine;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
|
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||||
|
import org.jcnc.snow.compiler.parser.core.ParserEngine;
|
||||||
|
import org.jcnc.snow.compiler.parser.function.ASTPrinter;
|
||||||
|
import org.jcnc.snow.compiler.semantic.core.SemanticAnalyzerRunner;
|
||||||
|
import org.jcnc.snow.vm.engine.VMMode;
|
||||||
|
import org.jcnc.snow.vm.engine.VirtualMachineEngine;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SnowCompiler CLI —— 多文件 / 单文件 / 目录 模式。
|
||||||
|
*/
|
||||||
|
public class SnowCompiler {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
if (args.length == 0) {
|
||||||
|
System.err.println("""
|
||||||
|
Usage:
|
||||||
|
snow <file1.snow> [file2.snow …]
|
||||||
|
snow -d <srcDir> (compile all *.snow recursively)
|
||||||
|
""");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 1. 收集所有待编译源码 ---------- */
|
||||||
|
List<Path> sources = collectSources(args);
|
||||||
|
if (sources.isEmpty()) {
|
||||||
|
System.err.println("No .snow source files found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 2. 逐个词法+语法分析,合并 AST ---------- */
|
||||||
|
List<Node> allAst = new ArrayList<>();
|
||||||
|
for (Path p : sources) {
|
||||||
|
if (!Files.exists(p)) {
|
||||||
|
System.err.println("File not found: " + p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String code = Files.readString(p, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
// 保持原有“## 源代码”打印,但标注文件名,兼容旧脚本
|
||||||
|
System.out.println("## 源代码 (" + p.getFileName() + ")");
|
||||||
|
System.out.println(code);
|
||||||
|
|
||||||
|
LexerEngine lexer = new LexerEngine(code, p.toString());
|
||||||
|
ParserContext ctx = new ParserContext(lexer.getAllTokens());
|
||||||
|
allAst.addAll(new ParserEngine(ctx).parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 3. 语义分析 ---------- */
|
||||||
|
SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false);
|
||||||
|
|
||||||
|
/* ---------- 4. AST → IR ---------- */
|
||||||
|
IRProgram program = new IRProgramBuilder().buildProgram(allAst);
|
||||||
|
program = reorderForEntry(program); // 保证入口 main 在首位
|
||||||
|
|
||||||
|
System.out.println("## 编译器输出");
|
||||||
|
System.out.println("### AST");
|
||||||
|
ASTPrinter.printJson(allAst);
|
||||||
|
System.out.println("### IR");
|
||||||
|
System.out.println(program);
|
||||||
|
|
||||||
|
/* ---------- 5. IR → VM 指令 ---------- */
|
||||||
|
VMProgramBuilder builder = new VMProgramBuilder();
|
||||||
|
List<InstructionGenerator<? extends IRInstruction>> generators = Arrays.asList(
|
||||||
|
new LoadConstGenerator(),
|
||||||
|
new BinaryOpGenerator(),
|
||||||
|
new UnaryOpGenerator(),
|
||||||
|
new CallGenerator(),
|
||||||
|
new ReturnGenerator(),
|
||||||
|
new LabelGenerator(),
|
||||||
|
new JumpGenerator(),
|
||||||
|
new CmpJumpGenerator()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (IRFunction fn : program.functions()) {
|
||||||
|
Map<org.jcnc.snow.compiler.ir.value.IRVirtualRegister, Integer> slotMap =
|
||||||
|
new RegisterAllocator().allocate(fn);
|
||||||
|
new VMCodeGenerator(slotMap, builder, generators).generate(fn);
|
||||||
|
}
|
||||||
|
List<String> finalCode = builder.build();
|
||||||
|
|
||||||
|
System.out.println("### VM code");
|
||||||
|
finalCode.forEach(System.out::println);
|
||||||
|
|
||||||
|
/* ---------- 6. 运行虚拟机 ---------- */
|
||||||
|
VirtualMachineEngine vm = new VirtualMachineEngine(VMMode.RUN);
|
||||||
|
vm.execute(finalCode);
|
||||||
|
vm.printLocalVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数收集待编译文件:
|
||||||
|
* - snow file1 file2 … ← 多文件 / 单文件
|
||||||
|
* - snow -d srcDir ← 目录递归所有 *.snow
|
||||||
|
*/
|
||||||
|
private static List<Path> collectSources(String[] args) throws IOException {
|
||||||
|
if (args.length == 2 && "-d".equals(args[0])) {
|
||||||
|
Path dir = Path.of(args[1]);
|
||||||
|
if (!Files.isDirectory(dir)) {
|
||||||
|
System.err.println("Not a directory: " + dir);
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
try (var stream = Files.walk(dir)) {
|
||||||
|
return stream.filter(p -> p.toString().endsWith(".snow"))
|
||||||
|
.sorted() // 稳定顺序,方便比对输出
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 普通文件参数
|
||||||
|
return Arrays.stream(args).map(Path::of).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把 main 函数放到 Program.functions()[0],保证 PC=0 即入口;
|
||||||
|
* 如果用户未写 main,则保持原顺序(语义分析会报错)。
|
||||||
|
*/
|
||||||
|
private static IRProgram reorderForEntry(IRProgram in) {
|
||||||
|
List<IRFunction> ordered = new ArrayList<>(in.functions());
|
||||||
|
int idx = -1;
|
||||||
|
for (int i = 0; i < ordered.size(); i++) {
|
||||||
|
if ("main".equals(ordered.get(i).name())) {
|
||||||
|
idx = i; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idx > 0) Collections.swap(ordered, 0, idx);
|
||||||
|
|
||||||
|
IRProgram out = new IRProgram();
|
||||||
|
ordered.forEach(out::add);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,182 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.builder;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.*;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>表达式构建器</b>
|
||||||
|
* <p>
|
||||||
|
* 该类负责将抽象语法树(AST)的表达式节点转换为中间表示(IR)指令和虚拟寄存器,
|
||||||
|
* 是编译器IR生成阶段的核心工具。
|
||||||
|
* <br/>
|
||||||
|
* 主要职责包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>将数字字面量、标识符、二元表达式、函数调用等AST表达式节点,翻译为对应的IR指令序列</li>
|
||||||
|
* <li>管理并分配虚拟寄存器,保证IR操作的数据流正确</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
public record ExpressionBuilder(IRContext ctx) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建并返回某个表达式节点对应的虚拟寄存器。
|
||||||
|
*
|
||||||
|
* <p>会根据节点的实际类型分别处理:
|
||||||
|
* <ul>
|
||||||
|
* <li>数字字面量:新建常量寄存器</li>
|
||||||
|
* <li>标识符:查找当前作用域中的寄存器</li>
|
||||||
|
* <li>二元表达式:递归处理子表达式并进行相应运算</li>
|
||||||
|
* <li>函数调用:生成对应的Call指令</li>
|
||||||
|
* <li>其它类型不支持,抛出异常</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param expr 要转换的表达式AST节点
|
||||||
|
* @return 该表达式的计算结果寄存器
|
||||||
|
* @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型
|
||||||
|
*/
|
||||||
|
public IRVirtualRegister build(ExpressionNode expr) {
|
||||||
|
return switch (expr) {
|
||||||
|
// 数字字面量,如 "123", "1.0f"
|
||||||
|
case NumberLiteralNode n -> buildNumberLiteral(n.value());
|
||||||
|
// 标识符,如变量x,直接查作用域
|
||||||
|
case IdentifierNode id -> {
|
||||||
|
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
|
||||||
|
if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
|
||||||
|
yield reg;
|
||||||
|
}
|
||||||
|
// 二元表达式,如 "a + b"
|
||||||
|
case BinaryExpressionNode bin -> buildBinary(bin);
|
||||||
|
// 函数调用,如 foo(a, b)
|
||||||
|
case CallExpressionNode call -> buildCall(call);
|
||||||
|
// 其它不支持
|
||||||
|
default -> throw new IllegalStateException("不支持的表达式类型: " + expr.getClass().getSimpleName());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接将表达式计算结果写入指定的目标寄存器(dest)。
|
||||||
|
* <p>
|
||||||
|
* 与{@link #build(ExpressionNode)}类似,但支持目标寄存器复用(避免不必要的move)。
|
||||||
|
*
|
||||||
|
* @param node 表达式AST节点
|
||||||
|
* @param dest 目标寄存器
|
||||||
|
* @throws IllegalStateException 未定义标识符/不支持的表达式类型时报错
|
||||||
|
*/
|
||||||
|
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
|
||||||
|
switch (node) {
|
||||||
|
// 数字字面量,直接加载到目标寄存器
|
||||||
|
case NumberLiteralNode n ->
|
||||||
|
InstructionFactory.loadConstInto(ctx, dest, ExpressionUtils.parseIntSafely(n.value()));
|
||||||
|
// 标识符,查找并move到目标寄存器
|
||||||
|
case IdentifierNode id -> {
|
||||||
|
IRVirtualRegister src = ctx.getScope().lookup(id.name());
|
||||||
|
if (src == null) throw new IllegalStateException("未定义标识符: " + id.name());
|
||||||
|
InstructionFactory.move(ctx, src, dest);
|
||||||
|
}
|
||||||
|
// 二元表达式,直接写入目标寄存器
|
||||||
|
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
|
||||||
|
// 其他表达式,先递归生成寄存器,再move到目标寄存器
|
||||||
|
default -> {
|
||||||
|
IRVirtualRegister tmp = build(node);
|
||||||
|
InstructionFactory.move(ctx, tmp, dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== 内部私有方法 =====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建二元表达式的IR,生成新寄存器存储结果。
|
||||||
|
* <p>
|
||||||
|
* 先递归构建左右操作数,之后根据操作符类别(算术或比较)决定生成的IR操作码,
|
||||||
|
* 并生成对应的二元运算指令。
|
||||||
|
*
|
||||||
|
* @param bin 二元表达式节点
|
||||||
|
* @return 存放结果的虚拟寄存器
|
||||||
|
*/
|
||||||
|
private IRVirtualRegister buildBinary(BinaryExpressionNode bin) {
|
||||||
|
String op = bin.operator();
|
||||||
|
IRVirtualRegister left = build(bin.left());
|
||||||
|
IRVirtualRegister right = build(bin.right());
|
||||||
|
// 处理比较操作符
|
||||||
|
if (ExpressionUtils.isComparisonOperator(op)) {
|
||||||
|
return InstructionFactory.binOp(ctx, ExpressionUtils.cmpOp(op), left, right);
|
||||||
|
}
|
||||||
|
// 处理算术运算符
|
||||||
|
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||||
|
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||||
|
return InstructionFactory.binOp(ctx, code, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将二元表达式的结果直接写入指定寄存器dest。
|
||||||
|
* <p>
|
||||||
|
* 结构与{@link #buildBinary(BinaryExpressionNode)}类似,但不会新分配寄存器。
|
||||||
|
*
|
||||||
|
* @param bin 二元表达式节点
|
||||||
|
* @param dest 目标寄存器
|
||||||
|
*/
|
||||||
|
private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) {
|
||||||
|
IRVirtualRegister a = build(bin.left());
|
||||||
|
IRVirtualRegister b = build(bin.right());
|
||||||
|
String op = bin.operator();
|
||||||
|
if (ExpressionUtils.isComparisonOperator(op)) {
|
||||||
|
InstructionFactory.binOpInto(ctx, ExpressionUtils.cmpOp(op), a, b, dest);
|
||||||
|
} else {
|
||||||
|
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||||
|
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||||
|
InstructionFactory.binOpInto(ctx, code, a, b, dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理函数调用表达式,生成对应的Call指令和目标寄存器。
|
||||||
|
* <p>
|
||||||
|
* 支持普通标识符调用和成员调用(如 mod.func),会为每个参数依次生成子表达式的寄存器。
|
||||||
|
*
|
||||||
|
* @param call 调用表达式AST节点
|
||||||
|
* @return 返回结果存放的寄存器
|
||||||
|
*/
|
||||||
|
private IRVirtualRegister buildCall(CallExpressionNode call) {
|
||||||
|
// 递归构建所有参数的寄存器
|
||||||
|
List<IRVirtualRegister> argv = call.arguments().stream()
|
||||||
|
.map(this::build)
|
||||||
|
.toList();
|
||||||
|
// 获取完整调用目标名称(支持成员/模块调用和普通调用)
|
||||||
|
String fullName = switch (call.callee()) {
|
||||||
|
case MemberExpressionNode member when member.object() instanceof IdentifierNode mod ->
|
||||||
|
((IdentifierNode)member.object()).name() + "." + member.member();
|
||||||
|
case IdentifierNode id -> id.name();
|
||||||
|
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||||
|
};
|
||||||
|
// 申请目标寄存器
|
||||||
|
IRVirtualRegister dest = ctx.newRegister();
|
||||||
|
// 添加Call指令到IR上下文
|
||||||
|
ctx.addInstruction(new CallInstruction(dest, fullName, new ArrayList<>(argv)));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理数字字面量,生成常量寄存器和加载指令。
|
||||||
|
* <p>
|
||||||
|
* 会将字符串型字面量(如 "123", "1.0f")解析为具体的IRConstant,
|
||||||
|
* 并分配一个新的虚拟寄存器来存放该常量。
|
||||||
|
*
|
||||||
|
* @param value 字面量字符串
|
||||||
|
* @return 存放该常量的寄存器
|
||||||
|
*/
|
||||||
|
private IRVirtualRegister buildNumberLiteral(String value) {
|
||||||
|
IRConstant constant = ExpressionUtils.buildNumberConstant(value);
|
||||||
|
IRVirtualRegister reg = ctx.newRegister();
|
||||||
|
ctx.addInstruction(new LoadConstInstruction(reg, constant));
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.builder;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||||
|
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ParameterNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IR 函数构建器。
|
||||||
|
* <p>
|
||||||
|
* 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction,
|
||||||
|
* 包含参数声明、返回类型推断、函数体语句转换等步骤。
|
||||||
|
*/
|
||||||
|
public class FunctionBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 AST 中的 FunctionNode 构建为可执行的 IRFunction。
|
||||||
|
* <p>
|
||||||
|
* 构建过程包括:
|
||||||
|
* <ol>
|
||||||
|
* <li>初始化 IRFunction 实例和上下文</li>
|
||||||
|
* <li>根据函数返回类型,设置默认类型后缀,便于表达式推断</li>
|
||||||
|
* <li>声明参数到作用域,并为每个参数分配虚拟寄存器</li>
|
||||||
|
* <li>遍历并转换函数体内的每条语句为 IR 指令</li>
|
||||||
|
* <li>函数构建完成后,清理默认类型后缀,防止影响其他函数</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param functionNode 表示函数定义的语法树节点
|
||||||
|
* @return 构建得到的 IRFunction 对象
|
||||||
|
*/
|
||||||
|
public IRFunction build(FunctionNode functionNode) {
|
||||||
|
|
||||||
|
// 0) 基本初始化:创建 IRFunction 实例与对应上下文
|
||||||
|
IRFunction irFunction = new IRFunction(functionNode.name());
|
||||||
|
IRContext irContext = new IRContext(irFunction);
|
||||||
|
|
||||||
|
// 1) 把函数返回类型注入为默认类型后缀(供表达式类型推断)
|
||||||
|
char _returnSuffix = switch (functionNode.returnType().toLowerCase()) {
|
||||||
|
case "double" -> 'd';
|
||||||
|
case "float" -> 'f';
|
||||||
|
case "long" -> 'l';
|
||||||
|
case "short" -> 's';
|
||||||
|
case "byte" -> 'b';
|
||||||
|
default -> '\0';
|
||||||
|
};
|
||||||
|
ExpressionUtils.setDefaultSuffix(_returnSuffix);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 2) 声明形参:为每个参数分配虚拟寄存器并声明到作用域
|
||||||
|
for (ParameterNode p : functionNode.parameters()) {
|
||||||
|
IRVirtualRegister reg = irFunction.newRegister(); // 新寄存器
|
||||||
|
irContext.getScope().declare(p.name(), reg); // 变量名→寄存器绑定
|
||||||
|
irFunction.addParameter(reg); // 添加到函数参数列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) 生成函数体 IR:遍历每条语句,逐一转化
|
||||||
|
StatementBuilder stmtBuilder = new StatementBuilder(irContext);
|
||||||
|
for (StatementNode stmt : functionNode.body()) {
|
||||||
|
stmtBuilder.build(stmt);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// 4) 清除默认后缀,避免影响后续函数的推断
|
||||||
|
ExpressionUtils.clearDefaultSuffix();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回构建好的 IRFunction
|
||||||
|
return irFunction;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
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.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系。
|
||||||
|
*
|
||||||
|
* <p>主要功能包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>维护在当前作用域中已声明变量的寄存器分配信息;</li>
|
||||||
|
* <li>支持将已有虚拟寄存器与变量名重新绑定;</li>
|
||||||
|
* <li>根据变量名查找对应的虚拟寄存器实例。</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
final class IRBuilderScope {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储变量名到对应 IRVirtualRegister 的映射。
|
||||||
|
* 变量名为键,虚拟寄存器对象为值,用于查找和更新。
|
||||||
|
*/
|
||||||
|
private final Map<String, IRVirtualRegister> vars = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前作用域所绑定的 IRFunction 对象,用于申请新的虚拟寄存器。
|
||||||
|
*/
|
||||||
|
private IRFunction fn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定的 IRFunction 关联到当前作用域,以便后续声明变量时能够
|
||||||
|
* 调用该函数的 newRegister() 方法生成新的寄存器。
|
||||||
|
*
|
||||||
|
* @param fn 要绑定到本作用域的 IRFunction 实例
|
||||||
|
*/
|
||||||
|
void attachFunction(IRFunction fn) {
|
||||||
|
this.fn = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在当前作用域中声明一个新变量,并为其分配一个新的虚拟寄存器。
|
||||||
|
* 调用绑定的 IRFunction.newRegister() 生成寄存器后保存到映射表中。
|
||||||
|
*
|
||||||
|
* @param name 变量名称,作为映射键使用
|
||||||
|
*/
|
||||||
|
void declare(String name) {
|
||||||
|
IRVirtualRegister reg = fn.newRegister();
|
||||||
|
vars.put(name, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在当前作用域中声明或导入一个已有的虚拟寄存器,并将其与指定变量名绑定。
|
||||||
|
* 该方法可用于将外部或前一作用域的寄存器导入到本作用域。
|
||||||
|
*
|
||||||
|
* @param name 变量名称,作为映射键使用
|
||||||
|
* @param reg 要绑定到该名称的 IRVirtualRegister 实例
|
||||||
|
*/
|
||||||
|
void declare(String name, IRVirtualRegister reg) {
|
||||||
|
vars.put(name, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新已存在变量的虚拟寄存器绑定关系。若变量已声明,则替换其对应的寄存器;
|
||||||
|
* 若尚未声明,则等同于声明新变量。
|
||||||
|
*
|
||||||
|
* @param name 变量名称,作为映射键使用
|
||||||
|
* @param reg 新的 IRVirtualRegister 实例,用于替换旧绑定
|
||||||
|
*/
|
||||||
|
void put(String name, IRVirtualRegister reg) {
|
||||||
|
vars.put(name, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据变量名称在当前作用域中查找对应的虚拟寄存器。
|
||||||
|
*
|
||||||
|
* @param name 需要查询的变量名称
|
||||||
|
* @return 如果该名称已绑定寄存器,则返回对应的 IRVirtualRegister;
|
||||||
|
* 如果未声明,则返回 null
|
||||||
|
*/
|
||||||
|
IRVirtualRegister lookup(String name) {
|
||||||
|
return vars.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.builder;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRContext 类负责封装当前正在构建的 IRFunction 实例
|
||||||
|
* 以及与之配套的作用域管理(IRBuilderScope),
|
||||||
|
* 并简化虚拟寄存器分配与 IR 指令添加操作。
|
||||||
|
*
|
||||||
|
* <p>本类提供以下核心功能:
|
||||||
|
* <ul>
|
||||||
|
* <li>持有并操作当前 IRFunction 对象;</li>
|
||||||
|
* <li>管理变量名与虚拟寄存器的映射关系;</li>
|
||||||
|
* <li>分配新的虚拟寄存器实例;</li>
|
||||||
|
* <li>将生成的 IRInstruction 自动添加到 IRFunction 中;</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public class IRContext {
|
||||||
|
|
||||||
|
/* ➡ 新增:生成唯一标签用 */
|
||||||
|
private int labelCounter = 0;
|
||||||
|
/**
|
||||||
|
* 当前正在构建的 IRFunction 对象,所有指令将添加至此
|
||||||
|
*/
|
||||||
|
private final IRFunction function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于管理当前函数作用域内变量与虚拟寄存器的映射
|
||||||
|
*/
|
||||||
|
private final IRBuilderScope scope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个新的 IRContext,并将指定的 IRFunction 与作用域关联。
|
||||||
|
*
|
||||||
|
* @param function 要构建的 IRFunction 实例
|
||||||
|
*/
|
||||||
|
public IRContext(IRFunction function) {
|
||||||
|
this.function = function;
|
||||||
|
this.scope = new IRBuilderScope();
|
||||||
|
// 关联作用域与 IRFunction,以便在声明变量时申请寄存器
|
||||||
|
this.scope.attachFunction(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前正在构建的 IRFunction 对象。
|
||||||
|
*
|
||||||
|
* @return 当前 IRFunction 实例
|
||||||
|
*/
|
||||||
|
public IRFunction getFunction() {
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前函数的变量与寄存器映射作用域。
|
||||||
|
*
|
||||||
|
* <p>包内可见:仅限 builder 包内部使用。
|
||||||
|
*
|
||||||
|
* @return IRBuilderScope 实例
|
||||||
|
*/
|
||||||
|
IRBuilderScope getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为当前函数分配一个新的虚拟寄存器。
|
||||||
|
*
|
||||||
|
* @return 分配到的 IRVirtualRegister 对象
|
||||||
|
*/
|
||||||
|
public IRVirtualRegister newRegister() {
|
||||||
|
return function.newRegister();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定的 IRInstruction 添加到当前 IRFunction 的指令列表中。
|
||||||
|
*
|
||||||
|
* @param instr 要添加的 IRInstruction 实例
|
||||||
|
*/
|
||||||
|
public void addInstruction(IRInstruction instr) {
|
||||||
|
function.add(instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 生成一个形如 L0 / L1 ... 的唯一标签名 */
|
||||||
|
public String newLabel() {
|
||||||
|
return "L" + (labelCounter++);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.builder;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRProgram;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本类负责将解析生成的 AST 根节点列表转换为可执行的 IRProgram。
|
||||||
|
*
|
||||||
|
* <p>主要职责:
|
||||||
|
* <ul>
|
||||||
|
* <li>遍历输入的顶层节点,识别 ModuleNode、FunctionNode 及脚本式顶层 StatementNode;</li>
|
||||||
|
* <li>对 ModuleNode 中的所有函数节点调用 FunctionBuilder 构建 IRFunction 并添加至 IRProgram;</li>
|
||||||
|
* <li>对单独的 FunctionNode 节点直接构建并纳入 IRProgram;</li>
|
||||||
|
* <li>对顶层脚本式 StatementNode 自动封装为名称固定的“_start”函数,再行构建并纳入 IRProgram;</li>
|
||||||
|
* <li>对不支持的节点类型抛出 IllegalStateException,以确保编译流程严谨。</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public final class IRProgramBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完整的 IRProgram 实例。
|
||||||
|
*
|
||||||
|
* @param roots 含 ModuleNode、FunctionNode 或 StatementNode 的顶层 AST 根节点列表
|
||||||
|
* @return 包含所有转换后 IRFunction 的 IRProgram 对象
|
||||||
|
* @throws IllegalStateException 遇到不支持的顶层节点类型时抛出
|
||||||
|
*/
|
||||||
|
public IRProgram buildProgram(List<Node> roots) {
|
||||||
|
IRProgram irProgram = new IRProgram();
|
||||||
|
for (Node node : roots) {
|
||||||
|
switch (node) {
|
||||||
|
case ModuleNode moduleNode ->
|
||||||
|
// 模块节点:批量构建并添加模块内所有函数
|
||||||
|
moduleNode.functions().forEach(f -> irProgram.add(buildFunction(f)));
|
||||||
|
case FunctionNode functionNode ->
|
||||||
|
// 顶层函数节点:直接构建并添加
|
||||||
|
irProgram.add(buildFunction(functionNode));
|
||||||
|
case StatementNode statementNode ->
|
||||||
|
// 脚本式顶层语句:封装为“_start”函数后构建并添加
|
||||||
|
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
|
||||||
|
default ->
|
||||||
|
// 严格校验节点类型,遇不支持者立即失败
|
||||||
|
throw new IllegalStateException("Unsupported top-level node: " + node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return irProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 利用 FunctionBuilder 将 FunctionNode 转换为 IRFunction。
|
||||||
|
*
|
||||||
|
* @param functionNode 待构建的 AST FunctionNode
|
||||||
|
* @return 构建完成的 IRFunction 实例
|
||||||
|
*/
|
||||||
|
private IRFunction buildFunction(FunctionNode functionNode) {
|
||||||
|
return new FunctionBuilder().build(functionNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将单个脚本式顶层 StatementNode 封装为名称固定的“_start”函数节点。
|
||||||
|
*
|
||||||
|
* <p>封装规则:
|
||||||
|
* <ul>
|
||||||
|
* <li>函数名固定为“_start”;</li>
|
||||||
|
* <li>返回类型设为 null,由后续流程处理;</li>
|
||||||
|
* <li>参数列表为空;</li>
|
||||||
|
* <li>函数主体仅包含传入的单条语句。</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param stmt 待封装的顶层脚本语句节点
|
||||||
|
* @return 生成的 FunctionNode,用于后续 IRFunction 构建
|
||||||
|
*/
|
||||||
|
private FunctionNode wrapTopLevel(StatementNode stmt) {
|
||||||
|
return new FunctionNode("_start", null, String.valueOf(List.of()), List.of(stmt));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,155 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.builder;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.*;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InstructionFactory —— 统一生成并注册 IR 指令的工厂类。
|
||||||
|
* <p>
|
||||||
|
* 该类封装了常见的 IR 指令生成方式,包括常量加载、二元运算、赋值、控制流等,
|
||||||
|
* 统一简化指令插入和寄存器分配逻辑,提升 IR 生成阶段的代码可维护性和复用性。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class InstructionFactory {
|
||||||
|
|
||||||
|
/* ====================================================================== */
|
||||||
|
/* 常量 / 通用二元运算(新寄存器) */
|
||||||
|
/* ====================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载整数常量,将其写入一个新分配的虚拟寄存器,并返回该寄存器。
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文(用于分配寄存器与添加指令)
|
||||||
|
* @param value 要加载的整数常量值
|
||||||
|
* @return 存储该常量的新虚拟寄存器
|
||||||
|
*/
|
||||||
|
public static IRVirtualRegister loadConst(IRContext ctx, int value) {
|
||||||
|
IRVirtualRegister r = ctx.newRegister();
|
||||||
|
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(value)));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行二元运算(如加法、减法等),结果写入新分配的虚拟寄存器并返回该寄存器。
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文
|
||||||
|
* @param op 运算类型(IROpCode 枚举,如 ADD_I32 等)
|
||||||
|
* @param a 第一个操作数寄存器
|
||||||
|
* @param b 第二个操作数寄存器
|
||||||
|
* @return 保存运算结果的新虚拟寄存器
|
||||||
|
*/
|
||||||
|
public static IRVirtualRegister binOp(IRContext ctx, IROpCode op,
|
||||||
|
IRVirtualRegister a, IRVirtualRegister b) {
|
||||||
|
IRVirtualRegister dest = ctx.newRegister();
|
||||||
|
ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================================== */
|
||||||
|
/* 直接写入指定寄存器 */
|
||||||
|
/* ====================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载整数常量到指定虚拟寄存器。
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文
|
||||||
|
* @param dest 目标寄存器
|
||||||
|
* @param value 要加载的整数常量
|
||||||
|
*/
|
||||||
|
public static void loadConstInto(IRContext ctx, IRVirtualRegister dest, int value) {
|
||||||
|
ctx.addInstruction(new LoadConstInstruction(dest, new IRConstant(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对两个寄存器执行二元运算,将结果写入指定目标寄存器。
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文
|
||||||
|
* @param op 运算类型(IROpCode 枚举)
|
||||||
|
* @param a 第一个操作数寄存器
|
||||||
|
* @param b 第二个操作数寄存器
|
||||||
|
* @param dest 运算结果目标寄存器
|
||||||
|
*/
|
||||||
|
public static void binOpInto(IRContext ctx, IROpCode op,
|
||||||
|
IRVirtualRegister a, IRVirtualRegister b,
|
||||||
|
IRVirtualRegister dest) {
|
||||||
|
ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简易 Move 指令(src → dest)。若寄存器相同也安全。
|
||||||
|
* <p>
|
||||||
|
* 实现方式:dest = src + 0(即加上常量 0)。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文
|
||||||
|
* @param src 源寄存器
|
||||||
|
* @param dest 目标寄存器
|
||||||
|
*/
|
||||||
|
public static void move(IRContext ctx, IRVirtualRegister src, IRVirtualRegister dest) {
|
||||||
|
/* 采用 “dest = src + 0” 的最简实现 */
|
||||||
|
IRVirtualRegister zero = loadConst(ctx, 0);
|
||||||
|
ctx.addInstruction(new BinaryOperationInstruction(IROpCode.ADD_I32, dest, src, zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================================== */
|
||||||
|
/* 控制流指令 */
|
||||||
|
/* ====================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成无条件跳转(JMP)指令,跳转到指定标签。
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文
|
||||||
|
* @param label 目标标签名
|
||||||
|
*/
|
||||||
|
public static void jmp(IRContext ctx, String label) {
|
||||||
|
ctx.addInstruction(new IRJumpInstruction(label));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在 IR 中插入一个标签(Label)。
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文
|
||||||
|
* @param label 标签名
|
||||||
|
*/
|
||||||
|
public static void label(IRContext ctx, String label) {
|
||||||
|
ctx.addInstruction(new IRLabelInstruction(label));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 比较跳转(如 if a < b goto label),根据条件跳转到目标标签。
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文
|
||||||
|
* @param cmp 比较操作码(如 IROpCode.LT_I32 等)
|
||||||
|
* @param a 第一个操作数寄存器
|
||||||
|
* @param b 第二个操作数寄存器
|
||||||
|
* @param targetLabel 跳转目标标签
|
||||||
|
*/
|
||||||
|
public static void cmpJump(IRContext ctx, IROpCode cmp,
|
||||||
|
IRVirtualRegister a, IRVirtualRegister b,
|
||||||
|
String targetLabel) {
|
||||||
|
ctx.addInstruction(new IRCompareJumpInstruction(cmp, a, b, targetLabel));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- 返回 ---------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成返回指令(带返回值)。
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文
|
||||||
|
* @param value 返回值寄存器
|
||||||
|
*/
|
||||||
|
public static void ret(IRContext ctx, IRVirtualRegister value) {
|
||||||
|
ctx.addInstruction(new ReturnInstruction(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成无返回值的 return 指令(如 void 函数)。
|
||||||
|
*
|
||||||
|
* @param ctx 当前 IR 上下文
|
||||||
|
*/
|
||||||
|
public static void retVoid(IRContext ctx) {
|
||||||
|
ctx.addInstruction(new ReturnInstruction(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,180 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.builder;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
|
||||||
|
import org.jcnc.snow.compiler.ir.utils.IROpCodeUtils;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.*;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StatementBuilder —— 将 AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器。
|
||||||
|
* <p>
|
||||||
|
* 负责将各种语句节点(循环、分支、表达式、赋值、声明、返回等)生成对应的 IR 指令,并管理作用域和控制流标签。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class StatementBuilder {
|
||||||
|
|
||||||
|
/** 当前 IR 上下文,包含作用域、指令序列等信息。 */
|
||||||
|
private final IRContext ctx;
|
||||||
|
/** 表达式 IR 构建器,用于将表达式节点转为 IR 指令。 */
|
||||||
|
private final ExpressionBuilder expr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法。
|
||||||
|
* @param ctx IR 编译上下文环境
|
||||||
|
*/
|
||||||
|
public StatementBuilder(IRContext ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.expr = new ExpressionBuilder(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将一个 AST 语句节点转为 IR 指令序列。
|
||||||
|
* 根据节点类型分发到对应的处理方法。
|
||||||
|
* @param stmt 待转换的语句节点
|
||||||
|
*/
|
||||||
|
public void build(StatementNode stmt) {
|
||||||
|
if (stmt instanceof LoopNode loop) {
|
||||||
|
// 循环语句
|
||||||
|
buildLoop(loop);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stmt instanceof IfNode ifNode) {
|
||||||
|
// 分支(if-else)语句
|
||||||
|
buildIf(ifNode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stmt instanceof ExpressionStatementNode(ExpressionNode exp)) {
|
||||||
|
// 纯表达式语句,如 foo();
|
||||||
|
expr.build(exp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs)) {
|
||||||
|
// 赋值语句,如 a = b + 1;
|
||||||
|
IRVirtualRegister target = getOrDeclareRegister(var);
|
||||||
|
expr.buildInto(rhs, target);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stmt instanceof DeclarationNode decl) {
|
||||||
|
// 变量声明,如 int a = 1;
|
||||||
|
if (decl.getInitializer().isPresent()) {
|
||||||
|
// 声明同时有初值
|
||||||
|
IRVirtualRegister r = expr.build(decl.getInitializer().get());
|
||||||
|
ctx.getScope().declare(decl.getName(), r);
|
||||||
|
} else {
|
||||||
|
// 仅声明,无初值
|
||||||
|
ctx.getScope().declare(decl.getName());
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// 不支持的语句类型
|
||||||
|
throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取变量名对应的寄存器,不存在则声明一个新的。
|
||||||
|
* @param name 变量名
|
||||||
|
* @return 变量对应的虚拟寄存器
|
||||||
|
*/
|
||||||
|
private IRVirtualRegister getOrDeclareRegister(String name) {
|
||||||
|
IRVirtualRegister reg = ctx.getScope().lookup(name);
|
||||||
|
if (reg == null) {
|
||||||
|
reg = ctx.newRegister();
|
||||||
|
ctx.getScope().declare(name, reg);
|
||||||
|
}
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量构建一组语句节点,顺序处理每个语句。
|
||||||
|
* @param stmts 语句节点集合
|
||||||
|
*/
|
||||||
|
private void buildStatements(Iterable<StatementNode> stmts) {
|
||||||
|
for (StatementNode s : stmts) build(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建循环语句(for/while)。
|
||||||
|
* 处理流程:初始语句 → 条件判断 → 循环体 → 更新语句 → 跳回条件。
|
||||||
|
* @param loop 循环节点
|
||||||
|
*/
|
||||||
|
private void buildLoop(LoopNode loop) {
|
||||||
|
if (loop.initializer() != null) build(loop.initializer());
|
||||||
|
String lblStart = ctx.newLabel();
|
||||||
|
String lblEnd = ctx.newLabel();
|
||||||
|
// 循环开始标签
|
||||||
|
InstructionFactory.label(ctx, lblStart);
|
||||||
|
|
||||||
|
// 条件不满足则跳出循环
|
||||||
|
emitConditionalJump(loop.condition(), lblEnd);
|
||||||
|
// 构建循环体
|
||||||
|
buildStatements(loop.body());
|
||||||
|
// 更新部分(如 for 的 i++)
|
||||||
|
if (loop.update() != null) build(loop.update());
|
||||||
|
|
||||||
|
// 跳回循环起点
|
||||||
|
InstructionFactory.jmp(ctx, lblStart);
|
||||||
|
// 循环结束标签
|
||||||
|
InstructionFactory.label(ctx, lblEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建分支语句(if/else)。
|
||||||
|
* 处理流程:条件判断 → then 分支 → else 分支(可选)。
|
||||||
|
* @param ifNode if 语句节点
|
||||||
|
*/
|
||||||
|
private void buildIf(IfNode ifNode) {
|
||||||
|
String lblElse = ctx.newLabel();
|
||||||
|
String lblEnd = ctx.newLabel();
|
||||||
|
// 条件不成立则跳转到 else
|
||||||
|
emitConditionalJump(ifNode.condition(), lblElse);
|
||||||
|
|
||||||
|
// then 分支
|
||||||
|
buildStatements(ifNode.thenBranch());
|
||||||
|
// then 分支执行完后直接跳到结束
|
||||||
|
InstructionFactory.jmp(ctx, lblEnd);
|
||||||
|
|
||||||
|
// else 分支(可能为空)
|
||||||
|
InstructionFactory.label(ctx, lblElse);
|
||||||
|
if (ifNode.elseBranch() != null) buildStatements(ifNode.elseBranch());
|
||||||
|
// 结束标签
|
||||||
|
InstructionFactory.label(ctx, lblEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件跳转指令的生成。
|
||||||
|
* 如果是二元比较表达式,直接使用对应比较操作码;否则等价于与 0 比较。
|
||||||
|
* @param cond 条件表达式
|
||||||
|
* @param falseLabel 条件不成立时跳转到的标签
|
||||||
|
*/
|
||||||
|
private void emitConditionalJump(ExpressionNode cond, String falseLabel) {
|
||||||
|
if (cond instanceof BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right)
|
||||||
|
&& ExpressionUtils.isComparisonOperator(operator)) {
|
||||||
|
// 如果是比较操作(如 ==, >, <),直接生成对应的条件跳转
|
||||||
|
IRVirtualRegister a = expr.build(left);
|
||||||
|
IRVirtualRegister b = expr.build(right);
|
||||||
|
// 获取反向比较操作码
|
||||||
|
IROpCode falseOp = IROpCodeUtils.invert(ExpressionUtils.cmpOp(operator));
|
||||||
|
InstructionFactory.cmpJump(ctx, falseOp, a, b, falseLabel);
|
||||||
|
} else {
|
||||||
|
// 否则将 cond 与 0 比较,相等则跳转
|
||||||
|
IRVirtualRegister condReg = expr.build(cond);
|
||||||
|
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
|
||||||
|
InstructionFactory.cmpJump(ctx, IROpCode.CMP_EQ, condReg, zero, falseLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
144
src/main/java/org/jcnc/snow/compiler/ir/core/IRFunction.java
Normal file
144
src/main/java/org/jcnc/snow/compiler/ir/core/IRFunction.java
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示单个函数的中间表示(IR)。
|
||||||
|
* <p>
|
||||||
|
* IRFunction 跟踪代码生成和优化所需的所有信息,
|
||||||
|
* 包括函数标识符、IR 指令序列、
|
||||||
|
* 声明参数列表以及生成唯一虚拟寄存器的机制。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class IRFunction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 函数名,对应源级函数的标识。
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IR 指令列表,组成函数体。
|
||||||
|
*/
|
||||||
|
private final List<IRInstruction> body = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于生成新的虚拟寄存器编号的计数器。
|
||||||
|
*/
|
||||||
|
private int regCounter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正式参数所对应的虚拟寄存器列表,按声明顺序排列。
|
||||||
|
*/
|
||||||
|
private final List<IRVirtualRegister> parameters = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个具有指定名称的 IRFunction 实例。
|
||||||
|
*
|
||||||
|
* @param name 要关联的函数名称
|
||||||
|
*/
|
||||||
|
public IRFunction(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分配一个新的虚拟寄存器。
|
||||||
|
* 每次调用会生成一个带有唯一编号的 IRVirtualRegister。
|
||||||
|
*
|
||||||
|
* @return 新分配的虚拟寄存器
|
||||||
|
*/
|
||||||
|
public IRVirtualRegister newRegister() {
|
||||||
|
return new IRVirtualRegister(regCounter++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将一个虚拟寄存器添加到函数的正式参数列表中。
|
||||||
|
* <p>
|
||||||
|
* 应按源函数签名中参数的声明顺序逐一调用此方法。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param vr 表示函数某个参数的虚拟寄存器
|
||||||
|
*/
|
||||||
|
public void addParameter(IRVirtualRegister vr) {
|
||||||
|
parameters.add(vr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取函数正式参数的只读列表。
|
||||||
|
*
|
||||||
|
* @return 按声明顺序排列的虚拟寄存器列表
|
||||||
|
*/
|
||||||
|
public List<IRVirtualRegister> parameters() {
|
||||||
|
return List.copyOf(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向函数体末尾追加一条 IR 指令。
|
||||||
|
*
|
||||||
|
* @param inst 要追加的 IRInstruction 实例
|
||||||
|
*/
|
||||||
|
public void add(IRInstruction inst) {
|
||||||
|
body.add(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取函数体中所有指令的只读列表。
|
||||||
|
*
|
||||||
|
* @return 表示函数体的 IRInstruction 列表
|
||||||
|
*/
|
||||||
|
public List<IRInstruction> body() {
|
||||||
|
return List.copyOf(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取函数的源级名称。
|
||||||
|
*
|
||||||
|
* @return 函数名称
|
||||||
|
*/
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取已分配的虚拟寄存器总数。
|
||||||
|
*
|
||||||
|
* @return 虚拟寄存器计数
|
||||||
|
*/
|
||||||
|
public int registerCount() {
|
||||||
|
return regCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以IR代码表示,示例:
|
||||||
|
* <pre>
|
||||||
|
* func 名称(%0, %1, ...) {
|
||||||
|
* 指令0
|
||||||
|
* 指令1
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return 函数的 IR 文本表示
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder("func ")
|
||||||
|
.append(name)
|
||||||
|
.append('(');
|
||||||
|
for (int i = 0; i < parameters.size(); i++) {
|
||||||
|
sb.append(parameters.get(i));
|
||||||
|
if (i < parameters.size() - 1) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(") {\n");
|
||||||
|
for (IRInstruction inst : body) {
|
||||||
|
sb.append(" ").append(inst).append('\n');
|
||||||
|
}
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRInstruction —— 所有 IR(中间表示)指令的抽象基类。
|
||||||
|
* <p>
|
||||||
|
* 本类定义了编译器中间表示系统中所有指令的基本结构和行为。
|
||||||
|
* 具体指令通过继承此类并实现各自的操作码(Opcode)和访问者方法,
|
||||||
|
* 以支持统一的指令处理和访问模式。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public abstract class IRInstruction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令的操作码(Opcode)。
|
||||||
|
* <p>
|
||||||
|
* 每个具体指令子类必须实现此方法,返回对应的 IROpCode 枚举值。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 表示指令类型的 IROpCode 实例
|
||||||
|
*/
|
||||||
|
public abstract IROpCode op();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指令的目标虚拟寄存器(destination register)。
|
||||||
|
* <p>
|
||||||
|
* 默认实现返回 null;只有具有目标寄存器的指令(如赋值、算术运算)
|
||||||
|
* 应重写此方法以返回相应的 IRVirtualRegister。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 目标虚拟寄存器,若无目标寄存器则返回 null
|
||||||
|
*/
|
||||||
|
public IRVirtualRegister dest() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指令的操作数列表。
|
||||||
|
* <p>
|
||||||
|
* 默认实现返回空列表;具体指令子类应根据需要重写此方法,
|
||||||
|
* 提供所有参与运算或调用的 IRValue 操作数集合。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 包含本指令所有操作数的列表
|
||||||
|
*/
|
||||||
|
public List<IRValue> operands() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受一个 IRVisitor 实例,实现访问者模式的入口。
|
||||||
|
* <p>
|
||||||
|
* 具体指令子类必须实现此方法,以便 IRVisitor 根据指令类型
|
||||||
|
* 调用相应的访问逻辑。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param visitor 实现 IRVisitor 接口的访问者对象
|
||||||
|
*/
|
||||||
|
public abstract void accept(IRVisitor visitor);
|
||||||
|
}
|
||||||
89
src/main/java/org/jcnc/snow/compiler/ir/core/IROpCode.java
Normal file
89
src/main/java/org/jcnc/snow/compiler/ir/core/IROpCode.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code IROpCode} 枚举类型定义了中间表示(IR)层支持的全部操作码。
|
||||||
|
* <p>
|
||||||
|
* 每个操作码代表一种低层次、语义明确的中间指令,用于构建目标函数的中间表示。
|
||||||
|
* 这些操作涵盖了不同数据位宽的整数与浮点数算术运算、逻辑与比较操作、
|
||||||
|
* 数据加载与存储指令、控制流指令(如跳转、条件跳转、标签)、
|
||||||
|
* 以及函数调用与返回等功能。
|
||||||
|
* <p>
|
||||||
|
* 本枚举用于 {@link IRInstruction} 体系结构中,是 IR 指令识别和转换的核心部分,
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public enum IROpCode {
|
||||||
|
|
||||||
|
// 整型 ↔ 单精度
|
||||||
|
CONV_I32_TO_F32,
|
||||||
|
CONV_F32_TO_I32,
|
||||||
|
// 整型 ↔ 双精度
|
||||||
|
CONV_I32_TO_D64,
|
||||||
|
CONV_D64_TO_I32,
|
||||||
|
// 单精度 ↔ 双精度
|
||||||
|
CONV_F32_TO_D64,
|
||||||
|
CONV_D64_TO_F32,
|
||||||
|
|
||||||
|
|
||||||
|
/* ───── 算术运算(8位整数:byte)───── */
|
||||||
|
ADD_B8, // 8位整型加法:a = b + c
|
||||||
|
SUB_B8, // 8位整型减法:a = b - c
|
||||||
|
MUL_B8, // 8位整型乘法:a = b * c
|
||||||
|
DIV_B8, // 8位整型除法:a = b / c
|
||||||
|
NEG_B8, // 8位整型取负:a = -b
|
||||||
|
|
||||||
|
/* ───── 算术运算(16位整数:short)───── */
|
||||||
|
ADD_S16, // 16位整型加法
|
||||||
|
SUB_S16, // 16位整型减法
|
||||||
|
MUL_S16, // 16位整型乘法
|
||||||
|
DIV_S16, // 16位整型除法
|
||||||
|
NEG_S16, // 16位整型取负
|
||||||
|
|
||||||
|
/* ───── 算术运算(32位整数:int)───── */
|
||||||
|
ADD_I32, // 32位整型加法
|
||||||
|
SUB_I32, // 32位整型减法
|
||||||
|
MUL_I32, // 32位整型乘法
|
||||||
|
DIV_I32, // 32位整型除法
|
||||||
|
NEG_I32, // 32位整型取负
|
||||||
|
|
||||||
|
/* ───── 算术运算(64位整数:long)───── */
|
||||||
|
ADD_L64, // 64位整型加法
|
||||||
|
SUB_L64, // 64位整型减法
|
||||||
|
MUL_L64, // 64位整型乘法
|
||||||
|
DIV_L64, // 64位整型除法
|
||||||
|
NEG_L64, // 64位整型取负
|
||||||
|
|
||||||
|
/* ───── 算术运算(32位浮点数:float)───── */
|
||||||
|
ADD_F32, // 32位浮点加法
|
||||||
|
SUB_F32, // 32位浮点减法
|
||||||
|
MUL_F32, // 32位浮点乘法
|
||||||
|
DIV_F32, // 32位浮点除法
|
||||||
|
NEG_F32, // 32位浮点取负
|
||||||
|
|
||||||
|
/* ───── 算术运算(64位浮点数:double)───── */
|
||||||
|
ADD_D64, // 64位浮点加法
|
||||||
|
SUB_D64, // 64位浮点减法
|
||||||
|
MUL_D64, // 64位浮点乘法
|
||||||
|
DIV_D64, // 64位浮点除法
|
||||||
|
NEG_D64, // 64位浮点取负
|
||||||
|
|
||||||
|
/* ───── 逻辑与比较运算指令 ───── */
|
||||||
|
CMP_EQ, // 相等比较:a == b
|
||||||
|
CMP_NE, // 不等比较:a != b
|
||||||
|
CMP_LT, // 小于比较:a < b
|
||||||
|
CMP_GT, // 大于比较:a > b
|
||||||
|
CMP_LE, // 小于等于:a <= b
|
||||||
|
CMP_GE, // 大于等于:a >= b
|
||||||
|
|
||||||
|
/* ───── 数据访问与常量操作 ───── */
|
||||||
|
LOAD, // 从内存加载数据至寄存器
|
||||||
|
STORE, // 将寄存器数据写回内存
|
||||||
|
CONST, // 将常量写入目标寄存器
|
||||||
|
|
||||||
|
/* ───── 控制流指令 ───── */
|
||||||
|
JUMP, // 无条件跳转至标签
|
||||||
|
LABEL, // 标签定义
|
||||||
|
|
||||||
|
/* ───── 函数调用与返回 ───── */
|
||||||
|
CALL, // 函数调用
|
||||||
|
RET // 函数返回
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.core;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作符与IR操作码映射表,统一管理所有类型的算术和比较操作映射。
|
||||||
|
*/
|
||||||
|
public final class IROpCodeMappings {
|
||||||
|
private IROpCodeMappings() {} // 禁止实例化
|
||||||
|
|
||||||
|
// 8位整型运算符映射
|
||||||
|
public static final Map<String, IROpCode> OP_I8 = Map.of(
|
||||||
|
"+", IROpCode.ADD_B8, "-", IROpCode.SUB_B8,
|
||||||
|
"*", IROpCode.MUL_B8, "/", IROpCode.DIV_B8
|
||||||
|
);
|
||||||
|
// 16位整型
|
||||||
|
public static final Map<String, IROpCode> OP_I16 = Map.of(
|
||||||
|
"+", IROpCode.ADD_S16, "-", IROpCode.SUB_S16,
|
||||||
|
"*", IROpCode.MUL_S16, "/", IROpCode.DIV_S16
|
||||||
|
);
|
||||||
|
// 32位整型
|
||||||
|
public static final Map<String, IROpCode> OP_I32 = Map.of(
|
||||||
|
"+", IROpCode.ADD_I32, "-", IROpCode.SUB_I32,
|
||||||
|
"*", IROpCode.MUL_I32, "/", IROpCode.DIV_I32
|
||||||
|
);
|
||||||
|
// 64位长整型
|
||||||
|
public static final Map<String, IROpCode> OP_L64 = Map.of(
|
||||||
|
"+", IROpCode.ADD_L64, "-", IROpCode.SUB_L64,
|
||||||
|
"*", IROpCode.MUL_L64, "/", IROpCode.DIV_L64
|
||||||
|
);
|
||||||
|
// 32位浮点型
|
||||||
|
public static final Map<String, IROpCode> OP_F32 = Map.of(
|
||||||
|
"+", IROpCode.ADD_F32, "-", IROpCode.SUB_F32,
|
||||||
|
"*", IROpCode.MUL_F32, "/", IROpCode.DIV_F32
|
||||||
|
);
|
||||||
|
// 64位双精度浮点型
|
||||||
|
public static final Map<String, IROpCode> OP_D64 = Map.of(
|
||||||
|
"+", IROpCode.ADD_D64, "-", IROpCode.SUB_D64,
|
||||||
|
"*", IROpCode.MUL_D64, "/", IROpCode.DIV_D64
|
||||||
|
);
|
||||||
|
// 比较操作符映射
|
||||||
|
public static final Map<String, IROpCode> CMP = Map.of(
|
||||||
|
"==", IROpCode.CMP_EQ,
|
||||||
|
"!=", IROpCode.CMP_NE,
|
||||||
|
"<", IROpCode.CMP_LT,
|
||||||
|
">", IROpCode.CMP_GT,
|
||||||
|
"<=", IROpCode.CMP_LE,
|
||||||
|
">=", IROpCode.CMP_GE
|
||||||
|
);
|
||||||
|
}
|
||||||
59
src/main/java/org/jcnc/snow/compiler/ir/core/IRPrinter.java
Normal file
59
src/main/java/org/jcnc/snow/compiler/ir/core/IRPrinter.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.IRAddInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.IRJumpInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.IRReturnInstruction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code IRPrinter} 是一个用于打印 IR 指令的访问者实现。
|
||||||
|
* <p>
|
||||||
|
* 本类实现 {@link IRVisitor} 接口,通过覆盖各类指令的访问方法,
|
||||||
|
* 提供对不同类型 IR 指令的格式化输出,通常用于调试或测试。
|
||||||
|
* 默认行为是在控制台(System.out)输出指令的基本信息。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 可通过继承该类进一步扩展对更多指令类型的支持,或重写输出格式以适配不同的前端/后端需求。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public abstract class IRPrinter implements IRVisitor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问 {@link IRAddInstruction} 加法指令。
|
||||||
|
* <p>
|
||||||
|
* 默认输出形式为 "Add: <inst>",其中 <inst> 为指令对象的字符串表示。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param inst 加法 IR 指令实例
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visit(IRAddInstruction inst) {
|
||||||
|
System.out.println("Add: " + inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问 {@link IRJumpInstruction} 跳转指令。
|
||||||
|
* <p>
|
||||||
|
* 默认输出形式为 "Jump: <inst>"。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param inst 跳转 IR 指令实例
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visit(IRJumpInstruction inst) {
|
||||||
|
System.out.println("Jump: " + inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问 {@link IRReturnInstruction} 返回指令。
|
||||||
|
* <p>
|
||||||
|
* 默认输出形式为 "Return: <inst>"。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param inst 返回 IR 指令实例
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visit(IRReturnInstruction inst) {
|
||||||
|
System.out.println("Return: " + inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
62
src/main/java/org/jcnc/snow/compiler/ir/core/IRProgram.java
Normal file
62
src/main/java/org/jcnc/snow/compiler/ir/core/IRProgram.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.core;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code IRProgram} 表示一份完整的中间表示(Intermediate Representation, IR)程序。
|
||||||
|
* <p>
|
||||||
|
* 它作为编译器后端处理阶段的核心结构,承载所有由源代码翻译得到的 {@link IRFunction} 实例,
|
||||||
|
* 形成整体性的中间表示单元,便于进行后续的优化、目标代码生成或静态分析。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public final class IRProgram {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储程序中所有函数的有序集合。
|
||||||
|
*/
|
||||||
|
private final List<IRFunction> functions = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将一个 {@link IRFunction} 添加到程序中。
|
||||||
|
* <p>
|
||||||
|
* 函数会按添加顺序保留在内部集合中。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param irFunction 要加入的 IR 函数对象
|
||||||
|
*/
|
||||||
|
public void add(IRFunction irFunction) {
|
||||||
|
functions.add(irFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取程序中全部函数的只读视图。
|
||||||
|
* <p>
|
||||||
|
* 外部调用者无法通过返回的列表修改函数集合,从而确保封装性与结构完整性。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 不可变的函数列表
|
||||||
|
*/
|
||||||
|
public List<IRFunction> functions() {
|
||||||
|
return Collections.unmodifiableList(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该 IR 程序的字符串形式。
|
||||||
|
* <p>
|
||||||
|
* 每个函数按其 {@code toString()} 表示输出,换行分隔。
|
||||||
|
* 通常用于调试与日志输出。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 表示整个 IR 程序的格式化字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (IRFunction f : functions) {
|
||||||
|
sb.append(f).append('\n');
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/main/java/org/jcnc/snow/compiler/ir/core/IRValue.java
Normal file
29
src/main/java/org/jcnc/snow/compiler/ir/core/IRValue.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRLabel;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code IRValue} 表示 IR 指令系统中可被操作的值类型。
|
||||||
|
* <p>
|
||||||
|
* 它定义了所有 IR 指令在使用操作数、参数、结果或跳转目标时的统一抽象。
|
||||||
|
* 实现该接口的类型可以作为 {@link IRInstruction} 中的操作数出现。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>当前支持的 IR 值类型包括:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link IRVirtualRegister}:虚拟寄存器,表示计算结果或中间变量</li>
|
||||||
|
* <li>{@link IRConstant}:常量值,表示不可变的字面量或数值</li>
|
||||||
|
* <li>{@link IRLabel}:标签,表示跳转指令的目标地址</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 该接口声明为 {@code sealed interface},限制只能被上述类型实现。
|
||||||
|
* 这种设计允许编译器对 {@code IRValue} 的使用进行静态穷尽性检查,
|
||||||
|
* 有助于提升类型安全性与维护性。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public sealed interface IRValue
|
||||||
|
permits IRVirtualRegister, IRConstant, IRLabel {
|
||||||
|
}
|
||||||
80
src/main/java/org/jcnc/snow/compiler/ir/core/IRVisitor.java
Normal file
80
src/main/java/org/jcnc/snow/compiler/ir/core/IRVisitor.java
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.instruction.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code IRVisitor} 是中间表示(IR)指令体系的访问者接口。
|
||||||
|
* <p>
|
||||||
|
* 它定义了访问者模式的核心机制,通过对每种 {@link IRInstruction} 子类
|
||||||
|
* 提供独立的 {@code visit} 方法,实现对指令的分发与处理。
|
||||||
|
* 不同的访问者实现可用于执行不同任务,例如:
|
||||||
|
* </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code IRPrinter}:打印指令内容</li>
|
||||||
|
* <li>{@code IROptimizer}:分析与重写 IR 以优化性能</li>
|
||||||
|
* <li>{@code IRCodeGenerator}:生成平台相关的机器码或汇编代码</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 每当添加新的 {@code IRInstruction} 子类,应同步扩展该接口,
|
||||||
|
* 以确保访问行为的一致性与完整性。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface IRVisitor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问加法指令(示例实现)。
|
||||||
|
*
|
||||||
|
* @param inst 加法指令实例
|
||||||
|
*/
|
||||||
|
void visit(IRAddInstruction inst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问跳转指令。
|
||||||
|
*
|
||||||
|
* @param inst 跳转指令实例
|
||||||
|
*/
|
||||||
|
void visit(IRJumpInstruction inst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问返回指令(无返回值)。
|
||||||
|
*
|
||||||
|
* @param inst 返回指令实例
|
||||||
|
*/
|
||||||
|
void visit(IRReturnInstruction inst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问二元运算指令(如加减乘除等)。
|
||||||
|
*
|
||||||
|
* @param inst 二元运算 IR 指令实例
|
||||||
|
*/
|
||||||
|
void visit(BinaryOperationInstruction inst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问加载常量的指令。
|
||||||
|
*
|
||||||
|
* @param inst 常量加载指令实例
|
||||||
|
*/
|
||||||
|
void visit(LoadConstInstruction inst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问返回指令(支持返回值)。
|
||||||
|
*
|
||||||
|
* @param inst 通用返回指令实例
|
||||||
|
*/
|
||||||
|
void visit(ReturnInstruction inst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问一元运算指令(如取负等)。
|
||||||
|
*
|
||||||
|
* @param inst 一元运算指令实例
|
||||||
|
*/
|
||||||
|
void visit(UnaryOperationInstruction inst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问函数调用指令。
|
||||||
|
*
|
||||||
|
* @param instruction 函数调用指令实例
|
||||||
|
*/
|
||||||
|
void visit(CallInstruction instruction);
|
||||||
|
}
|
||||||
37
src/main/java/org/jcnc/snow/compiler/ir/doc/README.md
Normal file
37
src/main/java/org/jcnc/snow/compiler/ir/doc/README.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Snow Compiler - IR 模块
|
||||||
|
|
||||||
|
> Snow 编译器的中间表示模块 —— 负责构建、组织和输出中间表示指令(IR)
|
||||||
|
|
||||||
|
## 项目简介
|
||||||
|
|
||||||
|
**IR(Intermediate Representation)** 是 [Snow 编译器]() 项目的核心模块,承担中间表示的构建、组织与管理任务。
|
||||||
|
它用于在前端语法分析与后端目标代码生成之间,提供结构清晰、便于优化和转换的抽象表示形式。
|
||||||
|
|
||||||
|
IR 模块以类 SSA(Static Single Assignment)形式设计,通过统一的指令体系、虚拟寄存器模型和构建器架构,实现了良好的表达力与可扩展性,为后续优化和代码生成阶段打下基础。
|
||||||
|
|
||||||
|
## 核心功能
|
||||||
|
|
||||||
|
* **统一的中间表示模型**:表达控制流与数据流,支持函数、指令、值等核心结构
|
||||||
|
* **IR 构建器体系**:模块化构建函数、表达式与语句 IR,简化前端对接
|
||||||
|
* **灵活的指令层级结构**:支持二元操作、跳转、返回等多种基本指令
|
||||||
|
* **寄存器与常量模型**:统一管理虚拟寄存器、常量、标签等值类型
|
||||||
|
* **IR 打印与调试支持**:辅助输出 IR 文本格式,支持可视化与调试
|
||||||
|
|
||||||
|
## 模块结构
|
||||||
|
|
||||||
|
```
|
||||||
|
ir/
|
||||||
|
├── builder/ // 构建器模块:负责构造表达式、函数与语句的 IR
|
||||||
|
├── core/ // 核心定义:IR 基础结构,如函数、指令、程序、访问器等
|
||||||
|
├── instruction/ // 指令实现:具体的 IR 指令类型(如加法、跳转、返回等)
|
||||||
|
├── utils/ // 工具模块:提供 IR 操作相关的辅助函数(如表达式工具、操作码工具等)
|
||||||
|
└── value/ // 值模型:常量、标签、虚拟寄存器等
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发环境
|
||||||
|
|
||||||
|
* JDK 23 或更高版本
|
||||||
|
* Maven 构建管理
|
||||||
|
* 推荐 IDE:IntelliJ IDEA
|
||||||
|
|
||||||
|
---
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BinaryOperationInstruction —— 表示一个二元运算指令,格式为:dest = lhs OP rhs
|
||||||
|
* <p>
|
||||||
|
* 该类用于描述形如 a = b + c 或 a = x * y 的二元运算指令。
|
||||||
|
* 运算类型(OP)由 {@link IROpCode} 指定,包括加法、减法、乘法、除法等。
|
||||||
|
* 左右操作数为 IRValue 类型,结果保存在目标虚拟寄存器 dest 中。
|
||||||
|
*/
|
||||||
|
public final class BinaryOperationInstruction extends IRInstruction {
|
||||||
|
|
||||||
|
/** 指令操作符,如 ADD_I32、SUB_I32 等,取自 IROpCode 枚举 */
|
||||||
|
private final IROpCode op;
|
||||||
|
|
||||||
|
/** 运算结果将写入的目标虚拟寄存器 */
|
||||||
|
private final IRVirtualRegister dest;
|
||||||
|
|
||||||
|
/** 运算的左操作数 */
|
||||||
|
private final IRValue lhs;
|
||||||
|
|
||||||
|
/** 运算的右操作数 */
|
||||||
|
private final IRValue rhs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,创建一个完整的二元运算指令。
|
||||||
|
*
|
||||||
|
* @param op 运算类型(加、减、乘、除等)
|
||||||
|
* @param dest 运算结果的目标寄存器
|
||||||
|
* @param lhs 左操作数
|
||||||
|
* @param rhs 右操作数
|
||||||
|
*/
|
||||||
|
public BinaryOperationInstruction(IROpCode op, IRVirtualRegister dest, IRValue lhs, IRValue rhs) {
|
||||||
|
this.op = op;
|
||||||
|
this.dest = dest;
|
||||||
|
this.lhs = lhs;
|
||||||
|
this.rhs = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令的操作符。
|
||||||
|
*
|
||||||
|
* @return 运算类型(IROpCode)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令的目标寄存器。
|
||||||
|
*
|
||||||
|
* @return 运算结果将写入的虚拟寄存器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IRVirtualRegister dest() {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令使用的操作数。
|
||||||
|
*
|
||||||
|
* @return 一个包含左、右操作数的列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<IRValue> operands() {
|
||||||
|
return List.of(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为字符串格式,便于调试与打印。
|
||||||
|
* 例:v1 = ADD_I32 v2, v3
|
||||||
|
*
|
||||||
|
* @return 指令的字符串表示形式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return dest + " = " + op + " " + lhs + ", " + rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受访问者对象,实现访问者模式分发逻辑。
|
||||||
|
*
|
||||||
|
* @param visitor 实现 IRVisitor 的访问者对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CallInstruction —— 表示一次函数调用,格式:dest = CALL functionName, arg1, arg2, ...
|
||||||
|
*/
|
||||||
|
public class CallInstruction extends IRInstruction {
|
||||||
|
private final IRVirtualRegister dest;
|
||||||
|
private final String functionName;
|
||||||
|
private final List<IRValue> arguments;
|
||||||
|
|
||||||
|
public CallInstruction(IRVirtualRegister dest, String functionName, List<IRValue> args) {
|
||||||
|
this.dest = dest;
|
||||||
|
this.functionName = functionName;
|
||||||
|
this.arguments = List.copyOf(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return IROpCode.CALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IRValue> operands() {
|
||||||
|
List<IRValue> ops = new ArrayList<>();
|
||||||
|
ops.add(dest);
|
||||||
|
ops.addAll(arguments);
|
||||||
|
return ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRVirtualRegister getDest() {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFunctionName() {
|
||||||
|
return functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IRValue> getArguments() {
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder(dest + " = CALL " + functionName);
|
||||||
|
for (IRValue arg : arguments) {
|
||||||
|
sb.append(", ").append(arg);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRAddInstruction —— 表示一个加法指令,形如:dest = lhs + rhs
|
||||||
|
* <p>
|
||||||
|
* 本类是一个具体的 IRInstruction 子类,表示将两个值相加,并将结果写入目标寄存器的操作。
|
||||||
|
* 虽然功能与通用的 {@link BinaryOperationInstruction} 类似,但它作为更简化明确的指令实现,
|
||||||
|
* 通常用于测试或示例用途,也可为特殊优化保留独立形式。
|
||||||
|
*/
|
||||||
|
public class IRAddInstruction extends IRInstruction {
|
||||||
|
|
||||||
|
/** 运算结果存放的目标虚拟寄存器 */
|
||||||
|
private final IRVirtualRegister dest;
|
||||||
|
|
||||||
|
/** 左操作数 */
|
||||||
|
private final IRValue lhs;
|
||||||
|
|
||||||
|
/** 右操作数 */
|
||||||
|
private final IRValue rhs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,创建加法指令实例。
|
||||||
|
*
|
||||||
|
* @param dest 运算结果的存储位置
|
||||||
|
* @param lhs 加法左操作数
|
||||||
|
* @param rhs 加法右操作数
|
||||||
|
*/
|
||||||
|
public IRAddInstruction(IRVirtualRegister dest, IRValue lhs, IRValue rhs) {
|
||||||
|
this.dest = dest;
|
||||||
|
this.lhs = lhs;
|
||||||
|
this.rhs = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该指令的操作码:ADD_I32。
|
||||||
|
*
|
||||||
|
* @return 加法操作码
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return IROpCode.ADD_I32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指令的目标寄存器。
|
||||||
|
*
|
||||||
|
* @return 运算结果存放的虚拟寄存器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IRVirtualRegister dest() {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取加法指令的两个操作数。
|
||||||
|
*
|
||||||
|
* @return 包含左、右操作数的列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<IRValue> operands() {
|
||||||
|
return List.of(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用访问者处理当前加法指令。
|
||||||
|
*
|
||||||
|
* @param visitor 实现 IRVisitor 的访问者对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回指令的字符串形式,方便调试。
|
||||||
|
* 例如:v1 = v2 + v3
|
||||||
|
*
|
||||||
|
* @return 字符串表示形式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return dest + " = " + lhs + " + " + rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* “比较 + 条件跳转” 复合指令:
|
||||||
|
* if ( left <cmpOp> right ) jump targetLabel;
|
||||||
|
* <p>
|
||||||
|
* 其中 cmpOp 只能是 IROpCode.CMP_* 六种比较操作码。
|
||||||
|
*/
|
||||||
|
public final class IRCompareJumpInstruction extends IRInstruction {
|
||||||
|
|
||||||
|
private final IROpCode cmpOp; // CMP_EQ / CMP_NE / CMP_LT / ...
|
||||||
|
private final IRVirtualRegister left, right; // 两个比较操作数
|
||||||
|
private final String targetLabel; // 跳转目标
|
||||||
|
|
||||||
|
public IRCompareJumpInstruction(IROpCode cmpOp,
|
||||||
|
IRVirtualRegister left,
|
||||||
|
IRVirtualRegister right,
|
||||||
|
String targetLabel) {
|
||||||
|
this.cmpOp = cmpOp;
|
||||||
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
|
this.targetLabel = targetLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return cmpOp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRVirtualRegister left() { return left; }
|
||||||
|
public IRVirtualRegister right() { return right; }
|
||||||
|
public String label() { return targetLabel; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return cmpOp + " " + left + ", " + right + " -> " + targetLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 暂无访问者实现,留空 */
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) { /* no-op */ }
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRJumpInstruction —— 表示一个无条件跳转(jump)的 IR 指令。
|
||||||
|
* <p>
|
||||||
|
* 该指令用于控制流结构中,实现无条件跳转到指定标签(label)。
|
||||||
|
* 是 if-else、循环、函数跳转等高级语言结构翻译到中间表示的重要组成部分。
|
||||||
|
*/
|
||||||
|
public class IRJumpInstruction extends IRInstruction {
|
||||||
|
|
||||||
|
/** 跳转目标的标签名 */
|
||||||
|
private final String label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,创建跳转指令。
|
||||||
|
*
|
||||||
|
* @param label 跳转目标标签的名称
|
||||||
|
*/
|
||||||
|
public IRJumpInstruction(String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令对应的操作码:JUMP。
|
||||||
|
*
|
||||||
|
* @return IROpCode.JUMP
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return IROpCode.JUMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取跳转目标标签名。
|
||||||
|
*
|
||||||
|
* @return 标签名称字符串
|
||||||
|
*/
|
||||||
|
public String label() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受访问者,用于访问者模式处理。
|
||||||
|
*
|
||||||
|
* @param visitor 实现 IRVisitor 的访问者实例
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指令转为字符串形式,便于打印与调试。
|
||||||
|
* 例如:jump L1
|
||||||
|
*
|
||||||
|
* @return 指令的字符串表示
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "jump " + label;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在 IR 中标记一个代码位置,用于 Jump / 条件跳转目标。
|
||||||
|
* <p>
|
||||||
|
* 生成到 VM 时并不会真正发出可执行指令,
|
||||||
|
* 仅在 {@code VMCodeGenerator} 内部被用来计算真实地址。
|
||||||
|
*/
|
||||||
|
public final class IRLabelInstruction extends IRInstruction {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public IRLabelInstruction(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return IROpCode.LABEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name + ":";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 目前尚未对 Label 做访问者处理,空实现即可 */
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRReturnInstruction —— 表示一个带返回值的返回(ret)指令。
|
||||||
|
* <p>
|
||||||
|
* 此指令用于函数结束时将某个值作为返回结果返回给调用者。
|
||||||
|
* 返回值可以是常量、寄存器或表达式的结果。
|
||||||
|
* 若不返回值,也可以扩展为 null 代表 void 函数。
|
||||||
|
*/
|
||||||
|
public class IRReturnInstruction extends IRInstruction {
|
||||||
|
|
||||||
|
/** 要返回的值,可以是常量、虚拟寄存器等 */
|
||||||
|
private final IRValue returnValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,创建返回指令。
|
||||||
|
*
|
||||||
|
* @param returnValue 函数的返回值
|
||||||
|
*/
|
||||||
|
public IRReturnInstruction(IRValue returnValue) {
|
||||||
|
this.returnValue = returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令的操作码:RET。
|
||||||
|
*
|
||||||
|
* @return IROpCode.RET,表示返回操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return IROpCode.RET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该指令的操作数列表(仅包含返回值)。
|
||||||
|
*
|
||||||
|
* @return 含一个元素的列表,即返回值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<IRValue> operands() {
|
||||||
|
return List.of(returnValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受访问者处理该指令,适用于访问者模式。
|
||||||
|
*
|
||||||
|
* @param visitor 实现了 IRVisitor 的访问者对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为字符串形式,便于调试与打印。
|
||||||
|
* 示例:ret v1
|
||||||
|
*
|
||||||
|
* @return 字符串形式的返回指令
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ret " + returnValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LoadConstInstruction —— 表示一个常量加载指令,格式为:dest = CONST k
|
||||||
|
* <p>
|
||||||
|
* 该指令的功能是将一个常量(字面量或编译期已知值)加载到一个虚拟寄存器中,
|
||||||
|
* 供后续指令使用。例如,在表达式计算、参数传递、初始化等场景中常用。
|
||||||
|
*/
|
||||||
|
public final class LoadConstInstruction extends IRInstruction {
|
||||||
|
|
||||||
|
/** 要加载的常量值,类型为 IRConstant */
|
||||||
|
private final IRConstant k;
|
||||||
|
|
||||||
|
/** 存放常量结果的目标虚拟寄存器 */
|
||||||
|
private final IRVirtualRegister dest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,创建常量加载指令。
|
||||||
|
*
|
||||||
|
* @param dest 存放常量的目标虚拟寄存器
|
||||||
|
* @param k 要加载的常量值
|
||||||
|
*/
|
||||||
|
public LoadConstInstruction(IRVirtualRegister dest, IRConstant k) {
|
||||||
|
this.dest = dest;
|
||||||
|
this.k = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令的操作码,固定为 CONST。
|
||||||
|
*
|
||||||
|
* @return IROpCode.CONST
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return IROpCode.CONST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指令的目标虚拟寄存器。
|
||||||
|
*
|
||||||
|
* @return 用于存放常量的寄存器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IRVirtualRegister dest() {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令的操作数(仅包含要加载的常量)。
|
||||||
|
*
|
||||||
|
* @return 含一个元素(k)的操作数列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<IRValue> operands() {
|
||||||
|
return List.of(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该指令的字符串形式,便于调试或打印。
|
||||||
|
* 例如:v1 = CONST 42
|
||||||
|
*
|
||||||
|
* @return 指令的字符串表示
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return dest + " = CONST " + k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受访问者模式处理。
|
||||||
|
*
|
||||||
|
* @param visitor 实现 IRVisitor 的访问者对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReturnInstruction —— 表示函数返回指令,格式:RET 或 RET <value>
|
||||||
|
* <p>
|
||||||
|
* 此类用于描述函数执行完毕后的返回操作。支持两种返回形式:
|
||||||
|
* - 无返回值(void):生成无参的 RET 指令
|
||||||
|
* - 有返回值:将指定虚拟寄存器中的值返回给调用者
|
||||||
|
* <p>
|
||||||
|
* 与 {@link IRReturnInstruction} 类似,但更通用,适配多种函数返回风格。
|
||||||
|
*/
|
||||||
|
public final class ReturnInstruction extends IRInstruction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回值所在的虚拟寄存器。
|
||||||
|
* 如果为 null,则代表函数无返回值(即 void)。
|
||||||
|
*/
|
||||||
|
private final IRVirtualRegister value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,创建返回指令实例。
|
||||||
|
*
|
||||||
|
* @param value 若函数有返回值,传入对应虚拟寄存器;
|
||||||
|
* 若为 void 函数,则传 null。
|
||||||
|
*/
|
||||||
|
public ReturnInstruction(IRVirtualRegister value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该指令的操作码类型:RET。
|
||||||
|
*
|
||||||
|
* @return IROpCode.RET
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return IROpCode.RET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令的操作数。
|
||||||
|
* 如果为 void 返回,则返回空列表;
|
||||||
|
* 否则返回一个仅包含返回寄存器的列表。
|
||||||
|
*
|
||||||
|
* @return 操作数列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<IRValue> operands() {
|
||||||
|
return value == null ? List.of() : List.of(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取返回值所在的虚拟寄存器(如有)。
|
||||||
|
*
|
||||||
|
* @return 返回值寄存器,或 null(表示 void)
|
||||||
|
*/
|
||||||
|
public IRVirtualRegister value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为字符串形式,便于调试与输出。
|
||||||
|
* - 无返回值:RET
|
||||||
|
* - 有返回值:RET v1
|
||||||
|
*
|
||||||
|
* @return 字符串表示的返回指令
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value == null ? "RET" : "RET " + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受访问者对象,实现访问者模式分发。
|
||||||
|
*
|
||||||
|
* @param visitor 实现 IRVisitor 的访问者
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UnaryOperationInstruction —— 表示一个一元运算指令,格式:dest = OP val
|
||||||
|
* <p>
|
||||||
|
* 用于对单个操作数 val 执行指定的一元运算 OP(例如取负 NEG),
|
||||||
|
* 并将结果写入目标虚拟寄存器 dest。
|
||||||
|
* <p>
|
||||||
|
* 支持的操作由 {@link IROpCode} 定义,目前常见的一元操作包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>NEG_I32 —— 整数取负:dest = -val</li>
|
||||||
|
* <li>(可扩展)逻辑非、按位非等</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public final class UnaryOperationInstruction extends IRInstruction {
|
||||||
|
|
||||||
|
/** 一元运算操作符(如 NEG_I32) */
|
||||||
|
private final IROpCode op;
|
||||||
|
|
||||||
|
/** 运算结果写入的目标虚拟寄存器 */
|
||||||
|
private final IRVirtualRegister dest;
|
||||||
|
|
||||||
|
/** 被操作的值(唯一操作数) */
|
||||||
|
private final IRValue val;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,创建一元运算指令。
|
||||||
|
*
|
||||||
|
* @param op 一元运算操作符
|
||||||
|
* @param dest 运算结果目标寄存器
|
||||||
|
* @param val 参与运算的操作数
|
||||||
|
*/
|
||||||
|
public UnaryOperationInstruction(IROpCode op, IRVirtualRegister dest, IRValue val) {
|
||||||
|
this.op = op;
|
||||||
|
this.dest = dest;
|
||||||
|
this.val = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令的操作码。
|
||||||
|
*
|
||||||
|
* @return 一元运算的操作码(如 NEG_I32)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IROpCode op() {
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该指令的目标寄存器。
|
||||||
|
*
|
||||||
|
* @return 运算结果的目标寄存器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IRVirtualRegister dest() {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指令的操作数(仅一个)。
|
||||||
|
*
|
||||||
|
* @return 单元素列表,仅包含 val
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<IRValue> operands() {
|
||||||
|
return List.of(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将该指令格式化为字符串,便于打印与调试。
|
||||||
|
* 形式:dest = OP val,例如:v1 = NEG v2
|
||||||
|
*
|
||||||
|
* @return 字符串形式的指令
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return dest + " = " + op + " " + val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受访问者访问该指令,实现访问者模式。
|
||||||
|
*
|
||||||
|
* @param visitor 访问者实例
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void accept(IRVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,202 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.utils;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCodeMappings;
|
||||||
|
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.BinaryExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.NumberLiteralNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表达式分析与运算符辅助工具类。
|
||||||
|
* <p>
|
||||||
|
* 主要功能包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>字面量常量的解析与类型推断</li>
|
||||||
|
* <li>自动匹配操作码</li>
|
||||||
|
* <li>表达式类型合并与判定</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public class ExpressionUtils {
|
||||||
|
|
||||||
|
/** 用于存储默认的类型后缀(如函数返回类型),线程隔离。 */
|
||||||
|
private static final ThreadLocal<Character> DEFAULT_SUFFIX =
|
||||||
|
ThreadLocal.withInitial(() -> '\0');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在进入函数 IR 构建前设置默认的类型后缀(比如函数返回类型)。
|
||||||
|
* @param suffix 默认后缀字符,如 'i', 'l', 'f', 'd' 等
|
||||||
|
*/
|
||||||
|
public static void setDefaultSuffix(char suffix) {
|
||||||
|
DEFAULT_SUFFIX.set(suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在函数 IR 构建结束后清除默认后缀,避免影响后续分析。
|
||||||
|
*/
|
||||||
|
public static void clearDefaultSuffix() {
|
||||||
|
DEFAULT_SUFFIX.set('\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析整数字面量字符串,自动去除类型后缀(b/s/l/f/d/B/S/L/F/D),并转换为 int。
|
||||||
|
*
|
||||||
|
* @param literal 字面量字符串,如 "123", "123l", "42B"
|
||||||
|
* @return 解析得到的 int 整数
|
||||||
|
*/
|
||||||
|
public static int parseIntSafely(String literal) {
|
||||||
|
// 去掉类型后缀,只保留数字部分
|
||||||
|
String digits = literal.replaceAll("[bslfdBSDLF]$", "");
|
||||||
|
return Integer.parseInt(digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据数字字面量字符串自动判断类型,生成对应类型的 IRConstant。
|
||||||
|
* 支持 b/s/l/f/d 类型后缀与浮点格式,自动分配合适类型。
|
||||||
|
*
|
||||||
|
* @param value 字面量字符串(如 "1", "2l", "3.14f", "5D")
|
||||||
|
* @return 生成的 IRConstant 对象,包含正确类型
|
||||||
|
*/
|
||||||
|
public static IRConstant buildNumberConstant(String value) {
|
||||||
|
char suffix = value.isEmpty() ? '\0' : Character.toLowerCase(value.charAt(value.length() - 1));
|
||||||
|
String digits = switch (suffix) {
|
||||||
|
case 'b','s','l','f','d' -> value.substring(0, value.length() - 1);
|
||||||
|
default -> value;
|
||||||
|
};
|
||||||
|
// 根据类型后缀或数值格式创建常量
|
||||||
|
return switch (suffix) {
|
||||||
|
case 'b' -> new IRConstant(Byte.parseByte(digits));
|
||||||
|
case 's' -> new IRConstant(Short.parseShort(digits));
|
||||||
|
case 'l' -> new IRConstant(Long.parseLong(digits));
|
||||||
|
case 'f' -> new IRConstant(Float.parseFloat(digits));
|
||||||
|
case 'd' -> new IRConstant(Double.parseDouble(digits));
|
||||||
|
default -> looksLikeFloat(digits)
|
||||||
|
? new IRConstant(Double.parseDouble(digits))
|
||||||
|
: new IRConstant(Integer.parseInt(digits));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =================== 类型推断与操作符匹配 =================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归推断单个表达式节点的类型后缀(b/s/l/f/d)。
|
||||||
|
* 对于二元表达式,将左右两侧的类型自动提升合并,遵循优先级顺序:d > f > l > s > b > '\0'。
|
||||||
|
*
|
||||||
|
* @param node 表达式节点
|
||||||
|
* @return 类型后缀字符,b/s/l/f/d 或 '\0'
|
||||||
|
*/
|
||||||
|
private static char typeChar(ExpressionNode node) {
|
||||||
|
// 字面量节点,直接判断最后一位
|
||||||
|
if (node instanceof NumberLiteralNode(String value)) {
|
||||||
|
char last = Character.toLowerCase(value.charAt(value.length() - 1));
|
||||||
|
return switch (last) {
|
||||||
|
case 'b', 's', 'l', 'f', 'd' -> last;
|
||||||
|
default -> looksLikeFloat(value) ? 'd' : '\0';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 二元表达式,递归判断左右子节点
|
||||||
|
if (node instanceof BinaryExpressionNode bin) {
|
||||||
|
char l = typeChar(bin.left());
|
||||||
|
char r = typeChar(bin.right());
|
||||||
|
return maxTypeChar(l, r);
|
||||||
|
}
|
||||||
|
// 其他情况(如变量节点),暂不处理,默认返回 '\0'
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推断两个表达式节点合并后的类型后缀。
|
||||||
|
* 返回优先级更高的类型后缀字符。
|
||||||
|
*
|
||||||
|
* @param left 左表达式节点
|
||||||
|
* @param right 右表达式节点
|
||||||
|
* @return 合并后类型的后缀字符
|
||||||
|
*/
|
||||||
|
public static char resolveSuffix(ExpressionNode left, ExpressionNode right) {
|
||||||
|
return maxTypeChar(typeChar(left), typeChar(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在两个类型后缀中选取精度更高的一个。
|
||||||
|
* 优先级依次为:d > f > l > s > b > '\0'
|
||||||
|
*
|
||||||
|
* @param l 左类型后缀
|
||||||
|
* @param r 右类型后缀
|
||||||
|
* @return 更高优先级的类型后缀字符
|
||||||
|
*/
|
||||||
|
private static char maxTypeChar(char l, char r) {
|
||||||
|
if (l == 'd' || r == 'd') return 'd';
|
||||||
|
if (l == 'f' || r == 'f') return 'f';
|
||||||
|
if (l == 'l' || r == 'l') return 'l';
|
||||||
|
if (l == 's' || r == 's') return 's';
|
||||||
|
if (l == 'b' || r == 'b') return 'b';
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断给定字符串是否为比较运算符(如 >, <, == 等)。
|
||||||
|
*
|
||||||
|
* @param op 操作符字符串
|
||||||
|
* @return 如果是比较操作符返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
public static boolean isComparisonOperator(String op) {
|
||||||
|
return IROpCodeMappings.CMP.containsKey(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取比较操作符对应的中间表示操作码(IROpCode)。
|
||||||
|
*
|
||||||
|
* @param op 比较操作符字符串
|
||||||
|
* @return 对应的 IROpCode,如果不存在则返回 null
|
||||||
|
*/
|
||||||
|
public static IROpCode cmpOp(String op) {
|
||||||
|
return IROpCodeMappings.CMP.get(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据操作符和两侧表达式自动选择正确的 IROpCode。
|
||||||
|
* 首先根据参与表达式类型推断后缀,若无法推断则回退到函数默认类型,
|
||||||
|
* 还无法推断则默认使用 i32(32位整型)。
|
||||||
|
*
|
||||||
|
* @param op 操作符字符串,如 "+"
|
||||||
|
* @param left 左侧表达式节点
|
||||||
|
* @param right 右侧表达式节点
|
||||||
|
* @return 匹配的 IROpCode,如果不存在则返回 null
|
||||||
|
*/
|
||||||
|
public static IROpCode resolveOpCode(String op,
|
||||||
|
ExpressionNode left,
|
||||||
|
ExpressionNode right) {
|
||||||
|
|
||||||
|
/* 1) 尝试从参与者常量字面量推断 */
|
||||||
|
char suffix = resolveSuffix(left, right);
|
||||||
|
|
||||||
|
/* 2) 若无法推断,退回到函数返回类型(DEFAULT_SUFFIX) */
|
||||||
|
if (suffix == '\0') {
|
||||||
|
suffix = DEFAULT_SUFFIX.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3) 再次失败则默认为 i32 */
|
||||||
|
Map<String, IROpCode> table = switch (suffix) {
|
||||||
|
case 'b' -> IROpCodeMappings.OP_I8;
|
||||||
|
case 's' -> IROpCodeMappings.OP_I16;
|
||||||
|
case 'l' -> IROpCodeMappings.OP_L64;
|
||||||
|
case 'f' -> IROpCodeMappings.OP_F32;
|
||||||
|
case 'd' -> IROpCodeMappings.OP_D64;
|
||||||
|
default -> IROpCodeMappings.OP_I32;
|
||||||
|
};
|
||||||
|
|
||||||
|
return table.get(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字符串是否为浮点数形式(即包含小数点或科学计数法 e/E)。
|
||||||
|
*
|
||||||
|
* @param digits 数字字符串
|
||||||
|
* @return 如果看起来像浮点数则返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
private static boolean looksLikeFloat(String digits) {
|
||||||
|
return digits.indexOf('.') >= 0 || digits.indexOf('e') >= 0 || digits.indexOf('E') >= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.utils;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IR 操作码辅助工具。
|
||||||
|
*/
|
||||||
|
public class IROpCodeUtils {
|
||||||
|
private static final Map<IROpCode, IROpCode> INVERT = Map.of(
|
||||||
|
IROpCode.CMP_EQ, IROpCode.CMP_NE,
|
||||||
|
IROpCode.CMP_NE, IROpCode.CMP_EQ,
|
||||||
|
IROpCode.CMP_LT, IROpCode.CMP_GE,
|
||||||
|
IROpCode.CMP_GE, IROpCode.CMP_LT,
|
||||||
|
IROpCode.CMP_GT, IROpCode.CMP_LE,
|
||||||
|
IROpCode.CMP_LE, IROpCode.CMP_GT
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取给定比较操作的“相反操作码”。
|
||||||
|
*/
|
||||||
|
public static IROpCode invert(IROpCode op) {
|
||||||
|
return INVERT.get(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.value;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRConstant —— 表示中间表示(IR)系统中的常量值。
|
||||||
|
* <p>
|
||||||
|
* 常量用于表示在编译期间已知的不可变值,例如字面整数、浮点数、布尔值或字符串。
|
||||||
|
* 与 {@link IRVirtualRegister} 不同,常量不需要通过寄存器存储,
|
||||||
|
* 可直接作为 IR 指令的操作数使用。
|
||||||
|
* <p>
|
||||||
|
* 典型应用:
|
||||||
|
* - 加载常量指令:v1 = CONST 42
|
||||||
|
* - 计算表达式:v2 = ADD v1, 100
|
||||||
|
*/
|
||||||
|
public record IRConstant(Object value) implements IRValue {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将常量值转换为字符串,用于打印 IR 指令或调试输出。
|
||||||
|
* <p>
|
||||||
|
* 例如:
|
||||||
|
* - 整数常量:42
|
||||||
|
* - 字符串常量:"hello"
|
||||||
|
*
|
||||||
|
* @return 常量的字符串表示
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/main/java/org/jcnc/snow/compiler/ir/value/IRLabel.java
Normal file
29
src/main/java/org/jcnc/snow/compiler/ir/value/IRLabel.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.value;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRLabel —— 表示中间表示(IR)系统中的跳转目标标签。
|
||||||
|
* <p>
|
||||||
|
* 标签用于控制流指令(如 JUMP等)中,
|
||||||
|
* 作为程序执行跳转的目的地,是 IR 控制流图(CFG)中的基本构建块。
|
||||||
|
* <p>
|
||||||
|
* 每个标签由一个唯一的名称(String name)标识,
|
||||||
|
* 可用于生成目标代码中的符号标签或跳转地址。
|
||||||
|
* <p>
|
||||||
|
* 该类实现了 {@link IRValue} 接口,因此也可被视为指令操作数,
|
||||||
|
* 在某些 IRInstruction 中以参数形式出现(如条件跳转目标)。
|
||||||
|
*/
|
||||||
|
public record IRLabel(String name) implements IRValue {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回标签的字符串形式,便于打印或调试。
|
||||||
|
* 通常表示为带冒号的形式,例如 "L1:"。
|
||||||
|
*
|
||||||
|
* @return 格式化后的标签字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name + ":";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package org.jcnc.snow.compiler.ir.value;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRVirtualRegister —— 表示一个静态单赋值(SSA)形式的虚拟寄存器。
|
||||||
|
* <p>
|
||||||
|
* 在 IR 系统中,虚拟寄存器用于存储每个中间计算结果,是 SSA(Static Single Assignment)形式的核心。
|
||||||
|
* 每个虚拟寄存器在程序中只被赋值一次,其值来源于一条明确的指令输出。
|
||||||
|
* <p>
|
||||||
|
* 特点:
|
||||||
|
* <ul>
|
||||||
|
* <li>每个寄存器有唯一编号 {@code id},由 {@code IRFunction.newRegister()} 自动生成</li>
|
||||||
|
* <li>实现 {@link IRValue} 接口,可作为 IRInstruction 的操作数</li>
|
||||||
|
* <li>具备良好的打印与调试格式:%id</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* 适用于表达式求值、参数传递、函数返回值、临时变量等所有中间值场景。
|
||||||
|
*
|
||||||
|
* @param id 寄存器的唯一编号,通常从 0 开始递增
|
||||||
|
*/
|
||||||
|
public record IRVirtualRegister(int id) implements IRValue {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将虚拟寄存器转换为字符串格式,方便输出和调试。
|
||||||
|
* 格式为:%<id>,例如 %3 表示编号为 3 的虚拟寄存器。
|
||||||
|
*
|
||||||
|
* @return 格式化的字符串表示
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "%" + id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.base;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code TokenScanner} 接口定义了所有词法扫描器的统一行为规范。
|
||||||
|
* <p>
|
||||||
|
* 编译器前端中的词法分析阶段将源代码字符流解析为语义上有意义的记号(token),
|
||||||
|
* 每种类型的记号(如标识符、数字、字符串、符号等)应有对应的 {@code TokenScanner} 实现类。
|
||||||
|
* 词法分析器根据当前输入字符判断并分派给能处理该字符的扫描器进行处理。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 实现类通常会结合 {@link LexerContext} 提供的流访问与状态接口,
|
||||||
|
* 完成一个完整 Token 的提取,并将其添加到结果集中。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface TokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前字符是否可以由该扫描器处理。
|
||||||
|
* <p>
|
||||||
|
* 词法分析器会按顺序查询已注册的 {@code TokenScanner} 实例,
|
||||||
|
* 使用该方法决定当前字符是否可由某个扫描器识别与处理。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param c 当前读取的字符
|
||||||
|
* @param ctx 当前词法分析上下文,提供字符流和辅助状态
|
||||||
|
* @return 若该扫描器可处理当前字符,则返回 {@code true},否则返回 {@code false}
|
||||||
|
*/
|
||||||
|
boolean canHandle(char c, LexerContext ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理以当前字符为起始的 token,并将扫描结果添加至 tokens 列表中。
|
||||||
|
* <p>
|
||||||
|
* 扫描器需消费一定数量的字符,构建合法的 {@link Token} 实例,
|
||||||
|
* 并调用 {@code tokens.add(...)} 添加至结果集中。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @param tokens 存储扫描结果的 token 列表
|
||||||
|
*/
|
||||||
|
void handle(LexerContext ctx, List<Token> tokens);
|
||||||
|
}
|
||||||
@ -0,0 +1,125 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code LexerContext} 是词法分析阶段的上下文状态管理器。
|
||||||
|
* <p>
|
||||||
|
* 该类提供对源代码字符流的读取访问,追踪当前行号与列号,
|
||||||
|
* 并支持字符匹配、回看与指针推进等操作,是 {@link TokenScanner} 实现进行词法识别的重要支撑工具。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 所有源代码输入在构造时统一将 Windows 风格的换行符(\r\n)转换为 Unix 风格(\n),
|
||||||
|
* 保证换行行为一致性。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class LexerContext {
|
||||||
|
|
||||||
|
/** 源代码字符串,换行符已标准化为 \n */
|
||||||
|
private final String source;
|
||||||
|
|
||||||
|
/** 当前扫描位置(从 0 开始的偏移) */
|
||||||
|
private int pos = 0;
|
||||||
|
|
||||||
|
/** 当前行号,从 1 开始 */
|
||||||
|
private int line = 1;
|
||||||
|
|
||||||
|
/** 当前列号,从 1 开始 */
|
||||||
|
private int col = 1;
|
||||||
|
|
||||||
|
/** 上一个字符对应的列号(用于位置精确记录) */
|
||||||
|
private int lastCol = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个新的 {@code LexerContext} 实例,并标准化换行符。
|
||||||
|
*
|
||||||
|
* @param source 原始源代码字符串
|
||||||
|
*/
|
||||||
|
public LexerContext(String source) {
|
||||||
|
this.source = source.replace("\r\n", "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否已读取到源代码末尾。
|
||||||
|
*
|
||||||
|
* @return 若已结束,返回 {@code true};否则返回 {@code false}
|
||||||
|
*/
|
||||||
|
public boolean isAtEnd() {
|
||||||
|
return pos >= source.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消费当前字符并前进一个位置,自动更新行列信息。
|
||||||
|
*
|
||||||
|
* @return 当前字符,若已结束则返回空字符('\0')
|
||||||
|
*/
|
||||||
|
public char advance() {
|
||||||
|
if (isAtEnd()) return '\0';
|
||||||
|
char c = source.charAt(pos++);
|
||||||
|
lastCol = col;
|
||||||
|
if (c == '\n') {
|
||||||
|
line++;
|
||||||
|
col = 1;
|
||||||
|
} else {
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看当前位置的字符,但不前进。
|
||||||
|
*
|
||||||
|
* @return 当前字符,若结束则返回空字符
|
||||||
|
*/
|
||||||
|
public char peek() {
|
||||||
|
return isAtEnd() ? '\0' : source.charAt(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看下一个字符,但不改变位置。
|
||||||
|
*
|
||||||
|
* @return 下一个字符,若结束则返回空字符
|
||||||
|
*/
|
||||||
|
public char peekNext() {
|
||||||
|
return pos + 1 >= source.length() ? '\0' : source.charAt(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若当前字符与期望字符相同,则前进并返回 {@code true},否则不动并返回 {@code false}。
|
||||||
|
*
|
||||||
|
* @param expected 期待匹配的字符
|
||||||
|
* @return 是否匹配成功并消费
|
||||||
|
*/
|
||||||
|
public boolean match(char expected) {
|
||||||
|
if (isAtEnd() || source.charAt(pos) != expected) return false;
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前位置的行号。
|
||||||
|
*
|
||||||
|
* @return 当前行号(从 1 开始)
|
||||||
|
*/
|
||||||
|
public int getLine() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前位置的列号。
|
||||||
|
*
|
||||||
|
* @return 当前列号(从 1 开始)
|
||||||
|
*/
|
||||||
|
public int getCol() {
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取上一个字符所在的列号。
|
||||||
|
*
|
||||||
|
* @return 上一个字符对应的列位置
|
||||||
|
*/
|
||||||
|
public int getLastCol() {
|
||||||
|
return lastCol;
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
Normal file
111
src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
|
||||||
|
import org.jcnc.snow.compiler.lexer.scanners.*;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code LexerEngine} 是编译器前端的词法分析器核心实现。
|
||||||
|
* <p>
|
||||||
|
* 负责将源代码字符串按顺序扫描并转换为一系列 {@link Token} 实例,
|
||||||
|
* 每个 Token 表示语法上可识别的最小单位(如标识符、关键字、常量、运算符等)。
|
||||||
|
* <p>
|
||||||
|
* 分析流程通过注册多个 {@link TokenScanner} 扫描器实现类型识别,
|
||||||
|
* 并由 {@link LexerContext} 提供字符流与位置信息支持。
|
||||||
|
* 支持文件名传递,遇到非法字符时会以“文件名:行:列:错误信息”输出简洁诊断。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class LexerEngine {
|
||||||
|
/**
|
||||||
|
* 扫描生成的 Token 序列(包含文件结束符 EOF)
|
||||||
|
*/
|
||||||
|
private final List<Token> tokens = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 词法上下文,提供字符流读取与位置信息
|
||||||
|
*/
|
||||||
|
private final LexerContext context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token 扫描器集合,按优先级顺序组织,用于识别不同类别的 Token
|
||||||
|
*/
|
||||||
|
private final List<TokenScanner> scanners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造词法分析器(假定输入源自标准输入,文件名默认为 <stdin>)
|
||||||
|
*
|
||||||
|
* @param source 源代码文本
|
||||||
|
*/
|
||||||
|
public LexerEngine(String source) {
|
||||||
|
this(source, "<stdin>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造词法分析器,并指定源文件名(用于诊断信息)。
|
||||||
|
* 构造时立即进行全量扫描。
|
||||||
|
*
|
||||||
|
* @param source 源代码文本
|
||||||
|
* @param sourceName 文件名或来源描述(如"main.snow")
|
||||||
|
*/
|
||||||
|
public LexerEngine(String source, String sourceName) {
|
||||||
|
this.context = new LexerContext(source);
|
||||||
|
this.scanners = List.of(
|
||||||
|
new WhitespaceTokenScanner(), // 跳过空格、制表符等
|
||||||
|
new NewlineTokenScanner(), // 处理换行符,生成 NEWLINE Token
|
||||||
|
new CommentTokenScanner(), // 处理单行/多行注释
|
||||||
|
new NumberTokenScanner(), // 识别整数与浮点数字面量
|
||||||
|
new IdentifierTokenScanner(), // 识别标识符和关键字
|
||||||
|
new StringTokenScanner(), // 处理字符串常量
|
||||||
|
new OperatorTokenScanner(), // 识别运算符
|
||||||
|
new SymbolTokenScanner(), // 识别括号、分号等符号
|
||||||
|
new UnknownTokenScanner() // 捕捉无法识别的字符,最后兜底
|
||||||
|
);
|
||||||
|
|
||||||
|
// 主扫描流程,遇到非法字符立即输出错误并终止进程
|
||||||
|
try {
|
||||||
|
scanAllTokens();
|
||||||
|
} catch (LexicalException le) {
|
||||||
|
// 输出:文件名:行:列: 错误信息,简洁明了
|
||||||
|
System.err.printf(
|
||||||
|
"%s:%d:%d: %s%n",
|
||||||
|
sourceName,
|
||||||
|
le.getLine(), // 获取出错行号
|
||||||
|
le.getColumn(), // 获取出错列号
|
||||||
|
le.getMessage() // 错误描述
|
||||||
|
);
|
||||||
|
System.exit(65); // 65 = EX_DATAERR,标准数据错误退出码
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主扫描循环,将源代码转为 Token 序列
|
||||||
|
* 依次尝试每个扫描器,直到找到可处理当前字符的扫描器为止
|
||||||
|
* 扫描到结尾后补充 EOF Token
|
||||||
|
*/
|
||||||
|
private void scanAllTokens() {
|
||||||
|
while (!context.isAtEnd()) {
|
||||||
|
char currentChar = context.peek();
|
||||||
|
// 依次查找能处理当前字符的扫描器
|
||||||
|
for (TokenScanner scanner : scanners) {
|
||||||
|
if (scanner.canHandle(currentChar, context)) {
|
||||||
|
scanner.handle(context, tokens);
|
||||||
|
break; // 已处理,跳到下一个字符
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 末尾补一个 EOF 标记
|
||||||
|
tokens.add(Token.eof(context.getLine()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全部 Token(包含 EOF),返回只读列表
|
||||||
|
*
|
||||||
|
* @return 词法分析结果 Token 列表
|
||||||
|
*/
|
||||||
|
public List<Token> getAllTokens() {
|
||||||
|
return List.copyOf(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 词法异常(LexicalException)。
|
||||||
|
* <p>
|
||||||
|
* 当 {@link LexerEngine} 在扫描过程中遇到
|
||||||
|
* 非法或无法识别的字符序列时抛出该异常。
|
||||||
|
* <ul>
|
||||||
|
* <li>异常消息仅包含一行简明错误信息(包含行号与列号);</li>
|
||||||
|
* <li>完全禁止 Java 堆栈信息输出,使命令行输出保持整洁。</li>
|
||||||
|
* </ul>
|
||||||
|
* <pre>
|
||||||
|
* 例:
|
||||||
|
* main.s:2:19: Lexical error: Illegal character sequence '@' at 2:19
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public class LexicalException extends RuntimeException {
|
||||||
|
/** 错误发生的行号(从1开始) */
|
||||||
|
private final int line;
|
||||||
|
/** 错误发生的列号(从1开始) */
|
||||||
|
private final int column;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造词法异常
|
||||||
|
* @param reason 错误原因(如:非法字符描述)
|
||||||
|
* @param line 出错行号
|
||||||
|
* @param column 出错列号
|
||||||
|
*/
|
||||||
|
public LexicalException(String reason, int line, int column) {
|
||||||
|
// 构造出错消息,并禁止异常堆栈打印
|
||||||
|
super(String.format("Lexical error: %s at %d:%d", reason, line, column),
|
||||||
|
null, false, false);
|
||||||
|
this.line = line;
|
||||||
|
this.column = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 屏蔽异常堆栈填充(始终不打印堆栈信息)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized Throwable fillInStackTrace() { return this; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取出错的行号
|
||||||
|
* @return 行号
|
||||||
|
*/
|
||||||
|
public int getLine() { return line; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取出错的列号
|
||||||
|
* @return 列号
|
||||||
|
*/
|
||||||
|
public int getColumn() { return column; }
|
||||||
|
}
|
||||||
38
src/main/java/org/jcnc/snow/compiler/lexer/doc/README.md
Normal file
38
src/main/java/org/jcnc/snow/compiler/lexer/doc/README.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Snow Compiler - Lexer 模块
|
||||||
|
|
||||||
|
> Snow 编译器的前端模块 —— 负责将源代码扫描并生成 Token 流
|
||||||
|
|
||||||
|
## 项目简介
|
||||||
|
|
||||||
|
**Lexer** 是 [Snow 编译器]() 项目的子模块,承担编译器前端的词法分析阶段任务。
|
||||||
|
它负责将源代码输入,根据 Snow 语言的语法规则,识别并划分为有意义的 Token 流,为后续语法解析(Parser)提供标准输入。
|
||||||
|
|
||||||
|
该模块采用模块化设计,每类 Token 类型(如标识符、运算符、数字、字符串等)由独立的扫描器(Scanner)负责识别,提高了扩展性和可维护性。
|
||||||
|
|
||||||
|
## 核心功能
|
||||||
|
|
||||||
|
- **词法分析引擎**:从源代码提取标准化 Token 流
|
||||||
|
- **模块化扫描器体系**:按需扩展不同类型的 Token 识别
|
||||||
|
- **灵活的上下文管理**:跟踪源代码位置,支持错误处理
|
||||||
|
- **统一的 Token 工厂**:集中创建 Token 实例
|
||||||
|
- **工具支持**:Token 打印与调试辅助
|
||||||
|
|
||||||
|
## 模块结构
|
||||||
|
|
||||||
|
```
|
||||||
|
lexer/
|
||||||
|
├── base/ // 公共基础接口
|
||||||
|
├── core/ // 词法分析核心引擎与上下文
|
||||||
|
├── scanners/ // 各类 Token 扫描器
|
||||||
|
├── token/ // Token 定义、工厂与类型
|
||||||
|
├── utils/ // 辅助工具(如 Token 打印)
|
||||||
|
└── doc/ // 项目文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发环境
|
||||||
|
|
||||||
|
* JDK 23 或更高版本
|
||||||
|
* Maven 构建管理
|
||||||
|
* 推荐 IDE:IntelliJ IDEA
|
||||||
|
|
||||||
|
---
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code AbstractTokenScanner} 是 {@link TokenScanner} 的抽象实现,
|
||||||
|
* 封装了常用的扫描行为与模板逻辑,简化子类的实现负担。
|
||||||
|
* <p>
|
||||||
|
* 子类只需实现 {@link #scanToken(LexerContext, int, int)} 方法,
|
||||||
|
* 专注于处理具体的 Token 构造逻辑,
|
||||||
|
* 而位置信息提取、Token 添加等通用操作由本类统一完成。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public abstract class AbstractTokenScanner implements TokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理当前字符起始的 Token,附带行列信息并加入 Token 列表。
|
||||||
|
*
|
||||||
|
* @param ctx 当前词法分析上下文
|
||||||
|
* @param tokens 存储扫描结果的 Token 列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void handle(LexerContext ctx, List<Token> tokens) {
|
||||||
|
int line = ctx.getLine();
|
||||||
|
int col = ctx.getCol();
|
||||||
|
Token token = scanToken(ctx, line, col);
|
||||||
|
if (token != null) {
|
||||||
|
tokens.add(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象方法:由子类实现具体的扫描逻辑。
|
||||||
|
* <p>
|
||||||
|
* 实现应消费一定字符并根据规则构造 Token。
|
||||||
|
* 若无需生成 Token,可返回 null。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param ctx 当前扫描上下文
|
||||||
|
* @param line 当前行号
|
||||||
|
* @param col 当前列号
|
||||||
|
* @return 构造的 Token 或 null
|
||||||
|
*/
|
||||||
|
protected abstract Token scanToken(LexerContext ctx, int line, int col);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具方法:连续读取字符直到遇到不满足指定条件的字符。
|
||||||
|
*
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @param predicate 字符匹配条件
|
||||||
|
* @return 满足条件的字符组成的字符串
|
||||||
|
*/
|
||||||
|
protected String readWhile(LexerContext ctx, Predicate<Character> predicate) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
while (!ctx.isAtEnd() && predicate.test(ctx.peek())) {
|
||||||
|
sb.append(ctx.advance());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注释扫描器:处理源代码中的注释部分,包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>单行注释(以 "//" 开头,直到行尾)</li>
|
||||||
|
* <li>多行注释(以 "/*" 开头,以 "*/" 结尾)</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* 本扫描器会识别注释并生成 {@code TokenType.COMMENT} 类型的 Token,
|
||||||
|
* 不会丢弃注释内容,而是将完整注释文本保留在 Token 中,便于后续分析(如文档提取、保留注释等场景)。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class CommentTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以处理当前位置的字符。
|
||||||
|
* <p>当当前位置字符为 '/' 且下一个字符为 '/' 或 '*' 时,表示可能是注释的起始。</p>
|
||||||
|
*
|
||||||
|
* @param c 当前字符
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @return 如果是注释的起始符,则返回 true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
|
return c == '/' && (ctx.peekNext() == '/' || ctx.peekNext() == '*');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实现注释的扫描逻辑。
|
||||||
|
* <p>支持两种注释格式:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li><b>单行注释:</b> 以 "//" 开头,直到遇到换行符</li>
|
||||||
|
* <li><b>多行注释:</b> 以 "/*" 开头,直到遇到 "*/" 结束</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @param line 当前行号(用于 Token 位置信息)
|
||||||
|
* @param col 当前列号(用于 Token 位置信息)
|
||||||
|
* @return 包含完整注释内容的 COMMENT 类型 Token
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
|
// 消费第一个 '/' 字符
|
||||||
|
ctx.advance();
|
||||||
|
StringBuilder sb = new StringBuilder("/");
|
||||||
|
|
||||||
|
// 处理单行注释 //
|
||||||
|
if (ctx.match('/')) {
|
||||||
|
sb.append('/');
|
||||||
|
while (!ctx.isAtEnd() && ctx.peek() != '\n') {
|
||||||
|
sb.append(ctx.advance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理多行注释 /* ... */
|
||||||
|
else if (ctx.match('*')) {
|
||||||
|
sb.append('*');
|
||||||
|
while (!ctx.isAtEnd()) {
|
||||||
|
char ch = ctx.advance();
|
||||||
|
sb.append(ch);
|
||||||
|
if (ch == '*' && ctx.peek() == '/') {
|
||||||
|
sb.append(ctx.advance()); // 消费 '/'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Token(TokenType.COMMENT, sb.toString(), line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识符扫描器:处理标识符的识别,如变量名、函数名等。
|
||||||
|
* <p>
|
||||||
|
* 识别规则如下:
|
||||||
|
* <ul>
|
||||||
|
* <li>必须以字母或下划线(_)开头</li>
|
||||||
|
* <li>后续字符可以是字母、数字或下划线</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* 扫描完成后会调用 {@link TokenFactory} 自动判断是否为关键字,
|
||||||
|
* 并返回对应类型的 {@link Token}。
|
||||||
|
*/
|
||||||
|
public class IdentifierTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以处理当前位置的字符。
|
||||||
|
* <p>如果字符为字母或下划线,则认为是标识符的起始。</p>
|
||||||
|
*
|
||||||
|
* @param c 当前字符
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @return 如果是标识符起始字符,则返回 true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
|
return Character.isLetter(c) || c == '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行标识符的扫描逻辑。
|
||||||
|
* <p>连续读取满足标识符规则的字符序列,交由 {@code TokenFactory} 创建对应的 Token。</p>
|
||||||
|
*
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @param line 当前行号
|
||||||
|
* @param col 当前列号
|
||||||
|
* @return 标识符或关键字类型的 Token
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
|
String lexeme = readWhile(ctx, ch -> Character.isLetterOrDigit(ch) || ch == '_');
|
||||||
|
return TokenFactory.create(lexeme, line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 换行符扫描器:将源代码中的换行符(\n)识别为 {@code NEWLINE} 类型的 Token。
|
||||||
|
* <p>
|
||||||
|
* 通常用于记录行的分界,辅助语法分析阶段进行行敏感的判断或保持结构清晰。
|
||||||
|
*/
|
||||||
|
public class NewlineTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以处理当前位置的字符。
|
||||||
|
* <p>当字符为换行符(\n)时返回 true。</p>
|
||||||
|
*
|
||||||
|
* @param c 当前字符
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @return 如果为换行符,则返回 true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
|
return c == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行换行符的扫描逻辑。
|
||||||
|
* <p>读取一个换行符并生成对应的 {@code NEWLINE} 类型 Token。</p>
|
||||||
|
*
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @param line 当前行号
|
||||||
|
* @param col 当前列号
|
||||||
|
* @return 表示换行的 Token
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
|
ctx.advance();
|
||||||
|
return new Token(TokenType.NEWLINE, "\n", line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字扫描器:识别整数、小数以及带有<strong>类型后缀</strong>的数字字面量。
|
||||||
|
* <p>
|
||||||
|
* 支持的格式示例:
|
||||||
|
* <ul>
|
||||||
|
* <li>整数:123、0、45678</li>
|
||||||
|
* <li>小数:3.14、0.5、12.0</li>
|
||||||
|
* <li>带类型后缀:2.0f、42L、7s、255B</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* 语法允许在数字 (整数或小数) 末尾添加以下<strong>单字符后缀</strong>来显式指定常量类型:
|
||||||
|
* <pre>b | s | l | f | d // 分别对应 byte、short、long、float、double
|
||||||
|
* B | S | L | F | D // 同上,大小写皆可</pre>
|
||||||
|
* 生成的 Token 类型始终为 {@code NUMBER_LITERAL},词法单元将携带完整的文本(含后缀,若存在)。
|
||||||
|
*/
|
||||||
|
public class NumberTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选类型后缀字符集合 (大小写均可)。
|
||||||
|
* 与 {@code ExpressionBuilder} 内的后缀解析逻辑保持一致。
|
||||||
|
*/
|
||||||
|
private static final String SUFFIX_CHARS = "bslfdBSLFD";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以处理当前位置的字符。
|
||||||
|
* <p>当字符为数字时,表示可能是数字字面量的起始。</p>
|
||||||
|
*
|
||||||
|
* @param c 当前字符
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @return 如果为数字字符,则返回 true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
|
return Character.isDigit(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行数字扫描逻辑。
|
||||||
|
* <ol>
|
||||||
|
* <li>连续读取数字字符,允许出现<strong>一个</strong>小数点,用于识别整数或小数。</li>
|
||||||
|
* <li>读取完主体后,<strong>一次性</strong>检查下一个字符,若属于合法类型后缀则吸收。</li>
|
||||||
|
* </ol>
|
||||||
|
* 这样可以保证诸如 {@code 2.0f} 被视为一个整体的 {@code NUMBER_LITERAL},
|
||||||
|
* 而不是拆分成 "2.0" 与 "f" 两个 Token。
|
||||||
|
*
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @param line 当前行号
|
||||||
|
* @param col 当前列号
|
||||||
|
* @return 表示数字字面量的 Token
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean hasDot = false; // 标识是否已经遇到过小数点
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1️⃣ 扫描整数或小数主体
|
||||||
|
* 允许出现一个小数点,其余必须是数字。
|
||||||
|
*/
|
||||||
|
while (!ctx.isAtEnd()) {
|
||||||
|
char c = ctx.peek();
|
||||||
|
if (c == '.' && !hasDot) {
|
||||||
|
hasDot = true;
|
||||||
|
sb.append(ctx.advance());
|
||||||
|
} else if (Character.isDigit(c)) {
|
||||||
|
sb.append(ctx.advance());
|
||||||
|
} else {
|
||||||
|
break; // 遇到非数字/第二个点 => 主体结束
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2️⃣ 可选类型后缀
|
||||||
|
* 如果下一字符是合法后缀字母,则一起纳入当前 Token。
|
||||||
|
*/
|
||||||
|
if (!ctx.isAtEnd()) {
|
||||||
|
char suffix = ctx.peek();
|
||||||
|
if (SUFFIX_CHARS.indexOf(suffix) >= 0) {
|
||||||
|
sb.append(ctx.advance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造并返回 NUMBER_LITERAL Token,文本内容形如 "123", "3.14f" 等。
|
||||||
|
return new Token(TokenType.NUMBER_LITERAL, sb.toString(), line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运算符扫描器:识别逻辑与比较运算符,包括单字符和双字符组合。
|
||||||
|
* <p>
|
||||||
|
* 支持的运算符包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>赋值与比较:=、==、!=</li>
|
||||||
|
* <li>关系运算符:>、>=、<、<=</li>
|
||||||
|
* <li>逻辑运算符:&&、||</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* 不符合上述组合的字符会返回 {@code UNKNOWN} 类型的 Token。
|
||||||
|
*/
|
||||||
|
public class OperatorTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以处理当前位置的字符。
|
||||||
|
* <p>运算符扫描器关注的起始字符包括:=、!、<、>、|、&</p>
|
||||||
|
*
|
||||||
|
* @param c 当前字符
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @return 如果是潜在的运算符起始字符,则返回 true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
|
return "=!<>|&%".indexOf(c) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描并识别运算符 Token。
|
||||||
|
* <p>支持组合运算符判断,如 ==、!=、>= 等,
|
||||||
|
* 若无法匹配组合形式则退回单字符形式。</p>
|
||||||
|
*
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @param line 当前行号
|
||||||
|
* @param col 当前列号
|
||||||
|
* @return 对应的运算符 Token,无法识别的运算符返回 {@code UNKNOWN}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
|
char c = ctx.advance();
|
||||||
|
String lexeme;
|
||||||
|
TokenType type;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case '=':
|
||||||
|
if (ctx.match('=')) {
|
||||||
|
lexeme = "==";
|
||||||
|
type = TokenType.DOUBLE_EQUALS;
|
||||||
|
} else {
|
||||||
|
lexeme = "=";
|
||||||
|
type = TokenType.EQUALS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '!':
|
||||||
|
if (ctx.match('=')) {
|
||||||
|
lexeme = "!=";
|
||||||
|
type = TokenType.NOT_EQUALS;
|
||||||
|
} else {
|
||||||
|
lexeme = "!";
|
||||||
|
type = TokenType.UNKNOWN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
if (ctx.match('=')) {
|
||||||
|
lexeme = ">=";
|
||||||
|
type = TokenType.GREATER_EQUAL;
|
||||||
|
} else {
|
||||||
|
lexeme = ">";
|
||||||
|
type = TokenType.GREATER_THAN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
if (ctx.match('=')) {
|
||||||
|
lexeme = "<=";
|
||||||
|
type = TokenType.LESS_EQUAL;
|
||||||
|
} else {
|
||||||
|
lexeme = "<";
|
||||||
|
type = TokenType.LESS_THAN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
lexeme = "%";
|
||||||
|
type = TokenType.MODULO;
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
if (ctx.match('&')) {
|
||||||
|
lexeme = "&&";
|
||||||
|
type = TokenType.AND;
|
||||||
|
} else {
|
||||||
|
lexeme = "&";
|
||||||
|
type = TokenType.UNKNOWN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '|':
|
||||||
|
if (ctx.match('|')) {
|
||||||
|
lexeme = "||";
|
||||||
|
type = TokenType.OR;
|
||||||
|
} else {
|
||||||
|
lexeme = "|";
|
||||||
|
type = TokenType.UNKNOWN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
lexeme = String.valueOf(c);
|
||||||
|
type = TokenType.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Token(type, lexeme, line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串扫描器:处理双引号包裹的字符串字面量,支持基本的转义字符。
|
||||||
|
* <p>
|
||||||
|
* 支持格式示例:
|
||||||
|
* <ul>
|
||||||
|
* <li>"hello"</li>
|
||||||
|
* <li>"line\\nbreak"</li>
|
||||||
|
* <li>"escaped \\\" quote"</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* 扫描器会保留原始字符串的形式(包含双引号和转义符),
|
||||||
|
* 并生成 {@code STRING_LITERAL} 类型的 Token。
|
||||||
|
*/
|
||||||
|
public class StringTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以处理当前位置的字符。
|
||||||
|
* <p>当字符为双引号(")时,认为是字符串字面量的开始。</p>
|
||||||
|
*
|
||||||
|
* @param c 当前字符
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @return 如果为字符串起始符,则返回 true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
|
return c == '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行字符串的扫描逻辑。
|
||||||
|
* <p>从当前位置开始,读取直到匹配结束的双引号。
|
||||||
|
* 支持转义字符(如 \"、\\n 等),不会中断字符串扫描。</p>
|
||||||
|
*
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @param line 当前行号
|
||||||
|
* @param col 当前列号
|
||||||
|
* @return 字符串字面量类型的 Token
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(ctx.advance()); // 起始双引号
|
||||||
|
|
||||||
|
while (!ctx.isAtEnd()) {
|
||||||
|
char c = ctx.advance();
|
||||||
|
sb.append(c);
|
||||||
|
|
||||||
|
if (c == '\\') {
|
||||||
|
sb.append(ctx.advance()); // 添加转义字符后的实际字符
|
||||||
|
} else if (c == '"') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 符号扫描器:识别常见的单字符符号,如冒号、逗号、括号和算术符号。
|
||||||
|
* <p>
|
||||||
|
* 支持的符号包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>标点符号:: , .</li>
|
||||||
|
* <li>括号:( )</li>
|
||||||
|
* <li>算术运算符:+ - *</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* 生成的 Token 类型根据字符分别对应 {@link TokenType} 枚举中的定义。
|
||||||
|
*/
|
||||||
|
public class SymbolTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以处理当前位置的字符。
|
||||||
|
* <p>本扫描器处理的符号包括 : , ( ) . + - *</p>
|
||||||
|
*
|
||||||
|
* @param c 当前字符
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @return 如果是支持的符号字符,则返回 true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
|
return ":,().+-*/".indexOf(c) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行符号的扫描逻辑。
|
||||||
|
* <p>根据字符匹配对应的 {@code TokenType} 类型,构造并返回 Token。</p>
|
||||||
|
*
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @param line 当前行号
|
||||||
|
* @param col 当前列号
|
||||||
|
* @return 表示符号的 Token
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
|
char c = ctx.advance();
|
||||||
|
TokenType type = switch (c) {
|
||||||
|
case ':' -> TokenType.COLON;
|
||||||
|
case ',' -> TokenType.COMMA;
|
||||||
|
case '.' -> TokenType.DOT;
|
||||||
|
case '+' -> TokenType.PLUS;
|
||||||
|
case '-' -> TokenType.MINUS;
|
||||||
|
case '*' -> TokenType.MULTIPLY;
|
||||||
|
case '/' -> TokenType.DIVIDE;
|
||||||
|
case '(' -> TokenType.LPAREN;
|
||||||
|
case ')' -> TokenType.RPAREN;
|
||||||
|
default -> TokenType.UNKNOWN;
|
||||||
|
};
|
||||||
|
return new Token(type, String.valueOf(c), line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexicalException;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未知 Token 扫描器(UnknownTokenScanner)。
|
||||||
|
* <p>
|
||||||
|
* 作为所有扫描器的兜底处理器。当前字符若不被任何其他扫描器识别,
|
||||||
|
* 由本类处理并抛出 {@link LexicalException},终止词法分析流程。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 主要作用:保证所有非法、不可识别的字符(如@、$等)不会被静默跳过或误当作合法 Token,
|
||||||
|
* 而是在词法阶段立刻定位并报错,有助于尽早发现源代码问题。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class UnknownTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以处理当前字符。
|
||||||
|
* 对于 UnknownTokenScanner,始终返回 true(兜底扫描器,必须排在扫描器链末尾)。
|
||||||
|
* @param c 当前待处理字符
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @return 是否处理该字符(始终为 true)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际处理非法字符序列的方法。
|
||||||
|
* 连续读取所有无法被其他扫描器识别的字符,组成错误片段并抛出异常。
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @param line 错误发生行号
|
||||||
|
* @param col 错误发生列号
|
||||||
|
* @return 不会返回Token(始终抛异常)
|
||||||
|
* @throws LexicalException 非法字符导致的词法错误
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
|
// 读取一段非法字符(既不是字母数字、也不是常见符号)
|
||||||
|
String lexeme = readWhile(ctx, ch ->
|
||||||
|
!Character.isLetterOrDigit(ch) &&
|
||||||
|
!Character.isWhitespace(ch) &&
|
||||||
|
":,().+-*{};\"".indexOf(ch) < 0
|
||||||
|
);
|
||||||
|
// 如果没读到任何字符,则把当前字符单独作为非法片段
|
||||||
|
if (lexeme.isEmpty())
|
||||||
|
lexeme = String.valueOf(ctx.advance());
|
||||||
|
// 抛出词法异常,并带上错误片段与具体位置
|
||||||
|
throw new LexicalException("Illegal character sequence '" + lexeme + "'", line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.scanners;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.core.LexerContext;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空白符扫描器:跳过非换行的空白字符,不生成任何 Token。
|
||||||
|
* <p>
|
||||||
|
* 支持的空白字符包括空格、制表符(Tab)等,但不包括换行符(由 {@link NewlineTokenScanner} 处理)。
|
||||||
|
* <p>
|
||||||
|
* 此扫描器仅用于忽略多余的空白内容,保持 Token 流的紧凑性。
|
||||||
|
*/
|
||||||
|
public class WhitespaceTokenScanner extends AbstractTokenScanner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以处理当前位置的字符。
|
||||||
|
* <p>当字符为空白符但不是换行符(\n)时返回 true。</p>
|
||||||
|
*
|
||||||
|
* @param c 当前字符
|
||||||
|
* @param ctx 当前词法上下文
|
||||||
|
* @return 如果为非换行的空白字符,则返回 true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(char c, LexerContext ctx) {
|
||||||
|
return Character.isWhitespace(c) && c != '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳过空白字符,不生成 Token。
|
||||||
|
* <p>直接推进上下文位置,返回 null。</p>
|
||||||
|
*
|
||||||
|
* @param ctx 词法上下文
|
||||||
|
* @param line 当前行号
|
||||||
|
* @param col 当前列号
|
||||||
|
* @return 始终返回 null(表示无 Token 产生)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||||
|
ctx.advance();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/main/java/org/jcnc/snow/compiler/lexer/token/Token.java
Normal file
113
src/main/java/org/jcnc/snow/compiler/lexer/token/Token.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code Token} 表示词法分析过程中生成的最小语法单元,
|
||||||
|
* 包含类型信息、词素内容、源代码中对应的原始文本片段以及精确的位置信息。
|
||||||
|
* <p>
|
||||||
|
* 一个 Token 通常对应源代码中一个具有语义意义的片段,如关键字、标识符、常量、运算符等。
|
||||||
|
* 区分 lexeme(清洗后的词素)与 raw(原始片段)是为了支持如带引号的字符串、注释等需要保留原始形式的元素。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class Token {
|
||||||
|
|
||||||
|
/** Token 的类型,如 KEYWORD、IDENTIFIER、TYPE 等。 */
|
||||||
|
private final TokenType type;
|
||||||
|
|
||||||
|
/** 清洗后的词素内容,例如去掉引号的字符串正文或注释正文。 */
|
||||||
|
private final String lexeme;
|
||||||
|
|
||||||
|
/** 源代码中对应的原始片段,可能包含引号、注释符号等。 */
|
||||||
|
private final String raw;
|
||||||
|
|
||||||
|
/** Token 在源文件中的行号,从 1 开始计数。 */
|
||||||
|
private final int line;
|
||||||
|
|
||||||
|
/** Token 在源文件行中的列号,从 1 开始计数。 */
|
||||||
|
private final int col;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个完整信息的 Token 实例。
|
||||||
|
*
|
||||||
|
* @param type Token 类型
|
||||||
|
* @param lexeme 清洗后的词素内容
|
||||||
|
* @param raw 源代码中的原始片段
|
||||||
|
* @param line 所在源文件的行号(从 1 开始)
|
||||||
|
* @param col 所在源文件行的列号(从 1 开始)
|
||||||
|
*/
|
||||||
|
public Token(TokenType type, String lexeme, String raw, int line, int col) {
|
||||||
|
this.type = type;
|
||||||
|
this.lexeme = lexeme;
|
||||||
|
this.raw = raw;
|
||||||
|
this.line = line;
|
||||||
|
this.col = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个简化形式的 Token,词素与原始片段一致。
|
||||||
|
* <p>
|
||||||
|
* 适用于标识符、关键字、符号等不需要区分原始与清洗内容的 Token。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param type Token 类型
|
||||||
|
* @param lexeme Token 内容(同时作为原始片段)
|
||||||
|
* @param line 行号
|
||||||
|
* @param col 列号
|
||||||
|
*/
|
||||||
|
public Token(TokenType type, String lexeme, int line, int col) {
|
||||||
|
this(type, lexeme, lexeme, line, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造并返回一个表示文件结束(EOF)的 Token 实例。
|
||||||
|
* <p>
|
||||||
|
* 用于表示扫描结束的特殊符号。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param line 文件结束所在行号
|
||||||
|
* @return EOF 类型的 Token
|
||||||
|
*/
|
||||||
|
public static Token eof(int line) {
|
||||||
|
return new Token(TokenType.EOF, "", "", line, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 此 Token 的类型 */
|
||||||
|
public TokenType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 清洗后的词素内容(lexeme) */
|
||||||
|
public String getLexeme() {
|
||||||
|
return lexeme;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 源代码中的原始片段 */
|
||||||
|
public String getRaw() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Token 所在的源文件行号(从 1 开始) */
|
||||||
|
public int getLine() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Token 所在行的列号(从 1 开始) */
|
||||||
|
public int getCol() {
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该 Token 的字符串表示,包含类型、词素、行列信息。
|
||||||
|
* <p>
|
||||||
|
* 通常用于日志打印或调试目的。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return Token 的描述性字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format(
|
||||||
|
"Token(type=%s, lexeme='%s', line=%d, col=%d)",
|
||||||
|
type, lexeme, line, col
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.token;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code TokenFactory} 是一个用于词法分析的静态工厂类。
|
||||||
|
* <p>
|
||||||
|
* 该类提供静态方法,根据输入的原始词素(字符串),
|
||||||
|
* 自动识别其对应的词法类型,并生成相应的 {@link Token} 实例。
|
||||||
|
* 支持自动区分语言关键字、内置类型、合法标识符等,
|
||||||
|
* 简化了词法扫描器(Lexer)的核心处理流程。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 主要功能与特性:
|
||||||
|
* <ul>
|
||||||
|
* <li>统一管理语言关键字和类型名集合,便于扩展与维护。</li>
|
||||||
|
* <li>自动推断 Token 类型,无需外部干预。</li>
|
||||||
|
* <li>对不合法的词素自动标记为 UNKNOWN 类型。</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author 你的名字
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class TokenFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语言的保留关键字集合。
|
||||||
|
*/
|
||||||
|
private static final Set<String> KEYWORDS = Set.of(
|
||||||
|
"module", "function", "parameter", "return_type",
|
||||||
|
"body", "end", "if", "then", "else", "loop",
|
||||||
|
"declare", "return", "import",
|
||||||
|
"initializer", "condition", "update"
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内置类型名称集合,如 int、string 等。
|
||||||
|
*/
|
||||||
|
private static final Set<String> TYPES = Set.of(
|
||||||
|
"int", "string", "float", "bool", "void", "double", "long", "short", "byte"
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个根据内容自动推断类型的 {@link Token} 实例。
|
||||||
|
* <p>
|
||||||
|
* 优先级顺序为:类型(TYPE) > 关键字(KEYWORD) > 标识符(IDENTIFIER) > 未知(UNKNOWN)。
|
||||||
|
* 若原始字符串同时属于多类,则按优先级最高者处理。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param raw 原始词法单元文本
|
||||||
|
* @param line Token 所在行号(从1开始)
|
||||||
|
* @param col Token 所在列号(从1开始)
|
||||||
|
* @return 构造好的 {@link Token} 实例
|
||||||
|
*/
|
||||||
|
public static Token create(String raw, int line, int col) {
|
||||||
|
TokenType type = determineTokenType(raw);
|
||||||
|
return new Token(type, raw, line, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断并推断给定字符串的 {@link TokenType} 类型。
|
||||||
|
* <p>
|
||||||
|
* 优先级依次为:TYPE > KEYWORD > IDENTIFIER > UNKNOWN。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param raw 原始词素字符串
|
||||||
|
* @return 推断出的 {@link TokenType} 类型
|
||||||
|
*/
|
||||||
|
private static TokenType determineTokenType(String raw) {
|
||||||
|
if (isType(raw)) return TokenType.TYPE;
|
||||||
|
if (isKeyword(raw)) return TokenType.KEYWORD;
|
||||||
|
if (isIdentifier(raw)) return TokenType.IDENTIFIER;
|
||||||
|
return TokenType.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断指定字符串是否为内置类型标识。
|
||||||
|
*
|
||||||
|
* @param raw 输入的字符串
|
||||||
|
* @return 若为类型名则返回 {@code true},否则返回 {@code false}
|
||||||
|
*/
|
||||||
|
private static boolean isType(String raw) {
|
||||||
|
return TYPES.contains(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断指定字符串是否为语言保留关键字。
|
||||||
|
*
|
||||||
|
* @param raw 输入的字符串
|
||||||
|
* @return 若为关键字则返回 {@code true},否则返回 {@code false}
|
||||||
|
*/
|
||||||
|
private static boolean isKeyword(String raw) {
|
||||||
|
return KEYWORDS.contains(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断指定字符串是否为合法的标识符(Identifier)。
|
||||||
|
* <p>
|
||||||
|
* 合法标识符需以字母(a-z/A-Z)或下划线(_)开头,
|
||||||
|
* 后续可包含字母、数字或下划线。
|
||||||
|
* 例如:_abc, a1b2, name_123 均为合法标识符。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param raw 输入的字符串
|
||||||
|
* @return 若为合法标识符则返回 {@code true},否则返回 {@code false}
|
||||||
|
*/
|
||||||
|
private static boolean isIdentifier(String raw) {
|
||||||
|
return raw.matches("[a-zA-Z_][a-zA-Z0-9_]*");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code TokenType} 枚举定义了 Snow 编程语言词法分析阶段可识别的所有词法单元类型。
|
||||||
|
* <p>
|
||||||
|
* 每个枚举值代表一种语义类别,
|
||||||
|
* 用于描述源代码中出现的关键字、标识符、字面量、运算符、控制符号等语言构件。
|
||||||
|
* 在语法分析及后续处理阶段,依赖该枚举对 Token 进行分类、判断与分支处理。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public enum TokenType {
|
||||||
|
|
||||||
|
/** 普通标识符,如变量名、函数名等 */
|
||||||
|
IDENTIFIER,
|
||||||
|
|
||||||
|
/** 语言保留关键字(如 if、return、module 等) */
|
||||||
|
KEYWORD,
|
||||||
|
|
||||||
|
/** 内置类型名称(如 int、string、bool 等) */
|
||||||
|
TYPE,
|
||||||
|
|
||||||
|
/** 字符串字面量(如 "hello") */
|
||||||
|
STRING_LITERAL,
|
||||||
|
|
||||||
|
/** 数字字面量(整数或浮点数) */
|
||||||
|
NUMBER_LITERAL,
|
||||||
|
|
||||||
|
/** 冒号 ':' */
|
||||||
|
COLON,
|
||||||
|
|
||||||
|
/** 逗号 ',' */
|
||||||
|
COMMA,
|
||||||
|
|
||||||
|
/** 点号 '.' */
|
||||||
|
DOT,
|
||||||
|
|
||||||
|
/** 赋值符号 '=' */
|
||||||
|
EQUALS,
|
||||||
|
|
||||||
|
/** 取模运算符 '%' */
|
||||||
|
MODULO,
|
||||||
|
|
||||||
|
/** 加号 '+' */
|
||||||
|
PLUS,
|
||||||
|
|
||||||
|
/** 乘号 '*' */
|
||||||
|
MULTIPLY,
|
||||||
|
/**除号 '/' */
|
||||||
|
DIVIDE,
|
||||||
|
|
||||||
|
/** 减号 '-' */
|
||||||
|
MINUS,
|
||||||
|
|
||||||
|
/** 左括号 '(' */
|
||||||
|
LPAREN,
|
||||||
|
|
||||||
|
/** 右括号 ')' */
|
||||||
|
RPAREN,
|
||||||
|
|
||||||
|
/** 相等比较符号 '==' */
|
||||||
|
DOUBLE_EQUALS,
|
||||||
|
|
||||||
|
/** 不等比较符号 '!=' */
|
||||||
|
NOT_EQUALS,
|
||||||
|
|
||||||
|
/** 大于符号 '>' */
|
||||||
|
GREATER_THAN,
|
||||||
|
|
||||||
|
/** 大于等于符号 '>=' */
|
||||||
|
GREATER_EQUAL,
|
||||||
|
|
||||||
|
/** 小于符号 '<' */
|
||||||
|
LESS_THAN,
|
||||||
|
|
||||||
|
/** 小于等于符号 '<=' */
|
||||||
|
LESS_EQUAL,
|
||||||
|
|
||||||
|
/** 逻辑与符号 '&&' */
|
||||||
|
AND,
|
||||||
|
|
||||||
|
/** 逻辑或符号 '||' */
|
||||||
|
OR,
|
||||||
|
|
||||||
|
/** 换行符,标识逻辑换行 */
|
||||||
|
NEWLINE,
|
||||||
|
|
||||||
|
/** 单行或多行注释内容 */
|
||||||
|
COMMENT,
|
||||||
|
|
||||||
|
/** 文件结束符(End of File) */
|
||||||
|
EOF,
|
||||||
|
|
||||||
|
/** 无法识别的非法或未知符号 */
|
||||||
|
UNKNOWN
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
package org.jcnc.snow.compiler.lexer.utils;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TokenPrinter 用于以表格形式将词法分析生成的 Token 列表输出到控制台。
|
||||||
|
* <p>
|
||||||
|
* 输出包含每个 Token 的行号、列号、类型以及词素(lexeme),
|
||||||
|
* 并对换行、制表符等特殊字符进行转义显示。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class TokenPrinter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将给定的 Token 列表打印到标准输出(控制台)。
|
||||||
|
* <p>
|
||||||
|
* 输出格式:
|
||||||
|
* <pre>
|
||||||
|
* line col type lexeme
|
||||||
|
* ----------------------------------------------------
|
||||||
|
* 1 1 KEYWORD module
|
||||||
|
* 1 7 IDENTIFIER MyModule
|
||||||
|
* ...
|
||||||
|
* </pre>
|
||||||
|
* 并且对 lexeme 中的换行符、制表符、回车符等进行转义(\n、\t、\r),
|
||||||
|
* 以便在表格中保持排版整齐。如果遇到类型为 {@link TokenType#NEWLINE} 的 Token,
|
||||||
|
* 会在该行后额外插入一个空行以增强可读性。
|
||||||
|
*
|
||||||
|
* @param tokens 要打印的 Token 列表,不应为 null;列表中的每个元素
|
||||||
|
* 都应包含有效的行号、列号、类型和词素信息
|
||||||
|
*/
|
||||||
|
public static void print(List<Token> tokens) {
|
||||||
|
// 打印表头:列名对齐,宽度分别为 6、6、16
|
||||||
|
System.out.printf("%-6s %-6s %-16s %s%n", "line", "col", "type", "lexeme");
|
||||||
|
System.out.println("----------------------------------------------------");
|
||||||
|
|
||||||
|
// 逐个 Token 输出对应信息
|
||||||
|
for (Token token : tokens) {
|
||||||
|
// 对 lexeme 中的特殊字符进行转义,避免表格错位
|
||||||
|
String lexeme = token.getLexeme()
|
||||||
|
.replace("\n", "\\n")
|
||||||
|
.replace("\t", "\\t")
|
||||||
|
.replace("\r", "\\r");
|
||||||
|
|
||||||
|
// 按照固定格式输出:行号、列号、类型、词素
|
||||||
|
System.out.printf("%-6d %-6d %-16s %s%n",
|
||||||
|
token.getLine(),
|
||||||
|
token.getCol(),
|
||||||
|
token.getType(),
|
||||||
|
lexeme
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果当前 Token 是换行符类型,则额外打印一行空白行
|
||||||
|
if (token.getType() == TokenType.NEWLINE) {
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code AssignmentNode} 表示抽象语法树(AST)中的赋值语句节点。
|
||||||
|
* <p>
|
||||||
|
* 赋值语句用于将右侧表达式的值存储到左侧指定的变量中,
|
||||||
|
* 通常形式为 {@code x = expression},其中 {@code x} 是目标变量,
|
||||||
|
* {@code expression} 是用于计算赋值结果的表达式。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 该节点作为语句节点的一种实现,适用于语义分析、类型检查、IR 构建等多个阶段。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param variable 左值变量名(即赋值目标)
|
||||||
|
* @param value 表达式右值(即赋值来源)
|
||||||
|
*/
|
||||||
|
public record AssignmentNode(String variable, ExpressionNode value) implements StatementNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回赋值语句的字符串形式,便于调试与日志输出。
|
||||||
|
* <p>
|
||||||
|
* 典型格式形如 {@code x = y + 1}。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 表示赋值语句的字符串形式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return variable + " = " + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code BinaryExpressionNode} 表示抽象语法树(AST)中的二元运算表达式节点。
|
||||||
|
* <p>
|
||||||
|
* 二元表达式通常由两个操作数和一个中间操作符构成,例如 {@code a + b}。
|
||||||
|
* 此结构广泛用于数学计算、逻辑判断、字符串拼接等语法结构中。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param left 左操作数(子表达式)
|
||||||
|
* @param operator 运算符字符串(如 "+", "-", "*", "/" 等)
|
||||||
|
* @param right 右操作数(子表达式)
|
||||||
|
*/
|
||||||
|
public record BinaryExpressionNode(ExpressionNode left, String operator,
|
||||||
|
ExpressionNode right) implements ExpressionNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该二元运算表达式的字符串表示形式。
|
||||||
|
* <p>
|
||||||
|
* 输出格式为:{@code left + " " + operator + " " + right},
|
||||||
|
* 适用于调试或打印语法树结构。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 表示该二元表达式的字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return left + " " + operator + " " + right;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code CallExpressionNode} 表示抽象语法树(AST)中的函数调用表达式节点。
|
||||||
|
* <p>
|
||||||
|
* 函数调用表达式用于表示函数或过程的调用操作,
|
||||||
|
* 包括被调用对象(callee)以及一组参数表达式(arguments)。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param callee 被调用的表达式节点,通常为函数标识符或成员访问表达式。
|
||||||
|
* @param arguments 参数表达式列表,依照调用顺序排列。
|
||||||
|
*/
|
||||||
|
public record CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments) implements ExpressionNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回函数调用表达式的字符串形式。
|
||||||
|
* <p>
|
||||||
|
* 该格式将输出为类似 {@code foo(a, b, c)} 的形式,
|
||||||
|
* 便于调试与语法树可视化。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 表示函数调用的字符串表示
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(callee).append("(");
|
||||||
|
for (int i = 0; i < arguments.size(); i++) {
|
||||||
|
sb.append(arguments.get(i));
|
||||||
|
if (i + 1 < arguments.size()) sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code DeclarationNode} 表示抽象语法树(AST)中的变量声明语句节点。
|
||||||
|
* <p>
|
||||||
|
* 变量声明用于在语法层引入新的标识符及其类型信息,
|
||||||
|
* 通常格式为 {@code type name = initializer;},其中初始化表达式可省略。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class DeclarationNode implements StatementNode {
|
||||||
|
|
||||||
|
/** 声明的变量名称 */
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/** 变量的数据类型(如 "int", "string") */
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
/** 可选的初始化表达式 */
|
||||||
|
private final Optional<ExpressionNode> initializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个 {@code DeclarationNode} 实例。
|
||||||
|
*
|
||||||
|
* @param name 变量名称
|
||||||
|
* @param type 变量类型字符串(如 "int"、"string")
|
||||||
|
* @param initializer 可选初始化表达式,若为 {@code null} 表示未初始化
|
||||||
|
*/
|
||||||
|
public DeclarationNode(String name, String type, ExpressionNode initializer) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.initializer = Optional.ofNullable(initializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取变量名称。
|
||||||
|
*
|
||||||
|
* @return 变量名字符串
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取变量类型字符串。
|
||||||
|
*
|
||||||
|
* @return 类型名称(如 "int")
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取可选的初始化表达式。
|
||||||
|
*
|
||||||
|
* @return 一个 Optional 包装的初始化表达式对象,可能为空
|
||||||
|
*/
|
||||||
|
public Optional<ExpressionNode> getInitializer() {
|
||||||
|
return initializer;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code ExpressionStatementNode} 表示抽象语法树(AST)中的表达式语句节点。
|
||||||
|
* <p>
|
||||||
|
* 表达式语句通常由一个单独的表达式组成,并以语句形式出现。
|
||||||
|
* 例如:{@code foo();}、{@code x = 1;}、{@code print("hello");} 等。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param expression 表达式主体,通常为函数调用、赋值、方法链式调用等可求值表达式。
|
||||||
|
*/
|
||||||
|
public record ExpressionStatementNode(ExpressionNode expression) implements StatementNode {
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code FunctionNode} 表示抽象语法树(AST)中的函数定义结构。
|
||||||
|
* <p>
|
||||||
|
* 函数定义通常包含函数名、形参列表、返回类型以及函数体,
|
||||||
|
* 在语义分析、类型检查与代码生成等阶段具有核心地位。
|
||||||
|
* 示例:{@code int add(int a, int b) { return a + b; }}
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param name 函数名称标识符
|
||||||
|
* @param parameters 参数列表,每项为 {@link ParameterNode} 表示一个形参定义
|
||||||
|
* @param returnType 函数的返回类型(如 "int"、"void" 等)
|
||||||
|
* @param body 函数体语句块,由一组 {@link StatementNode} 构成
|
||||||
|
*/
|
||||||
|
public record FunctionNode(String name, List<ParameterNode> parameters, String returnType,
|
||||||
|
List<StatementNode> body) implements Node {
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code IdentifierNode} 表示抽象语法树(AST)中的标识符表达式节点。
|
||||||
|
* <p>
|
||||||
|
* 该节点用于表示变量名、函数名、字段名等符号引用。
|
||||||
|
* 在语义分析中,通常需要将此类节点绑定到其声明位置或符号表项。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param name 标识符的文本名称(如变量名 "x",函数名 "foo")
|
||||||
|
*/
|
||||||
|
public record IdentifierNode(String name) implements ExpressionNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回标识符节点的字符串形式,通常为其名称本身。
|
||||||
|
*
|
||||||
|
* @return 标识符名称字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java
Normal file
38
src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code IfNode} 表示抽象语法树(AST)中的条件语句结构(if-else)。
|
||||||
|
* <p>
|
||||||
|
* 该节点包含一个条件表达式(condition)、一个 then 分支语句列表,
|
||||||
|
* 以及一个可选的 else 分支语句列表。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 条件表达式为布尔类型,决定是否执行 then 分支。
|
||||||
|
* 若 condition 为假,则执行 else 分支(如果提供)。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 示例语法结构:
|
||||||
|
* </p>
|
||||||
|
* <pre>{@code
|
||||||
|
* if (x > 0) {
|
||||||
|
* print("Positive");
|
||||||
|
* } else {
|
||||||
|
* print("Negative");
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param condition 控制分支执行的条件表达式
|
||||||
|
* @param thenBranch 条件为 true 时执行的语句块
|
||||||
|
* @param elseBranch 条件为 false 时执行的语句块(可为空)
|
||||||
|
*/
|
||||||
|
public record IfNode(
|
||||||
|
ExpressionNode condition,
|
||||||
|
List<StatementNode> thenBranch,
|
||||||
|
List<StatementNode> elseBranch
|
||||||
|
) implements StatementNode {
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code ImportNode} 表示抽象语法树(AST)中的 import 语句节点。
|
||||||
|
* <p>
|
||||||
|
* import 语句用于引入外部模块或库文件,其语法形式一般为:
|
||||||
|
* {@code import my.module;}
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 本节点仅存储导入目标模块的名称,不包含路径解析或绑定逻辑,
|
||||||
|
* 这些通常由语义分析器或模块加载器处理。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param moduleName 被导入的模块名称,通常为点分层次结构(如 "core.utils")
|
||||||
|
*/
|
||||||
|
public record ImportNode(String moduleName) implements Node {
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code LoopNode} 表示抽象语法树(AST)中的循环语句结构。
|
||||||
|
* <p>
|
||||||
|
* 该节点建模了类似传统 {@code for} 循环的控制结构,
|
||||||
|
* 包含初始化语句、循环条件、更新语句及循环体。
|
||||||
|
* 每一部分均对应为 AST 中的子节点,便于进一步语义分析与代码生成。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param initializer 在循环开始前执行的初始化语句
|
||||||
|
* @param condition 每次迭代前评估的条件表达式,控制循环是否继续
|
||||||
|
* @param update 每轮迭代完成后执行的更新语句
|
||||||
|
* @param body 循环体语句列表,表示循环主体执行逻辑
|
||||||
|
*/
|
||||||
|
public record LoopNode(StatementNode initializer, ExpressionNode condition, StatementNode update,
|
||||||
|
List<StatementNode> body) implements StatementNode {
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code MemberExpressionNode} 表示抽象语法树(AST)中的成员访问表达式节点。
|
||||||
|
* <p>
|
||||||
|
* 用于表示对象字段或方法的访问操作,语法形式如 {@code object.member}。
|
||||||
|
* 成员访问常见于结构体、模块、对象导入等上下文中,是表达式链中常见的构件之一。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param object 左侧对象表达式,表示成员所属的作用域或容器
|
||||||
|
* @param member 要访问的成员名称(字段名或方法名)
|
||||||
|
*/
|
||||||
|
public record MemberExpressionNode(ExpressionNode object, String member) implements ExpressionNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回成员访问表达式的字符串形式。
|
||||||
|
* <p>
|
||||||
|
* 输出格式为 {@code object.member},用于调试或语法树可视化。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 成员访问表达式的字符串形式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return object + "." + member;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示模块定义的 AST 节点。
|
||||||
|
* 一个模块通常由模块名、导入语句列表和函数定义列表组成。
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param name 模块名称。
|
||||||
|
* @param imports 模块导入列表,每个导入是一个 {@link ImportNode}。
|
||||||
|
* @param functions 模块中的函数列表,每个函数是一个 {@link FunctionNode}。
|
||||||
|
*/
|
||||||
|
public record ModuleNode(String name, List<ImportNode> imports, List<FunctionNode> functions) implements Node {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回模块节点的字符串表示形式,包含模块名、导入模块列表和函数列表。
|
||||||
|
*
|
||||||
|
* @return 模块的简洁字符串表示,用于调试和日志输出。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringJoiner importJoiner = new StringJoiner(", ");
|
||||||
|
for (ImportNode imp : imports) {
|
||||||
|
importJoiner.add(imp.moduleName());
|
||||||
|
}
|
||||||
|
StringJoiner funcJoiner = new StringJoiner(", ");
|
||||||
|
for (FunctionNode fn : functions) {
|
||||||
|
funcJoiner.add(fn.name());
|
||||||
|
}
|
||||||
|
return "Module(name=" + name
|
||||||
|
+ ", imports=[" + importJoiner + "]"
|
||||||
|
+ ", functions=[" + funcJoiner + "])";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code NumberLiteralNode} 表示抽象语法树(AST)中的数字字面量表达式节点。
|
||||||
|
* <p>
|
||||||
|
* 用于表示源代码中的数值常量,如整数 {@code 42} 或浮点数 {@code 3.14}。
|
||||||
|
* 为了兼容不同数值格式,本节点以字符串形式存储原始值,
|
||||||
|
* 在语义分析或类型推导阶段再行解析为具体数值类型。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value 数字字面量的原始字符串表示
|
||||||
|
*/
|
||||||
|
public record NumberLiteralNode(String value) implements ExpressionNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回数字字面量的字符串形式。
|
||||||
|
*
|
||||||
|
* @return 字面量原始字符串值(例如 "42" 或 "3.14")
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code ParameterNode} 表示抽象语法树(AST)中的函数参数定义节点。
|
||||||
|
* <p>
|
||||||
|
* 每个参数节点包含参数的名称和类型信息,
|
||||||
|
* 用于构成函数签名并参与类型检查与函数调用匹配。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param name 参数名称标识符
|
||||||
|
* @param type 参数类型字符串(如 "int"、"string")
|
||||||
|
*/
|
||||||
|
public record ParameterNode(String name, String type) implements Node {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回参数的字符串形式,格式为 {@code name:type}。
|
||||||
|
* <p>
|
||||||
|
* 用于调试输出或构建函数签名描述。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 参数的字符串形式(如 {@code count:int})
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name + ":" + type;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code ReturnNode} 表示抽象语法树(AST)中的 return 语句节点。
|
||||||
|
* <p>
|
||||||
|
* return 语句用于从当前函数中返回控制权,并可携带一个可选的返回值表达式。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 示例:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code return;}</li>
|
||||||
|
* <li>{@code return x + 1;}</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class ReturnNode implements StatementNode {
|
||||||
|
|
||||||
|
/** 可选的返回值表达式 */
|
||||||
|
private final Optional<ExpressionNode> expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一个 {@code ReturnNode} 实例。
|
||||||
|
*
|
||||||
|
* @param expression 返回值表达式,如果无返回值则可为 {@code null}
|
||||||
|
*/
|
||||||
|
public ReturnNode(ExpressionNode expression) {
|
||||||
|
this.expression = Optional.ofNullable(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取可选的返回值表达式。
|
||||||
|
*
|
||||||
|
* @return 如果有返回值则返回 {@code Optional.of(expression)},否则返回 {@code Optional.empty()}
|
||||||
|
*/
|
||||||
|
public Optional<ExpressionNode> getExpression() {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code StringLiteralNode} 表示抽象语法树(AST)中的字符串字面量表达式节点。
|
||||||
|
* <p>
|
||||||
|
* 用于表示源代码中出现的字符串常量,如 {@code "hello"}、{@code "abc123"} 等。
|
||||||
|
* 节点内部仅保存不带引号的字符串内容,便于后续语义处理或编码。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param value 字符串常量的内容,原始值中不包含双引号
|
||||||
|
*/
|
||||||
|
public record StringLiteralNode(String value) implements ExpressionNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回字符串字面量的带引号表示,适用于语法树调试或文本输出。
|
||||||
|
* <p>
|
||||||
|
* 例如,当 {@code value = Result:} 时,返回 {@code "Result:"}。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return 字符串字面量的完整表示形式(带双引号)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "\"" + value + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code ExpressionNode} 表示抽象语法树(AST)中所有表达式类型节点的统一接口。
|
||||||
|
* <p>
|
||||||
|
* 作为标记接口(Marker Interface),该接口用于区分表达式与其他语法结构,
|
||||||
|
* 不定义具体方法,其子类型通常表示可参与求值运算的结构,
|
||||||
|
* 如常量表达式、变量引用、函数调用、算术运算等。
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 所有实现此接口的节点可参与表达式求值、语义分析、类型检查与中间代码生成等处理流程。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface ExpressionNode extends Node {}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code Node} 是抽象语法树(AST)中所有语法节点的统一根接口。
|
||||||
|
* <p>
|
||||||
|
* 作为标记接口(Marker Interface),该接口不定义任何方法,
|
||||||
|
* 主要用于统一标识并组织 AST 体系中的各种语法构件节点,包括:
|
||||||
|
* </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link ExpressionNode}:表达式节点,如常量、变量引用、函数调用等</li>
|
||||||
|
* <li>{@link StatementNode}:语句节点,如声明、赋值、条件控制、循环、返回语句等</li>
|
||||||
|
* <li>模块、函数、参数等高层结构节点</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* 所有 AST 处理逻辑(如遍历、分析、代码生成)均可基于该接口实现统一调度和类型判定。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface Node {}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package org.jcnc.snow.compiler.parser.ast.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code StatementNode} 表示抽象语法树(AST)中所有语句结构的统一接口。
|
||||||
|
* <p>
|
||||||
|
* 该接口为标记接口(Marker Interface),用于识别和区分语句类节点,
|
||||||
|
* 包括但不限于变量声明、赋值语句、控制结构(如 if、loop)、返回语句等。
|
||||||
|
* 实现此接口的类应表示程序在运行时执行的具体语法行为。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface StatementNode extends Node {}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user