!63 feat: 增强语义分析与全局常量处理
Merge pull request !63 from Luke/feature/add-constant
This commit is contained in:
commit
3595631e2c
@ -7,4 +7,12 @@
|
|||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</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>
|
</component>
|
||||||
10
.run/Demo22.run.xml
Normal file
10
.run/Demo22.run.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<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>
|
||||||
10
.run/Demo23.run.xml
Normal file
10
.run/Demo23.run.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<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>
|
||||||
10
.run/Demo24.run.xml
Normal file
10
.run/Demo24.run.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<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>
|
||||||
6
.run/build-project2tar.ps1.run.xml
Normal file
6
.run/build-project2tar.ps1.run.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<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>
|
||||||
@ -3,4 +3,8 @@
|
|||||||
<envs />
|
<envs />
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</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">
|
||||||
|
<envs />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
@ -1,10 +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>
|
|
||||||
<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>
|
|
||||||
15
playground/Demo/Demo22/Main.snow
Normal file
15
playground/Demo/Demo22/Main.snow
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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
|
||||||
11
playground/Demo/Demo22/OS.snow
Normal file
11
playground/Demo/Demo22/OS.snow
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module: os
|
||||||
|
import: os
|
||||||
|
function: print
|
||||||
|
params:
|
||||||
|
declare i1: int
|
||||||
|
returns: void
|
||||||
|
body:
|
||||||
|
syscall("PRINT",i1)
|
||||||
|
end body
|
||||||
|
end function
|
||||||
|
end module
|
||||||
14
playground/Demo/Demo23/Main.snow
Normal file
14
playground/Demo/Demo23/Main.snow
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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
|
||||||
26
playground/Demo/Demo24/Main.snow
Normal file
26
playground/Demo/Demo24/Main.snow
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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
|
||||||
11
playground/Demo/Demo24/OS.snow
Normal file
11
playground/Demo/Demo24/OS.snow
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module: os
|
||||||
|
import: os
|
||||||
|
function: print
|
||||||
|
params:
|
||||||
|
declare i1: int
|
||||||
|
returns: void
|
||||||
|
body:
|
||||||
|
syscall("PRINT",i1)
|
||||||
|
end body
|
||||||
|
end function
|
||||||
|
end module
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package org.jcnc.snow.compiler.ir.builder;
|
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.IROpCode;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRValue;
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
|
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
|
||||||
@ -48,6 +49,8 @@ public record ExpressionBuilder(IRContext ctx) {
|
|||||||
if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
|
if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
|
||||||
yield reg;
|
yield reg;
|
||||||
}
|
}
|
||||||
|
// 模块常量 / 全局变量,如 ModuleA.a
|
||||||
|
case MemberExpressionNode mem -> buildMember(mem);
|
||||||
// 二元表达式(如 a+b, x==y)
|
// 二元表达式(如 a+b, x==y)
|
||||||
case BinaryExpressionNode bin -> buildBinary(bin);
|
case BinaryExpressionNode bin -> buildBinary(bin);
|
||||||
// 函数/方法调用表达式
|
// 函数/方法调用表达式
|
||||||
@ -62,6 +65,40 @@ public record ExpressionBuilder(IRContext ctx) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成员访问表达式构建
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ───────────────── 写入指定寄存器 ───────────────── */
|
/* ───────────────── 写入指定寄存器 ───────────────── */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package org.jcnc.snow.compiler.ir.builder;
|
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.common.GlobalFunctionTable;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||||
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
|
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
|
||||||
@ -13,6 +14,13 @@ import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
|||||||
* <p>
|
* <p>
|
||||||
* 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction,
|
* 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction,
|
||||||
* 包含参数声明、返回类型推断、函数体语句转换等步骤。
|
* 包含参数声明、返回类型推断、函数体语句转换等步骤。
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>支持自动导入全局/跨模块常量,使跨模块常量引用(如 ModuleA.a)在 IR 阶段可用。</li>
|
||||||
|
* <li>将函数形参声明为虚拟寄存器,并注册到作用域,便于后续指令生成。</li>
|
||||||
|
* <li>根据返回类型设置表达式默认字面量类型,保证 IR 层类型一致性。</li>
|
||||||
|
* <li>遍历并转换函数体语句为 IR 指令。</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class FunctionBuilder {
|
public class FunctionBuilder {
|
||||||
|
|
||||||
@ -21,11 +29,15 @@ public class FunctionBuilder {
|
|||||||
* <p>
|
* <p>
|
||||||
* 构建过程包括:
|
* 构建过程包括:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>初始化 IRFunction 实例和上下文</li>
|
* <li>在全局函数表注册函数签名,便于后续模块/语义分析阶段查询。</li>
|
||||||
* <li>根据函数返回类型,设置默认类型后缀,便于表达式推断</li>
|
* <li>初始化 IRFunction 实例和 IRContext 上下文对象(包含作用域与寄存器信息)。</li>
|
||||||
* <li>声明参数到作用域,并为每个参数分配虚拟寄存器</li>
|
* <li>自动导入全局常量(包括跨模块 const 变量)到当前作用域,
|
||||||
* <li>遍历并转换函数体内的每条语句为 IR 指令</li>
|
* 使成员访问如 ModuleA.a 可直接折叠为常量。</li>
|
||||||
* <li>函数构建完成后,清理默认类型后缀,防止影响其他函数</li>
|
* <li>根据函数返回类型,设置表达式推断的默认字面量类型后缀
|
||||||
|
* (如 double→d, float→f),避免类型不一致。</li>
|
||||||
|
* <li>遍历声明形参,每个参数分配虚拟寄存器,并注册到作用域。</li>
|
||||||
|
* <li>依次转换函数体中的每条语句为 IR 指令。</li>
|
||||||
|
* <li>函数体转换完成后,清理默认类型后缀,防止影响后续函数构建。</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* @param functionNode 表示函数定义的语法树节点
|
* @param functionNode 表示函数定义的语法树节点
|
||||||
@ -33,44 +45,56 @@ public class FunctionBuilder {
|
|||||||
*/
|
*/
|
||||||
public IRFunction build(FunctionNode functionNode) {
|
public IRFunction build(FunctionNode functionNode) {
|
||||||
|
|
||||||
// 在全局函数表中注册函数信息
|
// 1. 在全局函数表注册函数名与返回类型
|
||||||
|
// 方便其他阶段/模块调用、类型检查。
|
||||||
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
|
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
|
||||||
|
|
||||||
|
// 2. 初始化 IRFunction 实例与上下文对象
|
||||||
// 0) 基本初始化: 创建 IRFunction 实例与对应上下文
|
// IRFunction: 表示该函数的中间代码容器
|
||||||
|
// IRContext: 负责作用域、寄存器分配等编译上下文管理
|
||||||
IRFunction irFunction = new IRFunction(functionNode.name());
|
IRFunction irFunction = new IRFunction(functionNode.name());
|
||||||
IRContext irContext = new IRContext(irFunction);
|
IRContext irContext = new IRContext(irFunction);
|
||||||
|
|
||||||
// 1) 把函数返回类型注入为默认类型后缀(供表达式类型推断)
|
// 3. 自动导入所有全局/跨模块常量到当前作用域
|
||||||
|
// 支持如 ModuleA.a 这样的常量访问/折叠(参见 ExpressionBuilder)
|
||||||
|
GlobalConstTable.all().forEach((k, v) ->
|
||||||
|
irContext.getScope().importExternalConst(k, v));
|
||||||
|
|
||||||
|
// 4. 根据函数返回类型设置默认类型后缀
|
||||||
|
// 例如返回类型为 double 时, 字面量表达式自动用 d 后缀。
|
||||||
char _returnSuffix = switch (functionNode.returnType().toLowerCase()) {
|
char _returnSuffix = switch (functionNode.returnType().toLowerCase()) {
|
||||||
case "double" -> 'd';
|
case "double" -> 'd';
|
||||||
case "float" -> 'f';
|
case "float" -> 'f';
|
||||||
case "long" -> 'l';
|
case "long" -> 'l';
|
||||||
case "short" -> 's';
|
case "short" -> 's';
|
||||||
case "byte" -> 'b';
|
case "byte" -> 'b';
|
||||||
default -> '\0';
|
default -> '\0'; // 其它类型不设默认后缀
|
||||||
};
|
};
|
||||||
ExpressionUtils.setDefaultSuffix(_returnSuffix);
|
ExpressionUtils.setDefaultSuffix(_returnSuffix);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 2) 声明形参: 为每个参数分配虚拟寄存器并声明到作用域
|
// 5. 遍历函数参数列表
|
||||||
|
// - 为每个参数分配一个新的虚拟寄存器
|
||||||
|
// - 注册参数名、类型、寄存器到当前作用域
|
||||||
|
// - 添加参数寄存器到 IRFunction(用于后续调用与指令生成)
|
||||||
for (ParameterNode p : functionNode.parameters()) {
|
for (ParameterNode p : functionNode.parameters()) {
|
||||||
IRVirtualRegister reg = irFunction.newRegister(); // 新寄存器
|
IRVirtualRegister reg = irFunction.newRegister();
|
||||||
irContext.getScope().declare(p.name(), p.type(), reg); // 变量名→寄存器绑定
|
irContext.getScope().declare(p.name(), p.type(), reg);
|
||||||
irFunction.addParameter(reg); // 添加到函数参数列表
|
irFunction.addParameter(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) 生成函数体 IR: 遍历每条语句,逐一转化
|
// 6. 遍历函数体语句节点,转换为 IR 指令
|
||||||
|
// StatementBuilder 负责将每条语句递归转换为 IR
|
||||||
StatementBuilder stmtBuilder = new StatementBuilder(irContext);
|
StatementBuilder stmtBuilder = new StatementBuilder(irContext);
|
||||||
for (StatementNode stmt : functionNode.body()) {
|
for (StatementNode stmt : functionNode.body()) {
|
||||||
stmtBuilder.build(stmt);
|
stmtBuilder.build(stmt);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// 4) 清除默认后缀,避免影响后续函数的推断
|
// 7. 清理默认类型后缀,防止影响后续其他函数的类型推断
|
||||||
ExpressionUtils.clearDefaultSuffix();
|
ExpressionUtils.clearDefaultSuffix();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回构建好的 IRFunction
|
// 8. 返回构建完成的 IRFunction
|
||||||
return irFunction;
|
return irFunction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,30 +15,25 @@ import java.util.Map;
|
|||||||
* <li>支持变量与虚拟寄存器的重新绑定与查找</li>
|
* <li>支持变量与虚拟寄存器的重新绑定与查找</li>
|
||||||
* <li>支持变量的类型信息记录与查询</li>
|
* <li>支持变量的类型信息记录与查询</li>
|
||||||
* <li>支持变量的编译期常量值记录与查询(便于常量折叠等优化)</li>
|
* <li>支持变量的编译期常量值记录与查询(便于常量折叠等优化)</li>
|
||||||
|
* <li>支持跨模块全局常量(如 ModuleA.a)查找</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
final class IRBuilderScope {
|
final class IRBuilderScope {
|
||||||
|
|
||||||
/**
|
/** 变量名到虚拟寄存器的映射表(本地变量) */
|
||||||
* 变量名到虚拟寄存器的映射表。
|
|
||||||
* 用于跟踪所有声明和分配的变量。
|
|
||||||
*/
|
|
||||||
private final Map<String, IRVirtualRegister> vars = new HashMap<>();
|
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 为其常量值
|
||||||
*/
|
|
||||||
private final Map<String, String> varTypes = new HashMap<>();
|
|
||||||
/**
|
|
||||||
* 变量名到编译期常量值的映射表。
|
|
||||||
* 用于常量折叠优化(如 int、string、数组等常量)。
|
|
||||||
*/
|
|
||||||
private final Map<String, Object> varConstValues = new HashMap<>();
|
|
||||||
/**
|
|
||||||
* 当前作用域所绑定的 IRFunction 实例。
|
|
||||||
* 用于申请新的虚拟寄存器。
|
|
||||||
*/
|
*/
|
||||||
|
private final Map<String, Object> externalConsts = new HashMap<>();
|
||||||
|
|
||||||
|
/** 当前作用域所绑定的 IRFunction 实例 */
|
||||||
private IRFunction fn;
|
private IRFunction fn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,7 +113,7 @@ final class IRBuilderScope {
|
|||||||
// ---------------- 编译期常量相关接口 ----------------
|
// ---------------- 编译期常量相关接口 ----------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置变量的编译期常量值。
|
* 设置变量的编译期常量值(本地变量)。
|
||||||
*
|
*
|
||||||
* @param name 变量名称
|
* @param name 变量名称
|
||||||
* @param value 常量值(null 表示清除)
|
* @param value 常量值(null 表示清除)
|
||||||
@ -129,21 +124,38 @@ final class IRBuilderScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取变量的编译期常量值(如没有则返回 null)。
|
* 获取变量的编译期常量值(本地变量或导入的外部常量)。
|
||||||
|
* <br>
|
||||||
|
* 优先查找本地常量,未命中再查外部(如 "ModuleA.a")。
|
||||||
*
|
*
|
||||||
* @param name 变量名称
|
* @param name 变量名称或"模块名.常量名"
|
||||||
* @return 编译期常量值,或 null
|
* @return 编译期常量值,或 null
|
||||||
*/
|
*/
|
||||||
Object getConstValue(String name) {
|
Object getConstValue(String name) {
|
||||||
return varConstValues.get(name);
|
Object v = varConstValues.get(name);
|
||||||
|
if (v != null) return v;
|
||||||
|
// 支持跨模块常量/全局变量
|
||||||
|
return externalConsts.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除变量的编译期常量值绑定。
|
* 清除变量的编译期常量值绑定(本地)。
|
||||||
*
|
*
|
||||||
* @param name 变量名称
|
* @param name 变量名称
|
||||||
*/
|
*/
|
||||||
void clearConstValue(String name) {
|
void clearConstValue(String name) {
|
||||||
varConstValues.remove(name);
|
varConstValues.remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- 跨模块常量导入支持 ----------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入外部(其他模块)的全局常量/变量。
|
||||||
|
*
|
||||||
|
* @param qualifiedName 形如 "ModuleA.a"
|
||||||
|
* @param value 其常量值
|
||||||
|
*/
|
||||||
|
void importExternalConst(String qualifiedName, Object value) {
|
||||||
|
externalConsts.put(qualifiedName, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,26 @@
|
|||||||
package org.jcnc.snow.compiler.ir.builder;
|
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.IRFunction;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRProgram;
|
import org.jcnc.snow.compiler.ir.core.IRProgram;
|
||||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
import org.jcnc.snow.compiler.parser.ast.*;
|
||||||
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
|
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IRProgramBuilder 负责将 AST 根节点(如模块、函数、顶层语句)转换为可执行的 IRProgram 实例。
|
* IRProgramBuilder 负责将 AST 顶层节点转换为可执行的 {@link IRProgram}。
|
||||||
* <p>
|
*
|
||||||
* 主要职责:
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>遍历 AST 根节点,根据类型分别处理(模块、函数、顶层语句)。</li>
|
* <li>预扫描所有模块,将 <code>declare const</code> 常量登记到全局常量表,支持跨模块常量折叠。</li>
|
||||||
* <li>对模块内的函数添加全限定名,并在函数体前注入全局变量声明。</li>
|
* <li>对模块内的函数加上模块前缀,保证命名唯一,并将本模块全局声明注入到函数体前部。</li>
|
||||||
* <li>将单独的顶层语句封装为特殊的 "_start" 函数。</li>
|
* <li>将独立顶层语句自动包装为特殊的 "_start" 函数(脚本模式支持)。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public final class IRProgramBuilder {
|
public final class IRProgramBuilder {
|
||||||
@ -31,20 +33,23 @@ public final class IRProgramBuilder {
|
|||||||
* @throws IllegalStateException 遇到不支持的顶层节点类型时抛出
|
* @throws IllegalStateException 遇到不支持的顶层节点类型时抛出
|
||||||
*/
|
*/
|
||||||
public IRProgram buildProgram(List<Node> roots) {
|
public IRProgram buildProgram(List<Node> roots) {
|
||||||
|
// 预先收集并登记全部模块常量到全局常量表
|
||||||
|
preloadGlobals(roots);
|
||||||
|
|
||||||
IRProgram irProgram = new IRProgram();
|
IRProgram irProgram = new IRProgram();
|
||||||
for (Node node : roots) {
|
for (Node node : roots) {
|
||||||
switch (node) {
|
switch (node) {
|
||||||
case ModuleNode moduleNode -> {
|
case ModuleNode moduleNode -> {
|
||||||
// 处理模块节点:遍历其中所有函数,统一用“模块名.函数名”作为全限定名
|
// 处理模块节点:遍历其中所有函数,统一用“模块名.函数名”作为全限定名,避免命名冲突
|
||||||
for (FunctionNode f : moduleNode.functions()) {
|
for (FunctionNode f : moduleNode.functions()) {
|
||||||
irProgram.add(buildFunctionWithGlobals(moduleNode, f));
|
irProgram.add(buildFunctionWithGlobals(moduleNode, f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case FunctionNode functionNode ->
|
case FunctionNode functionNode ->
|
||||||
// 处理顶层函数节点:直接构建为 IRFunction 并加入
|
// 处理顶层函数节点:直接构建为 IRFunction 并加入
|
||||||
irProgram.add(buildFunction(functionNode));
|
irProgram.add(buildFunction(functionNode));
|
||||||
case StatementNode statementNode ->
|
case StatementNode statementNode ->
|
||||||
// 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
|
// 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
|
||||||
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
|
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
|
||||||
default ->
|
default ->
|
||||||
// 遇到未知类型节点,抛出异常
|
// 遇到未知类型节点,抛出异常
|
||||||
@ -54,25 +59,111 @@ public final class IRProgramBuilder {
|
|||||||
return irProgram;
|
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 moduleNode 所属模块节点
|
||||||
* @param functionNode 模块中的函数节点
|
* @param functionNode 待构建的函数节点
|
||||||
* @return 包含全局声明、已加前缀函数名的 IRFunction
|
* @return 包含全局声明的 IRFunction
|
||||||
*/
|
*/
|
||||||
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
|
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
|
||||||
// 拼接模块名和函数名,生成全限定名
|
// 拼接模块名和函数名,生成全限定名
|
||||||
String qualifiedName = moduleNode.name() + "." + functionNode.name();
|
String qualifiedName = moduleNode.name() + "." + functionNode.name();
|
||||||
// 若无全局声明,仅重命名后直接构建
|
|
||||||
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
|
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
|
||||||
|
// 无全局声明,直接重命名构建
|
||||||
return buildFunction(renameFunction(functionNode, qualifiedName));
|
return buildFunction(renameFunction(functionNode, qualifiedName));
|
||||||
}
|
}
|
||||||
// 若有全局声明,插入到函数体最前面
|
|
||||||
List<StatementNode> newBody = new ArrayList<>(moduleNode.globals().size() + functionNode.body().size());
|
// ------- 过滤与参数重名的全局声明 -------
|
||||||
newBody.addAll(moduleNode.globals());
|
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);
|
||||||
newBody.addAll(functionNode.body());
|
newBody.addAll(functionNode.body());
|
||||||
FunctionNode wrapped = new FunctionNode(
|
FunctionNode wrapped = new FunctionNode(
|
||||||
qualifiedName,
|
qualifiedName,
|
||||||
@ -85,11 +176,11 @@ public final class IRProgramBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成一个重命名的 FunctionNode(只修改函数名,其他属性保持不变)。
|
* 生成一个重命名的 FunctionNode(只修改函数名,其他属性保持不变)。
|
||||||
*
|
*
|
||||||
* @param fn 原始函数节点
|
* @param fn 原始函数节点
|
||||||
* @param newName 新的函数名(通常为全限定名)
|
* @param newName 新的函数名(全限定名)
|
||||||
* @return 重命名后的 FunctionNode
|
* @return 重命名后的函数节点
|
||||||
*/
|
*/
|
||||||
private FunctionNode renameFunction(FunctionNode fn, String newName) {
|
private FunctionNode renameFunction(FunctionNode fn, String newName) {
|
||||||
return new FunctionNode(
|
return new FunctionNode(
|
||||||
@ -104,8 +195,8 @@ public final class IRProgramBuilder {
|
|||||||
/**
|
/**
|
||||||
* 构建 IRFunction。
|
* 构建 IRFunction。
|
||||||
*
|
*
|
||||||
* @param functionNode 要转换的函数节点
|
* @param functionNode 待构建的 FunctionNode
|
||||||
* @return 转换结果 IRFunction
|
* @return 构建后的 IRFunction
|
||||||
*/
|
*/
|
||||||
private IRFunction buildFunction(FunctionNode functionNode) {
|
private IRFunction buildFunction(FunctionNode functionNode) {
|
||||||
return new FunctionBuilder().build(functionNode);
|
return new FunctionBuilder().build(functionNode);
|
||||||
@ -113,11 +204,10 @@ public final class IRProgramBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将顶层语句节点封装成特殊的 "_start" 函数。
|
* 将顶层语句节点封装成特殊的 "_start" 函数。
|
||||||
* <p>
|
* 主要用于脚本模式支持,使得顶层语句也可以被 IR 执行引擎统一处理。
|
||||||
* 这对于脚本式文件支持至关重要(即文件最外层直接写语句)。
|
|
||||||
*
|
*
|
||||||
* @param stmt 要封装的顶层语句
|
* @param stmt 顶层语句节点
|
||||||
* @return 包装成 FunctionNode 的 "_start" 函数
|
* @return 封装后的 FunctionNode
|
||||||
*/
|
*/
|
||||||
private FunctionNode wrapTopLevel(StatementNode stmt) {
|
private FunctionNode wrapTopLevel(StatementNode stmt) {
|
||||||
return new FunctionNode(
|
return new FunctionNode(
|
||||||
|
|||||||
@ -6,23 +6,16 @@ import org.jcnc.snow.compiler.ir.value.IRConstant;
|
|||||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InstructionFactory —— 统一生成并注册 IR 指令的工厂类。
|
* IR 指令统一生成工厂类,负责封装常量加载、二元运算、赋值、控制流等指令生成逻辑。
|
||||||
* <p>
|
* 提高 IR 生成阶段的可维护性与复用性。
|
||||||
* 该类封装了常见的 IR 指令生成方式,包括常量加载、二元运算、赋值、控制流等,
|
|
||||||
* 统一简化指令插入和寄存器分配逻辑,提升 IR 生成阶段的代码可维护性和复用性。
|
|
||||||
* </p>
|
|
||||||
*/
|
*/
|
||||||
public class InstructionFactory {
|
public class InstructionFactory {
|
||||||
|
|
||||||
/* ====================================================================== */
|
|
||||||
/* 常量 / 通用二元运算(新寄存器) */
|
|
||||||
/* ====================================================================== */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载整数常量,将其写入一个新分配的虚拟寄存器,并返回该寄存器。
|
* 加载整数常量,将其写入一个新分配的虚拟寄存器,并返回该寄存器。
|
||||||
*
|
*
|
||||||
* @param ctx 当前 IR 上下文(用于分配寄存器与添加指令)
|
* @param ctx 当前 IR 上下文
|
||||||
* @param value 要加载的整数常量值
|
* @param value 整数常量值
|
||||||
* @return 存储该常量的新虚拟寄存器
|
* @return 存储该常量的新虚拟寄存器
|
||||||
*/
|
*/
|
||||||
public static IRVirtualRegister loadConst(IRContext ctx, int value) {
|
public static IRVirtualRegister loadConst(IRContext ctx, int value) {
|
||||||
@ -31,88 +24,150 @@ public class InstructionFactory {
|
|||||||
return r;
|
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 ctx 当前 IR 上下文
|
||||||
* @param op 运算类型(IROpCode 枚举,如 ADD_I32 等)
|
* @param op 二元运算操作码
|
||||||
* @param a 第一个操作数寄存器
|
* @param a 左操作数寄存器
|
||||||
* @param b 第二个操作数寄存器
|
* @param b 右操作数寄存器
|
||||||
* @return 保存运算结果的新虚拟寄存器
|
* @return 存储结果的新虚拟寄存器
|
||||||
*/
|
*/
|
||||||
public static IRVirtualRegister binOp(IRContext ctx, IROpCode op,
|
public static IRVirtualRegister binOp(IRContext ctx, IROpCode op, IRVirtualRegister a, IRVirtualRegister b) {
|
||||||
IRVirtualRegister a, IRVirtualRegister b) {
|
|
||||||
IRVirtualRegister dest = ctx.newRegister();
|
IRVirtualRegister dest = ctx.newRegister();
|
||||||
ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b));
|
ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b));
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ====================================================================== */
|
|
||||||
/* 直接写入指定寄存器 */
|
|
||||||
/* ====================================================================== */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载整数常量到指定虚拟寄存器。
|
* 加载常量到指定寄存器。
|
||||||
*
|
*
|
||||||
* @param ctx 当前 IR 上下文
|
* @param ctx 当前 IR 上下文
|
||||||
* @param dest 目标寄存器
|
* @param dest 目标虚拟寄存器
|
||||||
* @param value 要加载的整数常量
|
* @param value IR 常量值
|
||||||
*/
|
*/
|
||||||
public static void loadConstInto(IRContext ctx, IRVirtualRegister dest, IRConstant value) {
|
public static void loadConstInto(IRContext ctx, IRVirtualRegister dest, IRConstant value) {
|
||||||
ctx.addInstruction(new LoadConstInstruction(dest, value));
|
ctx.addInstruction(new LoadConstInstruction(dest, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对两个寄存器执行二元运算,将结果写入指定目标寄存器。
|
* 执行二元运算,并将结果写入指定寄存器。
|
||||||
*
|
*
|
||||||
* @param ctx 当前 IR 上下文
|
* @param ctx 当前 IR 上下文
|
||||||
* @param op 运算类型(IROpCode 枚举)
|
* @param op 二元运算操作码
|
||||||
* @param a 第一个操作数寄存器
|
* @param a 左操作数寄存器
|
||||||
* @param b 第二个操作数寄存器
|
* @param b 右操作数寄存器
|
||||||
* @param dest 运算结果目标寄存器
|
* @param dest 目标虚拟寄存器
|
||||||
*/
|
*/
|
||||||
public static void binOpInto(IRContext ctx, IROpCode op,
|
public static void binOpInto(IRContext ctx, IROpCode op, IRVirtualRegister a, IRVirtualRegister b, IRVirtualRegister dest) {
|
||||||
IRVirtualRegister a, IRVirtualRegister b,
|
|
||||||
IRVirtualRegister dest) {
|
|
||||||
ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b));
|
ctx.addInstruction(new BinaryOperationInstruction(op, dest, a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move 指令(src → dest)。若寄存器相同也安全。
|
* 生成“值拷贝”语义(src → dest)。
|
||||||
* <p>
|
* 若类型无法推断,默认采用 int 方案(ADD_I32, src+0)。
|
||||||
* 实现方式: dest = src + 0(即加上常量 0)。
|
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param ctx 当前 IR 上下文
|
* @param ctx 当前 IR 上下文
|
||||||
* @param src 源寄存器
|
* @param src 源寄存器
|
||||||
* @param dest 目标寄存器
|
* @param dest 目标寄存器
|
||||||
*/
|
*/
|
||||||
public static void move(IRContext ctx, IRVirtualRegister src, IRVirtualRegister dest) {
|
public static void move(IRContext ctx, IRVirtualRegister src, IRVirtualRegister dest) {
|
||||||
// 自赋值无需任何操作,避免生成多余的常量 0 寄存器
|
|
||||||
if (src == dest) {
|
if (src == dest) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 回退实现: dest = src + 0
|
String varType = ctx.getVarType(); // 需要 IRContext 提供
|
||||||
IRVirtualRegister zero = loadConst(ctx, 0);
|
char suffix = '\0';
|
||||||
ctx.addInstruction(new BinaryOperationInstruction(IROpCode.ADD_I32, dest, src, zero));
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ====================================================================== */
|
|
||||||
/* 控制流指令 */
|
|
||||||
/* ====================================================================== */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成无条件跳转(JMP)指令,跳转到指定标签。
|
* 生成无条件跳转指令。
|
||||||
*
|
*
|
||||||
* @param ctx 当前 IR 上下文
|
* @param ctx 当前 IR 上下文
|
||||||
* @param label 目标标签名
|
* @param label 跳转目标标签
|
||||||
*/
|
*/
|
||||||
public static void jmp(IRContext ctx, String label) {
|
public static void jmp(IRContext ctx, String label) {
|
||||||
ctx.addInstruction(new IRJumpInstruction(label));
|
ctx.addInstruction(new IRJumpInstruction(label));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在 IR 中插入一个标签(Label)。
|
* 在 IR 流中插入标签。
|
||||||
*
|
*
|
||||||
* @param ctx 当前 IR 上下文
|
* @param ctx 当前 IR 上下文
|
||||||
* @param label 标签名
|
* @param label 标签名
|
||||||
@ -122,22 +177,18 @@ public class InstructionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 比较跳转(如 if a < b goto label),根据条件跳转到目标标签。
|
* 比较两个寄存器,并根据结果跳转到指定标签。
|
||||||
*
|
*
|
||||||
* @param ctx 当前 IR 上下文
|
* @param ctx 当前 IR 上下文
|
||||||
* @param cmp 比较操作码(如 IROpCode.LT_I32 等)
|
* @param cmp 比较操作码
|
||||||
* @param a 第一个操作数寄存器
|
* @param a 左操作数寄存器
|
||||||
* @param b 第二个操作数寄存器
|
* @param b 右操作数寄存器
|
||||||
* @param targetLabel 跳转目标标签
|
* @param targetLabel 跳转目标标签
|
||||||
*/
|
*/
|
||||||
public static void cmpJump(IRContext ctx, IROpCode cmp,
|
public static void cmpJump(IRContext ctx, IROpCode cmp, IRVirtualRegister a, IRVirtualRegister b, String targetLabel) {
|
||||||
IRVirtualRegister a, IRVirtualRegister b,
|
|
||||||
String targetLabel) {
|
|
||||||
ctx.addInstruction(new IRCompareJumpInstruction(cmp, a, b, targetLabel));
|
ctx.addInstruction(new IRCompareJumpInstruction(cmp, a, b, targetLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------- 返回 ---------------- */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成返回指令(带返回值)。
|
* 生成返回指令(带返回值)。
|
||||||
*
|
*
|
||||||
@ -149,7 +200,7 @@ public class InstructionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成无返回值的 return 指令(如 void 函数)。
|
* 生成无返回值(void)返回指令。
|
||||||
*
|
*
|
||||||
* @param ctx 当前 IR 上下文
|
* @param ctx 当前 IR 上下文
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
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,11 +1,9 @@
|
|||||||
package org.jcnc.snow.compiler.lexer.core;
|
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.base.TokenScanner;
|
||||||
import org.jcnc.snow.compiler.lexer.scanners.*;
|
import org.jcnc.snow.compiler.lexer.scanners.*;
|
||||||
import org.jcnc.snow.compiler.lexer.token.Token;
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||||
import org.jcnc.snow.compiler.lexer.utils.TokenPrinter;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -127,8 +125,8 @@ public class LexerEngine {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 目前包含2条规则: <br>
|
* 目前包含2条规则: <br>
|
||||||
* 1. Declare-Ident declare 后必须紧跟合法标识符,并且只能一个<br>
|
* 1. Declare-Ident declare 后必须紧跟合法标识符(或 const + 标识符),并且只能一个<br>
|
||||||
* 2. Double-Ident declare 后若出现第二个 IDENTIFIER 视为多余<br>
|
* 2. Double-Ident declare 后若出现第二个多余的 IDENTIFIER<br>
|
||||||
* <p>发现问题仅写入 {@link #errors},不抛异常。</p>
|
* <p>发现问题仅写入 {@link #errors},不抛异常。</p>
|
||||||
*/
|
*/
|
||||||
private void validateTokens() {
|
private void validateTokens() {
|
||||||
@ -139,17 +137,28 @@ public class LexerEngine {
|
|||||||
if (tok.getType() == TokenType.KEYWORD
|
if (tok.getType() == TokenType.KEYWORD
|
||||||
&& "declare".equalsIgnoreCase(tok.getLexeme())) {
|
&& "declare".equalsIgnoreCase(tok.getLexeme())) {
|
||||||
|
|
||||||
// 第一个非 NEWLINE token
|
// 找 declare 后第一个非 NEWLINE token
|
||||||
Token id1 = findNextNonNewline(i);
|
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
|
||||||
if (id1 == null || id1.getType() != TokenType.IDENTIFIER) {
|
if (id1 == null || id1.getType() != TokenType.IDENTIFIER) {
|
||||||
errors.add(err(
|
errors.add(err(
|
||||||
(id1 == null ? tok : id1),
|
(id1 == null ? (hasConst ? t1 : tok) : id1),
|
||||||
"declare 后必须跟合法标识符 (以字母或 '_' 开头)"
|
"declare 后必须跟合法标识符 (可选 const 关键字)"
|
||||||
));
|
));
|
||||||
continue; // 若首标识符就错,后续检查可略
|
continue; // 若首标识符就错,后续检查可略
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有第二个 IDENTIFIER
|
// 检查是否有第二个多余的 IDENTIFIER
|
||||||
Token id2 = findNextNonNewline(tokens.indexOf(id1));
|
Token id2 = findNextNonNewline(tokens.indexOf(id1));
|
||||||
if (id2 != null && id2.getType() == TokenType.IDENTIFIER) {
|
if (id2 != null && id2.getType() == TokenType.IDENTIFIER) {
|
||||||
errors.add(err(id2, "declare 声明中出现多余的标识符"));
|
errors.add(err(id2, "declare 声明中出现多余的标识符"));
|
||||||
|
|||||||
@ -28,7 +28,7 @@ public class TokenFactory {
|
|||||||
private static final Set<String> KEYWORDS = Set.of
|
private static final Set<String> KEYWORDS = Set.of
|
||||||
("module", "function", "params", "returns", "body", "end",
|
("module", "function", "params", "returns", "body", "end",
|
||||||
"if", "then", "else", "loop", "declare", "return", "import", "init",
|
"if", "then", "else", "loop", "declare", "return", "import", "init",
|
||||||
"cond", "step", "globals", "break", "continue");
|
"cond", "step", "globals", "break", "continue", "const");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内置类型名称集合,如 int、string 等。
|
* 内置类型名称集合,如 int、string 等。
|
||||||
|
|||||||
@ -10,21 +10,26 @@ import java.util.Optional;
|
|||||||
* {@code DeclarationNode} 表示抽象语法树(AST)中的变量声明语句节点。
|
* {@code DeclarationNode} 表示抽象语法树(AST)中的变量声明语句节点。
|
||||||
* <p>
|
* <p>
|
||||||
* 变量声明用于在语法层引入新的标识符及其类型信息,
|
* 变量声明用于在语法层引入新的标识符及其类型信息,
|
||||||
* 通常格式为 {@code type name = initializer;},其中初始化表达式可省略。
|
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class DeclarationNode implements StatementNode {
|
public class DeclarationNode implements StatementNode {
|
||||||
|
|
||||||
/** 声明的变量名称 */
|
/** 声明的变量名称。 */
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
/** 变量的数据类型(如 "int", "string") */
|
/** 变量的数据类型(如 "int", "string")。 */
|
||||||
private final String type;
|
private final String type;
|
||||||
|
|
||||||
/** 可选的初始化表达式 */
|
/** 是否为常量声明(true 表示 const 变量,false 表示普通变量)。 */
|
||||||
|
private final boolean isConst;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选的初始化表达式。
|
||||||
|
* 如果未指定初始化表达式,则为 Optional.empty()。
|
||||||
|
*/
|
||||||
private final Optional<ExpressionNode> initializer;
|
private final Optional<ExpressionNode> initializer;
|
||||||
|
|
||||||
/** 节点上下文信息(包含行号、列号等) */
|
/** 节点上下文信息(如源码中的行号、列号等)。 */
|
||||||
private final NodeContext context;
|
private final NodeContext context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,12 +37,14 @@ public class DeclarationNode implements StatementNode {
|
|||||||
*
|
*
|
||||||
* @param name 变量名称
|
* @param name 变量名称
|
||||||
* @param type 变量类型字符串(如 "int"、"string")
|
* @param type 变量类型字符串(如 "int"、"string")
|
||||||
|
* @param isConst 是否为常量声明
|
||||||
* @param initializer 可选初始化表达式,若为 {@code null} 表示未初始化
|
* @param initializer 可选初始化表达式,若为 {@code null} 表示未初始化
|
||||||
* @param context 节点上下文信息(包含行号、列号等)
|
* @param context 节点上下文信息(包含行号、列号等)
|
||||||
*/
|
*/
|
||||||
public DeclarationNode(String name, String type, ExpressionNode initializer, NodeContext context) {
|
public DeclarationNode(String name, String type, boolean isConst, ExpressionNode initializer, NodeContext context) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.isConst = isConst;
|
||||||
this.initializer = Optional.ofNullable(initializer);
|
this.initializer = Optional.ofNullable(initializer);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
@ -60,6 +67,15 @@ public class DeclarationNode implements StatementNode {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断该声明是否为常量(const)。
|
||||||
|
*
|
||||||
|
* @return 如果为常量声明则返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
public boolean isConst() {
|
||||||
|
return isConst;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取可选的初始化表达式。
|
* 获取可选的初始化表达式。
|
||||||
*
|
*
|
||||||
|
|||||||
@ -34,45 +34,51 @@ public class DeclarationStatementParser implements StatementParser {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public DeclarationNode parse(ParserContext ctx) {
|
public DeclarationNode parse(ParserContext ctx) {
|
||||||
|
// 便捷引用词法 token 流
|
||||||
|
var tokens = ctx.getTokens();
|
||||||
|
|
||||||
// 获取当前 token 的行号、列号和文件名
|
// 获取当前 token 的行号、列号和文件名
|
||||||
int line = ctx.getTokens().peek().getLine();
|
int line = tokens.peek().getLine();
|
||||||
int column = ctx.getTokens().peek().getCol();
|
int column = tokens.peek().getCol();
|
||||||
String file = ctx.getSourceName();
|
String file = ctx.getSourceName();
|
||||||
|
|
||||||
// 声明语句必须以 "declare" 开头
|
// 声明语句必须以 "declare" 开头
|
||||||
ctx.getTokens().expect("declare");
|
tokens.expect("declare");
|
||||||
|
|
||||||
|
// 是否声明为常量
|
||||||
|
boolean isConst = tokens.match("const");
|
||||||
|
|
||||||
// 获取变量名称(标识符)
|
// 获取变量名称(标识符)
|
||||||
String name = ctx.getTokens()
|
String name = tokens
|
||||||
.expectType(TokenType.IDENTIFIER)
|
.expectType(TokenType.IDENTIFIER)
|
||||||
.getLexeme();
|
.getLexeme();
|
||||||
|
|
||||||
// 类型标注的冒号分隔符
|
// 类型标注的冒号分隔符
|
||||||
ctx.getTokens().expect(":");
|
tokens.expect(":");
|
||||||
|
|
||||||
// 获取变量类型(类型标识符)
|
// 获取变量类型(类型标识符)
|
||||||
StringBuilder type = new StringBuilder(
|
StringBuilder type = new StringBuilder(
|
||||||
ctx.getTokens()
|
tokens
|
||||||
.expectType(TokenType.TYPE)
|
.expectType(TokenType.TYPE)
|
||||||
.getLexeme()
|
.getLexeme()
|
||||||
);
|
);
|
||||||
|
|
||||||
// 消费多维数组类型后缀 "[]"
|
// 消费多维数组类型后缀 "[]"
|
||||||
while (ctx.getTokens().match("[")) {
|
while (tokens.match("[")) {
|
||||||
ctx.getTokens().expectType(TokenType.RBRACKET); // 必须配对
|
tokens.expectType(TokenType.RBRACKET); // 必须配对
|
||||||
type.append("[]"); // 类型名称拼接 [],如 int[][] 等
|
type.append("[]"); // 类型名称拼接 [],如 int[][] 等
|
||||||
}
|
}
|
||||||
|
|
||||||
// 可选初始化表达式(=号右侧)
|
// 可选初始化表达式(=号右侧)
|
||||||
ExpressionNode init = null;
|
ExpressionNode init = null;
|
||||||
if (ctx.getTokens().match("=")) {
|
if (tokens.match("=")) {
|
||||||
init = new PrattExpressionParser().parse(ctx);
|
init = new PrattExpressionParser().parse(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 声明语句必须以换行符结尾
|
// 声明语句必须以换行符结尾
|
||||||
ctx.getTokens().expectType(TokenType.NEWLINE);
|
tokens.expectType(TokenType.NEWLINE);
|
||||||
|
|
||||||
// 返回构建好的声明语法树节点
|
// 返回构建好的声明语法树节点
|
||||||
return new DeclarationNode(name, type.toString(), init, new NodeContext(line, column, file));
|
return new DeclarationNode(name, type.toString(), isConst, init, new NodeContext(line, column, file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,94 @@
|
|||||||
|
package org.jcnc.snow.compiler.semantic.analyzers.expression;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.MemberExpressionNode;
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
|
||||||
|
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
|
||||||
|
import org.jcnc.snow.compiler.semantic.core.Context;
|
||||||
|
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
|
||||||
|
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||||
|
import org.jcnc.snow.compiler.semantic.symbol.Symbol;
|
||||||
|
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||||
|
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||||
|
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code MemberExpressionAnalyzer} 用于分析模块成员访问表达式的类型和语义。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 当前实现支持 <code>ModuleName.constOrVar</code> 形式的跨模块常量/全局变量访问,
|
||||||
|
* 能根据目标模块的全局符号表,返回准确的类型信息,完全支持跨模块类型推断。
|
||||||
|
* <br>
|
||||||
|
* 对于非模块成员的访问(如对象.属性、多级 a.b.c),暂不支持,遇到时将报告语义错误。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>核心特性:</b>
|
||||||
|
* <ul>
|
||||||
|
* <li>校验模块是否存在、是否已导入(或自身);</li>
|
||||||
|
* <li>跨模块访问目标模块的全局符号表,查找指定成员符号及其类型;</li>
|
||||||
|
* <li>若成员不存在,报告“模块成员未定义”语义错误;</li>
|
||||||
|
* <li>暂不支持更复杂的对象成员访问,遇到将报“不支持的成员访问对象类型”错误。</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class MemberExpressionAnalyzer implements ExpressionAnalyzer<MemberExpressionNode> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语义分析模块成员访问表达式。
|
||||||
|
*
|
||||||
|
* @param ctx 全局语义分析上下文,持有所有模块及错误记录
|
||||||
|
* @param mi 当前模块信息(用于判断导入关系)
|
||||||
|
* @param fn 当前函数节点
|
||||||
|
* @param locals 当前局部符号表
|
||||||
|
* @param expr 当前要分析的成员表达式(如 ModuleA.a)
|
||||||
|
* @return 成员表达式的类型;出错时类型降级为 int,并记录语义错误
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Type analyze(Context ctx,
|
||||||
|
ModuleInfo mi,
|
||||||
|
FunctionNode fn,
|
||||||
|
SymbolTable locals,
|
||||||
|
MemberExpressionNode expr) {
|
||||||
|
|
||||||
|
ctx.log("检查成员访问: " + expr);
|
||||||
|
|
||||||
|
// -------- 仅支持 ModuleName.member 形式 --------
|
||||||
|
if (expr.object() instanceof IdentifierNode(String mod, NodeContext _)) {
|
||||||
|
|
||||||
|
// 1. 校验模块存在且已导入或为本模块自身
|
||||||
|
if (!ctx.getModules().containsKey(mod)
|
||||||
|
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) {
|
||||||
|
ctx.getErrors().add(new SemanticError(expr,
|
||||||
|
"未知或未导入模块: " + mod));
|
||||||
|
ctx.log("错误: 未导入模块 " + mod);
|
||||||
|
return BuiltinType.INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查找目标模块的全局符号表,精确返回成员类型
|
||||||
|
ModuleInfo target = ctx.getModules().get(mod);
|
||||||
|
SymbolTable globals = target.getGlobals();
|
||||||
|
if (globals != null) {
|
||||||
|
Symbol sym = globals.resolve(expr.member());
|
||||||
|
if (sym != null) {
|
||||||
|
return sym.type(); // 找到成员,返回其真实类型
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 成员不存在,记录语义错误并类型降级
|
||||||
|
ctx.getErrors().add(new SemanticError(expr,
|
||||||
|
"模块成员未定义: " + mod + "." + expr.member()));
|
||||||
|
ctx.log("错误: 模块成员未定义 " + mod + "." + expr.member());
|
||||||
|
return BuiltinType.INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- 其它对象成员(如 a.b.c)暂不支持 --------
|
||||||
|
ctx.getErrors().add(new SemanticError(expr,
|
||||||
|
"不支持的成员访问对象类型: "
|
||||||
|
+ expr.object().getClass().getSimpleName()));
|
||||||
|
ctx.log("错误: 不支持的成员访问对象类型 "
|
||||||
|
+ expr.object().getClass().getSimpleName());
|
||||||
|
return BuiltinType.INT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,22 +6,28 @@ import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
|
|||||||
import org.jcnc.snow.compiler.semantic.core.Context;
|
import org.jcnc.snow.compiler.semantic.core.Context;
|
||||||
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
|
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
|
||||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||||
import org.jcnc.snow.compiler.semantic.symbol.*;
|
import org.jcnc.snow.compiler.semantic.symbol.Symbol;
|
||||||
|
import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
|
||||||
|
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code AssignmentAnalyzer} 是赋值语句的语义分析器。
|
* {@code AssignmentAnalyzer} 负责对赋值语句进行语义校验。
|
||||||
* <p>
|
*
|
||||||
* 负责分析和验证赋值语句的合法性,包括:
|
* <h2>校验要点</h2>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>变量是否已声明且可赋值(必须为 {@link SymbolKind#VARIABLE} 类型);</li>
|
* <li><b>左值解析</b>:确保标识符已声明;</li>
|
||||||
* <li>赋值右值的类型是否与变量类型兼容;</li>
|
* <li><b>常量保护</b>:禁止修改 {@link SymbolKind#CONSTANT};</li>
|
||||||
* <li>是否允许进行数值类型的自动宽化转换(如 {@code int → float})。</li>
|
* <li><b>类型兼容</b>:验证右值类型与目标类型兼容,若均为数值类型则允许宽化转换(如 <code>int → double</code>)。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* 若类型不兼容且无法自动宽化,则将记录语义错误并输出日志信息。
|
*
|
||||||
|
* 任何不满足条件的情况都会向 {@link Context#getErrors()} 记录 {@link SemanticError}。
|
||||||
*/
|
*/
|
||||||
public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> {
|
public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> {
|
||||||
|
|
||||||
|
/** 错误消息前缀,统一便于搜索定位 */
|
||||||
|
private static final String ERR_PREFIX = "赋值错误: ";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分析赋值语句的语义有效性,包括左值合法性与类型匹配性。
|
* 分析赋值语句的语义有效性,包括左值合法性与类型匹配性。
|
||||||
*
|
*
|
||||||
@ -41,29 +47,37 @@ public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> {
|
|||||||
// 获取赋值左值变量名并进行符号解析
|
// 获取赋值左值变量名并进行符号解析
|
||||||
ctx.log("赋值检查: " + asg.variable());
|
ctx.log("赋值检查: " + asg.variable());
|
||||||
Symbol sym = locals.resolve(asg.variable());
|
Symbol sym = locals.resolve(asg.variable());
|
||||||
|
if (sym == null) {
|
||||||
// 检查变量是否已声明且为可赋值的变量类型
|
|
||||||
if (sym == null || sym.kind() != SymbolKind.VARIABLE) {
|
|
||||||
ctx.getErrors().add(new SemanticError(asg,
|
ctx.getErrors().add(new SemanticError(asg,
|
||||||
"未声明的变量: " + asg.variable()));
|
ERR_PREFIX + "未声明的变量: " + asg.variable()));
|
||||||
ctx.log("错误: 未声明的变量 " + asg.variable());
|
ctx.log(ERR_PREFIX + "未声明的变量 " + asg.variable());
|
||||||
|
return; // 无需继续后续检查
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 2. 常量不可修改 ---------- */
|
||||||
|
if (sym.kind() == SymbolKind.CONSTANT) {
|
||||||
|
ctx.getErrors().add(new SemanticError(asg,
|
||||||
|
ERR_PREFIX + "无法修改常量: " + asg.variable()));
|
||||||
|
ctx.log(ERR_PREFIX + "尝试修改常量 " + asg.variable());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分析右值表达式类型
|
/* ---------- 3. 右值类型分析 ---------- */
|
||||||
Type valType = ctx.getRegistry().getExpressionAnalyzer(asg.value())
|
Type rhsType = ctx.getRegistry()
|
||||||
|
.getExpressionAnalyzer(asg.value())
|
||||||
.analyze(ctx, mi, fn, locals, asg.value());
|
.analyze(ctx, mi, fn, locals, asg.value());
|
||||||
|
|
||||||
// 类型检查: 若类型不兼容,则尝试判断是否允许宽化转换
|
/* ---------- 4. 类型兼容性检查 ---------- */
|
||||||
if (!sym.type().isCompatible(valType)) {
|
boolean compatible = sym.type().isCompatible(rhsType);
|
||||||
// 数值类型允许自动宽化转换(如 int → double)
|
boolean widenOK = sym.type().isNumeric()
|
||||||
if (!(sym.type().isNumeric() && valType.isNumeric()
|
&& rhsType.isNumeric()
|
||||||
&& Type.widen(valType, sym.type()) == sym.type())) {
|
&& Type.widen(rhsType, sym.type()) == sym.type();
|
||||||
|
|
||||||
|
if (!compatible && !widenOK) {
|
||||||
ctx.getErrors().add(new SemanticError(asg,
|
ctx.getErrors().add(new SemanticError(asg,
|
||||||
"赋值类型不匹配: 期望 " + sym.type()
|
ERR_PREFIX + "类型不匹配: 期望 "
|
||||||
+ ", 实际 " + valType));
|
+ sym.type() + ", 实际 " + rhsType));
|
||||||
ctx.log("错误: 赋值类型不匹配 " + asg.variable());
|
ctx.log(ERR_PREFIX + "类型不匹配 " + asg.variable());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
|
|||||||
import org.jcnc.snow.compiler.semantic.core.Context;
|
import org.jcnc.snow.compiler.semantic.core.Context;
|
||||||
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
|
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
|
||||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||||
import org.jcnc.snow.compiler.semantic.symbol.*;
|
import org.jcnc.snow.compiler.semantic.symbol.Symbol;
|
||||||
|
import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
|
||||||
|
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||||
|
|
||||||
@ -25,7 +27,7 @@ import org.jcnc.snow.compiler.semantic.type.Type;
|
|||||||
public class DeclarationAnalyzer implements StatementAnalyzer<DeclarationNode> {
|
public class DeclarationAnalyzer implements StatementAnalyzer<DeclarationNode> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对变量声明语句执行语义分析。
|
* 对单条声明语句执行语义分析。
|
||||||
*
|
*
|
||||||
* @param ctx 当前语义分析上下文对象,提供类型解析、错误记录、日志输出等功能。
|
* @param ctx 当前语义分析上下文对象,提供类型解析、错误记录、日志输出等功能。
|
||||||
* @param mi 当前模块信息,支持跨模块引用检查(本分析器未直接使用)。
|
* @param mi 当前模块信息,支持跨模块引用检查(本分析器未直接使用)。
|
||||||
@ -40,45 +42,53 @@ public class DeclarationAnalyzer implements StatementAnalyzer<DeclarationNode> {
|
|||||||
SymbolTable locals,
|
SymbolTable locals,
|
||||||
DeclarationNode decl) {
|
DeclarationNode decl) {
|
||||||
|
|
||||||
// 1. 解析声明类型
|
/* ---------- 1. 解析类型 ---------- */
|
||||||
Type varType = ctx.parseType(decl.getType());
|
Type varType = ctx.parseType(decl.getType());
|
||||||
if (varType == null) {
|
if (varType == null) {
|
||||||
|
// 未知类型:记录错误并使用 int 兜底,避免后续空指针
|
||||||
ctx.getErrors().add(new SemanticError(decl,
|
ctx.getErrors().add(new SemanticError(decl,
|
||||||
"未知类型: " + decl.getType()));
|
"未知类型: " + decl.getType()));
|
||||||
ctx.log("错误: 未知类型 " + decl.getType()
|
ctx.log("错误: 未知类型 " + decl.getType()
|
||||||
+ " 在声明 " + decl.getName());
|
+ " (声明 " + decl.getName() + ")");
|
||||||
varType = BuiltinType.INT; // 容错处理: 默认降级为 int
|
varType = BuiltinType.INT;
|
||||||
}
|
}
|
||||||
ctx.log("声明变量: " + decl.getName()
|
ctx.log("声明" + (decl.isConst() ? "常量" : "变量")
|
||||||
+ " 类型: " + varType);
|
+ ": " + decl.getName() + " 类型: " + varType);
|
||||||
|
|
||||||
// 2. 将变量注册到当前作用域符号表中,检查重复定义
|
/* ---------- 2. 常量必须初始化 ---------- */
|
||||||
if (!locals.define(new Symbol(
|
if (decl.isConst() && decl.getInitializer().isEmpty()) {
|
||||||
decl.getName(), varType, SymbolKind.VARIABLE
|
|
||||||
))) {
|
|
||||||
ctx.getErrors().add(new SemanticError(decl,
|
ctx.getErrors().add(new SemanticError(decl,
|
||||||
"变量重复声明: " + decl.getName()));
|
"常量必须在声明时初始化: " + decl.getName()));
|
||||||
ctx.log("错误: 变量重复声明 " + decl.getName());
|
// 继续分析以捕获更多错误
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 检查初始化表达式(如果存在)
|
/* ---------- 3. 注册符号并检测重名 ---------- */
|
||||||
Type finalVarType = varType; // 用于 lambda 捕获
|
SymbolKind kind = decl.isConst() ? SymbolKind.CONSTANT
|
||||||
decl.getInitializer().ifPresent(init -> {
|
: SymbolKind.VARIABLE;
|
||||||
Type initType = ctx.getRegistry().getExpressionAnalyzer(init)
|
if (!locals.define(new Symbol(decl.getName(), varType, kind))) {
|
||||||
.analyze(ctx, mi, fn, locals, init);
|
ctx.getErrors().add(new SemanticError(decl,
|
||||||
|
"重复声明: " + decl.getName()));
|
||||||
|
ctx.log("错误: 重复声明 " + decl.getName());
|
||||||
|
}
|
||||||
|
|
||||||
// 检查类型是否兼容,或是否允许数值宽化转换
|
/* ---------- 4. 校验初始化表达式(若存在) ---------- */
|
||||||
if (!finalVarType.isCompatible(initType)) {
|
Type finalVarType = varType;
|
||||||
boolean canWiden = finalVarType.isNumeric()
|
decl.getInitializer().ifPresent(initExpr -> {
|
||||||
|
// 4.1 获取初始化表达式类型
|
||||||
|
Type initType = ctx.getRegistry()
|
||||||
|
.getExpressionAnalyzer(initExpr)
|
||||||
|
.analyze(ctx, mi, fn, locals, initExpr);
|
||||||
|
|
||||||
|
// 4.2 类型兼容性检查 + 数值宽化
|
||||||
|
boolean compatible = finalVarType.isCompatible(initType);
|
||||||
|
boolean widenOK = finalVarType.isNumeric()
|
||||||
&& initType.isNumeric()
|
&& initType.isNumeric()
|
||||||
&& Type.widen(initType, finalVarType) == finalVarType;
|
&& Type.widen(initType, finalVarType) == finalVarType;
|
||||||
if (!canWiden) {
|
|
||||||
|
if (!compatible && !widenOK) {
|
||||||
ctx.getErrors().add(new SemanticError(decl,
|
ctx.getErrors().add(new SemanticError(decl,
|
||||||
"初始化类型不匹配: 期望 " + finalVarType
|
"初始化类型不匹配: 期望 " + finalVarType + ", 实际 " + initType));
|
||||||
+ ", 实际 " + initType));
|
ctx.log("错误: 初始化类型不匹配 " + decl.getName());
|
||||||
ctx.log("错误: 初始化类型不匹配 "
|
|
||||||
+ decl.getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,8 +65,8 @@ public final class AnalyzerRegistrar {
|
|||||||
// ---------- 注册一元表达式分析器 ----------
|
// ---------- 注册一元表达式分析器 ----------
|
||||||
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());
|
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());
|
||||||
|
|
||||||
// 对尚未实现的表达式类型使用兜底处理器
|
// ---------- 成员访问表达式 ----------
|
||||||
registry.registerExpressionAnalyzer(MemberExpressionNode.class,
|
registry.registerExpressionAnalyzer(MemberExpressionNode.class,
|
||||||
new UnsupportedExpressionAnalyzer<>());
|
new MemberExpressionAnalyzer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
package org.jcnc.snow.compiler.semantic.core;
|
package org.jcnc.snow.compiler.semantic.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
|
||||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||||
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
|
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
|
||||||
import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
|
|
||||||
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
|
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
|
||||||
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
|
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
|
||||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||||
@ -11,70 +11,85 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
|
|||||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code FunctionChecker} 是语义分析阶段中用于检查函数体语句合法性的调度器。
|
* {@code FunctionChecker} 是 Snow 编译器语义分析阶段用于检查所有函数体合法性的总控调度器。
|
||||||
* <p>
|
* <p>
|
||||||
* 它逐个遍历所有模块中的函数定义,并对函数体中的每一条语句调用对应的语义分析器,
|
* <b>设计核心:</b>采用“两遍扫描”方案,彻底解决跨模块全局变量/常量类型推断和引用依赖问题:
|
||||||
* 执行类型检查、作用域验证、错误记录等任务。
|
|
||||||
* <p>
|
|
||||||
* 核心职责包括:
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>为每个函数构建局部符号表并注册函数参数为变量;</li>
|
* <li><b>第一遍</b>:为所有模块预先构建并注册其全局符号表(globals),保证跨模块引用时可见。</li>
|
||||||
* <li>分发函数体语句至相应的 {@link StatementAnalyzer};</li>
|
* <li><b>第二遍</b>:在全局符号表全部就绪后,依次分析所有模块的函数体,实现局部作用域、类型推断、语义校验等任务。</li>
|
||||||
* <li>记录未支持语句类型为语义错误;</li>
|
* </ul>
|
||||||
* <li>依赖上下文 {@link Context} 提供模块信息、类型解析、错误收集等服务。</li>
|
* <b>功能职责:</b>
|
||||||
|
* <ul>
|
||||||
|
* <li>遍历所有模块,先建立 globals,再遍历并检查所有函数体语句。</li>
|
||||||
|
* <li>为每个函数体构建完整符号表,并注册参数变量。</li>
|
||||||
|
* <li>分发每条语句到对应 {@link StatementAnalyzer} 进行类型检查和错误校验。</li>
|
||||||
|
* <li>自动检查非 void 函数 return 完备性。</li>
|
||||||
|
* <li>记录所有语义错误,便于前端高亮和诊断。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param ctx 全局语义分析上下文,提供模块信息、注册表、错误记录等支持
|
* @param ctx 全局语义分析上下文,持有模块信息、符号表、错误收集等资源
|
||||||
*/
|
*/
|
||||||
public record FunctionChecker(Context ctx) {
|
public record FunctionChecker(Context ctx) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造函数体检查器。
|
* 主入口:对所有模块的所有函数体进行语义检查(两遍扫描实现)。
|
||||||
*
|
|
||||||
* @param ctx 当前语义分析上下文
|
|
||||||
*/
|
|
||||||
public FunctionChecker {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行函数体检查流程。
|
|
||||||
* <p>
|
* <p>
|
||||||
* 对所有模块中的所有函数依次进行处理:
|
* <b>第一遍</b>:为每个模块提前构建全局符号表(包含本模块所有全局变量和常量),
|
||||||
* <ol>
|
* 并注册到 {@link ModuleInfo},确保跨模块引用时所有全局符号都已可用。
|
||||||
* <li>查找模块对应的 {@link ModuleInfo};</li>
|
* <br>
|
||||||
* <li>创建函数局部符号表 {@link SymbolTable},并注册所有参数变量;</li>
|
* <b>第二遍</b>:遍历所有模块的所有函数,对每个函数体:
|
||||||
* <li>对函数体中的每一条语句分发到已注册的分析器进行语义分析;</li>
|
* <ul>
|
||||||
* <li>若某条语句无可用分析器,则记录为 {@link SemanticError}。</li>
|
* <li>构建局部作用域,父作用域为对应模块的 globals;</li>
|
||||||
* </ol>
|
* <li>注册参数变量;</li>
|
||||||
|
* <li>依次分发每条语句到对应 {@link StatementAnalyzer},进行类型和语义检查;</li>
|
||||||
|
* <li>自动校验非 void 函数 return 完备性;</li>
|
||||||
|
* <li>将所有发现的问题统一记录到 {@link SemanticError} 列表。</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param mods 所有模块的 AST 根节点集合
|
* @param mods 所有模块的 AST 根节点集合
|
||||||
*/
|
*/
|
||||||
public void check(Iterable<ModuleNode> mods) {
|
public void check(Iterable<ModuleNode> mods) {
|
||||||
|
List<ModuleNode> moduleList = new ArrayList<>();
|
||||||
|
// ---------- 第1遍:收集所有全局符号表 ----------
|
||||||
for (ModuleNode mod : mods) {
|
for (ModuleNode mod : mods) {
|
||||||
// 获取当前模块对应的语义信息
|
moduleList.add(mod);
|
||||||
ModuleInfo mi = ctx.modules().get(mod.name());
|
|
||||||
|
|
||||||
// 先构建全局符号表
|
// 获取当前模块的元信息
|
||||||
|
ModuleInfo mi = ctx.modules().get(mod.name());
|
||||||
|
// 创建本模块全局作用域(无父作用域)
|
||||||
SymbolTable globalScope = new SymbolTable(null);
|
SymbolTable globalScope = new SymbolTable(null);
|
||||||
|
|
||||||
|
// 注册所有全局变量/常量到符号表
|
||||||
for (DeclarationNode g : mod.globals()) {
|
for (DeclarationNode g : mod.globals()) {
|
||||||
var t = ctx.parseType(g.getType());
|
var t = ctx.parseType(g.getType());
|
||||||
// 检查全局变量是否重复声明
|
SymbolKind k = g.isConst() ? SymbolKind.CONSTANT : SymbolKind.VARIABLE;
|
||||||
if (!globalScope.define(new Symbol(g.getName(), t, SymbolKind.VARIABLE))) {
|
String dupType = g.isConst() ? "常量" : "变量";
|
||||||
|
// 检查重复声明
|
||||||
|
if (!globalScope.define(new Symbol(g.getName(), t, k))) {
|
||||||
ctx.errors().add(new SemanticError(
|
ctx.errors().add(new SemanticError(
|
||||||
g,
|
g,
|
||||||
"全局变量重复声明: " + g.getName()
|
dupType + "重复声明: " + g.getName()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 注册到模块信息,供跨模块引用
|
||||||
|
mi.setGlobals(globalScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 第2遍:遍历所有函数,分析函数体 ----------
|
||||||
|
for (ModuleNode mod : moduleList) {
|
||||||
|
ModuleInfo mi = ctx.modules().get(mod.name());
|
||||||
|
SymbolTable globalScope = mi.getGlobals();
|
||||||
|
|
||||||
// 遍历模块中所有函数定义
|
|
||||||
for (FunctionNode fn : mod.functions()) {
|
for (FunctionNode fn : mod.functions()) {
|
||||||
|
// 构建函数局部作用域,父作用域为 globalScope
|
||||||
// 构建函数局部作用域符号表,父作用域为 globalScope
|
|
||||||
SymbolTable locals = new SymbolTable(globalScope);
|
SymbolTable locals = new SymbolTable(globalScope);
|
||||||
|
|
||||||
// 将函数参数注册为局部变量
|
// 注册函数参数为局部变量
|
||||||
fn.parameters().forEach(p ->
|
fn.parameters().forEach(p ->
|
||||||
locals.define(new Symbol(
|
locals.define(new Symbol(
|
||||||
p.name(),
|
p.name(),
|
||||||
@ -83,7 +98,7 @@ public record FunctionChecker(Context ctx) {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
// 遍历并分析函数体内的每条语句
|
// 分析函数体内每条语句
|
||||||
for (var stmt : fn.body()) {
|
for (var stmt : fn.body()) {
|
||||||
var analyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
|
var analyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
|
||||||
if (analyzer != null) {
|
if (analyzer != null) {
|
||||||
|
|||||||
@ -1,33 +1,55 @@
|
|||||||
package org.jcnc.snow.compiler.semantic.core;
|
package org.jcnc.snow.compiler.semantic.core;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||||
import org.jcnc.snow.compiler.semantic.type.FunctionType;
|
import org.jcnc.snow.compiler.semantic.type.FunctionType;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code ModuleInfo} 表示单个模块在语义分析阶段的元信息封装。
|
* {@code ModuleInfo} 表示单个模块在语义分析阶段的元信息封装。
|
||||||
* <p>
|
* <p>
|
||||||
* 用于在分析期间管理模块间依赖、函数签名查找等关键任务。
|
* 用于在分析期间管理模块间依赖、函数签名查找、全局符号表等关键任务。
|
||||||
* 每个模块对应一个唯一的 {@code ModuleInfo} 实例。
|
* 每个模块对应一个唯一的 {@code ModuleInfo} 实例,贯穿整个语义分析流程。
|
||||||
* <p>
|
*
|
||||||
* 包含信息包括:
|
* <p><b>包含信息:</b>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>模块名称(唯一标识);</li>
|
* <li>模块名称(全局唯一标识);</li>
|
||||||
* <li>该模块导入的其他模块名集合;</li>
|
* <li>该模块导入的其他模块名集合(跨模块引用支持);</li>
|
||||||
* <li>该模块中定义的所有函数签名 {@code Map<String, FunctionType>}。</li>
|
* <li>该模块中定义的所有函数签名 {@code Map<String, FunctionType>};</li>
|
||||||
|
* <li>模块级全局符号表 {@link SymbolTable}(常量 / 全局变量,支持跨模块类型推断)。</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p><b>典型用途:</b>
|
||||||
|
* <ul>
|
||||||
|
* <li>用于函数签名类型查找、重名检测、跨模块引用校验等;</li>
|
||||||
|
* <li>全局符号表为类型检查与后端 IR 常量折叠等模块级分析提供支撑。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class ModuleInfo {
|
public class ModuleInfo {
|
||||||
|
|
||||||
/** 模块名称,作为全局唯一标识 */
|
/**
|
||||||
|
* 模块名称,作为全局唯一标识
|
||||||
|
*/
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
/** 该模块显式导入的模块名集合(用于跨模块访问符号) */
|
/**
|
||||||
|
* 该模块显式导入的模块名集合(用于跨模块访问符号)
|
||||||
|
*/
|
||||||
private final Set<String> imports = new HashSet<>();
|
private final Set<String> imports = new HashSet<>();
|
||||||
|
|
||||||
/** 该模块中定义的函数名 → 函数类型映射 */
|
/**
|
||||||
|
* 该模块中定义的函数名 → 函数类型映射
|
||||||
|
*/
|
||||||
private final Map<String, FunctionType> functions = new HashMap<>();
|
private final Map<String, FunctionType> functions = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块级全局符号表(常量 / 全局变量)
|
||||||
|
*/
|
||||||
|
private SymbolTable globals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造模块信息对象。
|
* 构造模块信息对象。
|
||||||
*
|
*
|
||||||
@ -49,7 +71,7 @@ public class ModuleInfo {
|
|||||||
/**
|
/**
|
||||||
* 获取该模块导入的模块名称集合。
|
* 获取该模块导入的模块名称集合。
|
||||||
* <p>
|
* <p>
|
||||||
* 返回集合为内部数据的直接引用,调用方可通过 {@code add/remove} 方法动态维护导入信息。
|
* 返回集合为内部数据的直接引用,调用方可通过 {@code add}/{@code remove} 方法动态维护导入信息。
|
||||||
*
|
*
|
||||||
* @return 可变集合,包含所有导入模块名
|
* @return 可变集合,包含所有导入模块名
|
||||||
*/
|
*/
|
||||||
@ -69,4 +91,26 @@ public class ModuleInfo {
|
|||||||
return functions;
|
return functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取模块的全局符号表(包含常量与全局变量)。
|
||||||
|
* <p>
|
||||||
|
* 该符号表由语义分析的 FunctionChecker 阶段构建完成并注入。
|
||||||
|
* 提供跨模块类型检查、常量折叠等能力。
|
||||||
|
*
|
||||||
|
* @return 当前模块的全局符号表
|
||||||
|
*/
|
||||||
|
public SymbolTable getGlobals() {
|
||||||
|
return globals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置模块的全局符号表。
|
||||||
|
* <p>
|
||||||
|
* 仅应由 FunctionChecker 在语义分析全局扫描阶段调用。
|
||||||
|
*
|
||||||
|
* @param globals 全局符号表实例
|
||||||
|
*/
|
||||||
|
public void setGlobals(SymbolTable globals) {
|
||||||
|
this.globals = globals;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,31 +3,47 @@ package org.jcnc.snow.compiler.semantic.symbol;
|
|||||||
/**
|
/**
|
||||||
* {@code SymbolKind} 枚举用于标识符号表中不同类型的命名实体。
|
* {@code SymbolKind} 枚举用于标识符号表中不同类型的命名实体。
|
||||||
* <p>
|
* <p>
|
||||||
* 在语义分析过程中,编译器需要根据符号的种类(Kind)采用不同的处理策略:
|
* 在语义分析过程中,编译器需要根据符号的种类(Kind)采用不同的处理策略:
|
||||||
* 例如变量参与类型推导、函数用于调用匹配、模块用于跨作用域引用等。
|
* 例如变量参与类型推导、函数用于调用匹配、模块用于跨作用域引用等。
|
||||||
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* 当前支持的符号种类包括:
|
* 当前支持的符号种类包括:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link #VARIABLE}: 变量符号(局部变量、全局变量、成员变量等);</li>
|
* <li>{@link #VARIABLE}: 变量符号(局部变量、全局变量、成员变量等);</li>
|
||||||
|
* <li>{@link #CONSTANT}: 常量符号(只读、不可变的公开常量);</li>
|
||||||
* <li>{@link #FUNCTION}: 函数符号(自由函数、方法、构造函数等);</li>
|
* <li>{@link #FUNCTION}: 函数符号(自由函数、方法、构造函数等);</li>
|
||||||
* <li>{@link #MODULE}: 模块符号(代表命名空间、库或逻辑模块);</li>
|
* <li>{@link #MODULE}: 模块符号(代表命名空间、库或逻辑模块);</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* 可根据需求扩展更多符号种类。
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public enum SymbolKind {
|
public enum SymbolKind {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 变量符号,表示在作用域中声明的可赋值实体。
|
* 变量符号,表示在作用域中声明的可赋值实体。
|
||||||
* <p>
|
* <p>
|
||||||
* 包括函数参数、局部变量、全局变量、常量等,
|
* 包括函数参数、局部变量、全局变量、成员变量等。
|
||||||
* 分析器会基于其类型参与表达式类型校验和赋值检查。
|
* 分析器会基于其类型参与表达式类型校验和赋值检查。
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
VARIABLE,
|
VARIABLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 常量符号,表示只读、不可变的公开常量。
|
||||||
|
* <p>
|
||||||
|
* 常量在声明后不可被修改,仅可读取。常用于定义全局、模块级的常量值,
|
||||||
|
* 在类型推断、常量折叠等语义分析过程中单独处理。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
CONSTANT,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 函数符号,表示可调用的过程实体。
|
* 函数符号,表示可调用的过程实体。
|
||||||
* <p>
|
* <p>
|
||||||
* 包括普通函数、方法、构造器等。
|
* 包括普通函数、方法、构造器等。
|
||||||
* 用于函数签名注册、函数调用检查及返回值推导。
|
* 用于函数签名注册、函数调用检查及返回值推导。
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
FUNCTION,
|
FUNCTION,
|
||||||
|
|
||||||
@ -35,6 +51,7 @@ public enum SymbolKind {
|
|||||||
* 模块符号,表示一个命名空间或模块单元。
|
* 模块符号,表示一个命名空间或模块单元。
|
||||||
* <p>
|
* <p>
|
||||||
* 在跨模块调用、导入语句校验、作用域隔离中发挥作用。
|
* 在跨模块调用、导入语句校验、作用域隔离中发挥作用。
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
MODULE
|
MODULE
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user