Compare commits
No commits in common. "main" and "v0.6.0" have entirely different histories.
2
.env
2
.env
@ -1,2 +0,0 @@
|
||||
# Auto-generated by build\tools\generate-dotenv.ps1
|
||||
SNOW_VERSION=0.8.0
|
||||
@ -69,7 +69,7 @@ body:
|
||||
attributes:
|
||||
label: 软件版本/分支
|
||||
options:
|
||||
- v0.8.0
|
||||
- v0.6.0
|
||||
- main
|
||||
- dev
|
||||
- 其他
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Bug6" type="Application" factoryName="Application" folderName="BugFarm">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/BugFarm/Bug6 -o target/Bug6 --debug" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -7,12 +7,4 @@
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<configuration default="false" name="Demo14" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo14 -o target/Demo14" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo19" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo19 -o target/Demo19 --debug" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo20" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo20 -o target/Demo20 --debug" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo21" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo21 -o target/Demo21 --debug" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo22" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo22 -o target/Demo22" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo23" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo23 -o target/Demo23 --debug" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo24" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo24 -o target/Demo24" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,10 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo25" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo25 -o target/Demo25" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
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="Demo1" type="Application" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,6 +0,0 @@
|
||||
<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>
|
||||
</component>
|
||||
@ -1,9 +1,9 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="build-release-all.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build-release-all.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
|
||||
<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-release-all.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build-release-all.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
|
||||
<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>
|
||||
@ -1,6 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="release-linux.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/release-linux.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,6 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="release-windows.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/release-windows.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@ -9,15 +9,7 @@
|
||||
<toRun name="Demo15" type="Application" />
|
||||
<toRun name="Demo16" type="Application" />
|
||||
<toRun name="Demo17" type="Application" />
|
||||
<toRun name="Demo18" type="Application" />
|
||||
<toRun name="Demo19" type="Application" />
|
||||
<toRun name="Demo2" type="Application" />
|
||||
<toRun name="Demo20" type="Application" />
|
||||
<toRun name="Demo21" type="Application" />
|
||||
<toRun name="Demo22" type="Application" />
|
||||
<toRun name="Demo23" type="Application" />
|
||||
<toRun name="Demo24" type="Application" />
|
||||
<toRun name="Demo25" type="Application" />
|
||||
<toRun name="Demo3" type="Application" />
|
||||
<toRun name="Demo4" type="Application" />
|
||||
<toRun name="Demo6" type="Application" />
|
||||
|
||||
57
Dockerfile
57
Dockerfile
@ -1,57 +0,0 @@
|
||||
# Stage 1: 官方 GraalVM 社区版(已含 native-image)
|
||||
FROM ghcr.io/graalvm/native-image-community:24.0.2 AS builder
|
||||
|
||||
RUN microdnf install -y \
|
||||
gcc gcc-c++ make git wget tar gzip which findutils maven \
|
||||
&& microdnf clean all
|
||||
|
||||
# ---------- 构建 musl ----------
|
||||
ARG MUSL_VER=1.2.5
|
||||
WORKDIR /tmp
|
||||
RUN wget -q https://musl.libc.org/releases/musl-${MUSL_VER}.tar.gz \
|
||||
&& tar -xzf musl-${MUSL_VER}.tar.gz \
|
||||
&& cd musl-${MUSL_VER} \
|
||||
&& ./configure --prefix=/opt/musl-${MUSL_VER} --disable-shared \
|
||||
&& make -j"$(nproc)" \
|
||||
&& make install \
|
||||
&& ln -s /opt/musl-${MUSL_VER} /opt/musl \
|
||||
&& cd / && rm -rf /tmp/musl-${MUSL_VER}*
|
||||
|
||||
RUN ln -s /opt/musl/bin/musl-gcc /usr/local/bin/x86_64-linux-musl-gcc \
|
||||
&& ln -s /opt/musl/bin/musl-gcc /usr/local/bin/x86_64-linux-musl-cc
|
||||
|
||||
ENV PATH="/opt/musl/bin:${PATH}"
|
||||
ENV CC="musl-gcc"
|
||||
ENV C_INCLUDE_PATH="/opt/musl/include"
|
||||
ENV LIBRARY_PATH="/opt/musl/lib"
|
||||
|
||||
# ---------- 静态 zlib ----------
|
||||
ARG ZLIB_VERSION=1.3.1
|
||||
WORKDIR /tmp
|
||||
RUN wget -q https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \
|
||||
&& tar -xzf zlib-${ZLIB_VERSION}.tar.gz \
|
||||
&& cd zlib-${ZLIB_VERSION} \
|
||||
&& CC=musl-gcc ./configure --static --prefix=/opt/musl \
|
||||
&& make -j"$(nproc)" \
|
||||
&& make install \
|
||||
&& cd / && rm -rf /tmp/zlib-${ZLIB_VERSION}*
|
||||
|
||||
# ---------- Maven 缓存优化 ----------
|
||||
WORKDIR /app
|
||||
COPY pom.xml ./
|
||||
|
||||
# 先拉依赖并缓存
|
||||
RUN mvn -B -P native-linux dependency:go-offline
|
||||
|
||||
# ---------- 复制源码 ----------
|
||||
COPY . /app
|
||||
|
||||
# ---------- 编译 native image ----------
|
||||
RUN mvn -P native-linux -DskipTests clean package
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Stage 2: 输出产物镜像(可以直接 cp 出二进制)
|
||||
# ------------------------------------------------------------
|
||||
FROM busybox AS export
|
||||
WORKDIR /export
|
||||
COPY --from=builder /app/org.jcnc.snow.cli.SnowCLI /export/Snow
|
||||
24
README.md
24
README.md
@ -11,8 +11,8 @@
|
||||
<a href="https://gitee.com/jcnc-org/snow/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/badge/%20license-Apache--2.0%20-blue" alt="">
|
||||
</a>
|
||||
<a href="https://gitee.com/jcnc-org/snow/tree/v0.8.0/">
|
||||
<img src="https://img.shields.io/badge/version-v0.8.0-blue" alt="">
|
||||
<a href="https://gitee.com/jcnc-org/snow/tree/v0.6.0/">
|
||||
<img src="https://img.shields.io/badge/version-v0.6.0-blue" alt="">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@ -44,7 +44,7 @@ SnowVM) 的完整编译-执行链路。
|
||||
|
||||
Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的语法和严格的类型系统,以帮助 LLM 更好地理解程序。
|
||||
|
||||
语言使用显式的 `module` 声明来组织代码,用 `function`,`params`,`returns`,`body` 等关键字分隔不同代码块,语法结构固定且易读。此外,Snow
|
||||
语言使用显式的 `module` 声明来组织代码,用 `function`,`parameter`,`return_type`,`body` 等关键字分隔不同代码块,语法结构固定且易读。此外,Snow
|
||||
实现了语义分析来检查变量作用域和类型一致性,在编译阶段捕获错误并确保生成的中间代码正确无误。这种自上而下的编译流程,使得代码设计和生成更加模块化,可解释,也有利于调试和优化。
|
||||
|
||||
相关背景: [心路历程](docs/Snow-Lang-Journey/Snow-Lang-Journey.md)
|
||||
@ -111,7 +111,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
module: Main
|
||||
import:Math
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
Math.add(6,1)
|
||||
return 0
|
||||
@ -135,7 +135,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
3 15 IDENTIFIER main
|
||||
3 19 NEWLINE \n
|
||||
|
||||
4 9 KEYWORD returns
|
||||
4 9 KEYWORD return_type
|
||||
4 20 COLON :
|
||||
4 22 TYPE int
|
||||
4 25 NEWLINE \n
|
||||
@ -174,10 +174,10 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
#### Math.snow
|
||||
module: Math
|
||||
function: add
|
||||
params:
|
||||
parameter:
|
||||
declare n1: int
|
||||
declare n2: int
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
return n1 + n2
|
||||
end body
|
||||
@ -195,7 +195,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
2 15 IDENTIFIER add
|
||||
2 18 NEWLINE \n
|
||||
|
||||
3 9 KEYWORD params
|
||||
3 9 KEYWORD parameter
|
||||
3 18 COLON :
|
||||
3 19 NEWLINE \n
|
||||
|
||||
@ -211,7 +211,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
5 25 TYPE int
|
||||
5 28 NEWLINE \n
|
||||
|
||||
6 9 KEYWORD returns
|
||||
6 9 KEYWORD return_type
|
||||
6 20 COLON :
|
||||
6 22 TYPE int
|
||||
6 25 NEWLINE \n
|
||||
@ -494,7 +494,7 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
||||
```snow
|
||||
module: Math
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
Math.factorial(6)
|
||||
return 0
|
||||
@ -502,9 +502,9 @@ module: Math
|
||||
end function
|
||||
|
||||
function: factorial
|
||||
params:
|
||||
parameter:
|
||||
declare n:int
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
declare num1:int = 1
|
||||
loop:
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
# Set the tar package name
|
||||
$tarName = "Snow.tar"
|
||||
|
||||
# Get the script's current directory (build folder)
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
|
||||
# Get the parent directory (the project root)
|
||||
$parentDir = Split-Path -Parent $scriptDir
|
||||
|
||||
# Set the full path to the tar package
|
||||
$tarPath = Join-Path $parentDir $tarName
|
||||
|
||||
# Output message: starting to create tar package
|
||||
Write-Output "Starting to create tar package: $tarName in $parentDir ..."
|
||||
|
||||
# Remove old tar package if it exists
|
||||
if (Test-Path $tarPath) {
|
||||
Write-Output "Found an old $tarName, removing it..."
|
||||
Remove-Item $tarPath -Force
|
||||
}
|
||||
|
||||
# Make sure the tar command is available
|
||||
$tarCommand = "tar"
|
||||
if (-not (Get-Command $tarCommand -ErrorAction SilentlyContinue)) {
|
||||
Write-Error "❌ 'tar' command is not available. Please make sure 'tar' is installed and can be run from PowerShell."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Execute tar: change to org\jcnc directory and compress the snow folder
|
||||
try {
|
||||
# Build the command and run it
|
||||
$tarCommandArgs = "-cf", $tarPath, "-C", "$scriptDir\..\src\main\java\org\jcnc", "snow"
|
||||
Write-Output "Running tar command: tar $tarCommandArgs"
|
||||
|
||||
& $tarCommand @tarCommandArgs
|
||||
} catch {
|
||||
Write-Error "❌ Failed to create tar package. Error: $_"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if tar package was created successfully
|
||||
if (Test-Path $tarPath) {
|
||||
Write-Output "✅ Successfully created $tarName"
|
||||
exit 0
|
||||
} else {
|
||||
Write-Error "❌ Creation failed. Please check the tar command and paths."
|
||||
exit 1
|
||||
}
|
||||
@ -1,129 +0,0 @@
|
||||
param(
|
||||
[string]$LogDir = (Join-Path $PSScriptRoot 'target\parallel-logs')
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$winScript = Join-Path $PSScriptRoot 'release-windows.ps1'
|
||||
$linScript = Join-Path $PSScriptRoot 'release-linux.ps1'
|
||||
|
||||
if (-not (Test-Path $winScript)) { throw "File not found: $winScript" }
|
||||
if (-not (Test-Path $linScript)) { throw "File not found: $linScript" }
|
||||
|
||||
$winLogOut = [System.IO.Path]::GetTempFileName()
|
||||
$winLogErr = [System.IO.Path]::GetTempFileName()
|
||||
$linLogOut = [System.IO.Path]::GetTempFileName()
|
||||
$linLogErr = [System.IO.Path]::GetTempFileName()
|
||||
|
||||
$winProc = Start-Process powershell.exe -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',"`"$winScript`"") `
|
||||
-RedirectStandardOutput $winLogOut -RedirectStandardError $winLogErr -NoNewWindow -PassThru
|
||||
$linProc = Start-Process powershell.exe -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',"`"$linScript`"") `
|
||||
-RedirectStandardOutput $linLogOut -RedirectStandardError $linLogErr -NoNewWindow -PassThru
|
||||
|
||||
$winPosOut = 0
|
||||
$winPosErr = 0
|
||||
$linPosOut = 0
|
||||
$linPosErr = 0
|
||||
|
||||
Write-Host "===== Build Started ====="
|
||||
while (-not $winProc.HasExited -or -not $linProc.HasExited) {
|
||||
# windows-release stdout
|
||||
if (Test-Path $winLogOut) {
|
||||
$size = (Get-Item $winLogOut).Length
|
||||
if ($size -gt $winPosOut) {
|
||||
$fs = [System.IO.File]::Open($winLogOut, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||
$fs.Position = $winPosOut
|
||||
$sr = New-Object System.IO.StreamReader($fs)
|
||||
while (!$sr.EndOfStream) {
|
||||
$line = $sr.ReadLine()
|
||||
if ($line) { Write-Host "[windows-release][OUT] $line" }
|
||||
}
|
||||
$winPosOut = $fs.Position
|
||||
$sr.Close()
|
||||
$fs.Close()
|
||||
}
|
||||
}
|
||||
# windows-release stderr
|
||||
if (Test-Path $winLogErr) {
|
||||
$size = (Get-Item $winLogErr).Length
|
||||
if ($size -gt $winPosErr) {
|
||||
$fs = [System.IO.File]::Open($winLogErr, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||
$fs.Position = $winPosErr
|
||||
$sr = New-Object System.IO.StreamReader($fs)
|
||||
while (!$sr.EndOfStream) {
|
||||
$line = $sr.ReadLine()
|
||||
if ($line) { Write-Host "[windows-release][ERR] $line" -ForegroundColor Red }
|
||||
}
|
||||
$winPosErr = $fs.Position
|
||||
$sr.Close()
|
||||
$fs.Close()
|
||||
}
|
||||
}
|
||||
# linux-release stdout
|
||||
if (Test-Path $linLogOut) {
|
||||
$size = (Get-Item $linLogOut).Length
|
||||
if ($size -gt $linPosOut) {
|
||||
$fs = [System.IO.File]::Open($linLogOut, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||
$fs.Position = $linPosOut
|
||||
$sr = New-Object System.IO.StreamReader($fs)
|
||||
while (!$sr.EndOfStream) {
|
||||
$line = $sr.ReadLine()
|
||||
if ($line) { Write-Host "[linux-release][OUT] $line" }
|
||||
}
|
||||
$linPosOut = $fs.Position
|
||||
$sr.Close()
|
||||
$fs.Close()
|
||||
}
|
||||
}
|
||||
# linux-release stderr
|
||||
if (Test-Path $linLogErr) {
|
||||
$size = (Get-Item $linLogErr).Length
|
||||
if ($size -gt $linPosErr) {
|
||||
$fs = [System.IO.File]::Open($linLogErr, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||
$fs.Position = $linPosErr
|
||||
$sr = New-Object System.IO.StreamReader($fs)
|
||||
while (!$sr.EndOfStream) {
|
||||
$line = $sr.ReadLine()
|
||||
if ($line) { Write-Host "[linux-release][ERR] $line" -ForegroundColor Red }
|
||||
}
|
||||
$linPosErr = $fs.Position
|
||||
$sr.Close()
|
||||
$fs.Close()
|
||||
}
|
||||
}
|
||||
Start-Sleep -Milliseconds 200
|
||||
}
|
||||
|
||||
# After processes exit, print any remaining output
|
||||
$tasks = @(
|
||||
@{proc=$winProc; log=$winLogOut; tag='windows-release'; type='OUT'; skip=$winPosOut},
|
||||
@{proc=$winProc; log=$winLogErr; tag='windows-release'; type='ERR'; skip=$winPosErr},
|
||||
@{proc=$linProc; log=$linLogOut; tag='linux-release'; type='OUT'; skip=$linPosOut},
|
||||
@{proc=$linProc; log=$linLogErr; tag='linux-release'; type='ERR'; skip=$linPosErr}
|
||||
)
|
||||
foreach ($item in $tasks) {
|
||||
if (Test-Path $item.log) {
|
||||
$fs = [System.IO.File]::Open($item.log, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||
$fs.Position = $item.skip
|
||||
$sr = New-Object System.IO.StreamReader($fs)
|
||||
while (!$sr.EndOfStream) {
|
||||
$line = $sr.ReadLine()
|
||||
if ($line) {
|
||||
if ($item.type -eq 'ERR') {
|
||||
Write-Host "[$($item.tag)][ERR] $line" -ForegroundColor Red
|
||||
} else {
|
||||
Write-Host "[$($item.tag)][OUT] $line"
|
||||
}
|
||||
}
|
||||
}
|
||||
$sr.Close()
|
||||
$fs.Close()
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "All tasks completed successfully." -ForegroundColor Green
|
||||
|
||||
Remove-Item $winLogOut, $winLogErr, $linLogOut, $linLogErr -Force
|
||||
exit 0
|
||||
47
build/build_project2tar.ps1
Normal file
47
build/build_project2tar.ps1
Normal file
@ -0,0 +1,47 @@
|
||||
# 设定 tar 包的名称
|
||||
$tarName = "Snow.tar"
|
||||
|
||||
# 获取脚本当前目录(build文件夹)
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
|
||||
# 获取上一级目录(snow 根目录)
|
||||
$parentDir = Split-Path -Parent $scriptDir
|
||||
|
||||
# 设置 tar 包的完整路径
|
||||
$tarPath = Join-Path $parentDir $tarName
|
||||
|
||||
# 输出开始创建 tar 包的消息
|
||||
Write-Output "开始创建 tar 包: $tarName 到 $parentDir ..."
|
||||
|
||||
# 如果存在旧 tar 包,先删除它
|
||||
if (Test-Path $tarPath) {
|
||||
Write-Output "发现旧的 $tarName,正在删除..."
|
||||
Remove-Item $tarPath -Force
|
||||
}
|
||||
|
||||
# 确保 tar 命令可用
|
||||
$tarCommand = "tar"
|
||||
if (-not (Get-Command $tarCommand -ErrorAction SilentlyContinue)) {
|
||||
Write-Error "❌ tar 命令不可用。请确保 tar 已安装并可在 PowerShell 中执行。"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 执行打包操作: 切换到 org\jcnc 目录下再压缩 snow 文件夹
|
||||
try {
|
||||
# 构建命令并执行
|
||||
$tarCommandArgs = "-cf", $tarPath, "-C", "$scriptDir\..\src\main\java\org\jcnc", "snow"
|
||||
Write-Output "执行 tar 命令: tar $tarCommandArgs"
|
||||
|
||||
& $tarCommand @tarCommandArgs
|
||||
} catch {
|
||||
Write-Error "❌ 创建 tar 包失败。错误信息: $_"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 检查 tar 包是否创建成功
|
||||
if (Test-Path $tarPath) {
|
||||
Write-Output "✅ 成功创建 $tarName"
|
||||
} else {
|
||||
Write-Error "❌ 创建失败,请检查 tar 命令和路径是否正确。"
|
||||
exit 1
|
||||
}
|
||||
@ -1,136 +0,0 @@
|
||||
# run-linux-snow-export.ps1
|
||||
# Build and package linux-snow-export, version read from SNOW_VERSION in .env
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Import shared dotenv parser function
|
||||
. "$PSScriptRoot\tools\dotenv.ps1"
|
||||
|
||||
Write-Host "Step 0: Generate .env..."
|
||||
try {
|
||||
& "$PSScriptRoot\tools\generate-dotenv.ps1" -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Error "Failed to generate .env: $( $_.Exception.Message )"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Step 1: Build and run linux-snow-export..."
|
||||
docker compose run --build --rm linux-snow-export
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Build & Run failed, exiting script."
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
|
||||
Write-Host "Step 2: Run linux-snow-export without rebuild..."
|
||||
docker compose run --rm linux-snow-export
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Run without rebuild failed, exiting script."
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
|
||||
# ===== Step 3: Read version from .env =====
|
||||
$projectRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
$dotenvPath = Join-Path $projectRoot ".env"
|
||||
|
||||
if (-not (Test-Path -LiteralPath $dotenvPath)) {
|
||||
Write-Error ".env not found at: $dotenvPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$version = Read-DotEnvValue -FilePath $dotenvPath -Key 'SNOW_VERSION'
|
||||
if (-not $version) {
|
||||
Write-Error "SNOW_VERSION not found in .env"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ===== Step 4: Define output paths =====
|
||||
$targetDir = Join-Path $projectRoot "target\release"
|
||||
$outDir = Join-Path $targetDir "Snow-v$version-linux-x64"
|
||||
$tgzPath = Join-Path $targetDir "Snow-v$version-linux-x64.tgz"
|
||||
|
||||
# ===== Step 5: Package to .tgz (no extra top-level dir, max compression) =====
|
||||
Write-Host "Step 5: Package to .tgz..."
|
||||
|
||||
if (-not (Test-Path -LiteralPath $outDir)) {
|
||||
Write-Error "Output directory not found: $outDir"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure target directory exists
|
||||
if (-not (Test-Path -LiteralPath $targetDir)) {
|
||||
New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
|
||||
}
|
||||
|
||||
# Remove old package if exists
|
||||
if (Test-Path -LiteralPath $tgzPath) {
|
||||
Write-Host "→ Removing existing tgz: $tgzPath"
|
||||
Remove-Item -LiteralPath $tgzPath -Force
|
||||
}
|
||||
|
||||
function Invoke-TarGz {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$SourceDir,
|
||||
[Parameter(Mandatory = $true)][string]$DestTgz
|
||||
)
|
||||
$tarExe = "tar"
|
||||
|
||||
$isWindows = $env:OS -eq 'Windows_NT'
|
||||
|
||||
if ($isWindows) {
|
||||
$psi = @{
|
||||
FilePath = $tarExe
|
||||
ArgumentList= @("-C", $SourceDir, "-czf", $DestTgz, ".")
|
||||
NoNewWindow = $true
|
||||
Wait = $true
|
||||
}
|
||||
try {
|
||||
$p = Start-Process @psi -PassThru -ErrorAction Stop
|
||||
$p.WaitForExit()
|
||||
if ($p.ExitCode -ne 0) {
|
||||
throw "tar exited with code $($p.ExitCode)"
|
||||
}
|
||||
} catch {
|
||||
throw "Packaging failed (Windows tar): $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$psi = @{
|
||||
FilePath = $tarExe
|
||||
ArgumentList= @("-C", $SourceDir, "-c", "-f", $DestTgz, "-I", "gzip -9", ".")
|
||||
NoNewWindow = $true
|
||||
Wait = $true
|
||||
}
|
||||
$p = Start-Process @psi -PassThru -ErrorAction Stop
|
||||
$p.WaitForExit()
|
||||
if ($p.ExitCode -eq 0) { return }
|
||||
} catch { }
|
||||
|
||||
try {
|
||||
$psi = @{
|
||||
FilePath = $tarExe
|
||||
ArgumentList= @("-C", $SourceDir, "-c", "-z", "-f", $DestTgz, ".")
|
||||
NoNewWindow = $true
|
||||
Wait = $true
|
||||
}
|
||||
$p = Start-Process @psi -PassThru -ErrorAction Stop
|
||||
$p.WaitForExit()
|
||||
if ($p.ExitCode -ne 0) {
|
||||
throw "tar exited with code $($p.ExitCode)"
|
||||
}
|
||||
} catch {
|
||||
throw "Packaging failed (Linux tar): $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
Invoke-TarGz -SourceDir $outDir -DestTgz $tgzPath
|
||||
} catch {
|
||||
Write-Error $_.Exception.Message
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ">>> Package ready!" -ForegroundColor Green
|
||||
Write-Host "Version : $version"
|
||||
Write-Host "Output Dir : $outDir"
|
||||
Write-Host "Tgz File : $tgzPath"
|
||||
@ -1,117 +0,0 @@
|
||||
# release-windows.ps1
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
# Import shared dotenv parser function
|
||||
. "$PSScriptRoot\tools\dotenv.ps1"
|
||||
|
||||
# ===== Utility Functions =====
|
||||
function Find-PomUpwards([string]$startDir) {
|
||||
$dir = Resolve-Path $startDir
|
||||
while ($true) {
|
||||
$pom = Join-Path $dir "pom.xml"
|
||||
if (Test-Path $pom) { return $pom }
|
||||
$parent = Split-Path $dir -Parent
|
||||
if ($parent -eq $dir -or [string]::IsNullOrEmpty($parent)) { return $null }
|
||||
$dir = $parent
|
||||
}
|
||||
}
|
||||
|
||||
# ===== Step 0: Generate .env =====
|
||||
Write-Host "Step 0: Generate .env..."
|
||||
try {
|
||||
& "$PSScriptRoot\tools\generate-dotenv.ps1" -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Error "Failed to generate .env: $($_.Exception.Message)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ===== Step 1: Locate project root & build =====
|
||||
Write-Host "Step 1: Locate project root and build..."
|
||||
$pom = Find-PomUpwards -startDir $PSScriptRoot
|
||||
if (-not $pom) {
|
||||
Write-Error "pom.xml not found. Please run this script within the project."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$projectRoot = Split-Path $pom -Parent
|
||||
Push-Location $projectRoot
|
||||
try {
|
||||
Write-Host "→ Running: mvn clean package"
|
||||
mvn clean package
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Maven build failed, exiting script."
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
|
||||
# ===== Step 2: Read SNOW_VERSION =====
|
||||
Write-Host "Step 2: Read SNOW_VERSION from .env..."
|
||||
$dotenvPath = Join-Path $projectRoot ".env"
|
||||
$snowVersion = Read-DotEnvValue -FilePath $dotenvPath -Key "SNOW_VERSION"
|
||||
if (-not $snowVersion) {
|
||||
Write-Host "SNOW_VERSION not found in .env, using placeholder 0.0.0." -ForegroundColor Yellow
|
||||
$snowVersion = "0.0.0"
|
||||
}
|
||||
Write-Host "SNOW_VERSION = $snowVersion"
|
||||
|
||||
# ===== Step 3: Prepare release directory structure =====
|
||||
Write-Host "Step 3: Prepare release directory structure..."
|
||||
$targetDir = Join-Path $projectRoot "target"
|
||||
$exePath = Join-Path $targetDir "Snow.exe"
|
||||
if (-not (Test-Path $exePath)) {
|
||||
Write-Error "Expected build artifact not found: $exePath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$verName = "Snow-v${snowVersion}-windows-x64"
|
||||
$releaseRoot = Join-Path $targetDir "release"
|
||||
$outDir = Join-Path $releaseRoot $verName
|
||||
$binDir = Join-Path $outDir "bin"
|
||||
$libDir = Join-Path $outDir "lib"
|
||||
|
||||
# Clean old directory
|
||||
if (Test-Path $outDir) {
|
||||
Write-Host "→ Cleaning previous output directory..."
|
||||
Remove-Item $outDir -Recurse -Force
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $binDir | Out-Null
|
||||
Copy-Item -Path $exePath -Destination (Join-Path $binDir "Snow.exe") -Force
|
||||
Write-Host ">>> Collected Snow.exe"
|
||||
|
||||
# Optional lib
|
||||
$projectLib = Join-Path $projectRoot "lib"
|
||||
if (Test-Path $projectLib) {
|
||||
New-Item -ItemType Directory -Force -Path $libDir | Out-Null
|
||||
Copy-Item -Path (Join-Path $projectLib "*") -Destination $libDir -Recurse -Force
|
||||
Write-Host ">>> Copied lib directory"
|
||||
} else {
|
||||
Write-Host ">>> lib directory not found, skipping." -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# ===== Step 4: Create release zip =====
|
||||
Write-Host "Step 4: Create release zip..."
|
||||
New-Item -ItemType Directory -Force -Path $releaseRoot | Out-Null
|
||||
$zipPath = Join-Path $releaseRoot ("{0}.zip" -f $verName)
|
||||
if (Test-Path $zipPath) {
|
||||
Write-Host "→ Removing existing zip: $zipPath"
|
||||
Remove-Item $zipPath -Force
|
||||
}
|
||||
|
||||
try {
|
||||
Compress-Archive -Path (Join-Path $outDir '*') -DestinationPath $zipPath -CompressionLevel Optimal -Force
|
||||
} catch {
|
||||
Write-Error "Failed to create zip: $($_.Exception.Message)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ">>> Package ready!" -ForegroundColor Green
|
||||
Write-Host "Version : $snowVersion"
|
||||
Write-Host "Output Dir : $outDir"
|
||||
Write-Host "Zip File : $zipPath"
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
# tools/dotenv.ps1
|
||||
# Unified .env reader function:
|
||||
# - Supports `KEY=VAL` and `export KEY=VAL`
|
||||
# - Skips blank lines and comments
|
||||
# - Handles quoted values (single or double quotes)
|
||||
# - Allows inline comments at the end of a line (space + #)
|
||||
# - If the same KEY is defined multiple times, the last one takes precedence
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
function Read-DotEnvValue {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$FilePath,
|
||||
[Parameter(Mandatory=$true)][string]$Key
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $FilePath)) { return $null }
|
||||
|
||||
# Match the target key (escaped), allowing optional "export" prefix
|
||||
$pattern = '^(?:\s*export\s+)?(?<k>' + [regex]::Escape($Key) + ')\s*=\s*(?<v>.*)$'
|
||||
$value = $null
|
||||
|
||||
# Read line by line for large file compatibility
|
||||
Get-Content -LiteralPath $FilePath | ForEach-Object {
|
||||
$line = $_
|
||||
|
||||
# Skip blank lines and full-line comments
|
||||
if ($line -match '^\s*$') { return }
|
||||
if ($line -match '^\s*#') { return }
|
||||
|
||||
if ($line -match $pattern) {
|
||||
$v = $matches['v']
|
||||
|
||||
# Remove surrounding quotes if present
|
||||
$trimmed = $v.Trim()
|
||||
if ($trimmed -match '^\s*"(.*)"\s*$') {
|
||||
$v = $matches[1]
|
||||
} elseif ($trimmed -match "^\s*'(.*)'\s*$") {
|
||||
$v = $matches[1]
|
||||
} else {
|
||||
# Strip inline comments (space + # …), ignoring escaped \#
|
||||
if ($v -match '^(.*?)(?<!\\)\s+#.*$') {
|
||||
$v = $matches[1]
|
||||
}
|
||||
}
|
||||
|
||||
$value = $v.Trim()
|
||||
}
|
||||
}
|
||||
|
||||
return $value
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
# build\tools\generate-dotenv.ps1
|
||||
|
||||
# Repository root: go up two levels from build\tools\
|
||||
$repoRoot = (Get-Item $PSScriptRoot).Parent.Parent.FullName
|
||||
$envPath = Join-Path $repoRoot ".env"
|
||||
$pomPath = Join-Path $repoRoot "pom.xml"
|
||||
|
||||
if (-not (Test-Path $pomPath -PathType Leaf)) {
|
||||
throw "pom.xml not found: $pomPath"
|
||||
}
|
||||
|
||||
[xml]$pom = Get-Content $pomPath -Encoding UTF8
|
||||
$version = $pom.project.version
|
||||
|
||||
$lines = @(
|
||||
"# Auto-generated by build\tools\generate-dotenv.ps1"
|
||||
"SNOW_VERSION=$version"
|
||||
)
|
||||
|
||||
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
[System.IO.File]::WriteAllLines($envPath, $lines, $utf8NoBom)
|
||||
Write-Host "Generated/overwritten $envPath (version: $version)"
|
||||
return
|
||||
@ -1,24 +0,0 @@
|
||||
services:
|
||||
# Run with: docker compose run --rm linux-snow-export
|
||||
linux-snow-export:
|
||||
build:
|
||||
context: .
|
||||
target: export
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
ver="Snow-v${SNOW_VERSION}-linux-x64"
|
||||
mkdir -p "/output/release/$$ver/bin"
|
||||
cp /export/Snow "/output/release/$$ver/bin/"
|
||||
if [ -d /export/lib ]; then
|
||||
mkdir -p "/output/release/$$ver/lib"
|
||||
cp -a /export/lib/. "/output/release/$$ver/lib/"
|
||||
fi
|
||||
tar -C /output/release -czf "/output/release/$$ver.tgz" "$$ver"
|
||||
volumes:
|
||||
- ./target:/output
|
||||
- ./lib:/export/lib:ro
|
||||
env_file:
|
||||
- .env
|
||||
@ -76,8 +76,8 @@
|
||||
module: Main
|
||||
import:Math
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
Math.factorial(6L,1L)
|
||||
|
||||
@ -90,10 +90,10 @@ end module
|
||||
## 源代码 (test.snow)
|
||||
module: Math
|
||||
function: factorial
|
||||
params:
|
||||
parameter:
|
||||
declare n1: long
|
||||
declare n2: long
|
||||
returns: long
|
||||
return_type: long
|
||||
body:
|
||||
return n1+n2
|
||||
end body
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
```snow
|
||||
module: Main
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
|
||||
return 1 + 1
|
||||
@ -57,8 +57,10 @@ bool 类型:
|
||||
|---------|----|
|
||||
| b、B | 7b |
|
||||
| s、S | 7s |
|
||||
| i、I | 7i |
|
||||
| l、L | 7l |
|
||||
| f、F | 7f |
|
||||
| d、D | 7d |
|
||||
|
||||
|
||||
### 变量
|
||||
@ -114,7 +116,7 @@ cond 可以是表达式(结果为 bool 类型)或者 bool 字面量
|
||||
```snow
|
||||
module: Main
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
if 5 > 7 then
|
||||
return 5
|
||||
@ -152,7 +154,7 @@ end loop
|
||||
```snow
|
||||
module: Main
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
declare sum: int = 0
|
||||
loop:
|
||||
@ -177,17 +179,17 @@ end module
|
||||
函数的形式如下:
|
||||
```snow
|
||||
function: add
|
||||
params:
|
||||
parameter:
|
||||
declare a: int
|
||||
declare b: int
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
return a + b
|
||||
end body
|
||||
end function
|
||||
```
|
||||
其中 add 是函数名,params 下面是参数列表(可省略),与变量的定义类似,但是不允许赋初值,
|
||||
接着 returns 设置返回值类型,最后的 body 为函数体。
|
||||
其中 add 是函数名,parameter 下面是参数列表(可省略),与变量的定义类似,但是不允许赋初值,
|
||||
接着 return_type 设置返回值类型,最后的 body 为函数体。
|
||||
|
||||
## 模块
|
||||
|
||||
@ -200,7 +202,7 @@ snow 会自动将同名模块的函数合并。
|
||||
```snow
|
||||
module: Main
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
|
||||
return 1 + 1
|
||||
@ -216,10 +218,10 @@ end module
|
||||
// Math.snow
|
||||
module: Math
|
||||
function: add
|
||||
params:
|
||||
parameter:
|
||||
declare a: int
|
||||
declare b: int
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
return a + b
|
||||
end body
|
||||
@ -233,7 +235,7 @@ end module
|
||||
module: Main
|
||||
import: Math
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
|
||||
return Math.add(5, 7)
|
||||
@ -248,7 +250,7 @@ end module
|
||||
module: Main
|
||||
import: Math, Time
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
|
||||
return Math.add(5, 7)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Math
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
Math.factorial(6)
|
||||
return 0
|
||||
@ -9,9 +9,9 @@ module: Math
|
||||
end function
|
||||
|
||||
function: factorial
|
||||
params:
|
||||
parameter:
|
||||
declare n:int
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
declare num1:int = 1
|
||||
loop:
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
parameter:
|
||||
declare i1: int
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
module: Main
|
||||
function: main
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
declare abc:int =1
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
parameter:
|
||||
declare i1: int
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
// 合法
|
||||
declare b1: byte = 127b
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
parameter:
|
||||
declare i1: int
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: ModuleB
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
return ModuleB.fun()
|
||||
end body
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
module: ModuleA
|
||||
function: fun
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
return 123
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: ModuleB
|
||||
import: ModuleA
|
||||
function: fun
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
return ModuleA.fun() + ModuleA.fun()
|
||||
end body
|
||||
|
||||
@ -3,8 +3,8 @@ module: Main
|
||||
globals:
|
||||
declare sum: int = 0
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
sum = 20
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
parameter:
|
||||
declare i1: int
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
module: Main
|
||||
import: os
|
||||
globals:
|
||||
declare sum: int = 123
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
os.print(sum)
|
||||
return 0
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,11 +0,0 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import:Math
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
Math.add(6,1)
|
||||
return 0
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: Math
|
||||
function: add
|
||||
params:
|
||||
parameter:
|
||||
declare n1: int
|
||||
declare n2: int
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
return n1 + n2
|
||||
end body
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
declare res: boolean = 8L > 7L
|
||||
if res then
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
return 65537
|
||||
end body
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
module: Main
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
foo()
|
||||
|
||||
@ -9,7 +9,7 @@ module: Main
|
||||
end function
|
||||
|
||||
function: foo
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
if false then
|
||||
return 1
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
module: Main
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
5 == 7
|
||||
5 == 7s
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
os.print(222)
|
||||
end body
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
parameter:
|
||||
declare i1: int
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -3,7 +3,7 @@ module: Main
|
||||
globals:
|
||||
declare num2:int=10
|
||||
function: main
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
declare num1:int=11
|
||||
os.print(num1+num2+abc())
|
||||
@ -11,7 +11,7 @@ module: Main
|
||||
end function
|
||||
|
||||
function: abc
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
return 1
|
||||
end body
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
parameter:
|
||||
declare i1: int
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
parameter:
|
||||
declare i1: int
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
parameter:
|
||||
declare i1: int
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
loop:
|
||||
init:
|
||||
@ -31,8 +31,8 @@ module: Main
|
||||
if j % 2 == 0 then
|
||||
continue
|
||||
end if
|
||||
os.print(i)
|
||||
os.print(j)
|
||||
print(i)
|
||||
print(j)
|
||||
end body
|
||||
end loop
|
||||
end body
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
parameter:
|
||||
declare i1: int
|
||||
returns: void
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare n: int[][][][] = [
|
||||
[
|
||||
[ [17, 18], [19, 20] ],
|
||||
[ [21, 22], [23, 24] ]
|
||||
],
|
||||
[
|
||||
[ [25, 26], [27, 28] ],
|
||||
[ [29, 30], [31, 32] ]
|
||||
]
|
||||
]
|
||||
|
||||
declare i: int = 0
|
||||
declare j: int = 1
|
||||
declare k: int = 0
|
||||
declare l: int = 0
|
||||
|
||||
declare x: int = n[i][j][k][l]
|
||||
os.print(x)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,11 +0,0 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
return (1+2) / 3 * 4 + 2 *2
|
||||
end body
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
module: Main
|
||||
import:os
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare arr: int[] = [1, 2, 3]
|
||||
arr[0] = 5
|
||||
|
||||
os.print(arr[0])
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,11 +0,0 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,35 +0,0 @@
|
||||
module: Main
|
||||
import: os
|
||||
globals:
|
||||
declare sum: int = 123
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
body:
|
||||
declare arr: int[][][][][][][][][][][][][][][][][][][][][][] = [[[[[[[[[[[[[[[[[[[[[[1], [2], [3]]]]]]]]]]]]]]]]]]]]]]
|
||||
loop:
|
||||
init:
|
||||
declare i: int = 0
|
||||
cond:
|
||||
i < 3
|
||||
step:
|
||||
i = i + 1
|
||||
body:
|
||||
arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0] = arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0] + 1
|
||||
end body
|
||||
end loop
|
||||
loop:
|
||||
init:
|
||||
declare i: int = 0
|
||||
cond:
|
||||
i < 3
|
||||
step:
|
||||
i = i + 1
|
||||
body:
|
||||
os.print(arr[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][i][0])
|
||||
end body
|
||||
end loop
|
||||
return 0
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,11 +0,0 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,15 +0,0 @@
|
||||
module: Main
|
||||
import: ModuleA,os
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare sum: int = ModuleA.a+2
|
||||
os.print(sum+1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
|
||||
module: ModuleA
|
||||
globals:
|
||||
declare const a: int =10
|
||||
end module
|
||||
@ -1,11 +0,0 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,14 +0,0 @@
|
||||
module: Main
|
||||
import: ModuleA
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare sum: byte = ModuleA.a
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
|
||||
module: ModuleA
|
||||
globals:
|
||||
declare const a: byte = 2b
|
||||
end module
|
||||
@ -1,26 +0,0 @@
|
||||
module: Main
|
||||
import: ModuleA,os
|
||||
globals:
|
||||
declare const c: int = 10
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
declare a: int = ModuleA.sum(c,2)
|
||||
os.print(a)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
|
||||
module: ModuleA
|
||||
globals:
|
||||
declare const a: int = 10
|
||||
function: sum
|
||||
params:
|
||||
declare a: int
|
||||
declare b: int
|
||||
returns: int
|
||||
body:
|
||||
return a + b
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,11 +0,0 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,23 +0,0 @@
|
||||
module: Main
|
||||
import: os,ModuleA
|
||||
globals:
|
||||
declare c: int = 10
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
c = ModuleA.getA()
|
||||
os.print(c)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
|
||||
module: ModuleA
|
||||
globals:
|
||||
declare a: int = 2
|
||||
function: getA
|
||||
returns: int
|
||||
body:
|
||||
return a
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,11 +0,0 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
params:
|
||||
declare i1: int
|
||||
returns: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,20 +0,0 @@
|
||||
module: Main
|
||||
function: main
|
||||
returns: void
|
||||
body:
|
||||
// 1. 常见转义符
|
||||
declare sNewline : string = "换行示例:\n第二行"
|
||||
declare sTab : string = "制表符示例:\t列二"
|
||||
declare sBackslash: string = "反斜杠示例: C:\\Snow"
|
||||
declare sDQuote : string = "双引号示例: \"Snow\""
|
||||
declare sSQuote : string = "单引号示例: \'Snow\'"
|
||||
declare sCarriage : string = "回车示例:\rCarriage"
|
||||
declare sBackspace: string = "退格示例: ABC\bD"
|
||||
declare sFormFeed : string = "换页示例:\fPage-2"
|
||||
|
||||
// 2. Unicode 转义
|
||||
declare sUnicode : string = "𪚥𠮷: \u4F60\u597D, Snow!"
|
||||
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
declare n1: int =1
|
||||
declare n2: int =2
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
params:
|
||||
returns: boolean
|
||||
parameter:
|
||||
return_type: boolean
|
||||
body:
|
||||
declare b1: boolean =true
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
declare b1: boolean = true
|
||||
loop:
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
declare b1 :int = -1
|
||||
return b1
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
params:
|
||||
returns: boolean
|
||||
parameter:
|
||||
return_type: boolean
|
||||
body:
|
||||
declare b1 :boolean = true
|
||||
return !b1
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Main
|
||||
function: main
|
||||
params:
|
||||
returns: long
|
||||
parameter:
|
||||
return_type: long
|
||||
body:
|
||||
declare n: long
|
||||
n = 2147483647
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
module: Math
|
||||
function: main
|
||||
params:
|
||||
returns: int
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
Math.factorial(6)
|
||||
return 0
|
||||
@ -9,9 +9,9 @@ module: Math
|
||||
end function
|
||||
|
||||
function: factorial
|
||||
params:
|
||||
parameter:
|
||||
declare n:int
|
||||
returns: int
|
||||
return_type: int
|
||||
body:
|
||||
declare num1:int = 1
|
||||
loop:
|
||||
|
||||
48
pom.xml
48
pom.xml
@ -7,7 +7,7 @@
|
||||
|
||||
<groupId>org.jcnc.snow</groupId>
|
||||
<artifactId>Snow</artifactId>
|
||||
<version>0.8.0</version>
|
||||
<version>0.6.0</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@ -70,7 +70,11 @@
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<!-- 原生镜像构建: Linux 平台(使用 Docker builder 中的 musl 工具链) -->
|
||||
<!--
|
||||
原生镜像构建: Linux 平台
|
||||
- 使用 GraalVM 的 native-image 工具,生成静态链接的可执行文件
|
||||
- 依赖 musl libc,需提前安装并配置 musl-gcc 工具链
|
||||
-->
|
||||
<profile>
|
||||
<id>native-linux</id>
|
||||
<activation>
|
||||
@ -78,39 +82,25 @@
|
||||
<family>unix</family>
|
||||
</os>
|
||||
</activation>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.graalvm.buildtools</groupId>
|
||||
<artifactId>native-maven-plugin</artifactId>
|
||||
<version>${native.maven.plugin.version}</version>
|
||||
<!-- 启用插件扩展,允许在 build 生命周期中无须额外配置 -->
|
||||
<extensions>true</extensions>
|
||||
|
||||
<configuration>
|
||||
<mainClass>org.jcnc.snow.cli.SnowCLI</mainClass>
|
||||
<imageName>Snow</imageName>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
<buildArgs>
|
||||
<buildArg>--static</buildArg>
|
||||
<buildArg>--libc=musl</buildArg>
|
||||
<buildArg>--emit=build-report</buildArg>
|
||||
<buildArg>-O2</buildArg>
|
||||
<buildArg>-H:Class=org.jcnc.snow.cli.SnowCLI</buildArg>
|
||||
<buildArg>-H:CCompilerPath=/opt/musl/bin/musl-gcc</buildArg>
|
||||
<buildArg>-H:CLibraryPath=/opt/musl/lib</buildArg>
|
||||
</buildArgs>
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<!-- 打包阶段生成原生可执行文件 -->
|
||||
<execution>
|
||||
<id>build-native</id>
|
||||
<goals>
|
||||
<!-- compile-no-fork 在当前 JVM 进程中执行 native-image -->
|
||||
<goal>compile-no-fork</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
|
||||
<!-- 测试阶段运行原生镜像的测试 -->
|
||||
<execution>
|
||||
<id>test-native</id>
|
||||
<goals>
|
||||
@ -119,6 +109,24 @@
|
||||
<phase>test</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<buildArgs>
|
||||
<!-- 静态链接 -->
|
||||
<buildArg>--static</buildArg>
|
||||
<!-- 指定 musl libc -->
|
||||
<buildArg>--libc=musl</buildArg>
|
||||
<!-- 输出构建报告 -->
|
||||
<buildArg>--emit build-report</buildArg>
|
||||
<!-- 优化级别 O2 -->
|
||||
<buildArg>-O2</buildArg>
|
||||
</buildArgs>
|
||||
<environment>
|
||||
<!-- 指定使用 musl 工具链 -->
|
||||
<PATH>/opt/musl-1.2.5/bin:${env.PATH}</PATH>
|
||||
<C_INCLUDE_PATH>/opt/musl-1.2.5/include</C_INCLUDE_PATH>
|
||||
<LIBRARY_PATH>/opt/musl-1.2.5/lib</LIBRARY_PATH>
|
||||
</environment>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
package org.jcnc.snow.common;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 字符串转义/反转义工具类,主要用于:
|
||||
* <ul>
|
||||
* <li><b>编译期</b>:将运行时的字符串安全地编码为单行形式(用于 .water 指令文件的保存)。</li>
|
||||
* <li><b>运行期</b>:在虚拟机(VM)执行相关指令时,将转义后的字符串还原成真实字符。</li>
|
||||
* </ul>
|
||||
* <br>
|
||||
* 转义规则兼容 Java 字符串转义(包括 \n, \t, \r 等常见控制字符),同时对于不可见或非 ASCII 字符,会编码为 Unicode 形式(如 <code>uXXXX</code>)。
|
||||
* </p>
|
||||
*/
|
||||
public final class StringEscape {
|
||||
|
||||
/**
|
||||
* 工具类私有构造方法,禁止实例化。
|
||||
*/
|
||||
private StringEscape() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>运行期方法:</b>
|
||||
* <p>将转义序列还原为实际字符。</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>支持常见的转义字符序列。</li>
|
||||
* <li>支持 uXXXX 形式的 Unicode 字符反转义。</li>
|
||||
* <li>对于无法识别的转义,按原样输出。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param src 含有转义序列的字符串
|
||||
* @return 反转义后的字符串,原样还原
|
||||
*/
|
||||
public static String unescape(String src) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
for (int i = 0; i < src.length(); i++) {
|
||||
char c = src.charAt(i);
|
||||
if (c != '\\') { // 非转义字符,直接输出
|
||||
out.append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果是最后一个字符为反斜杠,则原样输出
|
||||
if (i == src.length() - 1) {
|
||||
out.append('\\');
|
||||
break;
|
||||
}
|
||||
|
||||
char n = src.charAt(++i); // 下一个字符
|
||||
switch (n) {
|
||||
case 'n' -> out.append('\n'); // 换行
|
||||
case 't' -> out.append('\t'); // 制表符
|
||||
case 'r' -> out.append('\r'); // 回车
|
||||
case 'b' -> out.append('\b'); // 退格
|
||||
case 'f' -> out.append('\f'); // 换页
|
||||
case '\\' -> out.append('\\'); // 反斜杠
|
||||
case '"' -> out.append('"'); // 双引号
|
||||
case '\'' -> out.append('\''); // 单引号
|
||||
case 'u' -> {
|
||||
// Unicode 转义,需读取接下来的 4 位十六进制数字
|
||||
if (i + 4 <= src.length() - 1) {
|
||||
String hex = src.substring(i + 1, i + 5);
|
||||
try {
|
||||
out.append((char) Integer.parseInt(hex, 16));
|
||||
i += 4;
|
||||
} catch (NumberFormatException ignore) {
|
||||
// 非法 hex,原样输出
|
||||
out.append("\\u").append(hex);
|
||||
i += 4;
|
||||
}
|
||||
} else {
|
||||
// 字符串末尾长度不足,原样输出
|
||||
out.append("\\u");
|
||||
}
|
||||
}
|
||||
default -> out.append(n); // 其他未定义的转义序列,原样输出
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
@ -10,46 +10,45 @@ import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
import org.jcnc.snow.vm.engine.VMOpCode;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令。
|
||||
* <b>CallGenerator - 将 IR {@code CallInstruction} 生成 VM 指令</b>
|
||||
*
|
||||
* <p>
|
||||
* 支持 syscall、普通函数调用、一维/多维数组下标访问、以及字符串常量池绑定等功能。
|
||||
* 本类负责将中间表示(IR)中的函数调用指令 {@link CallInstruction} 转换为虚拟机(VM)指令。
|
||||
* 支持普通函数调用和特殊的 syscall 指令转换。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>能力说明:</b>
|
||||
* <ul>
|
||||
* <li>syscall: 支持字符串常量与寄存器到子命令的绑定与解析</li>
|
||||
* <li>数组下标访问: 支持所有主流基础类型 “__index_b/s/i/l/f/d/r”</li>
|
||||
* <li>普通函数: 支持自动推断返回值类型与槽位类型</li>
|
||||
* <li>支持识别 {@code IRConstant} 直接字面量或已绑定到虚拟寄存器的字符串常量,直接降级为 {@code SYSCALL <SUBCMD>} 指令。</li>
|
||||
* <li>对普通函数,完成参数加载、调用、返回值保存等指令生成。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
|
||||
/**
|
||||
* 字符串常量池:用于绑定虚拟寄存器 id 到字符串值(供 syscall 子命令使用)。
|
||||
* <br>
|
||||
* 使用 ConcurrentHashMap 保证并发安全,所有 registerStringConst 的注册和读取都是线程安全的。
|
||||
* <虚拟寄存器 id, 对应的字符串常量>
|
||||
* <p>用于记录虚拟寄存器与其绑定字符串常量的映射。由 {@link LoadConstGenerator} 在编译期间填充。</p>
|
||||
*/
|
||||
private static final Map<Integer, String> STRING_CONST_POOL = new ConcurrentHashMap<>();
|
||||
private static final Map<Integer, String> STRING_CONST_POOL = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 注册一个字符串常量,绑定到虚拟寄存器 id。
|
||||
* 注册字符串常量到虚拟寄存器
|
||||
* <p>供 {@link LoadConstGenerator} 在加载字符串常量时调用。</p>
|
||||
*
|
||||
* @param regId 虚拟寄存器 id
|
||||
* @param value 字符串常量
|
||||
* @param value 字符串常量值
|
||||
*/
|
||||
public static void registerStringConst(int regId, String value) {
|
||||
STRING_CONST_POOL.put(regId, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回本生成器支持的 IR 指令类型。
|
||||
*
|
||||
* @return {@link CallInstruction} 类型的 Class 对象
|
||||
* 返回本生成器支持的 IR 指令类型(CallInstruction)
|
||||
*/
|
||||
@Override
|
||||
public Class<CallInstruction> supportedClass() {
|
||||
@ -57,11 +56,11 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成指定的调用指令,包括 syscall、数组下标、普通函数。
|
||||
* 生成 VM 指令的主逻辑
|
||||
*
|
||||
* @param ins IR 层的调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位的映射表
|
||||
* @param ins 当前 IR 指令(函数调用)
|
||||
* @param out 指令输出构建器
|
||||
* @param slotMap IR 虚拟寄存器与物理槽位映射
|
||||
* @param currentFn 当前函数名
|
||||
*/
|
||||
@Override
|
||||
@ -69,357 +68,79 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
String currentFn) {
|
||||
String fn = ins.getFunctionName();
|
||||
|
||||
// 特殊处理 syscall 调用
|
||||
if ("syscall".equals(fn) || fn.endsWith(".syscall")) {
|
||||
generateSyscall(ins, out, slotMap, fn);
|
||||
/* ========== 特殊处理 syscall 调用 ========== */
|
||||
if ("syscall".equals(ins.getFunctionName()) ||
|
||||
ins.getFunctionName().endsWith(".syscall")) {
|
||||
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.isEmpty()) {
|
||||
throw new IllegalStateException("syscall 需要子命令参数");
|
||||
}
|
||||
|
||||
// ---------- 0. 解析 syscall 子命令 ----------
|
||||
// 子命令支持 IRConstant(直接字面量)或虚拟寄存器(需已绑定字符串)
|
||||
String subcmd;
|
||||
IRValue first = args.getFirst();
|
||||
|
||||
if (first instanceof IRConstant(Object value)) { // 直接字面量
|
||||
if (!(value instanceof String s))
|
||||
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
|
||||
subcmd = s.toUpperCase(Locale.ROOT);
|
||||
|
||||
} else if (first instanceof IRVirtualRegister vr) { // 虚拟寄存器
|
||||
// 从常量池中查找是否已绑定字符串
|
||||
subcmd = Optional.ofNullable(STRING_CONST_POOL.get(vr.id()))
|
||||
.orElseThrow(() ->
|
||||
new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr));
|
||||
subcmd = subcmd.toUpperCase(Locale.ROOT);
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
|
||||
}
|
||||
|
||||
// ---------- 1. 压栈其余 syscall 参数(index 1 开始) ----------
|
||||
for (int i = 1; i < args.size(); i++) {
|
||||
IRVirtualRegister vr = (IRVirtualRegister) args.get(i);
|
||||
int slotId = slotMap.get(vr);
|
||||
char t = out.getSlotType(slotId);
|
||||
if (t == '\0') t = 'I'; // 默认整型
|
||||
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
|
||||
}
|
||||
|
||||
// ---------- 2. 生成 SYSCALL 指令 ----------
|
||||
out.emit(VMOpCode.SYSCALL + " " + subcmd);
|
||||
return; // syscall 无返回值,直接返回
|
||||
}
|
||||
|
||||
/* ========== 普通函数调用 ========== */
|
||||
|
||||
// ---------- 1. 推断返回值类型(非 void 返回时用) ----------
|
||||
char retType = 'I'; // 默认为整型
|
||||
if (!ins.getArguments().isEmpty()) {
|
||||
int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst());
|
||||
retType = out.getSlotType(firstSlot);
|
||||
if (retType == '\0') retType = 'I';
|
||||
}
|
||||
|
||||
// ---------- 2. 加载全部实参 ----------
|
||||
for (var arg : ins.getArguments()) {
|
||||
int slotId = slotMap.get((IRVirtualRegister) arg);
|
||||
char t = out.getSlotType(slotId);
|
||||
if (t == '\0') t = 'I'; // 默认整型
|
||||
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
|
||||
}
|
||||
|
||||
// ---------- 3. 发出 CALL 指令 ----------
|
||||
out.emitCall(ins.getFunctionName(), ins.getArguments().size());
|
||||
|
||||
// ---------- 3.5 如果为 void 返回直接结束 ----------
|
||||
if ("void".equals(GlobalFunctionTable.getReturnType(ins.getFunctionName()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 各种一维数组类型(byte/short/int/long/float/double/boolean)读取
|
||||
switch (fn) {
|
||||
case "__index_b" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'B');
|
||||
return;
|
||||
}
|
||||
case "__index_s" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'S');
|
||||
return;
|
||||
}
|
||||
case "__index_i" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'I');
|
||||
return;
|
||||
}
|
||||
case "__index_l" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'L');
|
||||
return;
|
||||
}
|
||||
case "__index_f" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'F');
|
||||
return;
|
||||
}
|
||||
case "__index_d" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'D');
|
||||
return;
|
||||
}
|
||||
case "__index_r" -> {
|
||||
generateIndexInstruction(ins, out, slotMap, 'R');
|
||||
return;
|
||||
}
|
||||
|
||||
case "__setindex_b" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'B');
|
||||
return;
|
||||
}
|
||||
case "__setindex_s" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'S');
|
||||
return;
|
||||
}
|
||||
case "__setindex_i" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'I');
|
||||
return;
|
||||
}
|
||||
case "__setindex_l" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'L');
|
||||
return;
|
||||
}
|
||||
case "__setindex_f" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'F');
|
||||
return;
|
||||
}
|
||||
case "__setindex_d" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'D');
|
||||
return;
|
||||
}
|
||||
case "__setindex_r" -> {
|
||||
generateSetIndexInstruction(ins, out, slotMap, 'R');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 普通函数调用
|
||||
generateNormalCall(ins, out, slotMap, fn);
|
||||
}
|
||||
|
||||
// ========== 私有辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 生成数组元素赋值指令(arr[idx] = value),无返回值。
|
||||
* <p>
|
||||
* 调用栈压栈顺序为:arr (引用类型, R),idx (整型, I),value (元素类型, T)。
|
||||
* 执行 SYSCALL ARR_SET 指令以完成数组赋值操作。
|
||||
* </p>
|
||||
*
|
||||
* @param ins 当前的调用指令(包含参数信息)
|
||||
* @param out VM 指令生成器(用于输出 VM 指令)
|
||||
* @param slotMap 虚拟寄存器与槽位的映射表
|
||||
* @param valType 待写入的 value 的类型标识('B': byte, 'S': short, 'I': int, 'L': long, 'F': float, 'D': double, 其余为引用类型'R')
|
||||
* @throws IllegalStateException 如果参数数量不为3,则抛出异常
|
||||
*/
|
||||
private void generateSetIndexInstruction(CallInstruction ins,
|
||||
VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
char valType) {
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.size() != 3) {
|
||||
// 参数数量错误,抛出异常并输出实际参数列表
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] __setindex_* 需要三个参数(arr, idx, value),实际: " + args);
|
||||
}
|
||||
|
||||
// 第一个参数为数组对象,压入引用类型寄存器('R'),如 arr
|
||||
loadArgument(out, slotMap, args.get(0), 'R', ins.getFunctionName());
|
||||
|
||||
// 第二个参数为索引值,压入整型寄存器('I'),如 idx
|
||||
loadArgument(out, slotMap, args.get(1), 'I', ins.getFunctionName());
|
||||
|
||||
// 第三个参数为待赋值元素,根据元素类型压入相应类型寄存器
|
||||
// 支持类型:'B'(byte), 'S'(short), 'I'(int), 'L'(long), 'F'(float), 'D'(double)
|
||||
// 其他情况(如引用类型),按'R'处理
|
||||
switch (valType) {
|
||||
case 'B' -> loadArgument(out, slotMap, args.get(2), 'B', ins.getFunctionName());
|
||||
case 'S' -> loadArgument(out, slotMap, args.get(2), 'S', ins.getFunctionName());
|
||||
case 'I' -> loadArgument(out, slotMap, args.get(2), 'I', ins.getFunctionName());
|
||||
case 'L' -> loadArgument(out, slotMap, args.get(2), 'L', ins.getFunctionName());
|
||||
case 'F' -> loadArgument(out, slotMap, args.get(2), 'F', ins.getFunctionName());
|
||||
case 'D' -> loadArgument(out, slotMap, args.get(2), 'D', ins.getFunctionName());
|
||||
default -> loadArgument(out, slotMap, args.get(2), 'R', ins.getFunctionName());
|
||||
}
|
||||
|
||||
// 输出 VM 指令:SYSCALL ARR_SET,完成数组元素写入操作
|
||||
out.emit(VMOpCode.SYSCALL + " " + "ARR_SET");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析 syscall 子命令(第一个参数),支持字符串常量与已绑定字符串的虚拟寄存器。
|
||||
*
|
||||
* @param arg 子命令参数(应为字符串常量或寄存器)
|
||||
* @param fn 当前函数名(仅用于报错信息)
|
||||
* @return 子命令(大写字符串)
|
||||
* @throws IllegalStateException 如果参数不是字符串常量或已绑定寄存器
|
||||
*/
|
||||
private String resolveSyscallSubcmd(IRValue arg, String fn) {
|
||||
switch (arg) {
|
||||
case IRConstant(String s) -> {
|
||||
return s.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
case IRConstant(Object value) -> throw new IllegalStateException(
|
||||
"[CallGenerator] syscall 第一个参数必须是字符串常量 (function: %s, value: %s)"
|
||||
.formatted(fn, value)
|
||||
);
|
||||
case IRVirtualRegister(int id) -> {
|
||||
String s = STRING_CONST_POOL.get(id);
|
||||
if (s == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 未找到 syscall 字符串常量绑定 (function: %s, regId: %d)"
|
||||
.formatted(fn, id)
|
||||
);
|
||||
}
|
||||
return s.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
case null, default -> throw new IllegalStateException(
|
||||
"[CallGenerator] syscall 第一个参数必须是字符串常量或已绑定字符串的寄存器 (function: %s, arg: %s)"
|
||||
.formatted(fn, arg)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载一个参数到栈,支持指定默认类型。如果未设置类型则采用默认类型。
|
||||
*
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位的映射
|
||||
* @param arg 参数值(应为虚拟寄存器)
|
||||
* @param defaultType 默认类型
|
||||
* @param fn 当前函数名(用于错误提示)
|
||||
* @throws IllegalStateException 如果参数不是虚拟寄存器,或槽位未找到
|
||||
*/
|
||||
private void loadArgument(VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, IRValue arg, char defaultType, String fn) {
|
||||
if (!(arg instanceof IRVirtualRegister vr)) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 参数必须为虚拟寄存器 (function: %s, arg: %s)".formatted(fn, arg));
|
||||
}
|
||||
Integer slot = slotMap.get(vr);
|
||||
if (slot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 未找到虚拟寄存器的槽位映射 (function: %s, reg: %s)".formatted(fn, vr));
|
||||
}
|
||||
char t = out.getSlotType(slot);
|
||||
if (t == '\0') t = defaultType;
|
||||
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一维/多维数组的下标访问指令(支持类型分派)。
|
||||
* <p>
|
||||
* 本方法用于将 IR 层的数组下标访问表达式(如 arr[idx]),生成对应的 VM 指令序列。
|
||||
* 支持 byte/short/int/long/float/double/reference 等所有基础类型的数组元素访问。
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>1. 依次加载数组参数(arr)和下标参数(idx)到操作数栈</li>
|
||||
* <li>2. 发出 ARR_GET 系统调用指令(SYSCALL ARR_GET),通过 VM 访问对应元素</li>
|
||||
* <li>3. 根据元素声明类型,将结果写入目标槽位,支持类型分派(B/S/I/L/F/D/R)</li>
|
||||
* <li>4. 若参数或返回寄存器缺失,则抛出异常提示</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ins 下标访问对应的 IR 调用指令,函数名通常为 __index_x
|
||||
* @param out VM 程序构建器,用于发出 VM 指令
|
||||
* @param slotMap IR 虚拟寄存器到 VM 槽位的映射表
|
||||
* @param retType 元素类型标识('B' byte, 'S' short, 'I' int, 'L' long, 'F' float, 'D' double, 'R' ref/obj)
|
||||
* @throws IllegalStateException 参数个数不符、缺少目标寄存器、未找到槽位等情况
|
||||
*/
|
||||
private void generateIndexInstruction(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, char retType) {
|
||||
String fn = ins.getFunctionName();
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.size() != 2) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] %s 需要两个参数(arr, idx),实际: %s".formatted(fn, args));
|
||||
}
|
||||
// 加载数组参数(寄存器类型按 R/ref 处理,默认对象槽位)
|
||||
loadArgument(out, slotMap, args.get(0), 'R', fn);
|
||||
// 加载下标参数(寄存器类型按 I/int 处理)
|
||||
loadArgument(out, slotMap, args.get(1), 'I', fn);
|
||||
|
||||
// 发出 ARR_GET 系统调用(元素访问由 VM 完成类型分派)
|
||||
out.emit(VMOpCode.SYSCALL + " " + "ARR_GET");
|
||||
|
||||
// 保存返回值到目标寄存器
|
||||
IRVirtualRegister dest = ins.getDest();
|
||||
if (dest == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] %s 需要有目标寄存器用于保存返回值".formatted(fn));
|
||||
}
|
||||
Integer destSlot = slotMap.get(dest);
|
||||
if (destSlot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] %s 未找到目标寄存器的槽位映射 (dest: %s)".formatted(fn, dest));
|
||||
}
|
||||
|
||||
// 按元素类型分派写入 VM 槽位
|
||||
switch (retType) {
|
||||
case 'B' -> {
|
||||
out.emit(OpHelper.opcode("B_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'B');
|
||||
}
|
||||
case 'S' -> {
|
||||
out.emit(OpHelper.opcode("S_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'S');
|
||||
}
|
||||
case 'I' -> {
|
||||
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'I');
|
||||
}
|
||||
case 'L' -> {
|
||||
out.emit(OpHelper.opcode("L_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'L');
|
||||
}
|
||||
case 'F' -> {
|
||||
out.emit(OpHelper.opcode("F_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'F');
|
||||
}
|
||||
case 'D' -> {
|
||||
out.emit(OpHelper.opcode("D_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'D');
|
||||
}
|
||||
default -> {
|
||||
out.emit(OpHelper.opcode("R_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'R');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 syscall 指令分支逻辑。
|
||||
* <ol>
|
||||
* <li>解析 syscall 子命令</li>
|
||||
* <li>压栈剩余参数</li>
|
||||
* <li>发出 SYSCALL 指令</li>
|
||||
* <li>若有返回值则保存至目标槽位</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param ins 调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param fn 当前函数名
|
||||
*/
|
||||
private void generateSyscall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] syscall 需要子命令参数 (function: %s)".formatted(fn));
|
||||
}
|
||||
|
||||
// 0. 解析 syscall 子命令(第一个参数)
|
||||
String subcmd = resolveSyscallSubcmd(args.getFirst(), fn);
|
||||
|
||||
// 1. 压栈其余 syscall 参数(从 index 1 开始)
|
||||
for (int i = 1; i < args.size(); i++) {
|
||||
loadArgument(out, slotMap, args.get(i), 'R', fn);
|
||||
}
|
||||
|
||||
// 2. 生成 SYSCALL 指令
|
||||
out.emit(VMOpCode.SYSCALL + " " + subcmd);
|
||||
|
||||
// 3. 有返回值则保存到目标槽位
|
||||
IRVirtualRegister dest = ins.getDest();
|
||||
if (dest != null) {
|
||||
Integer destSlot = slotMap.get(dest);
|
||||
if (destSlot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] syscall 未找到目标寄存器的槽位映射 (function: %s, dest: %s)"
|
||||
.formatted(fn, dest));
|
||||
}
|
||||
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, 'I');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成普通函数调用指令:
|
||||
* <ol>
|
||||
* <li>推断返回值类型</li>
|
||||
* <li>压栈所有参数</li>
|
||||
* <li>生成 CALL 指令</li>
|
||||
* <li>保存返回值(若非 void)</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param ins 调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param fn 当前函数名
|
||||
*/
|
||||
private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
|
||||
// 1. 推断返回值类型(首字母大写,缺省为 'I')
|
||||
String retTypeName = GlobalFunctionTable.getReturnType(fn);
|
||||
char retType = (retTypeName != null && !retTypeName.isEmpty()) ? Character.toUpperCase(retTypeName.charAt(0)) : 'I';
|
||||
|
||||
// 2. 压栈所有参数
|
||||
for (IRValue arg : ins.getArguments()) {
|
||||
loadArgument(out, slotMap, arg, 'R', fn);
|
||||
}
|
||||
|
||||
// 3. 发出 CALL 指令
|
||||
out.emitCall(fn, ins.getArguments().size());
|
||||
|
||||
// 3.5 void 返回直接结束
|
||||
if ("void".equals(retTypeName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 保存返回值到目标寄存器
|
||||
IRVirtualRegister dest = ins.getDest();
|
||||
if (dest == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 普通函数调用未找到目标寄存器 (function: %s)".formatted(fn));
|
||||
}
|
||||
Integer destSlot = slotMap.get(dest);
|
||||
if (destSlot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 普通函数调用未找到目标寄存器的槽位映射 (function: %s, dest: %s)".formatted(fn, dest));
|
||||
}
|
||||
// ---------- 4. 保存返回值 ----------
|
||||
int destSlot = slotMap.get(ins.getDest());
|
||||
out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, retType);
|
||||
}
|
||||
|
||||
@ -7,136 +7,34 @@ import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
|
||||
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <b>LoadConstGenerator</b>
|
||||
* <b>LoadConstGenerator - 将 IR {@code LoadConstInstruction} 生成 VM 指令</b>
|
||||
*
|
||||
* <p>
|
||||
* This generator converts an IR-level {@link LoadConstInstruction} into corresponding VM instructions.
|
||||
* If the constant is a {@code String}, it will also be registered in the
|
||||
* {@link CallGenerator} string constant pool for later use.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Key implementation notes:
|
||||
* <ul>
|
||||
* <li>When the constant is an array (List), type information is preserved in the R_PUSH payload:</li>
|
||||
* <li>Float values get an <code>f</code> suffix (e.g., 0.1f)</li>
|
||||
* <li>Long values get an <code>L</code> suffix (e.g., 123L)</li>
|
||||
* <li>Double and Integer values use their default string format (e.g., 1.0, 42)</li>
|
||||
* <li>Nested arrays are recursively serialized with correct type suffixes.</li>
|
||||
* </ul>
|
||||
* This prevents type confusion on the VM side (e.g., float being misread as double)
|
||||
* and avoids cast exceptions during store operations.
|
||||
* 本类负责将 IR 层的常量加载指令 {@link LoadConstInstruction} 转换为对应的虚拟机指令。
|
||||
* 额外支持:如果常量类型为 {@code String},会同步登记到
|
||||
* {@link CallGenerator} 的字符串常量池,方便 syscall 降级场景使用。
|
||||
* </p>
|
||||
*/
|
||||
public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruction> {
|
||||
|
||||
/**
|
||||
* Formats a constant value for use as a VM instruction payload.
|
||||
* For lists, recursively formats each element with type suffixes where appropriate.
|
||||
*
|
||||
* @param v The constant value.
|
||||
* @return The formatted string payload for VM code.
|
||||
* 指定本生成器支持的 IR 指令类型(LoadConstInstruction)
|
||||
*/
|
||||
private static String formatConst(Object v) {
|
||||
return formatConst(v, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively formats constant values (including nested arrays), preserving
|
||||
* type suffixes and escaping strings. Used internally for array/list handling.
|
||||
*
|
||||
* @param v The constant value.
|
||||
* @param insideArray Whether this value is inside an array context (controls type suffixing).
|
||||
* @return The formatted string for VM code.
|
||||
*/
|
||||
private static String formatConst(Object v, boolean insideArray) {
|
||||
if (v instanceof List<?> list) {
|
||||
// Recursively process each element in the list
|
||||
return "[" + list.stream()
|
||||
.map(x -> formatConst(x, true))
|
||||
.collect(Collectors.joining(", ")) + "]";
|
||||
}
|
||||
if (v instanceof String s) {
|
||||
// Escape and wrap the string in double quotes, to avoid line breaks or control chars breaking VM code
|
||||
return "\"" + escape(s) + "\"";
|
||||
}
|
||||
if (v instanceof Float f) {
|
||||
float fv = f;
|
||||
String s = (fv == (long) fv) ? String.format("%.1f", fv) : f.toString();
|
||||
return insideArray ? (s + "f") : s;
|
||||
}
|
||||
if (v instanceof Long l) {
|
||||
return insideArray ? (l + "L") : l.toString();
|
||||
}
|
||||
if (v instanceof Double d) {
|
||||
double dv = d;
|
||||
return (dv == (long) dv) ? String.format("%.1f", dv) : Double.toString(dv);
|
||||
}
|
||||
if (v instanceof Short s) {
|
||||
return insideArray ? (s + "s") : Short.toString(s);
|
||||
}
|
||||
if (v instanceof Byte b) {
|
||||
return insideArray ? (b + "b") : Byte.toString(b);
|
||||
}
|
||||
if (v instanceof Boolean b) {
|
||||
return b ? "1" : "0";
|
||||
}
|
||||
return String.valueOf(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a string for use in VM code: replaces control characters and all non-ASCII characters
|
||||
* with their corresponding escape sequences, so the .water file remains single-line and parseable.
|
||||
* Supported escapes: \n, \r, \t, \f, \b, \", \', \\, and Unicode escapes like "uXXXX" for non-ASCII.
|
||||
*
|
||||
* @param s The input string.
|
||||
* @return The escaped string.
|
||||
*/
|
||||
private static String escape(String s) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < s.length(); ++i) {
|
||||
char ch = s.charAt(i);
|
||||
switch (ch) {
|
||||
case '\n' -> sb.append("\\n");
|
||||
case '\r' -> sb.append("\\r");
|
||||
case '\t' -> sb.append("\\t");
|
||||
case '\f' -> sb.append("\\f");
|
||||
case '\b' -> sb.append("\\b");
|
||||
case '\"' -> sb.append("\\\"");
|
||||
case '\'' -> sb.append("\\'");
|
||||
case '\\' -> sb.append("\\\\");
|
||||
default -> {
|
||||
// Escape non-ASCII and control characters using uXXXX
|
||||
if (ch < 0x20 || ch > 0x7E) {
|
||||
sb.append(String.format("\\u%04X", (int) ch));
|
||||
} else {
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<LoadConstInstruction> supportedClass() {
|
||||
return LoadConstInstruction.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates VM code for a LoadConstInstruction.
|
||||
* Produces PUSH and STORE instructions, sets the slot type,
|
||||
* and registers string constants if necessary.
|
||||
* 生成 VM 指令主流程
|
||||
*
|
||||
* @param ins The IR instruction to generate.
|
||||
* @param out The output program builder.
|
||||
* @param slotMap The mapping from IR virtual register to physical slot.
|
||||
* @param currentFn The current function name.
|
||||
* @param ins 当前常量加载指令
|
||||
* @param out 指令输出构建器
|
||||
* @param slotMap 虚拟寄存器与物理槽位映射
|
||||
* @param currentFn 当前函数名
|
||||
*/
|
||||
@Override
|
||||
public void generate(LoadConstInstruction ins,
|
||||
@ -144,35 +42,34 @@ public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruc
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
String currentFn) {
|
||||
|
||||
// 1. Retrieve the constant value from the instruction
|
||||
/* 1. 获取常量值 */
|
||||
IRConstant constant = (IRConstant) ins.operands().getFirst();
|
||||
Object value = constant.value();
|
||||
|
||||
// 2. Format and emit the PUSH instruction (arrays will use type-aware formatting)
|
||||
String payload = formatConst(value);
|
||||
out.emit(OpHelper.pushOpcodeFor(value) + " " + payload);
|
||||
/* 2. 生成 PUSH 指令,将常量值入栈 */
|
||||
out.emit(OpHelper.pushOpcodeFor(value) + " " + value);
|
||||
|
||||
// 3. Emit STORE to the destination slot
|
||||
/* 3. STORE 到目标槽位 */
|
||||
int slot = slotMap.get(ins.dest());
|
||||
out.emit(OpHelper.storeOpcodeFor(value) + " " + slot);
|
||||
|
||||
// 4. Mark the slot's data type for later use (type inference, instruction selection, etc.)
|
||||
/* 4. 标记槽位数据类型(用于后续类型推断和 LOAD/STORE 指令选择) */
|
||||
char prefix = switch (value) {
|
||||
case Integer _ -> 'I'; // Integer
|
||||
case Long _ -> 'L'; // Long
|
||||
case Short _ -> 'S'; // Short
|
||||
case Byte _ -> 'B'; // Byte
|
||||
case Double _ -> 'D'; // Double
|
||||
case Float _ -> 'F'; // Float
|
||||
case Boolean _ -> 'I'; // Booleans are treated as integers (1/0)
|
||||
case String _ -> 'R'; // Reference type for strings
|
||||
case java.util.List<?> _ -> 'R'; // Reference type for arrays/lists
|
||||
case null, default -> throw new IllegalStateException("Unknown constant type: "
|
||||
+ (value != null ? value.getClass() : null));
|
||||
case Integer _ -> 'I'; // 整型
|
||||
case Long _ -> 'L'; // 长整型
|
||||
case Short _ -> 'S'; // 短整型
|
||||
case Byte _ -> 'B'; // 字节型
|
||||
case Double _ -> 'D'; // 双精度
|
||||
case Float _ -> 'F'; // 单精度
|
||||
case Boolean _ -> 'I'; // 布尔类型用 I 处理
|
||||
case String _ -> 'R'; // 字符串常量
|
||||
case null, default -> // 其它类型异常
|
||||
throw new IllegalStateException("未知的常量类型: "
|
||||
+ (value != null ? value.getClass() : null));
|
||||
};
|
||||
out.setSlotType(slot, prefix);
|
||||
|
||||
// 5. Register the string constant for the string constant pool if needed
|
||||
/* 5. 如果是字符串常量,则登记到 CallGenerator 的常量池,便于 syscall 字符串降级使用 */
|
||||
if (value instanceof String s) {
|
||||
CallGenerator.registerStringConst(ins.dest().id(), s);
|
||||
}
|
||||
|
||||
@ -234,7 +234,6 @@ public final class OpHelper {
|
||||
if (v instanceof Double) return "D";
|
||||
if (v instanceof Float) return "F";
|
||||
if (v instanceof String) return "R"; //引用类型
|
||||
if (v instanceof java.util.List) return "R"; // 引用类型(数组等)
|
||||
throw new IllegalStateException("Unknown const type: " + v.getClass());
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package org.jcnc.snow.compiler.ir.builder;
|
||||
|
||||
import org.jcnc.snow.compiler.ir.common.GlobalConstTable;
|
||||
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
|
||||
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
|
||||
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
|
||||
@ -13,26 +11,40 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
import org.jcnc.snow.compiler.parser.ast.*;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code ExpressionBuilder} 表达式 → IR 构建器。
|
||||
* <b>ExpressionBuilder - 表达式 → IR 构建器</b>
|
||||
*
|
||||
* <p>
|
||||
* 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作,并生成对应的 IR 指令序列。
|
||||
* 支持字面量、标识符、二元表达式、一元表达式、函数调用、数组下标等多种类型表达式。
|
||||
* 支持字面量、标识符、二元表达式、一元表达式、函数调用等多种类型表达式。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 主要功能:
|
||||
* <ul>
|
||||
* <li>将表达式节点映射为虚拟寄存器</li>
|
||||
* <li>为每种表达式类型生成对应 IR 指令</li>
|
||||
* <li>支持表达式嵌套的递归构建</li>
|
||||
* <li>支持写入指定目标寄存器,避免冗余的 move 指令</li>
|
||||
* <li>支持 IndexExpressionNode 的编译期折叠(arr[2]),并在运行时降级为 __index_i/__index_r</li>
|
||||
* <li>将表达式节点映射为虚拟寄存器</li>
|
||||
* <li>为每种表达式类型生成对应 IR 指令</li>
|
||||
* <li>支持表达式嵌套的递归构建</li>
|
||||
* <li>支持写入指定目标寄存器,避免冗余的 move 指令</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public record ExpressionBuilder(IRContext ctx) {
|
||||
|
||||
/* ───────────────── 顶层入口 ───────────────── */
|
||||
|
||||
/**
|
||||
* 构建表达式,返回结果寄存器。
|
||||
* 构建任意 AST 表达式节点,自动为其分配一个新的虚拟寄存器,并返回该寄存器。
|
||||
*
|
||||
* <p>
|
||||
* 这是表达式 IR 生成的核心入口。它会根据不同的表达式类型进行分派,递归构建 IR 指令。
|
||||
* </p>
|
||||
*
|
||||
* @param expr 任意 AST 表达式节点
|
||||
* @return 存储该表达式结果的虚拟寄存器
|
||||
* @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符
|
||||
*/
|
||||
public IRVirtualRegister build(ExpressionNode expr) {
|
||||
return switch (expr) {
|
||||
@ -41,101 +53,58 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
// 字符串字面量,例如 "abc"
|
||||
case StringLiteralNode s -> buildStringLiteral(s.value());
|
||||
// 布尔字面量,例如 true / false
|
||||
case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
|
||||
case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
|
||||
// 标识符(变量名),如 a、b
|
||||
case IdentifierNode id -> {
|
||||
// 查找当前作用域中的变量寄存器
|
||||
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
|
||||
if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
|
||||
if (reg == null)
|
||||
throw new IllegalStateException("未定义标识符: " + id.name());
|
||||
yield reg;
|
||||
}
|
||||
// 模块常量 / 全局变量,如 ModuleA.a
|
||||
case MemberExpressionNode mem -> buildMember(mem);
|
||||
// 二元表达式(如 a+b, x==y)
|
||||
case BinaryExpressionNode bin -> buildBinary(bin);
|
||||
// 函数/方法调用表达式
|
||||
case CallExpressionNode call -> buildCall(call);
|
||||
case CallExpressionNode call -> buildCall(call);
|
||||
// 一元表达式(如 -a, !a)
|
||||
case UnaryExpressionNode un -> buildUnary(un);
|
||||
case IndexExpressionNode idx -> buildIndex(idx);
|
||||
case ArrayLiteralNode arr -> buildArrayLiteral(arr);
|
||||
case UnaryExpressionNode un -> buildUnary(un);
|
||||
|
||||
// 默认分支:遇到未知表达式类型则直接抛异常
|
||||
default -> throw new IllegalStateException(
|
||||
"不支持的表达式类型: " + expr.getClass().getSimpleName());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 成员访问表达式构建
|
||||
*
|
||||
* @param mem 成员表达式节点
|
||||
* @return 存储结果的虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildMember(MemberExpressionNode mem) {
|
||||
if (!(mem.object() instanceof IdentifierNode id)) {
|
||||
throw new IllegalStateException("不支持的成员访问对象类型: "
|
||||
+ mem.object().getClass().getSimpleName());
|
||||
}
|
||||
String qualified = id.name() + "." + mem.member();
|
||||
|
||||
/* 1) 尝试直接获取已有寄存器绑定 */
|
||||
IRVirtualRegister reg = ctx.getScope().lookup(qualified);
|
||||
if (reg != null) {
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* 2) 折叠为编译期常量:先查作用域,再查全局常量表 */
|
||||
Object v = ctx.getScope().getConstValue(qualified);
|
||||
if (v == null) {
|
||||
v = GlobalConstTable.get(qualified);
|
||||
}
|
||||
if (v != null) {
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(v)));
|
||||
return r;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("未定义的常量: " + qualified);
|
||||
}
|
||||
|
||||
|
||||
/* ───────────────── 写入指定寄存器 ───────────────── */
|
||||
|
||||
/**
|
||||
* 将表达式节点 {@link ExpressionNode} 的结果写入指定的虚拟寄存器 {@code dest}。
|
||||
* 生成表达式,并将其结果直接写入目标寄存器,避免冗余的 move 操作。
|
||||
*
|
||||
* <p>
|
||||
* 按表达式类型分派处理,包括:
|
||||
* <ul>
|
||||
* <li>字面量(数字、字符串、布尔、数组):生成 loadConst 指令直接写入目标寄存器</li>
|
||||
* <li>变量标识符:查表获取源寄存器,并 move 到目标寄存器</li>
|
||||
* <li>二元表达式、下标、调用表达式:递归生成子表达式结果,并写入目标寄存器</li>
|
||||
* <li>其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器</li>
|
||||
* </ul>
|
||||
* 某些简单表达式(如字面量、变量名)可以直接写入目标寄存器;复杂表达式则会先 build 到新寄存器,再 move 到目标寄存器。
|
||||
* </p>
|
||||
*
|
||||
* @param node 要求值的表达式节点
|
||||
* @param dest 结果目标虚拟寄存器
|
||||
* @throws IllegalStateException 若标识符未定义(如变量未声明时引用)
|
||||
* @param node 要生成的表达式节点
|
||||
* @param dest 目标虚拟寄存器(用于存储结果)
|
||||
*/
|
||||
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
|
||||
switch (node) {
|
||||
// 数字字面量:生成 loadConst 指令,将数值常量写入目标寄存器
|
||||
case NumberLiteralNode n -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
|
||||
// 数字字面量:生成 loadConst 指令写入目标寄存器
|
||||
case NumberLiteralNode n ->
|
||||
InstructionFactory.loadConstInto(
|
||||
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
|
||||
|
||||
// 字符串字面量:生成 loadConst 指令,将字符串常量写入目标寄存器
|
||||
case StringLiteralNode s -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(s.value()));
|
||||
// 字符串字面量:生成 loadConst 指令写入目标寄存器
|
||||
case StringLiteralNode s ->
|
||||
InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(s.value()));
|
||||
|
||||
// 布尔字面量:转换为 int 1/0,生成 loadConst 指令写入目标寄存器
|
||||
case BoolLiteralNode b -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
|
||||
// 布尔字面量:转换为 int 1/0,生成 loadConst
|
||||
case BoolLiteralNode b ->
|
||||
InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
|
||||
|
||||
// 数组字面量:生成数组常量并写入目标寄存器
|
||||
case ArrayLiteralNode arr -> InstructionFactory.loadConstInto(
|
||||
ctx, dest, buildArrayConstant(arr));
|
||||
|
||||
// 变量标识符:查表获得源寄存器,move 到目标寄存器
|
||||
// 标识符:查表获取原寄存器,然后 move 到目标寄存器
|
||||
case IdentifierNode id -> {
|
||||
IRVirtualRegister src = ctx.getScope().lookup(id.name());
|
||||
if (src == null)
|
||||
@ -143,22 +112,10 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
InstructionFactory.move(ctx, src, dest);
|
||||
}
|
||||
|
||||
// 二元表达式:递归生成左右子表达式,并将结果写入目标寄存器
|
||||
// 二元表达式:递归生成并写入目标寄存器
|
||||
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
|
||||
|
||||
// 下标表达式:递归生成索引结果,move 到目标寄存器
|
||||
case IndexExpressionNode idx -> {
|
||||
IRVirtualRegister tmp = buildIndex(idx);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
}
|
||||
|
||||
// 调用表达式:递归生成调用结果,move 到目标寄存器
|
||||
case CallExpressionNode call -> {
|
||||
IRVirtualRegister tmp = buildCall(call);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
}
|
||||
|
||||
// 其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器
|
||||
// 其它复杂情况:先 build 到新寄存器,再 move 到目标寄存器
|
||||
default -> {
|
||||
IRVirtualRegister tmp = build(node);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
@ -166,201 +123,7 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建下标访问表达式(IndexExpressionNode)。
|
||||
* <ul>
|
||||
* <li>若数组和下标均为编译期常量,则直接进行常量折叠(直接返回目标常量寄存器);</li>
|
||||
* <li>否则:
|
||||
* <ul>
|
||||
* <li>若数组表达式本身是下一个下标的中间值(即多维数组链式下标),则先用 __index_r 获取“引用”;</li>
|
||||
* <li>最后一级用 __index_b/s/i/l/f/d/r,按声明类型智能分派。</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param node 下标访问表达式节点
|
||||
* @return 存放结果的虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildIndex(IndexExpressionNode node) {
|
||||
// 1. 常量折叠:如果 array 和 index 都是编译期常量,直接取值
|
||||
Object arrConst = tryFoldConst(node.array());
|
||||
Object idxConst = tryFoldConst(node.index());
|
||||
if (arrConst instanceof java.util.List<?> list && idxConst instanceof Number num) {
|
||||
int i = num.intValue();
|
||||
// 越界检查
|
||||
if (i < 0 || i >= list.size())
|
||||
throw new IllegalStateException("数组下标越界: " + i + " (长度 " + list.size() + ")");
|
||||
Object elem = list.get(i);
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 加载常量元素到新寄存器
|
||||
InstructionFactory.loadConstInto(ctx, r, new IRConstant(elem));
|
||||
return r;
|
||||
}
|
||||
|
||||
// 2. 处理多级下标(如 arr[1][2]):中间层用 __index_r 返回“引用”
|
||||
IRVirtualRegister arrReg = (node.array() instanceof IndexExpressionNode inner)
|
||||
? buildIndexRef(inner) // 递归获取引用
|
||||
: build(node.array()); // 否则直接生成 array 的值
|
||||
|
||||
// 3. 生成下标值
|
||||
IRVirtualRegister idxReg = build(node.index());
|
||||
|
||||
// 4. 创建目标寄存器
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
|
||||
// 5. 准备参数
|
||||
List<IRValue> argv = new ArrayList<>();
|
||||
argv.add(arrReg);
|
||||
argv.add(idxReg);
|
||||
|
||||
// 6. 选择调用指令
|
||||
if (node.array() instanceof IndexExpressionNode) {
|
||||
// 非最末层,下标取“引用”
|
||||
ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
|
||||
} else {
|
||||
// 最末层,下标取实际元素值,按声明类型分派
|
||||
String func = "__index_i"; // 默认整型
|
||||
if (node.array() instanceof IdentifierNode id) {
|
||||
String declType = ctx.getScope().lookupType(id.name()); // 如 "double[]"、"int[]"
|
||||
if (declType != null) {
|
||||
String base = declType.toLowerCase();
|
||||
int p = base.indexOf('[');
|
||||
if (p > 0) base = base.substring(0, p); // 基本类型
|
||||
switch (base) {
|
||||
case "byte" -> func = "__index_b";
|
||||
case "short" -> func = "__index_s";
|
||||
case "int" -> func = "__index_i";
|
||||
case "long" -> func = "__index_l";
|
||||
case "float" -> func = "__index_f";
|
||||
case "double" -> func = "__index_d";
|
||||
case "boolean" -> func = "__index_i"; // 布尔型用 int 通道返回 1/0
|
||||
case "string" -> func = "__index_r"; // 字符串/其它未识别类型均走引用
|
||||
default -> func = "__index_r";
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.addInstruction(new CallInstruction(dest, func, argv));
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建中间层下标访问表达式(返回引用)。
|
||||
* <p>
|
||||
* 用于多维数组的链式下标访问(如 arr[1][2]),保证中间结果是“可被再次下标”的引用。
|
||||
* <ul>
|
||||
* <li>若数组和下标均为编译期常量,则直接常量折叠,返回目标常量寄存器;</li>
|
||||
* <li>否则,递归处理 array,生成“引用”指令(__index_r)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param node 下标访问表达式节点
|
||||
* @return 存放引用结果的虚拟寄存器
|
||||
*/
|
||||
public IRVirtualRegister buildIndexRef(IndexExpressionNode node) {
|
||||
// 1. 常量折叠:如果 array 和 index 都是编译期常量,直接取值
|
||||
Object arrConst = tryFoldConst(node.array());
|
||||
Object idxConst = tryFoldConst(node.index());
|
||||
if (arrConst instanceof java.util.List<?> list && idxConst instanceof Number num) {
|
||||
int i = num.intValue();
|
||||
// 越界检查
|
||||
if (i < 0 || i >= list.size())
|
||||
throw new IllegalStateException("数组下标越界: " + i + " (长度 " + list.size() + ")");
|
||||
Object elem = list.get(i);
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 加载常量元素到新寄存器
|
||||
InstructionFactory.loadConstInto(ctx, r, new IRConstant(elem));
|
||||
return r;
|
||||
}
|
||||
|
||||
// 2. 递归生成 array 的“引用”,用于支持链式多级下标
|
||||
IRVirtualRegister arrReg = (node.array() instanceof IndexExpressionNode inner)
|
||||
? buildIndexRef(inner) // 递归向下返回引用
|
||||
: build(node.array()); // 基础数组直接 build
|
||||
|
||||
// 3. 生成下标值
|
||||
IRVirtualRegister idxReg = build(node.index());
|
||||
|
||||
// 4. 创建目标寄存器
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
|
||||
// 5. 组织参数列表
|
||||
List<IRValue> argv = new ArrayList<>();
|
||||
argv.add(arrReg);
|
||||
argv.add(idxReg);
|
||||
|
||||
// 6. 生成 __index_r 调用指令(总是返回引用)
|
||||
ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 常量折叠工具(支持嵌套数组)。
|
||||
* <p>
|
||||
* 尝试将表达式节点 {@code expr} 折叠为常量值,用于编译期计算/优化。
|
||||
* <ul>
|
||||
* <li>数字字面量:返回 int 或 double。</li>
|
||||
* <li>字符串字面量:返回字符串。</li>
|
||||
* <li>布尔字面量:返回 1(true)或 0(false)。</li>
|
||||
* <li>数组字面量:递归折叠所有元素为 List,如果有一项不能折叠则返回 null。</li>
|
||||
* <li>标识符:尝试从作用域查找编译期常量值。</li>
|
||||
* <li>其它类型:无法折叠,返回 null。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param expr 需要折叠的表达式节点
|
||||
* @return 编译期常量值(支持 int、double、String、List),否则返回 null
|
||||
*/
|
||||
private Object tryFoldConst(ExpressionNode expr) {
|
||||
if (expr == null) return null;
|
||||
|
||||
// 数字字面量:尝试解析为 int 或 double
|
||||
if (expr instanceof NumberLiteralNode n) {
|
||||
String s = n.value();
|
||||
try {
|
||||
if (s.contains(".") || s.contains("e") || s.contains("E"))
|
||||
return Double.parseDouble(s); // 带小数或科学计数法为 double
|
||||
return Integer.parseInt(s); // 否则为 int
|
||||
} catch (NumberFormatException e) {
|
||||
return null; // 无法解析为数字
|
||||
}
|
||||
}
|
||||
|
||||
// 字符串字面量:直接返回字符串
|
||||
if (expr instanceof StringLiteralNode s) return s.value();
|
||||
|
||||
// 布尔字面量:true 返回 1,false 返回 0
|
||||
if (expr instanceof BoolLiteralNode b) return b.getValue() ? 1 : 0;
|
||||
|
||||
// 数组字面量:递归折叠所有元素
|
||||
if (expr instanceof ArrayLiteralNode arr) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (ExpressionNode e : arr.elements()) {
|
||||
Object v = tryFoldConst(e);
|
||||
if (v == null) return null; // 有一项无法折叠则整体失败
|
||||
list.add(v);
|
||||
}
|
||||
return List.copyOf(list);
|
||||
}
|
||||
|
||||
// 标识符:尝试查找作用域中的常量值
|
||||
if (expr instanceof IdentifierNode id) {
|
||||
Object v = null;
|
||||
try {
|
||||
v = ctx.getScope().getConstValue(id.name());
|
||||
} catch (Throwable ignored) {
|
||||
// 查不到常量或异常都视为无法折叠
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// 其它类型:不支持折叠,返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
/* ───────────────── 具体表达式类型 ───────────────── */
|
||||
|
||||
/**
|
||||
* 一元表达式构建
|
||||
@ -374,70 +137,62 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
*/
|
||||
private IRVirtualRegister buildUnary(UnaryExpressionNode un) {
|
||||
// 递归生成操作数的寄存器
|
||||
IRVirtualRegister src = build(un.operand());
|
||||
IRVirtualRegister src = build(un.operand());
|
||||
// 分配目标寄存器
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
|
||||
switch (un.operator()) {
|
||||
// 算术负号:生成取负指令(例如:-a)
|
||||
// 算术负号:生成取负指令
|
||||
case "-" -> ctx.addInstruction(
|
||||
new UnaryOperationInstruction(ExpressionUtils.negOp(un.operand()), dest, src));
|
||||
|
||||
// 逻辑非:等价于 a == 0,生成整数等于比较指令(!a)
|
||||
// 逻辑非:等价于 a == 0,生成比较指令
|
||||
case "!" -> {
|
||||
// 生成常量0的寄存器
|
||||
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
|
||||
// 比较 src 是否等于0,等价于逻辑非
|
||||
return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, src, zero);
|
||||
}
|
||||
// 其它一元运算符不支持,抛出异常
|
||||
// 其它一元运算符不支持,抛异常
|
||||
default -> throw new IllegalStateException("未知一元运算符: " + un.operator());
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建函数或方法调用表达式。
|
||||
* <p>
|
||||
* 支持普通函数调用(foo(a, b))与成员方法调用(obj.method(a, b))。
|
||||
* <ul>
|
||||
* <li>首先递归生成所有参数的虚拟寄存器列表。</li>
|
||||
* <li>根据 callee 类型区分成员访问或直接标识符调用,并规范化方法名(如加前缀)。</li>
|
||||
* <li>为返回值分配新寄存器,生成 Call 指令。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* 构建函数或方法调用表达式。模块内未限定调用会自动补全当前模块名。
|
||||
*
|
||||
* @param call 函数/方法调用表达式节点
|
||||
* @return 存放调用结果的虚拟寄存器
|
||||
* @param call AST 调用表达式节点
|
||||
* @return 存储调用结果的虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildCall(CallExpressionNode call) {
|
||||
// 1. 递归生成所有参数的寄存器
|
||||
// 递归生成所有参数(实参)对应的寄存器
|
||||
List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList();
|
||||
|
||||
// 2. 规范化被调用方法名(区分成员方法与普通函数)
|
||||
// 解析被调用目标名,支持普通函数/成员方法
|
||||
String callee = switch (call.callee()) {
|
||||
// 成员方法调用,如 obj.method()
|
||||
case MemberExpressionNode m when m.object() instanceof IdentifierNode id -> id.name() + "." + m.member();
|
||||
// 普通函数调用,或处理命名空间前缀(如当前方法名为 namespace.func)
|
||||
// 成员方法调用,例如 obj.foo()
|
||||
case MemberExpressionNode m when m.object() instanceof IdentifierNode id
|
||||
-> id.name() + "." + m.member();
|
||||
// 普通函数调用,如果未指定模块,自动补全当前模块名
|
||||
case IdentifierNode id -> {
|
||||
String current = ctx.getFunction().name();
|
||||
int dot = current.lastIndexOf('.');
|
||||
if (dot > 0)
|
||||
yield current.substring(0, dot) + "." + id.name(); // 同命名空间内调用
|
||||
yield id.name(); // 全局函数调用
|
||||
if (dot > 0) {
|
||||
// 当前处于模块内函数(Module.func),补全为同模块下的全限定名
|
||||
yield current.substring(0, dot) + "." + id.name();
|
||||
} else {
|
||||
// 顶层/脚本函数等不含模块前缀,保持原样
|
||||
yield id.name();
|
||||
}
|
||||
}
|
||||
// 其它类型不支持
|
||||
default -> throw new IllegalStateException(
|
||||
"不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
// 其它情况暂不支持
|
||||
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
};
|
||||
|
||||
// 3. 分配用于存放返回值的新寄存器,并生成 Call 指令
|
||||
// 为返回值分配新寄存器,生成 Call 指令
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 二元表达式构建,结果存储到新寄存器。
|
||||
* <br>
|
||||
@ -452,54 +207,44 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
IRVirtualRegister b = build(bin.right());
|
||||
String op = bin.operator();
|
||||
|
||||
// 判断是否为比较运算符(==、!=、>、<等)
|
||||
// 比较运算符(==、!=、>、< 等),需要生成条件跳转或布尔值寄存器
|
||||
if (ComparisonUtils.isComparisonOperator(op)) {
|
||||
// 生成比较操作,返回布尔值寄存器
|
||||
return InstructionFactory.binOp(
|
||||
ctx,
|
||||
// 根据运算符和操作数类型获得合适的 IR 操作码
|
||||
// 通过比较工具获得合适的 IR 操作码
|
||||
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
|
||||
a, b);
|
||||
}
|
||||
|
||||
// 其它二元运算(算术、位运算等)
|
||||
// 其它算术/位运算
|
||||
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||
if (code == null)
|
||||
throw new IllegalStateException("不支持的运算符: " + op);
|
||||
// 生成二元操作指令
|
||||
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||
return InstructionFactory.binOp(ctx, code, a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 二元表达式构建,结果直接写入目标寄存器(用于赋值左值等优化场景)。
|
||||
*
|
||||
* @param bin 二元表达式节点
|
||||
* @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 (ComparisonUtils.isComparisonOperator(op)) {
|
||||
InstructionFactory.binOpInto(
|
||||
ctx,
|
||||
// 选择对应类型和符号的比较操作码
|
||||
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
|
||||
a, b, dest);
|
||||
} else {
|
||||
// 算术或位运算符
|
||||
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||
if (code == null)
|
||||
throw new IllegalStateException("不支持的运算符: " + op);
|
||||
// 生成二元操作指令,写入目标寄存器
|
||||
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||
InstructionFactory.binOpInto(ctx, code, a, b, dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ───────────────── 字面量辅助方法 ───────────────── */
|
||||
|
||||
/**
|
||||
@ -509,11 +254,8 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储该字面量的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildNumberLiteral(String value) {
|
||||
// 解析数字常量
|
||||
IRConstant c = ExpressionUtils.buildNumberConstant(ctx, value);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
@ -525,11 +267,8 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储该字符串的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildStringLiteral(String value) {
|
||||
// 构建字符串常量
|
||||
IRConstant c = new IRConstant(value);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
@ -541,74 +280,9 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储 1/0 的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildBoolLiteral(boolean v) {
|
||||
// 转换为 1 或 0 的常量
|
||||
IRConstant c = new IRConstant(v ? 1 : 0);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建数组字面量表达式(元素均为常量时)。
|
||||
*
|
||||
* @param arr 数组字面量节点
|
||||
* @return 存储该数组的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildArrayLiteral(ArrayLiteralNode arr) {
|
||||
// 递归生成支持嵌套的数组常量
|
||||
IRConstant c = buildArrayConstant(arr);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建支持嵌套的数组常量表达式。
|
||||
* <p>
|
||||
* 遍历并递归处理数组字面量的所有元素:
|
||||
* <ul>
|
||||
* <li>数字字面量:根据内容生成 int 或 double 常量</li>
|
||||
* <li>字符串字面量:直接存储字符串内容</li>
|
||||
* <li>布尔字面量:转换为 1(true)或 0(false)存储</li>
|
||||
* <li>数组字面量:递归构建,允许多层嵌套,最终生成嵌套的 List</li>
|
||||
* </ul>
|
||||
* 若包含非常量元素,则抛出异常。
|
||||
* </p>
|
||||
*
|
||||
* @param arr 数组字面量节点
|
||||
* @return 封装所有常量元素(支持嵌套 List)的 {@link IRConstant}
|
||||
* @throws IllegalStateException 如果数组中存在非常量元素
|
||||
*/
|
||||
|
||||
private IRConstant buildArrayConstant(ArrayLiteralNode arr) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (ExpressionNode e : arr.elements()) {
|
||||
switch (e) {
|
||||
// 数字字面量,解析并加入
|
||||
case NumberLiteralNode n -> {
|
||||
IRConstant num = ExpressionUtils.buildNumberConstant(ctx, n.value());
|
||||
list.add(num.value());
|
||||
}
|
||||
// 字符串字面量,直接加入
|
||||
case StringLiteralNode s -> list.add(s.value());
|
||||
// 布尔字面量,转成 1/0
|
||||
case BoolLiteralNode b -> list.add(b.getValue() ? 1 : 0);
|
||||
// 嵌套数组,递归生成并加入
|
||||
case ArrayLiteralNode inner -> {
|
||||
IRConstant innerConst = buildArrayConstant(inner);
|
||||
list.add(innerConst.value());
|
||||
}
|
||||
// 其它类型暂不支持
|
||||
default -> throw new IllegalStateException(
|
||||
"暂不支持含非常量元素的数组字面量: " + e.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
// 返回不可变的 List 封装为 IRConstant
|
||||
return new IRConstant(List.copyOf(list));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package org.jcnc.snow.compiler.ir.builder;
|
||||
|
||||
import org.jcnc.snow.compiler.ir.common.GlobalConstTable;
|
||||
import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable;
|
||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
|
||||
@ -14,13 +13,6 @@ import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
* <p>
|
||||
* 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction,
|
||||
* 包含参数声明、返回类型推断、函数体语句转换等步骤。
|
||||
*
|
||||
* <ul>
|
||||
* <li>支持自动导入全局/跨模块常量,使跨模块常量引用(如 ModuleA.a)在 IR 阶段可用。</li>
|
||||
* <li>将函数形参声明为虚拟寄存器,并注册到作用域,便于后续指令生成。</li>
|
||||
* <li>根据返回类型设置表达式默认字面量类型,保证 IR 层类型一致性。</li>
|
||||
* <li>遍历并转换函数体语句为 IR 指令。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class FunctionBuilder {
|
||||
|
||||
@ -29,15 +21,11 @@ public class FunctionBuilder {
|
||||
* <p>
|
||||
* 构建过程包括:
|
||||
* <ol>
|
||||
* <li>在全局函数表注册函数签名,便于后续模块/语义分析阶段查询。</li>
|
||||
* <li>初始化 IRFunction 实例和 IRContext 上下文对象(包含作用域与寄存器信息)。</li>
|
||||
* <li>自动导入全局常量(包括跨模块 const 变量)到当前作用域,
|
||||
* 使成员访问如 ModuleA.a 可直接折叠为常量。</li>
|
||||
* <li>根据函数返回类型,设置表达式推断的默认字面量类型后缀
|
||||
* (如 double→d, float→f),避免类型不一致。</li>
|
||||
* <li>遍历声明形参,每个参数分配虚拟寄存器,并注册到作用域。</li>
|
||||
* <li>依次转换函数体中的每条语句为 IR 指令。</li>
|
||||
* <li>函数体转换完成后,清理默认类型后缀,防止影响后续函数构建。</li>
|
||||
* <li>初始化 IRFunction 实例和上下文</li>
|
||||
* <li>根据函数返回类型,设置默认类型后缀,便于表达式推断</li>
|
||||
* <li>声明参数到作用域,并为每个参数分配虚拟寄存器</li>
|
||||
* <li>遍历并转换函数体内的每条语句为 IR 指令</li>
|
||||
* <li>函数构建完成后,清理默认类型后缀,防止影响其他函数</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param functionNode 表示函数定义的语法树节点
|
||||
@ -45,56 +33,44 @@ public class FunctionBuilder {
|
||||
*/
|
||||
public IRFunction build(FunctionNode functionNode) {
|
||||
|
||||
// 1. 在全局函数表注册函数名与返回类型
|
||||
// 方便其他阶段/模块调用、类型检查。
|
||||
// 在全局函数表中注册函数信息
|
||||
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
|
||||
|
||||
// 2. 初始化 IRFunction 实例与上下文对象
|
||||
// IRFunction: 表示该函数的中间代码容器
|
||||
// IRContext: 负责作用域、寄存器分配等编译上下文管理
|
||||
|
||||
// 0) 基本初始化: 创建 IRFunction 实例与对应上下文
|
||||
IRFunction irFunction = new IRFunction(functionNode.name());
|
||||
IRContext irContext = new IRContext(irFunction);
|
||||
|
||||
// 3. 自动导入所有全局/跨模块常量到当前作用域
|
||||
// 支持如 ModuleA.a 这样的常量访问/折叠(参见 ExpressionBuilder)
|
||||
GlobalConstTable.all().forEach((k, v) ->
|
||||
irContext.getScope().importExternalConst(k, v));
|
||||
|
||||
// 4. 根据函数返回类型设置默认类型后缀
|
||||
// 例如返回类型为 double 时, 字面量表达式自动用 d 后缀。
|
||||
// 1) 把函数返回类型注入为默认类型后缀(供表达式类型推断)
|
||||
char _returnSuffix = switch (functionNode.returnType().toLowerCase()) {
|
||||
case "double" -> 'd';
|
||||
case "float" -> 'f';
|
||||
case "long" -> 'l';
|
||||
case "short" -> 's';
|
||||
case "byte" -> 'b';
|
||||
default -> '\0'; // 其它类型不设默认后缀
|
||||
default -> '\0';
|
||||
};
|
||||
ExpressionUtils.setDefaultSuffix(_returnSuffix);
|
||||
|
||||
try {
|
||||
// 5. 遍历函数参数列表
|
||||
// - 为每个参数分配一个新的虚拟寄存器
|
||||
// - 注册参数名、类型、寄存器到当前作用域
|
||||
// - 添加参数寄存器到 IRFunction(用于后续调用与指令生成)
|
||||
// 2) 声明形参: 为每个参数分配虚拟寄存器并声明到作用域
|
||||
for (ParameterNode p : functionNode.parameters()) {
|
||||
IRVirtualRegister reg = irFunction.newRegister();
|
||||
irContext.getScope().declare(p.name(), p.type(), reg);
|
||||
irFunction.addParameter(reg);
|
||||
IRVirtualRegister reg = irFunction.newRegister(); // 新寄存器
|
||||
irContext.getScope().declare(p.name(), p.type(), reg); // 变量名→寄存器绑定
|
||||
irFunction.addParameter(reg); // 添加到函数参数列表
|
||||
}
|
||||
|
||||
// 6. 遍历函数体语句节点,转换为 IR 指令
|
||||
// StatementBuilder 负责将每条语句递归转换为 IR
|
||||
// 3) 生成函数体 IR: 遍历每条语句,逐一转化
|
||||
StatementBuilder stmtBuilder = new StatementBuilder(irContext);
|
||||
for (StatementNode stmt : functionNode.body()) {
|
||||
stmtBuilder.build(stmt);
|
||||
}
|
||||
} finally {
|
||||
// 7. 清理默认类型后缀,防止影响后续其他函数的类型推断
|
||||
// 4) 清除默认后缀,避免影响后续函数的推断
|
||||
ExpressionUtils.clearDefaultSuffix();
|
||||
}
|
||||
|
||||
// 8. 返回构建完成的 IRFunction
|
||||
// 返回构建好的 IRFunction
|
||||
return irFunction;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,155 +7,109 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@code IRBuilderScope} 用于管理单个函数作用域内的变量名与虚拟寄存器的映射关系。
|
||||
* <p>
|
||||
* 主要职责:
|
||||
* IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系。
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>维护当前作用域内所有已声明变量的寄存器和类型信息</li>
|
||||
* <li>支持变量与虚拟寄存器的重新绑定与查找</li>
|
||||
* <li>支持变量的类型信息记录与查询</li>
|
||||
* <li>支持变量的编译期常量值记录与查询(便于常量折叠等优化)</li>
|
||||
* <li>支持跨模块全局常量(如 ModuleA.a)查找</li>
|
||||
* <li>维护在当前作用域中已声明变量的寄存器分配信息;</li>
|
||||
* <li>支持将已有虚拟寄存器与变量名重新绑定;</li>
|
||||
* <li>根据变量名查找对应的虚拟寄存器实例或类型。</li>
|
||||
* </ul>
|
||||
*/
|
||||
final class IRBuilderScope {
|
||||
|
||||
/** 变量名到虚拟寄存器的映射表(本地变量) */
|
||||
/**
|
||||
* 存储变量名到对应 IRVirtualRegister 的映射。
|
||||
* 变量名为键,虚拟寄存器对象为值,用于查找和更新。
|
||||
*/
|
||||
private final Map<String, IRVirtualRegister> vars = new HashMap<>();
|
||||
/** 变量名到类型字符串的映射表 */
|
||||
private final Map<String, String> varTypes = new HashMap<>();
|
||||
/** 变量名到编译期常量值的映射表(本作用域) */
|
||||
private final Map<String, Object> varConstValues = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 额外:存放跨模块导入的全局常量
|
||||
* key 形如 "ModuleA.a" value 为其常量值
|
||||
* 存储变量名到对应类型的映射。
|
||||
* <br>
|
||||
* 变量名为键,变量类型为值,用于变量类型提升。
|
||||
*/
|
||||
private final Map<String, Object> externalConsts = new HashMap<>();
|
||||
private final Map<String, String> varTypes = new HashMap<>();
|
||||
|
||||
/** 当前作用域所绑定的 IRFunction 实例 */
|
||||
/**
|
||||
* 当前作用域所绑定的 IRFunction 对象,用于申请新的虚拟寄存器。
|
||||
*/
|
||||
private IRFunction fn;
|
||||
|
||||
/**
|
||||
* 绑定当前作用域到指定 IRFunction。
|
||||
* 将指定的 IRFunction 关联到当前作用域,以便后续声明变量时能够
|
||||
* 调用该函数的 newRegister() 方法生成新的寄存器。
|
||||
*
|
||||
* @param fn 目标 IRFunction
|
||||
* @param fn 要绑定到本作用域的 IRFunction 实例
|
||||
*/
|
||||
void attachFunction(IRFunction fn) {
|
||||
this.fn = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 声明一个新变量并分配新的虚拟寄存器。
|
||||
* 在当前作用域中声明一个新变量,并为其分配一个新的虚拟寄存器。
|
||||
* 调用绑定的 IRFunction.newRegister() 生成寄存器后保存到映射表中。
|
||||
*
|
||||
* @param name 变量名称
|
||||
* @param type 变量类型名
|
||||
* @param name 变量名称,作为映射键使用
|
||||
* @param type 变量类型
|
||||
*/
|
||||
void declare(String name, String type) {
|
||||
IRVirtualRegister reg = fn.newRegister();
|
||||
vars.put(name, reg);
|
||||
varTypes.put(name, type);
|
||||
varConstValues.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 声明新变量,并绑定到指定的寄存器。
|
||||
* 在当前作用域中声明或导入一个已有的虚拟寄存器,并将其与指定变量名绑定。
|
||||
* 该方法可用于将外部或前一作用域的寄存器导入到本作用域。
|
||||
*
|
||||
* @param name 变量名称
|
||||
* @param type 变量类型名
|
||||
* @param reg 绑定的虚拟寄存器
|
||||
* @param name 变量名称,作为映射键使用
|
||||
* @param type 变量类型
|
||||
* @param reg 要绑定到该名称的 IRVirtualRegister 实例
|
||||
*/
|
||||
void declare(String name, String type, IRVirtualRegister reg) {
|
||||
vars.put(name, reg);
|
||||
varTypes.put(name, type);
|
||||
varConstValues.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新变量的虚拟寄存器绑定(如变量已存在则覆盖,否则等同于新声明)。
|
||||
* 更新已存在变量的虚拟寄存器绑定关系。若变量已声明,则替换其对应的寄存器;
|
||||
* 若尚未声明,则等同于声明新变量。
|
||||
*
|
||||
* @param name 变量名称
|
||||
* @param reg 新的虚拟寄存器
|
||||
* @param name 变量名称,作为映射键使用
|
||||
* @param reg 新的 IRVirtualRegister 实例,用于替换旧绑定
|
||||
*/
|
||||
void put(String name, IRVirtualRegister reg) {
|
||||
vars.put(name, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找变量名对应的虚拟寄存器。
|
||||
* 根据变量名称在当前作用域中查找对应的虚拟寄存器。
|
||||
*
|
||||
* @param name 变量名
|
||||
* @return 已绑定的虚拟寄存器,若未声明则返回 null
|
||||
* @param name 需要查询的变量名称
|
||||
* @return 如果该名称已绑定寄存器,则返回对应的 IRVirtualRegister;
|
||||
* 如果未声明,则返回 null
|
||||
*/
|
||||
IRVirtualRegister lookup(String name) {
|
||||
return vars.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找变量名对应的类型名。
|
||||
* 根据变量名称在当前作用域中查找对应的类型。
|
||||
*
|
||||
* @param name 变量名
|
||||
* @return 已声明类型字符串,若未声明则返回 null
|
||||
* @param name 需要查询的变量名称
|
||||
* @return 如果该名称已声明,则返回对应的类型
|
||||
* 如果未声明,则返回 null
|
||||
*/
|
||||
String lookupType(String name) {
|
||||
return varTypes.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取变量名到类型名映射的不可变副本。
|
||||
*
|
||||
* @return 变量名→类型名映射的只读视图
|
||||
* 获取 变量->类型的映射 的不可变副本
|
||||
* @return 变量->类型的映射 的不可变副本
|
||||
*/
|
||||
Map<String, String> getVarTypes() {
|
||||
return Map.copyOf(varTypes);
|
||||
}
|
||||
|
||||
// ---------------- 编译期常量相关接口 ----------------
|
||||
|
||||
/**
|
||||
* 设置变量的编译期常量值(本地变量)。
|
||||
*
|
||||
* @param name 变量名称
|
||||
* @param value 常量值(null 表示清除)
|
||||
*/
|
||||
void setConstValue(String name, Object value) {
|
||||
if (value == null) varConstValues.remove(name);
|
||||
else varConstValues.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取变量的编译期常量值(本地变量或导入的外部常量)。
|
||||
* <br>
|
||||
* 优先查找本地常量,未命中再查外部(如 "ModuleA.a")。
|
||||
*
|
||||
* @param name 变量名称或"模块名.常量名"
|
||||
* @return 编译期常量值,或 null
|
||||
*/
|
||||
Object getConstValue(String name) {
|
||||
Object v = varConstValues.get(name);
|
||||
if (v != null) return v;
|
||||
// 支持跨模块常量/全局变量
|
||||
return externalConsts.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除变量的编译期常量值绑定(本地)。
|
||||
*
|
||||
* @param name 变量名称
|
||||
*/
|
||||
void clearConstValue(String name) {
|
||||
varConstValues.remove(name);
|
||||
}
|
||||
|
||||
// ---------------- 跨模块常量导入支持 ----------------
|
||||
|
||||
/**
|
||||
* 导入外部(其他模块)的全局常量/变量。
|
||||
*
|
||||
* @param qualifiedName 形如 "ModuleA.a"
|
||||
* @param value 其常量值
|
||||
*/
|
||||
void importExternalConst(String qualifiedName, Object value) {
|
||||
externalConsts.put(qualifiedName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,26 +1,24 @@
|
||||
package org.jcnc.snow.compiler.ir.builder;
|
||||
|
||||
import org.jcnc.snow.compiler.ir.common.GlobalConstTable;
|
||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||
import org.jcnc.snow.compiler.ir.core.IRProgram;
|
||||
import org.jcnc.snow.compiler.parser.ast.*;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* IRProgramBuilder 负责将 AST 顶层节点转换为可执行的 {@link IRProgram}。
|
||||
*
|
||||
* IRProgramBuilder 负责将 AST 根节点(如模块、函数、顶层语句)转换为可执行的 IRProgram 实例。
|
||||
* <p>
|
||||
* 主要职责:
|
||||
* <ul>
|
||||
* <li>预扫描所有模块,将 <code>declare const</code> 常量登记到全局常量表,支持跨模块常量折叠。</li>
|
||||
* <li>对模块内的函数加上模块前缀,保证命名唯一,并将本模块全局声明注入到函数体前部。</li>
|
||||
* <li>将独立顶层语句自动包装为特殊的 "_start" 函数(脚本模式支持)。</li>
|
||||
* <li>遍历 AST 根节点,根据类型分别处理(模块、函数、顶层语句)。</li>
|
||||
* <li>对模块内的函数添加全限定名,并在函数体前注入全局变量声明。</li>
|
||||
* <li>将单独的顶层语句封装为特殊的 "_start" 函数。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class IRProgramBuilder {
|
||||
@ -33,23 +31,20 @@ public final class IRProgramBuilder {
|
||||
* @throws IllegalStateException 遇到不支持的顶层节点类型时抛出
|
||||
*/
|
||||
public IRProgram buildProgram(List<Node> roots) {
|
||||
// 预先收集并登记全部模块常量到全局常量表
|
||||
preloadGlobals(roots);
|
||||
|
||||
IRProgram irProgram = new IRProgram();
|
||||
for (Node node : roots) {
|
||||
switch (node) {
|
||||
case ModuleNode moduleNode -> {
|
||||
// 处理模块节点:遍历其中所有函数,统一用“模块名.函数名”作为全限定名,避免命名冲突
|
||||
// 处理模块节点:遍历其中所有函数,统一用“模块名.函数名”作为全限定名
|
||||
for (FunctionNode f : moduleNode.functions()) {
|
||||
irProgram.add(buildFunctionWithGlobals(moduleNode, f));
|
||||
}
|
||||
}
|
||||
case FunctionNode functionNode ->
|
||||
// 处理顶层函数节点:直接构建为 IRFunction 并加入
|
||||
// 处理顶层函数节点:直接构建为 IRFunction 并加入
|
||||
irProgram.add(buildFunction(functionNode));
|
||||
case StatementNode statementNode ->
|
||||
// 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
|
||||
// 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
|
||||
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
|
||||
default ->
|
||||
// 遇到未知类型节点,抛出异常
|
||||
@ -59,111 +54,25 @@ public final class IRProgramBuilder {
|
||||
return irProgram;
|
||||
}
|
||||
|
||||
// ===================== 全局常量收集 =====================
|
||||
|
||||
/**
|
||||
* 扫描所有模块节点,将其中声明的 const 全局变量(compile-time 常量)
|
||||
* 以 "模块名.常量名" 形式注册到全局常量表。
|
||||
* 支持跨模块常量折叠,用于后续 IR 生成过程中的常量折叠优化。
|
||||
*
|
||||
* @param roots AST 顶层节点列表
|
||||
*/
|
||||
private void preloadGlobals(List<Node> roots) {
|
||||
for (Node n : roots) {
|
||||
if (n instanceof ModuleNode mod) {
|
||||
String moduleName = mod.name();
|
||||
if (mod.globals() == null) continue;
|
||||
for (DeclarationNode decl : mod.globals()) {
|
||||
// 只处理 compile-time 的 const 常量,并要求有初始值
|
||||
if (!decl.isConst() || decl.getInitializer().isEmpty()) continue;
|
||||
ExpressionNode init = decl.getInitializer().get();
|
||||
Object value = evalLiteral(init);
|
||||
if (value != null) {
|
||||
GlobalConstTable.register(moduleName + "." + decl.getName(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字面量提取与类型折叠工具。
|
||||
* 用于将表达式节点还原为 Java 原生类型(int、long、double、String等),仅支持直接字面量。
|
||||
*
|
||||
* @param expr 要计算的表达式节点
|
||||
* @return 提取到的原生常量值,不支持的情况返回 null
|
||||
*/
|
||||
private Object evalLiteral(ExpressionNode expr) {
|
||||
return switch (expr) {
|
||||
case NumberLiteralNode num -> {
|
||||
String raw = num.value();
|
||||
String s = raw.replace("_", "");
|
||||
char last = Character.toLowerCase(s.charAt(s.length() - 1));
|
||||
String core = switch (last) {
|
||||
case 'b', 's', 'l', 'f', 'd' -> s.substring(0, s.length() - 1);
|
||||
default -> s;
|
||||
};
|
||||
try {
|
||||
if (core.contains(".") || core.contains("e") || core.contains("E")) {
|
||||
// 浮点数处理
|
||||
yield Double.parseDouble(core);
|
||||
}
|
||||
long lv = Long.parseLong(core);
|
||||
yield switch (last) {
|
||||
case 'b' -> (byte) lv;
|
||||
case 's' -> (short) lv;
|
||||
case 'l' -> lv;
|
||||
default -> (int) lv;
|
||||
};
|
||||
} catch (NumberFormatException ignore) {
|
||||
yield null;
|
||||
}
|
||||
}
|
||||
case StringLiteralNode str -> str.value();
|
||||
case BoolLiteralNode b -> b.getValue() ? 1 : 0;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
// ===================== IRFunction 构建辅助 =====================
|
||||
|
||||
/**
|
||||
* 构建带有模块全局声明“注入”的函数,并将函数名加上模块前缀,保证模块内函数名唯一。
|
||||
* 如果模块有全局声明,则这些声明会被插入到函数体前部(**会过滤掉与参数同名的全局声明**)。
|
||||
* <p>
|
||||
* 如果模块有全局声明,则这些声明会被插入到函数体前部。
|
||||
*
|
||||
* @param moduleNode 所属模块节点
|
||||
* @param functionNode 待构建的函数节点
|
||||
* @return 包含全局声明的 IRFunction
|
||||
* @param moduleNode 当前模块节点
|
||||
* @param functionNode 模块中的函数节点
|
||||
* @return 包含全局声明、已加前缀函数名的 IRFunction
|
||||
*/
|
||||
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
|
||||
// 拼接模块名和函数名,生成全限定名
|
||||
String qualifiedName = moduleNode.name() + "." + functionNode.name();
|
||||
// 若无全局声明,仅重命名后直接构建
|
||||
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
|
||||
// 无全局声明,直接重命名构建
|
||||
return buildFunction(renameFunction(functionNode, qualifiedName));
|
||||
}
|
||||
|
||||
// ------- 过滤与参数重名的全局声明 -------
|
||||
Set<String> paramNames = new HashSet<>();
|
||||
for (ParameterNode p : functionNode.parameters()) {
|
||||
paramNames.add(p.name());
|
||||
}
|
||||
List<StatementNode> filteredGlobals = new ArrayList<>();
|
||||
for (DeclarationNode g : moduleNode.globals()) {
|
||||
// 避免全局声明和参数重名,优先参数
|
||||
if (!paramNames.contains(g.getName())) {
|
||||
filteredGlobals.add(g);
|
||||
}
|
||||
}
|
||||
|
||||
if (filteredGlobals.isEmpty()) {
|
||||
// 过滤后已无可插入的全局声明
|
||||
return buildFunction(renameFunction(functionNode, qualifiedName));
|
||||
}
|
||||
|
||||
// 合并全局声明与函数体,前插全局声明
|
||||
List<StatementNode> newBody = new ArrayList<>(filteredGlobals.size() + functionNode.body().size());
|
||||
newBody.addAll(filteredGlobals);
|
||||
// 若有全局声明,插入到函数体最前面
|
||||
List<StatementNode> newBody = new ArrayList<>(moduleNode.globals().size() + functionNode.body().size());
|
||||
newBody.addAll(moduleNode.globals());
|
||||
newBody.addAll(functionNode.body());
|
||||
FunctionNode wrapped = new FunctionNode(
|
||||
qualifiedName,
|
||||
@ -176,11 +85,11 @@ public final class IRProgramBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个重命名的 FunctionNode(只修改函数名,其他属性保持不变)。
|
||||
* 生成一个重命名的 FunctionNode(只修改函数名,其他属性保持不变)。
|
||||
*
|
||||
* @param fn 原始函数节点
|
||||
* @param newName 新的函数名(全限定名)
|
||||
* @return 重命名后的函数节点
|
||||
* @param newName 新的函数名(通常为全限定名)
|
||||
* @return 重命名后的 FunctionNode
|
||||
*/
|
||||
private FunctionNode renameFunction(FunctionNode fn, String newName) {
|
||||
return new FunctionNode(
|
||||
@ -195,8 +104,8 @@ public final class IRProgramBuilder {
|
||||
/**
|
||||
* 构建 IRFunction。
|
||||
*
|
||||
* @param functionNode 待构建的 FunctionNode
|
||||
* @return 构建后的 IRFunction
|
||||
* @param functionNode 要转换的函数节点
|
||||
* @return 转换结果 IRFunction
|
||||
*/
|
||||
private IRFunction buildFunction(FunctionNode functionNode) {
|
||||
return new FunctionBuilder().build(functionNode);
|
||||
@ -204,10 +113,11 @@ public final class IRProgramBuilder {
|
||||
|
||||
/**
|
||||
* 将顶层语句节点封装成特殊的 "_start" 函数。
|
||||
* 主要用于脚本模式支持,使得顶层语句也可以被 IR 执行引擎统一处理。
|
||||
* <p>
|
||||
* 这对于脚本式文件支持至关重要(即文件最外层直接写语句)。
|
||||
*
|
||||
* @param stmt 顶层语句节点
|
||||
* @return 封装后的 FunctionNode
|
||||
* @param stmt 要封装的顶层语句
|
||||
* @return 包装成 FunctionNode 的 "_start" 函数
|
||||
*/
|
||||
private FunctionNode wrapTopLevel(StatementNode stmt) {
|
||||
return new FunctionNode(
|
||||
|
||||
@ -6,16 +6,23 @@ import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
|
||||
/**
|
||||
* IR 指令统一生成工厂类,负责封装常量加载、二元运算、赋值、控制流等指令生成逻辑。
|
||||
* 提高 IR 生成阶段的可维护性与复用性。
|
||||
* InstructionFactory —— 统一生成并注册 IR 指令的工厂类。
|
||||
* <p>
|
||||
* 该类封装了常见的 IR 指令生成方式,包括常量加载、二元运算、赋值、控制流等,
|
||||
* 统一简化指令插入和寄存器分配逻辑,提升 IR 生成阶段的代码可维护性和复用性。
|
||||
* </p>
|
||||
*/
|
||||
public class InstructionFactory {
|
||||
|
||||
/* ====================================================================== */
|
||||
/* 常量 / 通用二元运算(新寄存器) */
|
||||
/* ====================================================================== */
|
||||
|
||||
/**
|
||||
* 加载整数常量,将其写入一个新分配的虚拟寄存器,并返回该寄存器。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param value 整数常量值
|
||||
* @param ctx 当前 IR 上下文(用于分配寄存器与添加指令)
|
||||
* @param value 要加载的整数常量值
|
||||
* @return 存储该常量的新虚拟寄存器
|
||||
*/
|
||||
public static IRVirtualRegister loadConst(IRContext ctx, int value) {
|
||||
@ -24,150 +31,88 @@ public class InstructionFactory {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载 long 类型常量到新寄存器。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param value long 类型常量值
|
||||
* @return 存储该常量的新虚拟寄存器
|
||||
*/
|
||||
public static IRVirtualRegister loadConst(IRContext ctx, long value) {
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(value)));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载 float 类型常量到新寄存器。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param value float 类型常量值
|
||||
* @return 存储该常量的新虚拟寄存器
|
||||
*/
|
||||
public static IRVirtualRegister loadConst(IRContext ctx, float value) {
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(value)));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载 double 类型常量到新寄存器。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param value double 类型常量值
|
||||
* @return 存储该常量的新虚拟寄存器
|
||||
*/
|
||||
public static IRVirtualRegister loadConst(IRContext ctx, double value) {
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(value)));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行二元运算(如加法、减法等),结果写入新分配的虚拟寄存器并返回该寄存器。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param op 二元运算操作码
|
||||
* @param a 左操作数寄存器
|
||||
* @param b 右操作数寄存器
|
||||
* @return 存储结果的新虚拟寄存器
|
||||
* @param op 运算类型(IROpCode 枚举,如 ADD_I32 等)
|
||||
* @param a 第一个操作数寄存器
|
||||
* @param b 第二个操作数寄存器
|
||||
* @return 保存运算结果的新虚拟寄存器
|
||||
*/
|
||||
public static IRVirtualRegister binOp(IRContext ctx, IROpCode op, IRVirtualRegister a, IRVirtualRegister b) {
|
||||
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 IR 常量值
|
||||
* @param dest 目标寄存器
|
||||
* @param value 要加载的整数常量
|
||||
*/
|
||||
public static void loadConstInto(IRContext ctx, IRVirtualRegister dest, IRConstant value) {
|
||||
ctx.addInstruction(new LoadConstInstruction(dest, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行二元运算,并将结果写入指定寄存器。
|
||||
* 对两个寄存器执行二元运算,将结果写入指定目标寄存器。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param op 二元运算操作码
|
||||
* @param a 左操作数寄存器
|
||||
* @param b 右操作数寄存器
|
||||
* @param dest 目标虚拟寄存器
|
||||
* @param op 运算类型(IROpCode 枚举)
|
||||
* @param a 第一个操作数寄存器
|
||||
* @param b 第二个操作数寄存器
|
||||
* @param dest 运算结果目标寄存器
|
||||
*/
|
||||
public static void binOpInto(IRContext ctx, IROpCode op, IRVirtualRegister a, IRVirtualRegister b, IRVirtualRegister dest) {
|
||||
public static void binOpInto(IRContext ctx, IROpCode op,
|
||||
IRVirtualRegister a, IRVirtualRegister b,
|
||||
IRVirtualRegister dest) {
|
||||
ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成“值拷贝”语义(src → dest)。
|
||||
* 若类型无法推断,默认采用 int 方案(ADD_I32, src+0)。
|
||||
* 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) {
|
||||
// 自赋值无需任何操作,避免生成多余的常量 0 寄存器
|
||||
if (src == dest) {
|
||||
return;
|
||||
}
|
||||
String varType = ctx.getVarType(); // 需要 IRContext 提供
|
||||
char suffix = '\0';
|
||||
if (varType != null) {
|
||||
switch (varType) {
|
||||
case "byte" -> suffix = 'b';
|
||||
case "short" -> suffix = 's';
|
||||
case "int" -> suffix = 'i';
|
||||
case "long" -> suffix = 'l';
|
||||
case "float" -> suffix = 'f';
|
||||
case "double" -> suffix = 'd';
|
||||
}
|
||||
}
|
||||
IRVirtualRegister zero;
|
||||
IROpCode op = switch (suffix) {
|
||||
case 'd' -> {
|
||||
zero = loadConst(ctx, 0.0);
|
||||
yield IROpCode.ADD_D64;
|
||||
}
|
||||
case 'f' -> {
|
||||
zero = loadConst(ctx, 0.0f);
|
||||
yield IROpCode.ADD_F32;
|
||||
}
|
||||
case 'l' -> {
|
||||
zero = loadConst(ctx, 0L);
|
||||
yield IROpCode.ADD_L64;
|
||||
}
|
||||
case 's' -> {
|
||||
zero = loadConst(ctx, 0);
|
||||
yield IROpCode.ADD_S16;
|
||||
}
|
||||
case 'b' -> {
|
||||
zero = loadConst(ctx, 0);
|
||||
yield IROpCode.ADD_B8;
|
||||
}
|
||||
default -> {
|
||||
zero = loadConst(ctx, 0);
|
||||
yield IROpCode.ADD_I32;
|
||||
}
|
||||
};
|
||||
ctx.addInstruction(new BinaryOperationInstruction(op, dest, src, zero));
|
||||
// 回退实现: dest = src + 0
|
||||
IRVirtualRegister zero = loadConst(ctx, 0);
|
||||
ctx.addInstruction(new BinaryOperationInstruction(IROpCode.ADD_I32, dest, src, zero));
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
/* 控制流指令 */
|
||||
/* ====================================================================== */
|
||||
|
||||
/**
|
||||
* 生成无条件跳转指令。
|
||||
* 生成无条件跳转(JMP)指令,跳转到指定标签。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param label 跳转目标标签
|
||||
* @param label 目标标签名
|
||||
*/
|
||||
public static void jmp(IRContext ctx, String label) {
|
||||
ctx.addInstruction(new IRJumpInstruction(label));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 IR 流中插入标签。
|
||||
* 在 IR 中插入一个标签(Label)。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param label 标签名
|
||||
@ -177,18 +122,22 @@ public class InstructionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个寄存器,并根据结果跳转到指定标签。
|
||||
* 比较跳转(如 if a < b goto label),根据条件跳转到目标标签。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
* @param cmp 比较操作码
|
||||
* @param a 左操作数寄存器
|
||||
* @param b 右操作数寄存器
|
||||
* @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) {
|
||||
public static void cmpJump(IRContext ctx, IROpCode cmp,
|
||||
IRVirtualRegister a, IRVirtualRegister b,
|
||||
String targetLabel) {
|
||||
ctx.addInstruction(new IRCompareJumpInstruction(cmp, a, b, targetLabel));
|
||||
}
|
||||
|
||||
/* ---------------- 返回 ---------------- */
|
||||
|
||||
/**
|
||||
* 生成返回指令(带返回值)。
|
||||
*
|
||||
@ -200,7 +149,7 @@ public class InstructionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成无返回值(void)返回指令。
|
||||
* 生成无返回值的 return 指令(如 void 函数)。
|
||||
*
|
||||
* @param ctx 当前 IR 上下文
|
||||
*/
|
||||
|
||||
@ -10,23 +10,13 @@ import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* <b>StatementBuilder</b> —— AST 语句节点 ({@link StatementNode}) 到 IR 指令序列的构建器。
|
||||
* StatementBuilder —— 将 AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器。
|
||||
* <p>
|
||||
* 负责将各类语句(如循环、分支、表达式、赋值、声明、返回、break、continue 等)
|
||||
* 转换为对应的 IR 指令,并自动管理作用域、虚拟寄存器分配以及控制流标签(如 break/continue 目标)。
|
||||
* 负责将各种语句节点(循环、分支、表达式、赋值、声明、返回等)生成对应的 IR 指令,并管理作用域和控制流标签。
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>支持多种语句类型的分发与转换。</li>
|
||||
* <li>与 {@link ExpressionBuilder} 协作完成表达式相关 IR 生成。</li>
|
||||
* <li>负责控制流跳转(分支、循环)的标签分配与维护。</li>
|
||||
* <li>在变量赋值和声明时自动常量折叠和登记。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author [你的名字]
|
||||
* @since 1.0
|
||||
*/
|
||||
public class StatementBuilder {
|
||||
|
||||
@ -34,126 +24,79 @@ public class StatementBuilder {
|
||||
* 当前 IR 上下文,包含作用域、指令序列等信息。
|
||||
*/
|
||||
private final IRContext ctx;
|
||||
|
||||
/**
|
||||
* 表达式 IR 构建器,用于将表达式节点转为 IR 指令。
|
||||
*/
|
||||
private final ExpressionBuilder expr;
|
||||
|
||||
/**
|
||||
* break 目标标签栈(保存每层循环的结束标签,用于 break 跳转)
|
||||
* break 目标标签栈(保存每层循环的结束标签)
|
||||
*/
|
||||
private final ArrayDeque<String> breakTargets = new ArrayDeque<>();
|
||||
|
||||
/**
|
||||
* continue 目标标签栈(保存每层循环的 step 起始标签,用于 continue 跳转)
|
||||
* continue 目标标签栈(保存每层循环的 step 起始标签)
|
||||
*/
|
||||
private final ArrayDeque<String> continueTargets = new ArrayDeque<>();
|
||||
|
||||
/**
|
||||
* 构造方法。初始化 StatementBuilder。
|
||||
* 构造方法。
|
||||
*
|
||||
* @param ctx IR 编译上下文环境,包含作用域、标签、指令等信息
|
||||
* @param ctx IR 编译上下文环境
|
||||
*/
|
||||
public StatementBuilder(IRContext ctx) {
|
||||
this.ctx = ctx;
|
||||
this.expr = new ExpressionBuilder(ctx);
|
||||
}
|
||||
|
||||
private static char typeSuffixFromType(String type) {
|
||||
if (type == null) return '\0';
|
||||
return switch (type.toLowerCase(Locale.ROOT)) {
|
||||
case "byte" -> 'b';
|
||||
case "short" -> 's';
|
||||
case "long" -> 'l';
|
||||
case "float" -> 'f';
|
||||
case "double" -> 'd';
|
||||
default -> '\0'; // 其余默认按 32-bit 整型处理
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个 AST 语句节点转为 IR 指令序列。
|
||||
* <p>
|
||||
* 根据节点类型分发到对应的处理方法。
|
||||
* 支持:循环、分支、表达式语句、赋值、声明、返回、break、continue。
|
||||
* 不支持的语句类型会抛出异常。
|
||||
* </p>
|
||||
*
|
||||
* @param stmt 待转换的语句节点,不能为空
|
||||
* @throws IllegalStateException 若遇到不支持的语句类型,或 break/continue 不在循环体中
|
||||
* @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, NodeContext _)) {
|
||||
// 纯表达式语句,如 foo();
|
||||
expr.build(exp);
|
||||
return;
|
||||
}
|
||||
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) {
|
||||
// 赋值语句,如 a = b + 1;
|
||||
|
||||
final String type = ctx.getScope().lookupType(var);
|
||||
|
||||
// 1. 设置声明变量的类型
|
||||
ctx.setVarType(type);
|
||||
|
||||
IRVirtualRegister target = getOrDeclareRegister(var, type);
|
||||
expr.buildInto(rhs, target);
|
||||
|
||||
// 赋值时尝试记录/清除常量
|
||||
try {
|
||||
Object constVal = tryFoldConst(rhs);
|
||||
if (constVal != null)
|
||||
ctx.getScope().setConstValue(var, constVal);
|
||||
else
|
||||
ctx.getScope().clearConstValue(var);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
// 2. 清除变量声明
|
||||
ctx.clearVarType();
|
||||
return;
|
||||
}
|
||||
|
||||
// ==== 支持数组下标赋值 arr[idx] = value ====
|
||||
if (stmt instanceof IndexAssignmentNode idxAssign) {
|
||||
IndexExpressionNode target = idxAssign.target();
|
||||
|
||||
// 1. 目标数组寄存器:多维时用 buildIndexRef 拿引用
|
||||
IRVirtualRegister arrReg = (target.array() instanceof IndexExpressionNode inner)
|
||||
? expr.buildIndexRef(inner)
|
||||
: expr.build(target.array());
|
||||
|
||||
// 2. 下标与右值
|
||||
IRVirtualRegister idxReg = expr.build(target.index());
|
||||
IRVirtualRegister valReg = expr.build(idxAssign.value());
|
||||
|
||||
// 3. 选择内置函数名 __setindex_x,根据元素类型分派
|
||||
String func = "__setindex_r";
|
||||
org.jcnc.snow.compiler.parser.ast.base.ExpressionNode base = target.array();
|
||||
while (base instanceof IndexExpressionNode innerIdx) base = innerIdx.array();
|
||||
if (base instanceof IdentifierNode id) {
|
||||
String arrType = ctx.getScope().lookupType(id.name());
|
||||
if (arrType != null) {
|
||||
String elemType = arrType.endsWith("[]") ? arrType.substring(0, arrType.length() - 2) : arrType;
|
||||
switch (elemType) {
|
||||
case "byte" -> func = "__setindex_b";
|
||||
case "short" -> func = "__setindex_s";
|
||||
case "int" -> func = "__setindex_i";
|
||||
case "long" -> func = "__setindex_l";
|
||||
case "float" -> func = "__setindex_f";
|
||||
case "double" -> func = "__setindex_d";
|
||||
case "boolean" -> func = "__setindex_i";
|
||||
case "string" -> func = "__setindex_r";
|
||||
default -> func = "__setindex_r";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 生成 CALL 指令
|
||||
java.util.List<org.jcnc.snow.compiler.ir.core.IRValue> argv =
|
||||
java.util.List.of(arrReg, idxReg, valReg);
|
||||
ctx.addInstruction(new org.jcnc.snow.compiler.ir.instruction.CallInstruction(null, func, argv));
|
||||
|
||||
// 5. 赋值后清理常量绑定
|
||||
try {
|
||||
if (base instanceof IdentifierNode id2) {
|
||||
ctx.getScope().clearConstValue(id2.name());
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (stmt instanceof DeclarationNode decl) {
|
||||
// 变量声明语句(如 int a = 1;)
|
||||
if (decl.getInitializer().isPresent()) {
|
||||
@ -170,21 +113,16 @@ public class StatementBuilder {
|
||||
// 即使初始值是某个已存在变量(如 outer_i),这里是值的拷贝
|
||||
expr.buildInto(decl.getInitializer().get(), dest);
|
||||
|
||||
// 声明赋初值时登记常量
|
||||
try {
|
||||
Object constVal = tryFoldConst(decl.getInitializer().get());
|
||||
if (constVal != null)
|
||||
ctx.getScope().setConstValue(decl.getName(), constVal);
|
||||
else
|
||||
ctx.getScope().clearConstValue(decl.getName());
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
// 4. 清理类型设置,防止影响后续变量声明
|
||||
ctx.clearVarType();
|
||||
|
||||
// 5. 在作用域内将变量名与新分配的寄存器进行绑定
|
||||
// 这样后续对该变量的任何操作都只会影响 dest,不会反向影响初值表达式中的源变量
|
||||
ctx.getScope().declare(decl.getName(), decl.getType(), dest);
|
||||
} else {
|
||||
// 仅声明变量,无初值(如 int a;)
|
||||
// 在作用域内声明并分配新寄存器,但不进行初始化
|
||||
ctx.getScope().declare(decl.getName(), decl.getType());
|
||||
ctx.getScope().clearConstValue(decl.getName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -221,11 +159,10 @@ public class StatementBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取变量名对应的寄存器,如果尚未声明则新声明一个并返回。
|
||||
* 获取变量名对应的寄存器,不存在则声明一个新的。
|
||||
*
|
||||
* @param name 变量名,不能为空
|
||||
* @param type 变量类型,不能为空
|
||||
* @return 变量对应的虚拟寄存器 {@link IRVirtualRegister}
|
||||
* @param name 变量名
|
||||
* @return 变量对应的虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister getOrDeclareRegister(String name, String type) {
|
||||
IRVirtualRegister reg = ctx.getScope().lookup(name);
|
||||
@ -237,21 +174,19 @@ public class StatementBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量构建一组语句节点,按顺序依次处理。
|
||||
* 批量构建一组语句节点,顺序处理每个语句。
|
||||
*
|
||||
* @param stmts 语句节点集合,不可为 null
|
||||
* @param stmts 语句节点集合
|
||||
*/
|
||||
private void buildStatements(Iterable<StatementNode> stmts) {
|
||||
for (StatementNode s : stmts) build(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建循环语句(for/while),包括初始语句、条件判断、循环体、更新语句、跳回判断等 IR 指令。
|
||||
* <p>
|
||||
* 自动维护 break/continue 的目标标签。
|
||||
* </p>
|
||||
* 构建循环语句(for/while)。
|
||||
* 处理流程: 初始语句 → 条件判断 → 循环体 → 更新语句 → 跳回条件。
|
||||
*
|
||||
* @param loop 循环节点,不能为空
|
||||
* @param loop 循环节点
|
||||
*/
|
||||
private void buildLoop(LoopNode loop) {
|
||||
if (loop.init() != null) build(loop.init());
|
||||
@ -287,11 +222,9 @@ public class StatementBuilder {
|
||||
|
||||
/**
|
||||
* 构建分支语句(if/else)。
|
||||
* <p>
|
||||
* 包括:条件判断、then 分支、else 分支(可选)、结束标签等。
|
||||
* </p>
|
||||
* 处理流程: 条件判断 → then 分支 → else 分支(可选)。
|
||||
*
|
||||
* @param ifNode if 语句节点,不能为空
|
||||
* @param ifNode if 语句节点
|
||||
*/
|
||||
private void buildIf(IfNode ifNode) {
|
||||
String lblElse = ctx.newLabel();
|
||||
@ -312,14 +245,11 @@ public class StatementBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射条件跳转指令:如果 cond 不成立,则跳转到 falseLabel。
|
||||
* <p>
|
||||
* 对于二元比较表达式,会选择恰当的比较指令。
|
||||
* 其他类型表达式,等价于 (cond == 0) 时跳转。
|
||||
* </p>
|
||||
* 条件跳转指令的生成。
|
||||
* 如果是二元比较表达式,直接使用对应比较操作码;否则等价于与 0 比较。
|
||||
*
|
||||
* @param cond 条件表达式节点,不可为 null
|
||||
* @param falseLabel 条件不成立时跳转的标签,不可为 null
|
||||
* @param cond 条件表达式
|
||||
* @param falseLabel 条件不成立时跳转到的标签
|
||||
*/
|
||||
private void emitConditionalJump(ExpressionNode cond, String falseLabel) {
|
||||
if (cond instanceof BinaryExpressionNode(
|
||||
@ -341,79 +271,7 @@ public class StatementBuilder {
|
||||
} else {
|
||||
IRVirtualRegister condReg = expr.build(cond);
|
||||
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
|
||||
InstructionFactory.cmpJump(ctx, org.jcnc.snow.compiler.ir.core.IROpCode.CMP_IEQ, condReg, zero, falseLabel);
|
||||
InstructionFactory.cmpJump(ctx, IROpCode.CMP_IEQ, condReg, zero, falseLabel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归尝试对表达式做常量折叠(constant folding)。
|
||||
* <p>
|
||||
* 该方法会根据表达式类型进行常量求值:
|
||||
* <ul>
|
||||
* <li>如果是数字字面量,解析为 Integer 或 Double。</li>
|
||||
* <li>如果是字符串字面量,直接返回字符串内容。</li>
|
||||
* <li>如果是布尔字面量,返回 1(true)或 0(false)。</li>
|
||||
* <li>如果是数组字面量,会递归所有元素全部是常量时,返回包含常量元素的 List。</li>
|
||||
* <li>如果是标识符节点,尝试从作用域查找是否登记为常量,如果找到则返回。</li>
|
||||
* <li>其余类型或无法确定常量时,返回 null。</li>
|
||||
* </ul>
|
||||
* 用于全局/局部常量传播优化与类型推断。
|
||||
*
|
||||
* @param expr 待折叠的表达式节点,允许为 null
|
||||
* @return 如果可折叠则返回其常量值(如 Integer、Double、String、List),否则返回 null
|
||||
*/
|
||||
private Object tryFoldConst(ExpressionNode expr) {
|
||||
// 1. 空节点直接返回 null
|
||||
if (expr == null) return null;
|
||||
|
||||
// 2. 数字字面量:尝试解析为 Integer 或 Double
|
||||
if (expr instanceof NumberLiteralNode n) {
|
||||
String s = n.value(); // 获取文本内容
|
||||
try {
|
||||
// 判断是否为浮点型(包含 . 或 e/E 科学计数法)
|
||||
if (s.contains(".") || s.contains("e") || s.contains("E")) {
|
||||
return Double.parseDouble(s); // 解析为 Double
|
||||
}
|
||||
return Integer.parseInt(s); // 否则解析为 Integer
|
||||
} catch (NumberFormatException e) {
|
||||
// 解析失败,返回 null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 字符串字面量:直接返回字符串内容
|
||||
if (expr instanceof StringLiteralNode s) {
|
||||
return s.value();
|
||||
}
|
||||
|
||||
// 4. 布尔字面量:true 返回 1,false 返回 0
|
||||
if (expr instanceof BoolLiteralNode b) {
|
||||
return b.getValue() ? 1 : 0;
|
||||
}
|
||||
|
||||
// 5. 数组字面量:递归所有元素做常量折叠,只有全为常量时才返回 List
|
||||
if (expr instanceof ArrayLiteralNode arr) {
|
||||
java.util.List<Object> list = new java.util.ArrayList<>();
|
||||
for (ExpressionNode e : arr.elements()) {
|
||||
Object v = tryFoldConst(e); // 递归折叠每个元素
|
||||
if (v == null) return null; // 只要有一个不是常量,则整个数组不是常量
|
||||
list.add(v);
|
||||
}
|
||||
// 所有元素均为常量,返回只读 List
|
||||
return java.util.List.copyOf(list);
|
||||
}
|
||||
|
||||
// 6. 标识符:尝试查找该变量在当前作用域是否登记为常量
|
||||
if (expr instanceof IdentifierNode id) {
|
||||
try {
|
||||
Object v = ctx.getScope().getConstValue(id.name());
|
||||
if (v != null) return v; // 查到常量则返回
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// 7. 其他情况均视为不可折叠,返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
package org.jcnc.snow.compiler.ir.common;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 全局常量表,用于跨模块编译期常量查询和折叠。
|
||||
*
|
||||
* <p>
|
||||
* 主要功能:
|
||||
* <ul>
|
||||
* <li>在 IRProgramBuilder 预扫描阶段,将所有模块级 <code>const</code> 常量
|
||||
* (如 ModuleA.a)注册到全局常量表,支持跨模块访问。</li>
|
||||
* <li>后续任何阶段均可通过 {@link #get(String)} 查询已注册常量,实现编译期常量折叠。</li>
|
||||
* <li>保证线程安全,支持并发注册和访问。</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 常量的 key 格式为“模块名.常量名”,如 "ModuleA.a",以便唯一标识。
|
||||
*
|
||||
* <p>
|
||||
* 典型用法:
|
||||
* <pre>
|
||||
* GlobalConstTable.register("ModuleA.a", 10); // 注册常量
|
||||
* Object val = GlobalConstTable.get("ModuleA.a"); // 查询常量
|
||||
* </pre>
|
||||
*/
|
||||
public final class GlobalConstTable {
|
||||
|
||||
/** 存储全局常量: “ModuleName.constName” → 常量值。线程安全。 */
|
||||
private static final Map<String, Object> CONSTS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 工具类构造器,防止实例化。
|
||||
*/
|
||||
private GlobalConstTable() { /* utility class */ }
|
||||
|
||||
/**
|
||||
* 注册一个全局常量到表中(只在首次注册时生效,避免被覆盖)。
|
||||
*
|
||||
* @param qualifiedName 常量的全限定名(如 "ModuleA.a")
|
||||
* @param value 常量的字面值(如 10、字符串、布尔等)
|
||||
* @throws IllegalArgumentException 名称为 null 或空串时抛出
|
||||
*/
|
||||
public static void register(String qualifiedName, Object value) {
|
||||
if (qualifiedName == null || qualifiedName.isBlank()) {
|
||||
throw new IllegalArgumentException("常量名不能为空");
|
||||
}
|
||||
CONSTS.putIfAbsent(qualifiedName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定全局常量的值。
|
||||
*
|
||||
* @param qualifiedName 常量的全限定名(如 "ModuleA.a")
|
||||
* @return 查到的常量值,如果未注册则返回 null
|
||||
*/
|
||||
public static Object get(String qualifiedName) {
|
||||
return CONSTS.get(qualifiedName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回全部已注册常量的不可变视图(快照)。
|
||||
* <p>注意:只读,不可修改。</p>
|
||||
*
|
||||
* @return key=常量名,value=常量值的不可变 Map
|
||||
*/
|
||||
public static Map<String, Object> all() {
|
||||
return Map.copyOf(CONSTS);
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
package org.jcnc.snow.compiler.lexer.core;
|
||||
|
||||
import org.jcnc.snow.common.SnowConfig;
|
||||
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
|
||||
import org.jcnc.snow.compiler.lexer.scanners.*;
|
||||
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
import org.jcnc.snow.compiler.lexer.utils.TokenPrinter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@ -35,7 +37,7 @@ public class LexerEngine {
|
||||
* @param sourceName 文件名(诊断用)
|
||||
*/
|
||||
public LexerEngine(String source, String sourceName) {
|
||||
this.absPath = new File(sourceName).getAbsolutePath().replace('\\', '/');
|
||||
this.absPath = new File(sourceName).getAbsolutePath();
|
||||
this.context = new LexerContext(source);
|
||||
this.scanners = List.of(
|
||||
new WhitespaceTokenScanner(),
|
||||
@ -54,9 +56,9 @@ public class LexerEngine {
|
||||
/* 2. 后置整体校验 */
|
||||
validateTokens();
|
||||
/* 3. 打印 token */
|
||||
// if (SnowConfig.isDebug()) {
|
||||
// TokenPrinter.print(tokens);
|
||||
// }
|
||||
if (SnowConfig.isDebug()) {
|
||||
TokenPrinter.print(tokens);
|
||||
}
|
||||
|
||||
/* 4. 统一报告错误 */
|
||||
report(errors);
|
||||
@ -125,8 +127,8 @@ public class LexerEngine {
|
||||
|
||||
/**
|
||||
* 目前包含2条规则: <br>
|
||||
* 1. Declare-Ident declare 后必须紧跟合法标识符(或 const + 标识符),并且只能一个<br>
|
||||
* 2. Double-Ident declare 后若出现第二个多余的 IDENTIFIER<br>
|
||||
* 1. Declare-Ident declare 后必须紧跟合法标识符,并且只能一个<br>
|
||||
* 2. Double-Ident declare 后若出现第二个 IDENTIFIER 视为多余<br>
|
||||
* <p>发现问题仅写入 {@link #errors},不抛异常。</p>
|
||||
*/
|
||||
private void validateTokens() {
|
||||
@ -137,28 +139,17 @@ public class LexerEngine {
|
||||
if (tok.getType() == TokenType.KEYWORD
|
||||
&& "declare".equalsIgnoreCase(tok.getLexeme())) {
|
||||
|
||||
// 找 declare 后第一个非 NEWLINE token
|
||||
Token t1 = findNextNonNewline(i);
|
||||
|
||||
// 如果有 const,允许
|
||||
boolean hasConst = t1 != null
|
||||
&& t1.getType() == TokenType.KEYWORD
|
||||
&& "const".equalsIgnoreCase(t1.getLexeme());
|
||||
int identStartIdx = hasConst ? tokens.indexOf(t1) : i;
|
||||
|
||||
// 找下一个非 NEWLINE token,如果有 const,就找下一个
|
||||
Token id1 = findNextNonNewline(identStartIdx);
|
||||
|
||||
// id1 必须是 IDENTIFIER
|
||||
// 第一个非 NEWLINE token
|
||||
Token id1 = findNextNonNewline(i);
|
||||
if (id1 == null || id1.getType() != TokenType.IDENTIFIER) {
|
||||
errors.add(err(
|
||||
(id1 == null ? (hasConst ? t1 : tok) : id1),
|
||||
"declare 后必须跟合法标识符 (可选 const 关键字)"
|
||||
(id1 == null ? tok : id1),
|
||||
"declare 后必须跟合法标识符 (以字母或 '_' 开头)"
|
||||
));
|
||||
continue; // 若首标识符就错,后续检查可略
|
||||
}
|
||||
|
||||
// 检查是否有第二个多余的 IDENTIFIER
|
||||
// 检查是否有第二个 IDENTIFIER
|
||||
Token id2 = findNextNonNewline(tokens.indexOf(id1));
|
||||
if (id2 != null && id2.getType() == TokenType.IDENTIFIER) {
|
||||
errors.add(err(id2, "declare 声明中出现多余的标识符"));
|
||||
|
||||
@ -50,6 +50,6 @@ public class LexicalError {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "file:///" + file + ":" + line + ":" + column + ": " + message;
|
||||
return file + ": 行 " + line + ", 列 " + column + ": " + message;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
|
||||
*/
|
||||
@Override
|
||||
public boolean canHandle(char c, LexerContext ctx) {
|
||||
return ":,().+-*/[]".indexOf(c) >= 0;
|
||||
return ":,().+-*/".indexOf(c) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -53,8 +53,6 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
|
||||
case '/' -> TokenType.DIVIDE;
|
||||
case '(' -> TokenType.LPAREN;
|
||||
case ')' -> TokenType.RPAREN;
|
||||
case '[' -> TokenType.LBRACKET;
|
||||
case ']' -> TokenType.RBRACKET;
|
||||
default -> TokenType.UNKNOWN;
|
||||
};
|
||||
return new Token(type, String.valueOf(c), line, col);
|
||||
|
||||
@ -26,9 +26,9 @@ public class TokenFactory {
|
||||
* 语言的保留关键字集合。
|
||||
*/
|
||||
private static final Set<String> KEYWORDS = Set.of
|
||||
("module", "function", "params", "returns", "body", "end",
|
||||
"if", "then", "else", "loop", "declare", "return", "import", "init",
|
||||
"cond", "step", "globals", "break", "continue", "const");
|
||||
("module", "function", "parameter", "return_type", "body", "end",
|
||||
"if", "then", "else", "loop", "declare", "return", "import", "init",
|
||||
"cond", "step", "globals", "break", "continue");
|
||||
|
||||
/**
|
||||
* 内置类型名称集合,如 int、string 等。
|
||||
|
||||
@ -14,10 +14,10 @@ public enum TokenType {
|
||||
/** 普通标识符,如变量名、函数名等 */
|
||||
IDENTIFIER,
|
||||
|
||||
/** 关键字(declare、if、else、loop、break、continue、return 等) */
|
||||
/** 语言保留关键字(如 if、return、module 等) */
|
||||
KEYWORD,
|
||||
|
||||
/** 内置类型名(byte、short、int、long、float、double、string、boolean、void 等) */
|
||||
/** 内置类型名称(如 int、string、bool 等) */
|
||||
TYPE,
|
||||
|
||||
/* ---------- 字面量 ---------- */
|
||||
@ -67,12 +67,6 @@ public enum TokenType {
|
||||
/** 右括号 ')' */
|
||||
RPAREN,
|
||||
|
||||
/** 左中括号 '[' */
|
||||
LBRACKET,
|
||||
|
||||
/** 右中括号 ']' */
|
||||
RBRACKET,
|
||||
|
||||
/** 相等比较符号 '==' */
|
||||
DOUBLE_EQUALS,
|
||||
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
package org.jcnc.snow.compiler.parser.ast;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code ArrayLiteralNode} 表示数组字面量表达式节点。
|
||||
* <p>
|
||||
* 例如:[1, 2, 3] 或 [[1, 2], [3, 4]] 这样的语法结构,均对应本节点类型。
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #elements()} 保存所有元素表达式节点。</li>
|
||||
* <li>{@link #context()} 表示该节点在源代码中的上下文信息(如位置、父节点等)。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public record ArrayLiteralNode(
|
||||
/*
|
||||
数组字面量中的所有元素表达式(按顺序)。
|
||||
*/
|
||||
List<ExpressionNode> elements,
|
||||
|
||||
/*
|
||||
节点的上下文信息(如源码位置等)。
|
||||
*/
|
||||
NodeContext context
|
||||
) implements ExpressionNode {
|
||||
|
||||
/**
|
||||
* 返回字符串形式,如 {@code Array[1, 2, 3]}。
|
||||
*
|
||||
* @return 表示该数组字面量节点的字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Array" + elements;
|
||||
}
|
||||
}
|
||||
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