From 102e84bc01b9fbf6a6d9c95b908b0932d00b3657 Mon Sep 17 00:00:00 2001
From: Luke
Date: Fri, 1 Aug 2025 23:33:31 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E5=B9=B6=E6=89=A9?=
=?UTF-8?q?=E5=B1=95=E8=B0=83=E7=94=A8=E6=8C=87=E4=BB=A4=E7=94=9F=E6=88=90?=
=?UTF-8?q?=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 优化了 syscall、数组下标访问和普通函数调用的处理逻辑
- 新增对多维数组下标访问的支持
- 改进了字符串常量和寄存器的绑定机制
- 统一了参数加载和错误处理的代码
---
.../backend/generator/CallGenerator.java | 316 ++++++++++------
.../ir/builder/ExpressionBuilder.java | 340 +++++++++++++-----
.../vm/commands/ref/control/RPushCommand.java | 220 ++++++++----
3 files changed, 615 insertions(+), 261 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
index 15957a1..58d721d 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
@@ -10,42 +10,46 @@ 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.*;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令。
*
- * 支持:
- *
- * - 普通函数调用的参数压栈、调用、返回值保存
- * - 特殊的 {@code syscall} 指令转为 VM 的 SYSCALL 指令
- * - 数组访问内置函数 {@code __index_i(arr, idx)} 的专用指令序列
- *
- *
- * 对于 syscall 子命令,支持常量字符串和字符串寄存器两种来源,并支持寄存器-字符串常量池注册机制。
+ * 支持 syscall、普通函数调用、一维/多维数组下标访问、以及字符串常量池绑定等功能。
*
+ *
+ *
+ * - syscall: 支持字符串常量与寄存器到子命令的绑定与解析
+ * - 数组下标访问: 特殊的“__index_i”和“__index_r”内部调用
+ * - 普通函数: 支持自动推断返回值类型与槽位类型
+ *
*/
public class CallGenerator implements InstructionGenerator {
/**
* 字符串常量池:用于绑定虚拟寄存器 id 到字符串值(供 syscall 子命令使用)。
+ *
+ * 使用 ConcurrentHashMap 保证并发安全,所有 registerStringConst 的注册和读取都是线程安全的。
*/
- private static final Map STRING_CONST_POOL = new HashMap<>();
+ private static final Map STRING_CONST_POOL = new ConcurrentHashMap<>();
/**
* 注册一个字符串常量,绑定到虚拟寄存器 id。
*
- * @param regId 虚拟寄存器 id
- * @param value 字符串常量
+ * @param regId 虚拟寄存器 id
+ * @param value 字符串常量
*/
public static void registerStringConst(int regId, String value) {
STRING_CONST_POOL.put(regId, value);
}
/**
- * 返回当前指令生成器支持的 IR 指令类型(即 {@link CallInstruction})。
+ * 返回本生成器支持的 IR 指令类型。
*
- * @return {@code CallInstruction.class}
+ * @return {@link CallInstruction} 类型的 Class 对象
*/
@Override
public Class supportedClass() {
@@ -53,122 +57,234 @@ public class CallGenerator implements InstructionGenerator {
}
/**
- * 生成 VM 指令序列,实现函数调用/特殊 syscall/数组索引等 IR 指令的转换。
+ * 生成指定的调用指令,包括 syscall、数组下标、普通函数。
*
- * @param ins 当前函数调用 IR 指令
- * @param out VM 指令输出构建器
- * @param slotMap IR 虚拟寄存器 → VM 槽位映射表
- * @param currentFn 当前函数名
+ * @param ins IR 层的调用指令
+ * @param out VM 程序构建器
+ * @param slotMap 寄存器到槽位的映射表
+ * @param currentFn 当前函数名
*/
@Override
public void generate(CallInstruction ins,
VMProgramBuilder out,
Map slotMap,
String currentFn) {
+ String fn = ins.getFunctionName();
- /* ========== 特殊处理 syscall 调用 ========== */
- if ("syscall".equals(ins.getFunctionName()) ||
- ins.getFunctionName().endsWith(".syscall")) {
-
- List 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) { // 来自寄存器的字符串
- String s = STRING_CONST_POOL.get(vr.id());
- if (s == null)
- throw new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr);
- subcmd = s.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 无返回值,直接返回
+ // 特殊处理 syscall 调用
+ if ("syscall".equals(fn) || fn.endsWith(".syscall")) {
+ generateSyscall(ins, out, slotMap, fn);
+ return;
}
- /* ========== 特殊处理内部索引函数:__index_i(arr, idx) ========== */
- if ("__index_i".equals(ins.getFunctionName())) {
- // 加载参数(arr 为引用类型,idx 为整型)
- if (ins.getArguments().size() != 2) {
- throw new IllegalStateException("__index_i 需要两个参数(arr, idx)");
+ // 一维数组整型下标访问
+ if ("__index_i".equals(fn)) {
+ generateIndexInstruction(ins, out, slotMap, true);
+ return;
+ }
+
+ // 多维数组返回引用的下标访问
+ if ("__index_r".equals(fn)) {
+ generateIndexInstruction(ins, out, slotMap, false);
+ return;
+ }
+
+ // 普通函数调用
+ generateNormalCall(ins, out, slotMap, fn);
+ }
+
+ // ========== 私有辅助方法 ==========
+
+ /**
+ * 解析 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);
}
- IRVirtualRegister arr = (IRVirtualRegister) ins.getArguments().get(0);
- IRVirtualRegister idx = (IRVirtualRegister) ins.getArguments().get(1);
+ 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)
+ );
+ }
+ }
- int arrSlot = slotMap.get(arr);
- int idxSlot = slotMap.get(idx);
+ /**
+ * 加载一个参数到栈,支持指定默认类型。如果未设置类型则采用默认类型。
+ *
+ * @param out VM 程序构建器
+ * @param slotMap 寄存器到槽位的映射
+ * @param arg 参数值(应为虚拟寄存器)
+ * @param defaultType 默认类型
+ * @param fn 当前函数名(用于错误提示)
+ * @throws IllegalStateException 如果参数不是虚拟寄存器,或槽位未找到
+ */
+ private void loadArgument(VMProgramBuilder out, Map 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);
+ }
- char arrT = out.getSlotType(arrSlot);
- if (arrT == '\0') arrT = 'R'; // 默认为引用类型
- out.emit(OpHelper.opcode(arrT + "_LOAD") + " " + arrSlot);
+ /**
+ * 生成一维/多维数组的下标访问指令。
+ *
+ * @param ins 调用指令
+ * @param out VM 程序构建器
+ * @param slotMap 寄存器到槽位映射
+ * @param isInt 是否是一维整型下标
+ */
+ private void generateIndexInstruction(CallInstruction ins, VMProgramBuilder out, Map slotMap, boolean isInt) {
+ String fn = ins.getFunctionName();
+ List args = ins.getArguments();
+ if (args.size() != 2) {
+ throw new IllegalStateException(
+ "[CallGenerator] %s 需要两个参数(arr, idx),实际: %s".formatted(fn, args));
+ }
+ // 加载数组参数
+ loadArgument(out, slotMap, args.get(0), 'R', fn);
+ // 加载下标参数
+ loadArgument(out, slotMap, args.get(1), 'I', fn);
- char idxT = out.getSlotType(idxSlot);
- if (idxT == '\0') idxT = 'I'; // 默认为整型
- out.emit(OpHelper.opcode(idxT + "_LOAD") + " " + idxSlot);
+ out.emit(VMOpCode.SYSCALL + " " + "ARR_GET");
- // 调用 SYSCALL ARR_GET,让 VM 取出数组元素并压回栈顶
- out.emit(VMOpCode.SYSCALL + " " + "ARR_GET");
-
- // 取回返回值并保存(当前仅支持 int 元素)
- int destSlot = slotMap.get(ins.getDest());
+ // 保存返回值到目标寄存器
+ 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));
+ }
+ if (isInt) {
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'I');
- return;
+ } else {
+ out.emit(OpHelper.opcode("R_STORE") + " " + destSlot);
+ out.setSlotType(destSlot, 'R');
+ }
+ }
+
+ /**
+ * 生成 syscall 指令分支逻辑。
+ *
+ * - 解析 syscall 子命令
+ * - 压栈剩余参数
+ * - 发出 SYSCALL 指令
+ * - 若有返回值则保存至目标槽位
+ *
+ *
+ * @param ins 调用指令
+ * @param out VM 程序构建器
+ * @param slotMap 寄存器到槽位映射
+ * @param fn 当前函数名
+ */
+ private void generateSyscall(CallInstruction ins, VMProgramBuilder out, Map slotMap, String fn) {
+ List args = ins.getArguments();
+ if (args.isEmpty()) {
+ throw new IllegalStateException(
+ "[CallGenerator] syscall 需要子命令参数 (function: %s)".formatted(fn));
}
- /* ========== 普通函数调用 ========== */
+ // 0. 解析 syscall 子命令(第一个参数)
+ String subcmd = resolveSyscallSubcmd(args.getFirst(), fn);
- // ---------- 1. 推断返回值类型(非 void 返回时用) ----------
- char retType = 'I'; // 默认为整型
- if (!ins.getArguments().isEmpty()) {
- // 简化:根据第一个参数类型推断返回类型,或者通过全局表拿到返回类型
- String ret = GlobalFunctionTable.getReturnType(ins.getFunctionName());
- if (ret != null) {
- retType = Character.toUpperCase(ret.charAt(0));
+ // 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');
}
+ }
- // ---------- 2. 压栈所有参数 ----------
+ /**
+ * 生成普通函数调用指令:
+ *
+ * - 推断返回值类型
+ * - 压栈所有参数
+ * - 生成 CALL 指令
+ * - 保存返回值(若非 void)
+ *
+ *
+ * @param ins 调用指令
+ * @param out VM 程序构建器
+ * @param slotMap 寄存器到槽位映射
+ * @param fn 当前函数名
+ */
+ private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map 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()) {
- IRVirtualRegister vr = (IRVirtualRegister) arg;
- int slotId = slotMap.get(vr);
- char t = out.getSlotType(slotId);
- if (t == '\0') t = 'I';
- out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
+ loadArgument(out, slotMap, arg, 'R', fn);
}
- // ---------- 3. 发出 CALL 指令 ----------
- out.emitCall(ins.getFunctionName(), ins.getArguments().size());
+ // 3. 发出 CALL 指令
+ out.emitCall(fn, ins.getArguments().size());
- // ---------- 3.5 如果为 void 返回直接结束 ----------
- if ("void".equals(GlobalFunctionTable.getReturnType(ins.getFunctionName()))) {
+ // 3.5 void 返回直接结束
+ if ("void".equals(retTypeName)) {
return;
}
- // ---------- 4. 保存返回值 ----------
- int destSlot = slotMap.get(ins.getDest());
+ // 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));
+ }
out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
out.setSlotType(destSlot, retType);
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
index 4a5d525..70e456f 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
@@ -12,7 +12,8 @@ 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.*;
+import java.util.ArrayList;
+import java.util.List;
/**
* {@code ExpressionBuilder} 表达式 → IR 构建器。
@@ -24,17 +25,13 @@ import java.util.*;
* 为每种表达式类型生成对应 IR 指令
* 支持表达式嵌套的递归构建
* 支持写入指定目标寄存器,避免冗余的 move 指令
- * 支持 IndexExpressionNode 的编译期折叠(arr[2]),并自动降级为运行时调用 __index_i
+ * 支持 IndexExpressionNode 的编译期折叠(arr[2]),并在运行时降级为 __index_i/__index_r
*
*/
public record ExpressionBuilder(IRContext ctx) {
/**
- * 构建表达式,返回存储其结果的虚拟寄存器。
- *
- * @param expr 要生成 IR 的表达式节点
- * @return 存储表达式值的虚拟寄存器
- * @throws IllegalStateException 不支持的表达式类型或未定义标识符
+ * 构建表达式,返回结果寄存器。
*/
public IRVirtualRegister build(ExpressionNode expr) {
return switch (expr) {
@@ -43,22 +40,22 @@ 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;
}
// 二元表达式(如 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 UnaryExpressionNode un -> buildUnary(un);
+ case IndexExpressionNode idx -> buildIndex(idx);
+ case ArrayLiteralNode arr -> buildArrayLiteral(arr);
// 默认分支:遇到未知表达式类型则直接抛异常
default -> throw new IllegalStateException(
"不支持的表达式类型: " + expr.getClass().getSimpleName());
@@ -74,7 +71,7 @@ public record ExpressionBuilder(IRContext ctx) {
*
* - 字面量(数字、字符串、布尔、数组):生成 loadConst 指令直接写入目标寄存器
* - 变量标识符:查表获取源寄存器,并 move 到目标寄存器
- * - 二元表达式与下标表达式:递归生成子表达式结果,并写入目标寄存器
+ * - 二元表达式、下标、调用表达式:递归生成子表达式结果,并写入目标寄存器
* - 其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器
*
*
@@ -85,22 +82,23 @@ public record ExpressionBuilder(IRContext ctx) {
*/
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));
- case ArrayLiteralNode arr ->
- InstructionFactory.loadConstInto(ctx, dest, buildArrayConstant(arr));
+ // 布尔字面量:转换为 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 到目标寄存器
case IdentifierNode id -> {
IRVirtualRegister src = ctx.getScope().lookup(id.name());
if (src == null)
@@ -108,12 +106,22 @@ 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 到目标寄存器
default -> {
IRVirtualRegister tmp = build(node);
InstructionFactory.move(ctx, tmp, dest);
@@ -121,80 +129,182 @@ public record ExpressionBuilder(IRContext ctx) {
}
}
+
/**
- * 下标访问表达式处理。支持编译期常量折叠(数组和下标均为常量时直接求值),
- * 否则生成运行时调用 __index_i(由 VM 降级为 ARR_GET)。
+ * 构建下标访问表达式(IndexExpressionNode)。
+ *
+ * - 若数组和下标均为编译期常量,则直接进行常量折叠(直接返回目标常量寄存器);
+ * - 否则:
+ *
+ * - 若数组表达式本身是下一个下标的中间值(即多维数组链式下标),则先用 __index_r 获取“引用”;
+ * - 最后一级用 __index_i 获取实际整型元素值。
+ *
+ *
+ *
*
- * @param node 下标访问表达式
- * @return 存储结果的虚拟寄存器
+ * @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;
}
- IRVirtualRegister arrReg = build(node.array());
+
+ // 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());
- IRVirtualRegister dest = ctx.newRegister();
+
+ // 4. 创建目标寄存器
+ IRVirtualRegister dest = ctx.newRegister();
+
+ // 5. 准备参数
List argv = new ArrayList<>();
argv.add(arrReg);
argv.add(idxReg);
- ctx.addInstruction(new CallInstruction(dest, "__index_i", argv));
+
+ // 6. 选择调用指令
+ if (node.array() instanceof IndexExpressionNode) {
+ // 非最末层,下标取“引用”
+ ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
+ } else {
+ // 最末层,下标取实际元素值
+ ctx.addInstruction(new CallInstruction(dest, "__index_i", argv));
+ }
+
return dest;
}
/**
- * 尝试将表达式折叠为编译期常量(支持嵌套)。
- * 支持数字、字符串、布尔、数组、常量标识符。
+ * 构建中间层下标访问表达式(返回引用)。
+ *
+ * 用于多维数组的链式下标访问(如 arr[1][2]),保证中间结果是“可被再次下标”的引用。
+ *
+ * - 若数组和下标均为编译期常量,则直接常量折叠,返回目标常量寄存器;
+ * - 否则,递归处理 array,生成“引用”指令(__index_r)。
+ *
+ *
*
- * @param expr 要折叠的表达式节点
- * @return 常量对象(如数字、字符串、List),否则返回 null
+ * @param node 下标访问表达式节点
+ * @return 存放引用结果的虚拟寄存器
+ */
+ private 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 argv = new ArrayList<>();
+ argv.add(arrReg);
+ argv.add(idxReg);
+
+ // 6. 生成 __index_r 调用指令(总是返回引用)
+ ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
+
+ return dest;
+ }
+
+ /**
+ * 常量折叠工具(支持嵌套数组)。
+ *
+ * 尝试将表达式节点 {@code expr} 折叠为常量值,用于编译期计算/优化。
+ *
+ * - 数字字面量:返回 int 或 double。
+ * - 字符串字面量:返回字符串。
+ * - 布尔字面量:返回 1(true)或 0(false)。
+ * - 数组字面量:递归折叠所有元素为 List,如果有一项不能折叠则返回 null。
+ * - 标识符:尝试从作用域查找编译期常量值。
+ * - 其它类型:无法折叠,返回 null。
+ *
+ *
+ *
+ * @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);
- }
- return Integer.parseInt(s);
+ if (s.contains(".") || s.contains("e") || s.contains("E"))
+ return Double.parseDouble(s); // 带小数或科学计数法为 double
+ return Integer.parseInt(s); // 否则为 int
} catch (NumberFormatException e) {
- return null;
+ return null; // 无法解析为数字
}
}
- if (expr instanceof StringLiteralNode s) {
- return s.value();
- }
- if (expr instanceof BoolLiteralNode b) {
- return b.getValue() ? 1 : 0;
- }
+
+ // 字符串字面量:直接返回字符串
+ 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) {
- java.util.List