feat: 重构并扩展调用指令生成逻辑
- 优化了 syscall、数组下标访问和普通函数调用的处理逻辑 - 新增对多维数组下标访问的支持 - 改进了字符串常量和寄存器的绑定机制 - 统一了参数加载和错误处理的代码
This commit is contained in:
parent
e5f23b77bd
commit
102e84bc01
@ -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 层函数调用指令。
|
||||
* <p>
|
||||
* 支持:
|
||||
* <ul>
|
||||
* <li>普通函数调用的参数压栈、调用、返回值保存</li>
|
||||
* <li>特殊的 {@code syscall} 指令转为 VM 的 SYSCALL 指令</li>
|
||||
* <li>数组访问内置函数 {@code __index_i(arr, idx)} 的专用指令序列</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 对于 syscall 子命令,支持常量字符串和字符串寄存器两种来源,并支持寄存器-字符串常量池注册机制。
|
||||
* 支持 syscall、普通函数调用、一维/多维数组下标访问、以及字符串常量池绑定等功能。
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>syscall: 支持字符串常量与寄存器到子命令的绑定与解析</li>
|
||||
* <li>数组下标访问: 特殊的“__index_i”和“__index_r”内部调用</li>
|
||||
* <li>普通函数: 支持自动推断返回值类型与槽位类型</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
|
||||
/**
|
||||
* 字符串常量池:用于绑定虚拟寄存器 id 到字符串值(供 syscall 子命令使用)。
|
||||
* <br>
|
||||
* 使用 ConcurrentHashMap 保证并发安全,所有 registerStringConst 的注册和读取都是线程安全的。
|
||||
*/
|
||||
private static final Map<Integer, String> STRING_CONST_POOL = new HashMap<>();
|
||||
private static final Map<Integer, String> 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<CallInstruction> supportedClass() {
|
||||
@ -53,122 +57,234 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 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<IRVirtualRegister, Integer> slotMap,
|
||||
String currentFn) {
|
||||
String fn = ins.getFunctionName();
|
||||
|
||||
/* ========== 特殊处理 syscall 调用 ========== */
|
||||
if ("syscall".equals(ins.getFunctionName()) ||
|
||||
ins.getFunctionName().endsWith(".syscall")) {
|
||||
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.isEmpty()) {
|
||||
throw new IllegalStateException("syscall 需要子命令参数");
|
||||
}
|
||||
|
||||
// ---------- 0. 解析 syscall 子命令 ----------
|
||||
// 支持 IRConstant 字面量或虚拟寄存器(需已绑定字符串)
|
||||
String subcmd;
|
||||
IRValue first = args.getFirst();
|
||||
|
||||
if (first instanceof IRConstant(Object value)) { // 直接字面量
|
||||
if (!(value instanceof String s))
|
||||
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
|
||||
subcmd = s.toUpperCase(Locale.ROOT);
|
||||
|
||||
} else if (first instanceof IRVirtualRegister vr) { // 来自寄存器的字符串
|
||||
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<IRVirtualRegister, Integer> slotMap, IRValue arg, char defaultType, String fn) {
|
||||
if (!(arg instanceof IRVirtualRegister vr)) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 参数必须为虚拟寄存器 (function: %s, arg: %s)".formatted(fn, arg));
|
||||
}
|
||||
Integer slot = slotMap.get(vr);
|
||||
if (slot == null) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] 未找到虚拟寄存器的槽位映射 (function: %s, reg: %s)".formatted(fn, vr));
|
||||
}
|
||||
char t = out.getSlotType(slot);
|
||||
if (t == '\0') t = defaultType;
|
||||
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slot);
|
||||
}
|
||||
|
||||
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<IRVirtualRegister, Integer> slotMap, boolean isInt) {
|
||||
String fn = ins.getFunctionName();
|
||||
List<IRValue> 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 指令分支逻辑。
|
||||
* <ol>
|
||||
* <li>解析 syscall 子命令</li>
|
||||
* <li>压栈剩余参数</li>
|
||||
* <li>发出 SYSCALL 指令</li>
|
||||
* <li>若有返回值则保存至目标槽位</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param ins 调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param fn 当前函数名
|
||||
*/
|
||||
private void generateSyscall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"[CallGenerator] syscall 需要子命令参数 (function: %s)".formatted(fn));
|
||||
}
|
||||
|
||||
/* ========== 普通函数调用 ========== */
|
||||
// 0. 解析 syscall 子命令(第一个参数)
|
||||
String subcmd = resolveSyscallSubcmd(args.getFirst(), fn);
|
||||
|
||||
// ---------- 1. 推断返回值类型(非 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. 压栈所有参数 ----------
|
||||
/**
|
||||
* 生成普通函数调用指令:
|
||||
* <ol>
|
||||
* <li>推断返回值类型</li>
|
||||
* <li>压栈所有参数</li>
|
||||
* <li>生成 CALL 指令</li>
|
||||
* <li>保存返回值(若非 void)</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param ins 调用指令
|
||||
* @param out VM 程序构建器
|
||||
* @param slotMap 寄存器到槽位映射
|
||||
* @param fn 当前函数名
|
||||
*/
|
||||
private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
|
||||
// 1. 推断返回值类型(首字母大写,缺省为 'I')
|
||||
String retTypeName = GlobalFunctionTable.getReturnType(fn);
|
||||
char retType = (retTypeName != null && !retTypeName.isEmpty()) ? Character.toUpperCase(retTypeName.charAt(0)) : 'I';
|
||||
|
||||
// 2. 压栈所有参数
|
||||
for (IRValue arg : ins.getArguments()) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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.*;
|
||||
* <li>为每种表达式类型生成对应 IR 指令</li>
|
||||
* <li>支持表达式嵌套的递归构建</li>
|
||||
* <li>支持写入指定目标寄存器,避免冗余的 move 指令</li>
|
||||
* <li>支持 IndexExpressionNode 的编译期折叠(arr[2]),并自动降级为运行时调用 __index_i</li>
|
||||
* <li>支持 IndexExpressionNode 的编译期折叠(arr[2]),并在运行时降级为 __index_i/__index_r</li>
|
||||
* </ul>
|
||||
*/
|
||||
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) {
|
||||
* <ul>
|
||||
* <li>字面量(数字、字符串、布尔、数组):生成 loadConst 指令直接写入目标寄存器</li>
|
||||
* <li>变量标识符:查表获取源寄存器,并 move 到目标寄存器</li>
|
||||
* <li>二元表达式与下标表达式:递归生成子表达式结果,并写入目标寄存器</li>
|
||||
* <li>二元表达式、下标、调用表达式:递归生成子表达式结果,并写入目标寄存器</li>
|
||||
* <li>其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
@ -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)。
|
||||
* <ul>
|
||||
* <li>若数组和下标均为编译期常量,则直接进行常量折叠(直接返回目标常量寄存器);</li>
|
||||
* <li>否则:
|
||||
* <ul>
|
||||
* <li>若数组表达式本身是下一个下标的中间值(即多维数组链式下标),则先用 __index_r 获取“引用”;</li>
|
||||
* <li>最后一级用 __index_i 获取实际整型元素值。</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @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<IRValue> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试将表达式折叠为编译期常量(支持嵌套)。
|
||||
* 支持数字、字符串、布尔、数组、常量标识符。
|
||||
* 构建中间层下标访问表达式(返回引用)。
|
||||
* <p>
|
||||
* 用于多维数组的链式下标访问(如 arr[1][2]),保证中间结果是“可被再次下标”的引用。
|
||||
* <ul>
|
||||
* <li>若数组和下标均为编译期常量,则直接常量折叠,返回目标常量寄存器;</li>
|
||||
* <li>否则,递归处理 array,生成“引用”指令(__index_r)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @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<IRValue> argv = new ArrayList<>();
|
||||
argv.add(arrReg);
|
||||
argv.add(idxReg);
|
||||
|
||||
// 6. 生成 __index_r 调用指令(总是返回引用)
|
||||
ctx.addInstruction(new CallInstruction(dest, "__index_r", argv));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 常量折叠工具(支持嵌套数组)。
|
||||
* <p>
|
||||
* 尝试将表达式节点 {@code expr} 折叠为常量值,用于编译期计算/优化。
|
||||
* <ul>
|
||||
* <li>数字字面量:返回 int 或 double。</li>
|
||||
* <li>字符串字面量:返回字符串。</li>
|
||||
* <li>布尔字面量:返回 1(true)或 0(false)。</li>
|
||||
* <li>数组字面量:递归折叠所有元素为 List,如果有一项不能折叠则返回 null。</li>
|
||||
* <li>标识符:尝试从作用域查找编译期常量值。</li>
|
||||
* <li>其它类型:无法折叠,返回 null。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param expr 需要折叠的表达式节点
|
||||
* @return 编译期常量值(支持 int、double、String、List),否则返回 null
|
||||
*/
|
||||
private Object tryFoldConst(ExpressionNode expr) {
|
||||
if (expr == null) return null;
|
||||
|
||||
// 数字字面量:尝试解析为 int 或 double
|
||||
if (expr instanceof NumberLiteralNode n) {
|
||||
String s = n.value();
|
||||
try {
|
||||
if (s.contains(".") || s.contains("e") || s.contains("E")) {
|
||||
return Double.parseDouble(s);
|
||||
}
|
||||
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<Object> list = new java.util.ArrayList<>();
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (ExpressionNode e : arr.elements()) {
|
||||
Object v = tryFoldConst(e);
|
||||
if (v == null) return null;
|
||||
if (v == null) return null; // 有一项无法折叠则整体失败
|
||||
list.add(v);
|
||||
}
|
||||
return java.util.List.copyOf(list);
|
||||
return List.copyOf(list);
|
||||
}
|
||||
|
||||
// 标识符:尝试查找作用域中的常量值
|
||||
if (expr instanceof IdentifierNode id) {
|
||||
Object v = null;
|
||||
try {
|
||||
v = ctx.getScope().getConstValue(id.name());
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
// 查不到常量或异常都视为无法折叠
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// 其它类型:不支持折叠,返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 一元表达式构建
|
||||
*
|
||||
@ -207,62 +317,70 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
*/
|
||||
private IRVirtualRegister buildUnary(UnaryExpressionNode un) {
|
||||
// 递归生成操作数的寄存器
|
||||
IRVirtualRegister src = build(un.operand());
|
||||
IRVirtualRegister src = build(un.operand());
|
||||
// 分配目标寄存器
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
|
||||
switch (un.operator()) {
|
||||
// 算术负号:生成取负指令
|
||||
// 算术负号:生成取负指令(例如:-a)
|
||||
case "-" -> ctx.addInstruction(
|
||||
new UnaryOperationInstruction(ExpressionUtils.negOp(un.operand()), dest, src));
|
||||
// 逻辑非:等价于 a == 0,生成比较指令
|
||||
|
||||
// 逻辑非:等价于 a == 0,生成整数等于比较指令(!a)
|
||||
case "!" -> {
|
||||
// 生成常量0的寄存器
|
||||
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
|
||||
// 比较 src 是否等于0,等价于逻辑非
|
||||
return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, src, zero);
|
||||
}
|
||||
// 其它一元运算符不支持,抛异常
|
||||
// 其它一元运算符不支持,抛出异常
|
||||
default -> throw new IllegalStateException("未知一元运算符: " + un.operator());
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建函数或方法调用表达式。模块内未限定调用会自动补全当前模块名。
|
||||
* 构建函数或方法调用表达式。
|
||||
* <p>
|
||||
* 支持普通函数调用(foo(a, b))与成员方法调用(obj.method(a, b))。
|
||||
* <ul>
|
||||
* <li>首先递归生成所有参数的虚拟寄存器列表。</li>
|
||||
* <li>根据 callee 类型区分成员访问或直接标识符调用,并规范化方法名(如加前缀)。</li>
|
||||
* <li>为返回值分配新寄存器,生成 Call 指令。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param call AST 调用表达式节点
|
||||
* @return 存储调用结果的虚拟寄存器
|
||||
* @param call 函数/方法调用表达式节点
|
||||
* @return 存放调用结果的虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildCall(CallExpressionNode call) {
|
||||
// 递归生成所有参数(实参)对应的寄存器
|
||||
// 1. 递归生成所有参数的寄存器
|
||||
List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList();
|
||||
|
||||
// 解析被调用目标名,支持普通函数/成员方法
|
||||
// 2. 规范化被调用方法名(区分成员方法与普通函数)
|
||||
String callee = switch (call.callee()) {
|
||||
// 成员方法调用,例如 obj.foo()
|
||||
case MemberExpressionNode m when m.object() instanceof IdentifierNode id
|
||||
-> id.name() + "." + m.member();
|
||||
// 普通函数调用,如果未指定模块,自动补全当前模块名
|
||||
// 成员方法调用,如 obj.method()
|
||||
case MemberExpressionNode m when m.object() instanceof IdentifierNode id -> id.name() + "." + m.member();
|
||||
// 普通函数调用,或处理命名空间前缀(如当前方法名为 namespace.func)
|
||||
case IdentifierNode id -> {
|
||||
String current = ctx.getFunction().name();
|
||||
int dot = current.lastIndexOf('.');
|
||||
if (dot > 0) {
|
||||
// 当前处于模块内函数(Module.func),补全为同模块下的全限定名
|
||||
yield current.substring(0, dot) + "." + id.name();
|
||||
} else {
|
||||
// 顶层/脚本函数等不含模块前缀,保持原样
|
||||
yield id.name();
|
||||
}
|
||||
if (dot > 0)
|
||||
yield current.substring(0, dot) + "." + id.name(); // 同命名空间内调用
|
||||
yield id.name(); // 全局函数调用
|
||||
}
|
||||
// 其它情况暂不支持
|
||||
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
// 其它类型不支持
|
||||
default -> throw new IllegalStateException(
|
||||
"不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
};
|
||||
|
||||
// 为返回值分配新寄存器,生成 Call 指令
|
||||
// 3. 分配用于存放返回值的新寄存器,并生成 Call 指令
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 二元表达式构建,结果存储到新寄存器。
|
||||
* <br>
|
||||
@ -277,44 +395,54 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
IRVirtualRegister b = build(bin.right());
|
||||
String op = bin.operator();
|
||||
|
||||
// 比较运算符(==、!=、>、< 等),需要生成条件跳转或布尔值寄存器
|
||||
// 判断是否为比较运算符(==、!=、>、<等)
|
||||
if (ComparisonUtils.isComparisonOperator(op)) {
|
||||
// 生成比较操作,返回布尔值寄存器
|
||||
return InstructionFactory.binOp(
|
||||
ctx,
|
||||
// 通过比较工具获得合适的 IR 操作码
|
||||
// 根据运算符和操作数类型获得合适的 IR 操作码
|
||||
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
|
||||
a, b);
|
||||
}
|
||||
|
||||
// 其它算术/位运算
|
||||
// 其它二元运算(算术、位运算等)
|
||||
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||
if (code == null)
|
||||
throw new IllegalStateException("不支持的运算符: " + op);
|
||||
// 生成二元操作指令
|
||||
return InstructionFactory.binOp(ctx, code, a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 二元表达式构建,结果直接写入目标寄存器(用于赋值左值等优化场景)。
|
||||
*
|
||||
* @param bin 二元表达式节点
|
||||
* @param bin 二元表达式节点
|
||||
* @param dest 目标虚拟寄存器
|
||||
*/
|
||||
private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) {
|
||||
// 递归生成左、右操作数的寄存器
|
||||
IRVirtualRegister a = build(bin.left());
|
||||
IRVirtualRegister b = build(bin.right());
|
||||
String op = bin.operator();
|
||||
|
||||
// 处理比较运算符(==、!=、>、< 等)
|
||||
if (ComparisonUtils.isComparisonOperator(op)) {
|
||||
InstructionFactory.binOpInto(
|
||||
ctx,
|
||||
// 选择对应类型和符号的比较操作码
|
||||
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
|
||||
a, b, dest);
|
||||
} else {
|
||||
// 算术或位运算符
|
||||
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||
if (code == null)
|
||||
throw new IllegalStateException("不支持的运算符: " + op);
|
||||
// 生成二元操作指令,写入目标寄存器
|
||||
InstructionFactory.binOpInto(ctx, code, a, b, dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ───────────────── 字面量辅助方法 ───────────────── */
|
||||
|
||||
/**
|
||||
@ -324,8 +452,11 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储该字面量的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildNumberLiteral(String value) {
|
||||
// 解析数字常量
|
||||
IRConstant c = ExpressionUtils.buildNumberConstant(ctx, value);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
@ -337,8 +468,11 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储该字符串的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildStringLiteral(String value) {
|
||||
// 构建字符串常量
|
||||
IRConstant c = new IRConstant(value);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
@ -350,8 +484,11 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储 1/0 的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildBoolLiteral(boolean v) {
|
||||
// 转换为 1 或 0 的常量
|
||||
IRConstant c = new IRConstant(v ? 1 : 0);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
@ -363,33 +500,58 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 存储该数组的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildArrayLiteral(ArrayLiteralNode arr) {
|
||||
// 递归生成支持嵌套的数组常量
|
||||
IRConstant c = buildArrayConstant(arr);
|
||||
// 分配新寄存器
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
// 生成 LoadConst 指令
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建数组常量(所有元素均为数字、字符串或布尔常量)。
|
||||
* 构建支持嵌套的数组常量表达式。
|
||||
* <p>
|
||||
* 遍历并递归处理数组字面量的所有元素:
|
||||
* <ul>
|
||||
* <li>数字字面量:根据内容生成 int 或 double 常量</li>
|
||||
* <li>字符串字面量:直接存储字符串内容</li>
|
||||
* <li>布尔字面量:转换为 1(true)或 0(false)存储</li>
|
||||
* <li>数组字面量:递归构建,允许多层嵌套,最终生成嵌套的 List</li>
|
||||
* </ul>
|
||||
* 若包含非常量元素,则抛出异常。
|
||||
* </p>
|
||||
*
|
||||
* @param arr 数组字面量节点
|
||||
* @return 数组 IRConstant
|
||||
* @throws IllegalStateException 若含有非常量元素
|
||||
* @return 封装所有常量元素(支持嵌套 List)的 {@link IRConstant}
|
||||
* @throws IllegalStateException 如果数组中存在非常量元素
|
||||
*/
|
||||
|
||||
private IRConstant buildArrayConstant(ArrayLiteralNode arr) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (ExpressionNode e : arr.elements()) {
|
||||
switch (e) {
|
||||
// 数字字面量,解析并加入
|
||||
case NumberLiteralNode n -> {
|
||||
IRConstant num = ExpressionUtils.buildNumberConstant(ctx, n.value());
|
||||
list.add(num.value());
|
||||
}
|
||||
// 字符串字面量,直接加入
|
||||
case StringLiteralNode s -> list.add(s.value());
|
||||
case BoolLiteralNode b -> list.add(b.getValue() ? 1 : 0);
|
||||
// 布尔字面量,转成 1/0
|
||||
case BoolLiteralNode b -> list.add(b.getValue() ? 1 : 0);
|
||||
// 嵌套数组,递归生成并加入
|
||||
case ArrayLiteralNode inner -> {
|
||||
IRConstant innerConst = buildArrayConstant(inner);
|
||||
list.add(innerConst.value());
|
||||
}
|
||||
// 其它类型暂不支持
|
||||
default -> throw new IllegalStateException(
|
||||
"暂不支持含非常量元素的数组字面量: " + e.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
// 返回不可变的 List 封装为 IRConstant
|
||||
return new IRConstant(List.copyOf(list));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,6 +5,10 @@ import org.jcnc.snow.vm.module.CallStack;
|
||||
import org.jcnc.snow.vm.module.LocalVariableStore;
|
||||
import org.jcnc.snow.vm.module.OperandStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@code RPushCommand} class implements the {@link Command} interface
|
||||
* and represents the "reference push" instruction ({@code R_PUSH}) in the virtual machine.
|
||||
@ -14,75 +18,34 @@ import org.jcnc.snow.vm.module.OperandStack;
|
||||
* </p>
|
||||
*
|
||||
* <p><b>Instruction format:</b> {@code R_PUSH <literal>}</p>
|
||||
* <ul>
|
||||
* <li>{@code <literal>}: The reference value (e.g., string, boolean, integer, floating-point, or array literal)
|
||||
* to be pushed onto the stack. If the literal contains spaces, all arguments after {@code R_PUSH} are joined with spaces.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>Behavior:</b></p>
|
||||
* <ul>
|
||||
* <li>Ensures that at least one parameter is provided after the operator; otherwise, throws {@code IllegalStateException}.</li>
|
||||
* <li>Concatenates all parameters after {@code R_PUSH} into a single string, separated by spaces.</li>
|
||||
* <li>If the resulting string is an array literal (i.e., surrounded by square brackets), splits and parses its elements using {@link #getObject(String)} for primitive type support.</li>
|
||||
* <li>Otherwise, pushes the literal as a string reference onto the operand stack.</li>
|
||||
* <li>Increments the program counter to advance to the next instruction.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Supported element types in array literals include integers, floating-point numbers, booleans,
|
||||
* and quoted strings (surrounded by double quotes).
|
||||
* Example usages:
|
||||
* <ul>
|
||||
* <li>{@code R_PUSH "hello"} → push the string {@code "hello"} onto stack</li>
|
||||
* <li>{@code R_PUSH [1,2,3]} → push an (unmodifiable) {@code List<Integer>} onto stack</li>
|
||||
* <li>{@code R_PUSH [[1,2,3],[4,5,6]]} → push a nested (unmodifiable) {@code List<List<Integer>>} onto stack</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Supported element types in array literals include integers, floating-point numbers (parsed as {@code Double}),
|
||||
* booleans ({@code true}/{@code false} → {@code 1}/{@code 0}), quoted strings (surrounded by double quotes),
|
||||
* and further nested arrays.
|
||||
* </p>
|
||||
*/
|
||||
public final class RPushCommand implements Command {
|
||||
|
||||
/**
|
||||
* Parses a string element into its corresponding Java object.
|
||||
* <ul>
|
||||
* <li>If enclosed in double quotes, treats as a string literal (quotes are removed).</li>
|
||||
* <li>If "true" or "false" (case-insensitive), returns 1 or 0 respectively (as integer representation).</li>
|
||||
* <li>If numeric (integer or floating-point), parses accordingly.</li>
|
||||
* <li>Otherwise, returns the string as-is.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param e The string to parse.
|
||||
* @return The parsed object (String, Integer, Double, or Integer for boolean).
|
||||
*/
|
||||
private static Object getObject(String e) {
|
||||
String x = e.trim();
|
||||
Object v;
|
||||
if (x.startsWith("\"") && x.endsWith("\"") && x.length() >= 2) {
|
||||
// String literal (remove surrounding double quotes)
|
||||
v = x.substring(1, x.length() - 1);
|
||||
} else if ("true".equalsIgnoreCase(x) || "false".equalsIgnoreCase(x)) {
|
||||
// Boolean value: true → 1, false → 0
|
||||
v = Boolean.parseBoolean(x) ? 1 : 0;
|
||||
} else {
|
||||
try {
|
||||
// Attempt to parse as floating-point or integer number
|
||||
if (x.contains(".") || x.contains("e") || x.contains("E")) {
|
||||
v = Double.parseDouble(x);
|
||||
} else {
|
||||
v = Integer.parseInt(x);
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
// Fallback: treat as plain string
|
||||
v = x;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
// ======== Parsing helpers ========
|
||||
|
||||
private static final class Cursor {
|
||||
final String s;
|
||||
int i;
|
||||
Cursor(String s) { this.s = s; this.i = 0; }
|
||||
boolean end() { return i >= s.length(); }
|
||||
char ch() { return s.charAt(i); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the {@code R_PUSH} instruction: pushes a reference value onto the operand stack.
|
||||
*
|
||||
* @param parts The instruction split into its components.
|
||||
* @param pc The current program counter.
|
||||
* @param stack The operand stack.
|
||||
* @param lvs The local variable store (unused).
|
||||
* @param cs The call stack (unused).
|
||||
* @return The next program counter value.
|
||||
* @throws IllegalStateException If no parameter is supplied after {@code R_PUSH}.
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] parts, int pc,
|
||||
OperandStack stack,
|
||||
@ -102,22 +65,135 @@ public final class RPushCommand implements Command {
|
||||
|
||||
// If the literal is an array (e.g., [1, 2, "foo"]), parse elements and push as an unmodifiable list.
|
||||
if (literal.startsWith("[") && literal.endsWith("]")) {
|
||||
String inside = literal.substring(1, literal.length() - 1).trim();
|
||||
java.util.List<Object> list = new java.util.ArrayList<>();
|
||||
if (!inside.isEmpty()) {
|
||||
// Split by comma, support element parsing (numbers, booleans, quoted strings)
|
||||
String[] elems = inside.split(",");
|
||||
for (String e : elems) {
|
||||
Object v = getObject(e);
|
||||
list.add(v);
|
||||
}
|
||||
Object parsed = parseValue(new Cursor(literal));
|
||||
if (!(parsed instanceof List<?> list)) {
|
||||
// Should never happen for a bracketed value, but keep a guard.
|
||||
stack.push(parsed);
|
||||
} else {
|
||||
stack.push(deepUnmodifiable(list));
|
||||
}
|
||||
// Push as an unmodifiable list to prevent further modifications.
|
||||
stack.push(java.util.Collections.unmodifiableList(list));
|
||||
} else {
|
||||
// Otherwise, push the string literal as-is.
|
||||
stack.push(literal);
|
||||
}
|
||||
return pc + 1;
|
||||
}
|
||||
|
||||
/** Deeply wrap lists as unmodifiable; leave scalars intact. */
|
||||
private static Object deepUnmodifiableObject(Object v) {
|
||||
if (v instanceof List<?> l) {
|
||||
return deepUnmodifiable(l);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
private static List<?> deepUnmodifiable(List<?> l) {
|
||||
List<Object> out = new ArrayList<>(l.size());
|
||||
for (Object v : l) out.add(deepUnmodifiableObject(v));
|
||||
return Collections.unmodifiableList(out);
|
||||
}
|
||||
|
||||
// ======== Recursive-descent parser for array literals ========
|
||||
|
||||
private static Object parseValue(Cursor c) {
|
||||
skipWs(c);
|
||||
if (c.end()) return "";
|
||||
char ch = c.ch();
|
||||
if (ch == '[') return parseArray(c);
|
||||
if (ch == '\"') return parseQuoted(c);
|
||||
return parseAtom(c);
|
||||
}
|
||||
|
||||
private static List<Object> parseArray(Cursor c) {
|
||||
// assumes current char is '['
|
||||
expect(c, '[');
|
||||
skipWs(c);
|
||||
List<Object> values = new ArrayList<>();
|
||||
if (!peek(c, ']')) {
|
||||
while (true) {
|
||||
Object v = parseValue(c);
|
||||
values.add(v);
|
||||
skipWs(c);
|
||||
if (peek(c, ',')) {
|
||||
c.i++; // consume ','
|
||||
skipWs(c);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(c, ']');
|
||||
return values;
|
||||
}
|
||||
|
||||
private static String parseQuoted(Cursor c) {
|
||||
// assumes current char is '"'
|
||||
expect(c, '\"');
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (!c.end()) {
|
||||
char ch = c.ch();
|
||||
c.i++;
|
||||
if (ch == '\\') { // escape
|
||||
if (c.end()) break;
|
||||
char nxt = c.ch();
|
||||
c.i++;
|
||||
switch (nxt) {
|
||||
case 'n' -> sb.append('\n');
|
||||
case 'r' -> sb.append('\r');
|
||||
case 't' -> sb.append('\t');
|
||||
case '\"' -> sb.append('\"');
|
||||
case '\\' -> sb.append('\\');
|
||||
default -> sb.append(nxt);
|
||||
}
|
||||
} else if (ch == '\"') {
|
||||
// end quote
|
||||
return sb.toString();
|
||||
} else {
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
// Unclosed string: return what we have
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static Object parseAtom(Cursor c) {
|
||||
int start = c.i;
|
||||
while (!c.end()) {
|
||||
char ch = c.ch();
|
||||
if (ch == ',' || ch == ']' ) break;
|
||||
if (Character.isWhitespace(ch)) break;
|
||||
c.i++;
|
||||
}
|
||||
String token = c.s.substring(start, c.i).trim();
|
||||
if (token.isEmpty()) return "";
|
||||
// booleans
|
||||
if ("true".equalsIgnoreCase(token)) return 1;
|
||||
if ("false".equalsIgnoreCase(token)) return 0;
|
||||
// number (int or double)
|
||||
try {
|
||||
if (token.contains(".") || token.contains("e") || token.contains("E")) {
|
||||
return Double.parseDouble(token);
|
||||
} else {
|
||||
return Integer.parseInt(token);
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
// fallback: raw string
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
private static void skipWs(Cursor c) {
|
||||
while (!c.end() && Character.isWhitespace(c.ch())) c.i++;
|
||||
}
|
||||
|
||||
private static boolean peek(Cursor c, char ch) {
|
||||
return !c.end() && c.ch() == ch;
|
||||
}
|
||||
|
||||
private static void expect(Cursor c, char ch) {
|
||||
if (c.end() || c.ch() != ch)
|
||||
throw new IllegalArgumentException("R_PUSH array literal parse error: expected '" + ch + "' at position " + c.i);
|
||||
c.i++; // consume
|
||||
skipWs(c);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user