feat: 支持数组类型及数组字面量

- 新增 ArrayLiteralNode 表示数组字面量表达式节点
- 实现 ArrayLiteralAnalyzer 进行数组字面量的语义分析
- 添加 ArrayType 表示数组类型,并支持多维数组
- 修改 Context 类,增加对数组类型的支持
- 更新 DeclarationStatementParser,支持多维数组类型的声明
- 在 CallGenerator 中添加对特殊函数 __index_i 的处理,用于数组索引操作
This commit is contained in:
Luke 2025-08-01 18:34:03 +08:00
parent f1069d6e5d
commit 82f4ba1a6e
27 changed files with 1339 additions and 329 deletions

View File

@ -13,42 +13,39 @@ import org.jcnc.snow.vm.engine.VMOpCode;
import java.util.*; import java.util.*;
/** /**
* <b>CallGenerator - IR {@code CallInstruction} 生成 VM 指令</b> * {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令
*
* <p> * <p>
* 本类负责将中间表示IR中的函数调用指令 {@link CallInstruction} 转换为虚拟机VM指令 * 支持
* 支持普通函数调用和特殊的 syscall 指令转换
* </p>
*
* <p>
* <b>能力说明</b>
* <ul> * <ul>
* <li>支持识别 {@code IRConstant} 直接字面量或已绑定到虚拟寄存器的字符串常量直接降级为 {@code SYSCALL &lt;SUBCMD&gt;} 指令</li> * <li>普通函数调用的参数压栈调用返回值保存</li>
* <li>对普通函数完成参数加载调用返回值保存等指令生成</li> * <li>特殊的 {@code syscall} 指令转为 VM SYSCALL 指令</li>
* <li>数组访问内置函数 {@code __index_i(arr, idx)} 的专用指令序列</li>
* </ul> * </ul>
* <p>
* 对于 syscall 子命令支持常量字符串和字符串寄存器两种来源并支持寄存器-字符串常量池注册机制
* </p> * </p>
*/ */
public class CallGenerator implements InstructionGenerator<CallInstruction> { public class CallGenerator implements InstructionGenerator<CallInstruction> {
/** /**
* <虚拟寄存器 id, 对应的字符串常量> * 字符串常量池用于绑定虚拟寄存器 id 到字符串值 syscall 子命令使用
* <p>用于记录虚拟寄存器与其绑定字符串常量的映射 {@link LoadConstGenerator} 在编译期间填充</p>
*/ */
private static final Map<Integer, String> STRING_CONST_POOL = new HashMap<>(); private static final Map<Integer, String> STRING_CONST_POOL = new HashMap<>();
/** /**
* 注册字符串常量到虚拟寄存器 * 注册一个字符串常量绑定到虚拟寄存器 id
* <p> {@link LoadConstGenerator} 在加载字符串常量时调用</p>
* *
* @param regId 虚拟寄存器 id * @param regId 虚拟寄存器 id
* @param value 字符串常量 * @param value 字符串常量
*/ */
public static void registerStringConst(int regId, String value) { public static void registerStringConst(int regId, String value) {
STRING_CONST_POOL.put(regId, value); STRING_CONST_POOL.put(regId, value);
} }
/** /**
* 返回本生成器支持的 IR 指令类型CallInstruction * 返回当前指令生成器支持的 IR 指令类型 {@link CallInstruction}
*
* @return {@code CallInstruction.class}
*/ */
@Override @Override
public Class<CallInstruction> supportedClass() { public Class<CallInstruction> supportedClass() {
@ -56,12 +53,12 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
} }
/** /**
* 生成 VM 指令的主逻辑 * 生成 VM 指令序列实现函数调用/特殊 syscall/数组索引等 IR 指令的转换
* *
* @param ins 当前 IR 指令函数调用 * @param ins 当前函数调用 IR 指令
* @param out 指令输出构建器 * @param out VM 指令输出构建器
* @param slotMap IR 虚拟寄存器与物理槽位映射 * @param slotMap IR 虚拟寄存器 VM 槽位映射表
* @param currentFn 当前函数名 * @param currentFn 当前函数名
*/ */
@Override @Override
public void generate(CallInstruction ins, public void generate(CallInstruction ins,
@ -79,7 +76,7 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
} }
// ---------- 0. 解析 syscall 子命令 ---------- // ---------- 0. 解析 syscall 子命令 ----------
// 子命令支持 IRConstant直接字面量或虚拟寄存器需已绑定字符串 // 支持 IRConstant 字面量或虚拟寄存器需已绑定字符串
String subcmd; String subcmd;
IRValue first = args.getFirst(); IRValue first = args.getFirst();
@ -88,13 +85,11 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
throw new IllegalStateException("syscall 第一个参数必须是字符串常量"); throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
subcmd = s.toUpperCase(Locale.ROOT); subcmd = s.toUpperCase(Locale.ROOT);
} else if (first instanceof IRVirtualRegister vr) { // 虚拟寄存器 } else if (first instanceof IRVirtualRegister vr) { // 来自寄存器的字符串
// 从常量池中查找是否已绑定字符串 String s = STRING_CONST_POOL.get(vr.id());
subcmd = Optional.ofNullable(STRING_CONST_POOL.get(vr.id())) if (s == null)
.orElseThrow(() -> throw new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr);
new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr)); subcmd = s.toUpperCase(Locale.ROOT);
subcmd = subcmd.toUpperCase(Locale.ROOT);
} else { } else {
throw new IllegalStateException("syscall 第一个参数必须是字符串常量"); throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
} }
@ -113,21 +108,54 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
return; // syscall 无返回值直接返回 return; // syscall 无返回值直接返回
} }
/* ========== 特殊处理内部索引函数__index_i(arr, idx) ========== */
if ("__index_i".equals(ins.getFunctionName())) {
// 加载参数arr 为引用类型idx 为整型
if (ins.getArguments().size() != 2) {
throw new IllegalStateException("__index_i 需要两个参数(arr, idx)");
}
IRVirtualRegister arr = (IRVirtualRegister) ins.getArguments().get(0);
IRVirtualRegister idx = (IRVirtualRegister) ins.getArguments().get(1);
int arrSlot = slotMap.get(arr);
int idxSlot = slotMap.get(idx);
char arrT = out.getSlotType(arrSlot);
if (arrT == '\0') arrT = 'R'; // 默认为引用类型
out.emit(OpHelper.opcode(arrT + "_LOAD") + " " + arrSlot);
char idxT = out.getSlotType(idxSlot);
if (idxT == '\0') idxT = 'I'; // 默认为整型
out.emit(OpHelper.opcode(idxT + "_LOAD") + " " + idxSlot);
// 调用 SYSCALL ARR_GET VM 取出数组元素并压回栈顶
out.emit(VMOpCode.SYSCALL + " " + "ARR_GET");
// 取回返回值并保存当前仅支持 int 元素
int destSlot = slotMap.get(ins.getDest());
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'I');
return;
}
/* ========== 普通函数调用 ========== */ /* ========== 普通函数调用 ========== */
// ---------- 1. 推断返回值类型 void 返回时用 ---------- // ---------- 1. 推断返回值类型 void 返回时用 ----------
char retType = 'I'; // 默认为整型 char retType = 'I'; // 默认为整型
if (!ins.getArguments().isEmpty()) { if (!ins.getArguments().isEmpty()) {
int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst()); // 简化根据第一个参数类型推断返回类型或者通过全局表拿到返回类型
retType = out.getSlotType(firstSlot); String ret = GlobalFunctionTable.getReturnType(ins.getFunctionName());
if (retType == '\0') retType = 'I'; if (ret != null) {
retType = Character.toUpperCase(ret.charAt(0));
}
} }
// ---------- 2. 加载全部实参 ---------- // ---------- 2. 压栈所有参数 ----------
for (var arg : ins.getArguments()) { for (IRValue arg : ins.getArguments()) {
int slotId = slotMap.get((IRVirtualRegister) arg); IRVirtualRegister vr = (IRVirtualRegister) arg;
int slotId = slotMap.get(vr);
char t = out.getSlotType(slotId); char t = out.getSlotType(slotId);
if (t == '\0') t = 'I'; // 默认整型 if (t == '\0') t = 'I';
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId); out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
} }

View File

@ -63,7 +63,8 @@ public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruc
case Float _ -> 'F'; // 单精度 case Float _ -> 'F'; // 单精度
case Boolean _ -> 'I'; // 布尔类型用 I 处理 case Boolean _ -> 'I'; // 布尔类型用 I 处理
case String _ -> 'R'; // 字符串常量 case String _ -> 'R'; // 字符串常量
case null, default -> // 其它类型异常 case java.util.List<?> _ -> 'R'; // 引用类型如数组等
case null, default ->
throw new IllegalStateException("未知的常量类型: " throw new IllegalStateException("未知的常量类型: "
+ (value != null ? value.getClass() : null)); + (value != null ? value.getClass() : null));
}; };

View File

@ -234,6 +234,7 @@ public final class OpHelper {
if (v instanceof Double) return "D"; if (v instanceof Double) return "D";
if (v instanceof Float) return "F"; if (v instanceof Float) return "F";
if (v instanceof String) return "R"; //引用类型 if (v instanceof String) return "R"; //引用类型
if (v instanceof java.util.List) return "R"; // 引用类型数组等
throw new IllegalStateException("Unknown const type: " + v.getClass()); throw new IllegalStateException("Unknown const type: " + v.getClass());
} }

View File

@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.ir.builder; package org.jcnc.snow.compiler.ir.builder;
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.instruction.CallInstruction; import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction; import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction; import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
@ -14,37 +15,26 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import java.util.*; import java.util.*;
/** /**
* <b>ExpressionBuilder - 表达式 IR 构建器</b> * {@code ExpressionBuilder} 表达式 IR 构建器
*
* <p> * <p>
* 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作并生成对应的 IR 指令序列 * 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作并生成对应的 IR 指令序列
* 支持字面量标识符二元表达式一元表达式函数调用等多种类型表达式 * 支持字面量标识符二元表达式一元表达式函数调用数组下标等多种类型表达式
* </p>
*
* <p>
* 主要功能
* <ul> * <ul>
* <li>将表达式节点映射为虚拟寄存器</li> * <li>将表达式节点映射为虚拟寄存器</li>
* <li>为每种表达式类型生成对应 IR 指令</li> * <li>为每种表达式类型生成对应 IR 指令</li>
* <li>支持表达式嵌套的递归构建</li> * <li>支持表达式嵌套的递归构建</li>
* <li>支持写入指定目标寄存器避免冗余的 move 指令</li> * <li>支持写入指定目标寄存器避免冗余的 move 指令</li>
* <li>支持 IndexExpressionNode 的编译期折叠arr[2]并自动降级为运行时调用 __index_i</li>
* </ul> * </ul>
* </p>
*/ */
public record ExpressionBuilder(IRContext ctx) { public record ExpressionBuilder(IRContext ctx) {
/* ───────────────── 顶层入口 ───────────────── */
/** /**
* 构建任意 AST 表达式节点自动为其分配一个新的虚拟寄存器并返回该寄存器 * 构建表达式返回存储其结果的虚拟寄存器
* *
* <p> * @param expr 要生成 IR 的表达式节点
* 这是表达式 IR 生成的核心入口它会根据不同的表达式类型进行分派递归构建 IR 指令 * @return 存储表达式值的虚拟寄存器
* </p> * @throws IllegalStateException 不支持的表达式类型或未定义标识符
*
* @param expr 任意 AST 表达式节点
* @return 存储该表达式结果的虚拟寄存器
* @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符
*/ */
public IRVirtualRegister build(ExpressionNode expr) { public IRVirtualRegister build(ExpressionNode expr) {
return switch (expr) { return switch (expr) {
@ -78,14 +68,20 @@ public record ExpressionBuilder(IRContext ctx) {
/* ───────────────── 写入指定寄存器 ───────────────── */ /* ───────────────── 写入指定寄存器 ───────────────── */
/** /**
* 生成表达式并将其结果直接写入目标寄存器避免冗余的 move 操作 * 将表达式节点 {@link ExpressionNode} 的结果写入指定的虚拟寄存器 {@code dest}
*
* <p> * <p>
* 某些简单表达式如字面量变量名可以直接写入目标寄存器复杂表达式则会先 build 到新寄存器 move 到目标寄存器 * 按表达式类型分派处理包括
* <ul>
* <li>字面量数字字符串布尔数组生成 loadConst 指令直接写入目标寄存器</li>
* <li>变量标识符查表获取源寄存器 move 到目标寄存器</li>
* <li>二元表达式与下标表达式递归生成子表达式结果并写入目标寄存器</li>
* <li>其它类型统一先 build 到临时寄存器 move 到目标寄存器</li>
* </ul>
* </p> * </p>
* *
* @param node 要生成的表达式节点 * @param node 要求值的表达式节点
* @param dest 目标虚拟寄存器用于存储结果 * @param dest 结果目标虚拟寄存器
* @throws IllegalStateException 若标识符未定义如变量未声明时引用
*/ */
public void buildInto(ExpressionNode node, IRVirtualRegister dest) { public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
switch (node) { switch (node) {
@ -103,8 +99,8 @@ public record ExpressionBuilder(IRContext ctx) {
case BoolLiteralNode b -> case BoolLiteralNode b ->
InstructionFactory.loadConstInto( InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(b.getValue() ? 1 : 0)); ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
case ArrayLiteralNode arr ->
// 标识符查表获取原寄存器然后 move 到目标寄存器 InstructionFactory.loadConstInto(ctx, dest, buildArrayConstant(arr));
case IdentifierNode id -> { case IdentifierNode id -> {
IRVirtualRegister src = ctx.getScope().lookup(id.name()); IRVirtualRegister src = ctx.getScope().lookup(id.name());
if (src == null) if (src == null)
@ -114,8 +110,10 @@ public record ExpressionBuilder(IRContext ctx) {
// 二元表达式递归生成并写入目标寄存器 // 二元表达式递归生成并写入目标寄存器
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest); case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
case IndexExpressionNode idx -> {
// 其它复杂情况 build 到新寄存器 move 到目标寄存器 IRVirtualRegister tmp = buildIndex(idx);
InstructionFactory.move(ctx, tmp, dest);
}
default -> { default -> {
IRVirtualRegister tmp = build(node); IRVirtualRegister tmp = build(node);
InstructionFactory.move(ctx, tmp, dest); InstructionFactory.move(ctx, tmp, dest);
@ -123,7 +121,79 @@ public record ExpressionBuilder(IRContext ctx) {
} }
} }
/* ───────────────── 具体表达式类型 ───────────────── */ /**
* 下标访问表达式处理支持编译期常量折叠数组和下标均为常量时直接求值
* 否则生成运行时调用 __index_i VM 降级为 ARR_GET
*
* @param node 下标访问表达式
* @return 存储结果的虚拟寄存器
*/
private IRVirtualRegister buildIndex(IndexExpressionNode node) {
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());
IRVirtualRegister idxReg = build(node.index());
IRVirtualRegister dest = ctx.newRegister();
List<IRValue> argv = new ArrayList<>();
argv.add(arrReg);
argv.add(idxReg);
ctx.addInstruction(new CallInstruction(dest, "__index_i", argv));
return dest;
}
/**
* 尝试将表达式折叠为编译期常量支持嵌套
* 支持数字字符串布尔数组常量标识符
*
* @param expr 要折叠的表达式节点
* @return 常量对象如数字字符串List否则返回 null
*/
private Object tryFoldConst(ExpressionNode expr) {
if (expr == null) return null;
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);
} catch (NumberFormatException e) {
return null;
}
}
if (expr instanceof StringLiteralNode s) {
return s.value();
}
if (expr instanceof BoolLiteralNode b) {
return b.getValue() ? 1 : 0;
}
if (expr instanceof ArrayLiteralNode arr) {
java.util.List<Object> list = new java.util.ArrayList<>();
for (ExpressionNode e : arr.elements()) {
Object v = tryFoldConst(e);
if (v == null) return null;
list.add(v);
}
return java.util.List.copyOf(list);
}
if (expr instanceof IdentifierNode id) {
Object v = null;
try {
v = ctx.getScope().getConstValue(id.name());
} catch (Throwable ignored) {}
return v;
}
return null;
}
/** /**
* 一元表达式构建 * 一元表达式构建
@ -285,4 +355,41 @@ public record ExpressionBuilder(IRContext ctx) {
ctx.addInstruction(new LoadConstInstruction(r, c)); ctx.addInstruction(new LoadConstInstruction(r, c));
return r; return r;
} }
/**
* 构建数组字面量表达式元素均为常量时
*
* @param arr 数组字面量节点
* @return 存储该数组的寄存器
*/
private IRVirtualRegister buildArrayLiteral(ArrayLiteralNode arr) {
IRConstant c = buildArrayConstant(arr);
IRVirtualRegister r = ctx.newRegister();
ctx.addInstruction(new LoadConstInstruction(r, c));
return r;
}
/**
* 构建数组常量所有元素均为数字字符串或布尔常量
*
* @param arr 数组字面量节点
* @return 数组 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);
default -> throw new IllegalStateException(
"暂不支持含非常量元素的数组字面量: " + e.getClass().getSimpleName());
}
}
return new IRConstant(List.copyOf(list));
}
} }

View File

@ -7,109 +7,143 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* IRBuilderScope 用于管理单个函数内变量名与虚拟寄存器的映射关系 * {@code IRBuilderScope} 用于管理单个函数作用域变量名与虚拟寄存器的映射关系
* * <p>
* <p>主要功能包括: * 主要职责
* <ul> * <ul>
* <li>维护在当前作用域中已声明变量的寄存器分配信息</li> * <li>维护当前作用域内所有已声明变量的寄存器和类型信息</li>
* <li>支持将已有虚拟寄存器与变量名重新绑定</li> * <li>支持变量与虚拟寄存器的重新绑定与查找</li>
* <li>根据变量名查找对应的虚拟寄存器实例或类型</li> * <li>支持变量的类型信息记录与查询</li>
* <li>支持变量的编译期常量值记录与查询便于常量折叠等优化</li>
* </ul> * </ul>
*/ */
final class IRBuilderScope { final class IRBuilderScope {
/** /**
* 存储变量名到对应 IRVirtualRegister 的映射 * 变量名到虚拟寄存器的映射表
* 变量名为键虚拟寄存器对象为值用于查找和更新 * 用于跟踪所有声明和分配的变量
*/ */
private final Map<String, IRVirtualRegister> vars = new HashMap<>(); private final Map<String, IRVirtualRegister> vars = new HashMap<>();
/** /**
* 存储变量名到对应类型的映射 * 变量名到类型字符串的映射表
* <br> * 用于类型分析与推断
* 变量名为键变量类型为值用于变量类型提升
*/ */
private final Map<String, String> varTypes = new HashMap<>(); private final Map<String, String> varTypes = new HashMap<>();
/** /**
* 当前作用域所绑定的 IRFunction 对象用于申请新的虚拟寄存器 * 变量名到编译期常量值的映射表
* 用于常量折叠优化 intstring数组等常量
*/
private final Map<String, Object> varConstValues = new HashMap<>();
/**
* 当前作用域所绑定的 IRFunction 实例
* 用于申请新的虚拟寄存器
*/ */
private IRFunction fn; private IRFunction fn;
/** /**
* 将指定的 IRFunction 关联到当前作用域以便后续声明变量时能够 * 绑定当前作用域到指定 IRFunction
* 调用该函数的 newRegister() 方法生成新的寄存器
* *
* @param fn 要绑定到本作用域的 IRFunction 实例 * @param fn 目标 IRFunction
*/ */
void attachFunction(IRFunction fn) { void attachFunction(IRFunction fn) {
this.fn = fn; this.fn = fn;
} }
/** /**
* 在当前作用域中声明一个新变量并为其分配一个新的虚拟寄存器 * 声明一个新变量并分配新的虚拟寄存器
* 调用绑定的 IRFunction.newRegister() 生成寄存器后保存到映射表中
* *
* @param name 变量名称作为映射键使用 * @param name 变量名称
* @param type 变量类型 * @param type 变量类型
*/ */
void declare(String name, String type) { void declare(String name, String type) {
IRVirtualRegister reg = fn.newRegister(); IRVirtualRegister reg = fn.newRegister();
vars.put(name, reg); vars.put(name, reg);
varTypes.put(name, type); varTypes.put(name, type);
varConstValues.remove(name);
} }
/** /**
* 在当前作用域中声明或导入一个已有的虚拟寄存器并将其与指定变量名绑定 * 声明新变量并绑定到指定的寄存器
* 该方法可用于将外部或前一作用域的寄存器导入到本作用域
* *
* @param name 变量名称作为映射键使用 * @param name 变量名称
* @param type 变量类型 * @param type 变量类型
* @param reg 要绑定到该名称的 IRVirtualRegister 实例 * @param reg 绑定的虚拟寄存器
*/ */
void declare(String name, String type, IRVirtualRegister reg) { void declare(String name, String type, IRVirtualRegister reg) {
vars.put(name, reg); vars.put(name, reg);
varTypes.put(name, type); varTypes.put(name, type);
varConstValues.remove(name);
} }
/** /**
* 更新已存在变量的虚拟寄存器绑定关系若变量已声明则替换其对应的寄存器 * 更新变量的虚拟寄存器绑定如变量已存在则覆盖否则等同于新声明
* 若尚未声明则等同于声明新变量
* *
* @param name 变量名称作为映射键使用 * @param name 变量名称
* @param reg 新的 IRVirtualRegister 实例用于替换旧绑定 * @param reg 新的虚拟寄存器
*/ */
void put(String name, IRVirtualRegister reg) { void put(String name, IRVirtualRegister reg) {
vars.put(name, reg); vars.put(name, reg);
} }
/** /**
* 根据变量名称在当前作用域中查找对应的虚拟寄存器 * 查找变量名对应的虚拟寄存器
* *
* @param name 需要查询的变量名称 * @param name 变量名
* @return 如果该名称已绑定寄存器则返回对应的 IRVirtualRegister * @return 已绑定的虚拟寄存器若未声明则返回 null
* 如果未声明则返回 null
*/ */
IRVirtualRegister lookup(String name) { IRVirtualRegister lookup(String name) {
return vars.get(name); return vars.get(name);
} }
/** /**
* 根据变量名称在当前作用域中查找对应的类型 * 查找变量名对应的类型名
* *
* @param name 需要查询的变量名称 * @param name 变量名
* @return 如果该名称已声明则返回对应的类型 * @return 已声明类型字符串若未声明则返回 null
* 如果未声明则返回 null
*/ */
String lookupType(String name) { String lookupType(String name) {
return varTypes.get(name); return varTypes.get(name);
} }
/** /**
* 获取 变量->类型的映射 的不可变副本 * 获取变量名到类型名映射的不可变副本
* @return 变量->类型的映射 的不可变副本 *
* @return 变量名类型名映射的只读视图
*/ */
Map<String, String> getVarTypes() { Map<String, String> getVarTypes() {
return Map.copyOf(varTypes); return Map.copyOf(varTypes);
} }
// ---------------- 编译期常量相关接口 ----------------
/**
* 设置变量的编译期常量值
*
* @param name 变量名称
* @param value 常量值null 表示清除
*/
void setConstValue(String name, Object value) {
if (value == null) varConstValues.remove(name);
else varConstValues.put(name, value);
}
/**
* 获取变量的编译期常量值如没有则返回 null
*
* @param name 变量名称
* @return 编译期常量值 null
*/
Object getConstValue(String name) {
return varConstValues.get(name);
}
/**
* 清除变量的编译期常量值绑定
*
* @param name 变量名称
*/
void clearConstValue(String name) {
varConstValues.remove(name);
}
} }

View File

@ -10,13 +10,23 @@ 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.ArrayDeque; import java.util.ArrayDeque;
import java.util.Locale;
/** /**
* StatementBuilder AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器 * <b>StatementBuilder</b> AST 语句节点 ({@link StatementNode}) IR 指令序列的构建器
* <p> * <p>
* 负责将各种语句节点循环分支表达式赋值声明返回等生成对应的 IR 指令并管理作用域和控制流标签 * 负责将各类语句如循环分支表达式赋值声明返回breakcontinue
* 转换为对应的 IR 指令并自动管理作用域虚拟寄存器分配以及控制流标签 break/continue 目标
* </p> * </p>
*
* <ul>
* <li>支持多种语句类型的分发与转换</li>
* <li> {@link ExpressionBuilder} 协作完成表达式相关 IR 生成</li>
* <li>负责控制流跳转分支循环的标签分配与维护</li>
* <li>在变量赋值和声明时自动常量折叠和登记</li>
* </ul>
*
* @author [你的名字]
* @since 1.0
*/ */
public class StatementBuilder { public class StatementBuilder {
@ -24,77 +34,73 @@ public class StatementBuilder {
* 当前 IR 上下文包含作用域指令序列等信息 * 当前 IR 上下文包含作用域指令序列等信息
*/ */
private final IRContext ctx; private final IRContext ctx;
/** /**
* 表达式 IR 构建器用于将表达式节点转为 IR 指令 * 表达式 IR 构建器用于将表达式节点转为 IR 指令
*/ */
private final ExpressionBuilder expr; private final ExpressionBuilder expr;
/** /**
* break 目标标签栈保存每层循环的结束标签 * break 目标标签栈保存每层循环的结束标签用于 break 跳转
*/ */
private final ArrayDeque<String> breakTargets = new ArrayDeque<>(); private final ArrayDeque<String> breakTargets = new ArrayDeque<>();
/** /**
* continue 目标标签栈保存每层循环的 step 起始标签 * continue 目标标签栈保存每层循环的 step 起始标签用于 continue 跳转
*/ */
private final ArrayDeque<String> continueTargets = new ArrayDeque<>(); private final ArrayDeque<String> continueTargets = new ArrayDeque<>();
/** /**
* 构造方法 * 构造方法初始化 StatementBuilder
* *
* @param ctx IR 编译上下文环境 * @param ctx IR 编译上下文环境包含作用域标签指令等信息
*/ */
public StatementBuilder(IRContext ctx) { public StatementBuilder(IRContext ctx) {
this.ctx = ctx; this.ctx = ctx;
this.expr = new ExpressionBuilder(ctx); this.expr = new ExpressionBuilder(ctx);
} }
private static char typeSuffixFromType(String type) {
if (type == null) return '\0';
return switch (type.toLowerCase(Locale.ROOT)) {
case "byte" -> 'b';
case "short" -> 's';
case "long" -> 'l';
case "float" -> 'f';
case "double" -> 'd';
default -> '\0'; // 其余默认按 32-bit 整型处理
};
}
/** /**
* 将一个 AST 语句节点转为 IR 指令序列 * 将一个 AST 语句节点转为 IR 指令序列
* <p>
* 根据节点类型分发到对应的处理方法 * 根据节点类型分发到对应的处理方法
* 支持循环分支表达式语句赋值声明返回breakcontinue
* 不支持的语句类型会抛出异常
* </p>
* *
* @param stmt 待转换的语句节点 * @param stmt 待转换的语句节点不能为空
* @throws IllegalStateException 若遇到不支持的语句类型 break/continue 不在循环体中
*/ */
public void build(StatementNode stmt) { public void build(StatementNode stmt) {
if (stmt instanceof LoopNode loop) { if (stmt instanceof LoopNode loop) {
// 循环语句
buildLoop(loop); buildLoop(loop);
return; return;
} }
if (stmt instanceof IfNode ifNode) { if (stmt instanceof IfNode ifNode) {
// 分支if-else语句
buildIf(ifNode); buildIf(ifNode);
return; return;
} }
if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, NodeContext _)) { if (stmt instanceof ExpressionStatementNode(ExpressionNode exp, NodeContext _)) {
// 纯表达式语句 foo();
expr.build(exp); expr.build(exp);
return; return;
} }
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) { if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) {
// 赋值语句 a = b + 1;
final String type = ctx.getScope().lookupType(var); final String type = ctx.getScope().lookupType(var);
// 1. 设置声明变量的类型
ctx.setVarType(type); ctx.setVarType(type);
IRVirtualRegister target = getOrDeclareRegister(var, type); IRVirtualRegister target = getOrDeclareRegister(var, type);
expr.buildInto(rhs, target); expr.buildInto(rhs, target);
// 2. 清除变量声明 // 赋值时尝试记录/清除常量
ctx.clearVarType(); try {
Object constVal = tryFoldConst(rhs);
if (constVal != null)
ctx.getScope().setConstValue(var, constVal);
else
ctx.getScope().clearConstValue(var);
} catch (Throwable ignored) {
}
ctx.clearVarType();
return; return;
} }
if (stmt instanceof DeclarationNode decl) { if (stmt instanceof DeclarationNode decl) {
@ -113,16 +119,21 @@ public class StatementBuilder {
// 即使初始值是某个已存在变量 outer_i这里是值的拷贝 // 即使初始值是某个已存在变量 outer_i这里是值的拷贝
expr.buildInto(decl.getInitializer().get(), dest); expr.buildInto(decl.getInitializer().get(), dest);
// 4. 清理类型设置防止影响后续变量声明 // 声明赋初值时登记常量
ctx.clearVarType(); try {
Object constVal = tryFoldConst(decl.getInitializer().get());
if (constVal != null)
ctx.getScope().setConstValue(decl.getName(), constVal);
else
ctx.getScope().clearConstValue(decl.getName());
} catch (Throwable ignored) {
}
// 5. 在作用域内将变量名与新分配的寄存器进行绑定 ctx.clearVarType();
// 这样后续对该变量的任何操作都只会影响 dest不会反向影响初值表达式中的源变量
ctx.getScope().declare(decl.getName(), decl.getType(), dest); ctx.getScope().declare(decl.getName(), decl.getType(), dest);
} else { } else {
// 仅声明变量无初值 int a;
// 在作用域内声明并分配新寄存器但不进行初始化
ctx.getScope().declare(decl.getName(), decl.getType()); ctx.getScope().declare(decl.getName(), decl.getType());
ctx.getScope().clearConstValue(decl.getName());
} }
return; return;
} }
@ -159,10 +170,11 @@ public class StatementBuilder {
} }
/** /**
* 获取变量名对应的寄存器不存在则声明一个新的 * 获取变量名对应的寄存器如果尚未声明则新声明一个并返回
* *
* @param name 变量名 * @param name 变量名不能为空
* @return 变量对应的虚拟寄存器 * @param type 变量类型不能为空
* @return 变量对应的虚拟寄存器 {@link IRVirtualRegister}
*/ */
private IRVirtualRegister getOrDeclareRegister(String name, String type) { private IRVirtualRegister getOrDeclareRegister(String name, String type) {
IRVirtualRegister reg = ctx.getScope().lookup(name); IRVirtualRegister reg = ctx.getScope().lookup(name);
@ -174,19 +186,21 @@ public class StatementBuilder {
} }
/** /**
* 批量构建一组语句节点顺序处理每个语句 * 批量构建一组语句节点顺序依次处理
* *
* @param stmts 语句节点集合 * @param stmts 语句节点集合不可为 null
*/ */
private void buildStatements(Iterable<StatementNode> stmts) { private void buildStatements(Iterable<StatementNode> stmts) {
for (StatementNode s : stmts) build(s); for (StatementNode s : stmts) build(s);
} }
/** /**
* 构建循环语句for/while * 构建循环语句for/while包括初始语句条件判断循环体更新语句跳回判断等 IR 指令
* 处理流程: 初始语句 条件判断 循环体 更新语句 跳回条件 * <p>
* 自动维护 break/continue 的目标标签
* </p>
* *
* @param loop 循环节点 * @param loop 循环节点不能为空
*/ */
private void buildLoop(LoopNode loop) { private void buildLoop(LoopNode loop) {
if (loop.init() != null) build(loop.init()); if (loop.init() != null) build(loop.init());
@ -222,9 +236,11 @@ public class StatementBuilder {
/** /**
* 构建分支语句if/else * 构建分支语句if/else
* 处理流程: 条件判断 then 分支 else 分支可选 * <p>
* 包括条件判断then 分支else 分支可选结束标签等
* </p>
* *
* @param ifNode if 语句节点 * @param ifNode if 语句节点不能为空
*/ */
private void buildIf(IfNode ifNode) { private void buildIf(IfNode ifNode) {
String lblElse = ctx.newLabel(); String lblElse = ctx.newLabel();
@ -245,11 +261,14 @@ public class StatementBuilder {
} }
/** /**
* 条件跳转指令的生成 * 发射条件跳转指令如果 cond 不成立则跳转到 falseLabel
* 如果是二元比较表达式直接使用对应比较操作码否则等价于与 0 比较 * <p>
* 对于二元比较表达式会选择恰当的比较指令
* 其他类型表达式等价于 (cond == 0) 时跳转
* </p>
* *
* @param cond 条件表达式 * @param cond 条件表达式节点不可为 null
* @param falseLabel 条件不成立时跳转到的标签 * @param falseLabel 条件不成立时跳转的标签不可为 null
*/ */
private void emitConditionalJump(ExpressionNode cond, String falseLabel) { private void emitConditionalJump(ExpressionNode cond, String falseLabel) {
if (cond instanceof BinaryExpressionNode( if (cond instanceof BinaryExpressionNode(
@ -271,7 +290,79 @@ public class StatementBuilder {
} else { } else {
IRVirtualRegister condReg = expr.build(cond); IRVirtualRegister condReg = expr.build(cond);
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0); IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
InstructionFactory.cmpJump(ctx, IROpCode.CMP_IEQ, condReg, zero, falseLabel); InstructionFactory.cmpJump(ctx, org.jcnc.snow.compiler.ir.core.IROpCode.CMP_IEQ, condReg, zero, falseLabel);
} }
} }
/**
* 递归尝试对表达式做常量折叠constant folding
* <p>
* 该方法会根据表达式类型进行常量求值
* <ul>
* <li>如果是数字字面量解析为 Integer Double</li>
* <li>如果是字符串字面量直接返回字符串内容</li>
* <li>如果是布尔字面量返回 1true 0false</li>
* <li>如果是数组字面量会递归所有元素全部是常量时返回包含常量元素的 List</li>
* <li>如果是标识符节点尝试从作用域查找是否登记为常量如果找到则返回</li>
* <li>其余类型或无法确定常量时返回 null</li>
* </ul>
* 用于全局/局部常量传播优化与类型推断
*
* @param expr 待折叠的表达式节点允许为 null
* @return 如果可折叠则返回其常量值 IntegerDoubleStringList否则返回 null
*/
private Object tryFoldConst(ExpressionNode expr) {
// 1. 空节点直接返回 null
if (expr == null) return null;
// 2. 数字字面量尝试解析为 Integer Double
if (expr instanceof NumberLiteralNode n) {
String s = n.value(); // 获取文本内容
try {
// 判断是否为浮点型包含 . e/E 科学计数法
if (s.contains(".") || s.contains("e") || s.contains("E")) {
return Double.parseDouble(s); // 解析为 Double
}
return Integer.parseInt(s); // 否则解析为 Integer
} catch (NumberFormatException e) {
// 解析失败返回 null
return null;
}
}
// 3. 字符串字面量直接返回字符串内容
if (expr instanceof StringLiteralNode s) {
return s.value();
}
// 4. 布尔字面量true 返回 1false 返回 0
if (expr instanceof BoolLiteralNode b) {
return b.getValue() ? 1 : 0;
}
// 5. 数组字面量递归所有元素做常量折叠只有全为常量时才返回 List
if (expr instanceof ArrayLiteralNode arr) {
java.util.List<Object> list = new java.util.ArrayList<>();
for (ExpressionNode e : arr.elements()) {
Object v = tryFoldConst(e); // 递归折叠每个元素
if (v == null) return null; // 只要有一个不是常量则整个数组不是常量
list.add(v);
}
// 所有元素均为常量返回只读 List
return java.util.List.copyOf(list);
}
// 6. 标识符尝试查找该变量在当前作用域是否登记为常量
if (expr instanceof IdentifierNode id) {
try {
Object v = ctx.getScope().getConstValue(id.name());
if (v != null) return v; // 查到常量则返回
} catch (Throwable ignored) {
}
}
// 7. 其他情况均视为不可折叠返回 null
return null;
}
} }

View File

@ -28,7 +28,7 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
*/ */
@Override @Override
public boolean canHandle(char c, LexerContext ctx) { public boolean canHandle(char c, LexerContext ctx) {
return ":,().+-*/".indexOf(c) >= 0; return ":,().+-*/[]".indexOf(c) >= 0;
} }
/** /**
@ -53,6 +53,8 @@ public class SymbolTokenScanner extends AbstractTokenScanner {
case '/' -> TokenType.DIVIDE; case '/' -> TokenType.DIVIDE;
case '(' -> TokenType.LPAREN; case '(' -> TokenType.LPAREN;
case ')' -> TokenType.RPAREN; case ')' -> TokenType.RPAREN;
case '[' -> TokenType.LBRACKET;
case ']' -> TokenType.RBRACKET;
default -> TokenType.UNKNOWN; default -> TokenType.UNKNOWN;
}; };
return new Token(type, String.valueOf(c), line, col); return new Token(type, String.valueOf(c), line, col);

View File

@ -14,10 +14,10 @@ public enum TokenType {
/** 普通标识符,如变量名、函数名等 */ /** 普通标识符,如变量名、函数名等 */
IDENTIFIER, IDENTIFIER,
/** 语言保留关键字(如 if、return、module 等) */ /** 关键字declare、if、else、loop、break、continue、return 等) */
KEYWORD, KEYWORD,
/** 内置类型名称(如 int、string、bool 等) */ /** 内置类型名byte、short、int、long、float、double、string、boolean、void 等) */
TYPE, TYPE,
/* ---------- 字面量 ---------- */ /* ---------- 字面量 ---------- */
@ -67,6 +67,12 @@ public enum TokenType {
/** 右括号 ')' */ /** 右括号 ')' */
RPAREN, RPAREN,
/** 左中括号 '[' */
LBRACKET,
/** 右中括号 ']' */
RBRACKET,
/** 相等比较符号 '==' */ /** 相等比较符号 '==' */
DOUBLE_EQUALS, DOUBLE_EQUALS,

View File

@ -0,0 +1,40 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.List;
/**
* {@code ArrayLiteralNode} 表示数组字面量表达式节点
* <p>
* 例如[1, 2, 3] [[1, 2], [3, 4]] 这样的语法结构均对应本节点类型
* </p>
*
* <ul>
* <li>{@link #elements()} 保存所有元素表达式节点</li>
* <li>{@link #context()} 表示该节点在源代码中的上下文信息如位置父节点等</li>
* </ul>
*/
public record ArrayLiteralNode(
/*
数组字面量中的所有元素表达式按顺序
*/
List<ExpressionNode> elements,
/*
节点的上下文信息如源码位置等
*/
NodeContext context
) implements ExpressionNode {
/**
* 返回字符串形式 {@code Array[1, 2, 3]}
*
* @return 表示该数组字面量节点的字符串
*/
@Override
public String toString() {
return "Array" + elements;
}
}

View File

@ -0,0 +1,42 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
/**
* {@code IndexAssignmentNode} 表示数组元素赋值语句节点例如 {@code arr[i] = value}
* <p>
* 对于多维数组{@code target} 可以是嵌套的 {@link IndexExpressionNode}
* </p>
*
* @param target 要赋值的数组元素支持多维数组下标
* @param value 右侧赋值表达式
* @param context 该节点的源代码上下文信息
*/
public record IndexAssignmentNode(
/*
数组元素目标支持多维数组下标 arr[i][j]
*/
IndexExpressionNode target,
/*
被赋的右侧表达式
*/
ExpressionNode value,
/*
节点的上下文信息如源码位置等
*/
NodeContext context
) implements StatementNode {
/**
* 返回赋值语句的字符串表示 "arr[i] = value"
*
* @return 字符串形式
*/
@Override
public String toString() {
return target + " = " + value;
}
}

View File

@ -0,0 +1,41 @@
package org.jcnc.snow.compiler.parser.ast;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
/**
* {@code IndexExpressionNode} 表示数组/集合的下标访问表达式节点例如 {@code arr[i]}
* <p>
* 支持多维数组嵌套 arr[i][j]
* </p>
*
* @param array 被访问的目标表达式通常是数组集合或嵌套下标表达式
* @param index 下标表达式必须为数值类型
* @param context 源码上下文信息如位置
*/
public record IndexExpressionNode(
/*
下标访问的目标表达式 arr
*/
ExpressionNode array,
/*
下标表达式 i
*/
ExpressionNode index,
/*
源码上下文信息如行号文件名等
*/
NodeContext context
) implements ExpressionNode {
/**
* 返回形如 "arr[i]" 的字符串表示
* @return 表达式的字符串形式
*/
@Override
public String toString() {
return array + "[" + index + "]";
}
}

View File

@ -0,0 +1,80 @@
package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.ArrayLiteralNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
import java.util.ArrayList;
import java.util.List;
/**
* {@code ArrayLiteralParselet} 用于解析数组字面量表达式
* <p>
* 支持语法形如 {@code [a, b, c]} {@code [ [1,2], [3,4] ]}
* 允许元素及逗号前后出现换行符便于多行书写
* </p>
* <p>
* 语法结构为<br>
* <pre>[ (element (',' element)*)? ]</pre>
* </p>
*/
public class ArrayLiteralParselet implements PrefixParselet {
/**
* 解析数组字面量表达式
*
* @param ctx 解析上下文包含词法流源文件信息等
* @param token 已消费的左中括号LBRACKET用于定位节点源信息
* @return 解析得到的 {@link ArrayLiteralNode} 节点
*/
@Override
public ExpressionNode parse(ParserContext ctx, Token token) {
// token 为已消费的 LBRACKET使用其位置生成 NodeContext
int line = token.getLine();
int col = token.getCol();
String file = ctx.getSourceName();
TokenStream ts = ctx.getTokens();
skipNewlines(ts);
List<ExpressionNode> elements = new ArrayList<>();
// 空数组: 直接遇到 RBRACKET
if (ts.peek().getType() != TokenType.RBRACKET) {
while (true) {
// 解析一个元素
ExpressionNode elem = new PrattExpressionParser().parse(ctx);
elements.add(elem);
skipNewlines(ts);
// 逗号继续右中括号结束
if (ts.peek().getType() == TokenType.COMMA) {
ts.next();
skipNewlines(ts);
continue;
}
break;
}
}
// 期望并消费右中括号
ts.expectType(TokenType.RBRACKET);
return new ArrayLiteralNode(elements, new NodeContext(line, col, file));
}
/**
* 跳过词法流中连续的换行符允许数组元素跨多行书写
*
* @param ts 词法流
*/
private static void skipNewlines(TokenStream ts) {
while (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
}
}
}

View File

@ -0,0 +1,51 @@
package org.jcnc.snow.compiler.parser.expression;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.base.InfixParselet;
/**
* {@code IndexParselet} 负责解析数组或集合的下标访问表达式语法结构为
* <pre>
* primary '[' expr ']'
* </pre>
* 例如 {@code arr[expr]}
*/
public class IndexParselet implements InfixParselet {
/**
* 解析下标访问表达式
*
* @param ctx 解析上下文包含词法流错误信息等
* @param left 已解析的 primary 表达式如数组名表达式等
* @return {@link IndexExpressionNode} 下标访问表达式节点
*/
@Override
public ExpressionNode parse(ParserContext ctx, ExpressionNode left) {
int line = left.context().line();
int col = left.context().column();
String file = left.context().file();
// 消耗左中括号 '['
ctx.getTokens().expectType(TokenType.LBRACKET);
// 解析索引表达式
ExpressionNode index = new PrattExpressionParser().parse(ctx);
// 消耗右中括号 ']'
ctx.getTokens().expectType(TokenType.RBRACKET);
// 构造下标访问表达式节点
return new IndexExpressionNode(left, index, new NodeContext(line, col, file));
}
/**
* 下标访问优先级与函数调用一致
*
* @return {@link Precedence#CALL} 优先级
*/
@Override
public Precedence precedence() {
return Precedence.CALL;
}
}

View File

@ -25,45 +25,80 @@ import java.util.Map;
*/ */
public class PrattExpressionParser implements ExpressionParser { public class PrattExpressionParser implements ExpressionParser {
/** 前缀解析器注册表(按 Token 类型名索引) */ /**
* 前缀解析器注册表 Token 类型名或词素索引
* <p>
* 用于存储所有支持的前缀表达式解析器例如字面量变量分组数组一元运算等
* 支持通过 TokenType 的名称和特定词素 "(", "["两种方式索引
* </p>
*/
private static final Map<String, PrefixParselet> prefixes = new HashMap<>(); private static final Map<String, PrefixParselet> prefixes = new HashMap<>();
/** 中缀解析器注册表(按运算符词素索引) */
/**
* 中缀解析器注册表按运算符词素索引
* <p>
* 用于存储所有支持的中缀表达式解析器如二元运算函数调用下标成员访问等
* 仅通过词素索引 "+", "-", "(", "["
* </p>
*/
private static final Map<String, InfixParselet> infixes = new HashMap<>(); private static final Map<String, InfixParselet> infixes = new HashMap<>();
static { static {
// 前缀解析器注册 // -------- 前缀解析器注册 --------
// 注册数字字面量解析
prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet()); prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
// 注册标识符变量名解析
prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet()); prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
prefixes.put(TokenType.LPAREN.name(), new GroupingParselet()); // 注册字符串字面量解析
prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet()); prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
// 注册布尔字面量解析
prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet()); prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
// 一元前缀运算符 // 支持括号分组数组字面量
prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet());
// 兼容直接以词素注册 '(' '['
prefixes.put("(", new GroupingParselet());
prefixes.put("[", new ArrayLiteralParselet());
// 一元前缀运算符负号逻辑非
prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet()); prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet());
prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet()); prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
prefixes.put("-", new UnaryOperatorParselet());
prefixes.put("!", new UnaryOperatorParselet());
// 中缀解析器注册 // -------- 中缀解析器注册 --------
// 注册常见二元运算符加减乘除取模
infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true)); infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true)); infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true)); infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true)); infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true)); infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true)); // 比较运算符
infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true)); infixes.put(">", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true)); infixes.put("<", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put("!=", new BinaryOperatorParselet(Precedence.SUM, true)); infixes.put(">=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put(">=", new BinaryOperatorParselet(Precedence.SUM, true)); infixes.put("<=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put("<=", new BinaryOperatorParselet(Precedence.SUM, true)); // 相等性
infixes.put("==", new BinaryOperatorParselet(Precedence.EQUALITY, true));
infixes.put("!=", new BinaryOperatorParselet(Precedence.EQUALITY, true));
// 逻辑与或
infixes.put("&&", new BinaryOperatorParselet(Precedence.AND, true));
infixes.put("||", new BinaryOperatorParselet(Precedence.OR, true));
// 调用索引成员访问
infixes.put("(", new CallParselet()); infixes.put("(", new CallParselet());
infixes.put("[", new IndexParselet());
infixes.put(".", new MemberParselet()); infixes.put(".", new MemberParselet());
} }
/** /**
* 表达式解析统一入口 * 解析任意表达式的统一入口
* 以最低优先级启动递归下降适配任意表达式复杂度 * <p>
* 该方法将以最低优先级启动表达式递归解析能够自动适配和处理多层嵌套或复杂组合表达式
* </p>
* *
* @param ctx 当前解析上下文 * @param ctx 当前解析上下文对象持有 token 流等信息
* @return 解析的表达式 AST 节点 * @return 解析得到的表达式 AST 节点对象
*/ */
@Override @Override
public ExpressionNode parse(ParserContext ctx) { public ExpressionNode parse(ParserContext ctx) {
@ -71,21 +106,32 @@ public class PrattExpressionParser implements ExpressionParser {
} }
/** /**
* 按指定优先级解析表达式Pratt 算法主循环 * 按指定优先级解析表达式Pratt 算法核心
* <p> * <p>
* 先根据当前 Token 类型查找前缀解析器进行初始解析 * 1. 先取当前 token查找对应的前缀解析器进行初始解析构建表达式左侧如字面量变量等
* 然后根据优先级不断递归处理中缀运算符和右侧表达式 * 2. 然后循环检测是否有更高优先级的中缀操作符
* 若有则递归处理右侧表达式并组合为新的表达式节点
* </p>
* <p>
* 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常
* </p> * </p>
* *
* @param ctx 解析上下文 * @param ctx 解析上下文
* @param prec 当前运算符优先级阈值 * @param prec 当前运算符优先级用于控制递归层级
* @return 构建完成的表达式节点 * @return 解析构建好的表达式节点
* @throws UnsupportedFeature 遇到未注册的前缀或中缀解析器 * @throws UnsupportedFeature 遇到未注册的解析器时抛出
*/ */
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) { ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
// 取下一个 token 作为本轮前缀表达式起始
Token token = ctx.getTokens().next(); Token token = ctx.getTokens().next();
// 查找前缀解析器先按类型名再按词素
PrefixParselet prefix = prefixes.get(token.getType().name()); PrefixParselet prefix = prefixes.get(token.getType().name());
if (prefix == null) { if (prefix == null) {
prefix = prefixes.get(token.getLexeme());
}
if (prefix == null) {
// 未找到前缀解析器则报错
throw new UnsupportedFeature( throw new UnsupportedFeature(
"没有为该 Token 类型注册前缀解析器: " + token.getType(), "没有为该 Token 类型注册前缀解析器: " + token.getType(),
token.getLine(), token.getLine(),
@ -93,13 +139,17 @@ public class PrattExpressionParser implements ExpressionParser {
); );
} }
// 执行前缀解析获得左侧表达式
ExpressionNode left = prefix.parse(ctx, token); ExpressionNode left = prefix.parse(ctx, token);
// 不断尝试查找优先级更高的中缀运算符递归处理表达式链
while (!ctx.getTokens().isAtEnd() while (!ctx.getTokens().isAtEnd()
&& prec.ordinal() < nextPrecedence(ctx)) { && prec.ordinal() < nextPrecedence(ctx)) {
// 查看下一个 token 词素查找中缀解析器
String lex = ctx.getTokens().peek().getLexeme(); String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex); InfixParselet infix = infixes.get(lex);
if (infix == null) { if (infix == null) {
// 若未注册中缀解析器则直接抛异常常见于语法错误
Token t = ctx.getTokens().peek(); Token t = ctx.getTokens().peek();
throw new UnsupportedFeature( throw new UnsupportedFeature(
"没有为该运算符注册中缀解析器: '" + lex + "'", "没有为该运算符注册中缀解析器: '" + lex + "'",
@ -107,16 +157,21 @@ public class PrattExpressionParser implements ExpressionParser {
t.getCol() t.getCol()
); );
} }
// 使用中缀解析器处理表达式组合
left = infix.parse(ctx, left); left = infix.parse(ctx, left);
} }
// 返回本层递归已解析的表达式节点
return left; return left;
} }
/** /**
* 获取下一个中缀解析器的优先级Pratt 算法核心 * 获取下一个 token 词素对应的中缀运算符优先级Pratt 算法关键
* <p>
* 用于决定当前是否需要递归处理更高优先级的中缀操作
* </p>
* *
* @param ctx 当前解析上下文 * @param ctx 当前解析上下文
* @return 下一个中缀运算符的优先级序号若无解析器则为 -1 * @return 下一个中缀运算符的优先级序号若无注册解析器则返回 -1
*/ */
private int nextPrecedence(ParserContext ctx) { private int nextPrecedence(ParserContext ctx) {
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme()); InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());

View File

@ -9,26 +9,30 @@ package org.jcnc.snow.compiler.parser.expression;
*/ */
public enum Precedence { public enum Precedence {
/** /** 最低优先级,通常用于整个表达式解析的起始入口。 */
* 最低优先级通常用于整个表达式解析的起始入口
*/
LOWEST, LOWEST,
/** /** 逻辑或(|| */
* 加法和减法的优先级例如 +- OR,
*/
/** 逻辑与(&& */
AND,
/** 相等/不等(==, != */
EQUALITY,
/** 大小比较(<, >, <=, >= */
COMPARISON,
/** 加法和减法(+、- */
SUM, SUM,
/** /** 乘法、除法、取模(*、/、% */
* 乘法除法取模等更高优先级的二元运算符例如 */%
*/
PRODUCT, PRODUCT,
/** 一元前缀(-x !x */ /** 一元前缀(-x !x */
UNARY, UNARY,
/** /** 函数调用、成员访问等最强绑定foo()、obj.prop */
* 函数调用成员访问等最强绑定例如 foo()obj.prop
*/
CALL CALL
} }

View File

@ -8,39 +8,29 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/** /**
* {@code DeclarationStatementParser} 类负责解析变量声明语句是语句级解析器的一部分 * {@code DeclarationStatementParser} 负责解析变量声明语句节点
* <p> * <p>
* 本解析器支持以下两种形式的声明语法: * 支持以下两种语法结构
* <pre>{@code * <pre>{@code
* declare myVar:Integer * declare myVar:Integer
* declare myVar:Integer = 42 + 3 * declare myVar:Integer = 42 + 3
* }</pre> * }</pre>
* 其中: * 解析器能够识别多维数组类型 {@code int[]}, {@code string[][]}并支持可选初始化表达式
* <ul> * <p>
* <li>{@code myVar} 为变量名必须为标识符类型</li> * 每个声明语句均要求以换行符结尾语法不合法时会抛出异常
* <li>{@code Integer} 为类型标注必须为类型标记</li> * </p>
* <li>可选的初始化表达式由 {@link PrattExpressionParser} 解析</li>
* <li>每条声明语句必须以换行符{@code NEWLINE}结束</li>
* </ul>
* 若语法不满足上述结构将在解析过程中抛出异常
*/ */
public class DeclarationStatementParser implements StatementParser { public class DeclarationStatementParser implements StatementParser {
/** /**
* 解析一条 {@code declare} 声明语句并返回对应的抽象语法树节点 {@link DeclarationNode} * 解析一条 {@code declare} 语句生成对应的抽象语法树节点 {@link DeclarationNode}
* <p> * <p>
* 解析流程如下: * 支持类型标注和可选初始化表达式类型部分自动拼接数组维度 int[][]
* <ol> * </p>
* <li>匹配关键字 {@code declare}</li>
* <li>读取变量名称标识符类型</li>
* <li>读取类型标注在冒号后要求为 {@code TYPE} 类型</li>
* <li>若存在 {@code =}则继续解析其后的表达式作为初始化值</li>
* <li>最终必须匹配 {@code NEWLINE} 表示语句结束</li>
* </ol>
* 若遇到非法语法结构将触发异常并中断解析过程
* *
* @param ctx 当前语法解析上下文包含词法流错误信息等 * @param ctx 当前语法解析上下文包含词法流错误信息等
* @return 返回一个 {@link DeclarationNode} 节点表示解析完成的声明语法结构 * @return {@link DeclarationNode} 表示声明语句结构
* @throws RuntimeException 语法不合法时抛出
*/ */
@Override @Override
public DeclarationNode parse(ParserContext ctx) { public DeclarationNode parse(ParserContext ctx) {
@ -61,11 +51,19 @@ public class DeclarationStatementParser implements StatementParser {
ctx.getTokens().expect(":"); ctx.getTokens().expect(":");
// 获取变量类型类型标识符 // 获取变量类型类型标识符
String type = ctx.getTokens() StringBuilder type = new StringBuilder(
.expectType(TokenType.TYPE) ctx.getTokens()
.getLexeme(); .expectType(TokenType.TYPE)
.getLexeme()
);
// 可选的初始化表达式若存在 "="则解析等号右侧表达式 // 消费多维数组类型后缀 "[]"
while (ctx.getTokens().match("[")) {
ctx.getTokens().expectType(TokenType.RBRACKET); // 必须配对
type.append("[]"); // 类型名称拼接 [] int[][]
}
// 可选初始化表达式=号右侧
ExpressionNode init = null; ExpressionNode init = null;
if (ctx.getTokens().match("=")) { if (ctx.getTokens().match("=")) {
init = new PrattExpressionParser().parse(ctx); init = new PrattExpressionParser().parse(ctx);
@ -75,6 +73,6 @@ public class DeclarationStatementParser implements StatementParser {
ctx.getTokens().expectType(TokenType.NEWLINE); ctx.getTokens().expectType(TokenType.NEWLINE);
// 返回构建好的声明语法树节点 // 返回构建好的声明语法树节点
return new DeclarationNode(name, type, init, new NodeContext(line, column, file)); return new DeclarationNode(name, type.toString(), init, new NodeContext(line, column, file));
} }
} }

View File

@ -51,7 +51,7 @@ public class ExpressionStatementParser implements StatementParser {
int column = ts.peek().getCol(); int column = ts.peek().getCol();
String file = ctx.getSourceName(); String file = ctx.getSourceName();
// 赋值语句: IDENTIFIER = expr // 简单形式: IDENTIFIER = expr
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) { if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
String varName = ts.next().getLexeme(); String varName = ts.next().getLexeme();
ts.expect("="); ts.expect("=");
@ -60,9 +60,23 @@ public class ExpressionStatementParser implements StatementParser {
return new AssignmentNode(varName, value, new NodeContext(line, column, file)); return new AssignmentNode(varName, value, new NodeContext(line, column, file));
} }
// 尝试解析更通用的左值形式支持下标: <expr> = <expr>
ExpressionNode lhs = new PrattExpressionParser().parse(ctx);
if ("=".equals(ts.peek().getLexeme())) {
ts.next(); // consume '='
ExpressionNode rhs = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
// 根据左值类型构造具体赋值节点
if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode id) {
return new AssignmentNode(id.name(), rhs, new NodeContext(line, column, file));
} else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IndexExpressionNode idx) {
return new org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode(idx, rhs, new NodeContext(line, column, file));
} else {
throw new UnexpectedToken("不支持的赋值左值类型: " + lhs.getClass().getSimpleName(), line, column);
}
}
// 普通表达式语句 // 普通表达式语句
ExpressionNode expr = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
return new ExpressionStatementNode(expr, new NodeContext(line, column, file)); return new ExpressionStatementNode(lhs, new NodeContext(line, column, file));
} }
} }

View File

@ -12,68 +12,64 @@ import java.util.Map;
/** /**
* {@code AnalyzerRegistry} 是语义分析器的注册与分发中心 * {@code AnalyzerRegistry} 是语义分析器的注册与分发中心
* <p> * <p>
* 它负责根据 AST 节点的类型查找并返回相应的 {@link StatementAnalyzer} {@link ExpressionAnalyzer} 实例 * 该类维护了 AST 节点类型与相应 {@link StatementAnalyzer}
* 同时支持注册自定义分析器并在未找到对应表达式分析器时提供默认兜底处理器 * {@link ExpressionAnalyzer} 实例的映射关系调用者可以通过节点类型注册自定义的分析器
* 并在后续通过节点对象高效获取对应分析器实现语义分析分发
* </p>
* <p>
* 对于表达式分析器的获取{@link #getExpressionAnalyzer(ExpressionNode)}
* 支持最近父类匹配查找机制若找不到节点的精确类型分析器则向上递归查找已注册的最近父类类型分析器
* 若依然未找到则自动 fallback 到默认的 {@link UnsupportedExpressionAnalyzer}确保分析流程健壮性
* </p>
* <p> * <p>
* 主要职责:
* <ul>
* <li>支持注册语句和表达式节点类型对应的分析器</li>
* <li>在语义分析阶段根据 AST 节点动态查找对应的分析器</li>
* <li>为未注册的表达式类型提供默认处理器 {@link UnsupportedExpressionAnalyzer}</li>
* <li>不为语句提供默认兜底分析器未注册类型将返回 {@code null}</li>
* </ul>
*/ */
public class AnalyzerRegistry { public class AnalyzerRegistry {
/** Statement 节点类型 → 对应语义分析器映射表 */ /**
private final Map<Class<?>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>(); * Statement 节点类型 对应语义分析器映射表
*/
/** Expression 节点类型 → 对应语义分析器映射表 */ private final Map<Class<? extends StatementNode>, StatementAnalyzer<?>> stmtAnalyzers = new HashMap<>();
private final Map<Class<?>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>();
/** 默认兜底表达式分析器,用于处理未注册的表达式类型 */
private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported =
new UnsupportedExpressionAnalyzer<>();
// ========================= 注册方法 =========================
/** /**
* 注册一个 {@link StatementAnalyzer} 实例用于处理指定类型的语句节点 * Expression 节点类型 对应语义分析器映射表
*
* @param cls 要注册的语句节点类型Class 对象
* @param analyzer 与该类型匹配的分析器实例
* @param <S> {@link StatementNode} 的具体子类
*/ */
public <S extends StatementNode> void registerStatementAnalyzer( private final Map<Class<? extends ExpressionNode>, ExpressionAnalyzer<?>> exprAnalyzers = new HashMap<>();
Class<S> cls,
StatementAnalyzer<S> analyzer /**
) { * 默认表达式兜底分析器用于未注册或不能识别的表达式类型
stmtAnalyzers.put(cls, analyzer); */
private final ExpressionAnalyzer<ExpressionNode> defaultUnsupported = new UnsupportedExpressionAnalyzer<>();
/**
* 注册 Statement 类型的语义分析器
*
* @param clazz AST 语句节点类型class对象例如 {@code IfStatementNode.class}
* @param analyzer 针对该节点类型的语义分析器实例
* @param <S> 语句节点类型参数必须是 {@link StatementNode} 的子类
*/
public <S extends StatementNode> void registerStatementAnalyzer(Class<S> clazz, StatementAnalyzer<S> analyzer) {
stmtAnalyzers.put(clazz, analyzer);
} }
/** /**
* 注册一个 {@link ExpressionAnalyzer} 实例用于处理指定类型的表达式节点 * 注册 Expression 类型的语义分析器
* *
* @param cls 要注册的表达式节点类型Class 对象 * @param clazz AST 表达式节点类型class对象例如 {@code BinaryExprNode.class}
* @param analyzer 与该类型匹配的分析器实例 * @param analyzer 针对该节点类型的语义分析器实例
* @param <E> {@link ExpressionNode} 的具体子类 * @param <E> 表达式节点类型参数必须是 {@link ExpressionNode} 子类
*/ */
public <E extends ExpressionNode> void registerExpressionAnalyzer( public <E extends ExpressionNode> void registerExpressionAnalyzer(Class<E> clazz, ExpressionAnalyzer<E> analyzer) {
Class<E> cls, exprAnalyzers.put(clazz, analyzer);
ExpressionAnalyzer<E> analyzer
) {
exprAnalyzers.put(cls, analyzer);
} }
// ========================= 获取方法 =========================
/** /**
* 根据语句节点的实际类型查找对应的 {@link StatementAnalyzer} * 获取指定语句节点对象的分析器实例
* <p> * <p>
* 若节点类型未注册返回 {@code null} * 只支持精确类型匹配即仅当注册过该节点 class 时才返回分析器否则返回 null
* </p>
* *
* @param stmt 要分析的语句节点实例 * @param stmt 语句节点对象
* @param <S> 语句类型推断自参数 * @param <S> 节点类型需为 {@link StatementNode} 子类
* @return 与该节点类型对应的分析器若未注册则为 {@code null} * @return 匹配的 {@link StatementAnalyzer}若未注册则返回 null
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <S extends StatementNode> StatementAnalyzer<S> getStatementAnalyzer(S stmt) { public <S extends StatementNode> StatementAnalyzer<S> getStatementAnalyzer(S stmt) {
@ -81,17 +77,34 @@ public class AnalyzerRegistry {
} }
/** /**
* 根据表达式节点的实际类型查找对应的 {@link ExpressionAnalyzer} * 获取指定表达式节点对象的分析器实例
* <p> * <p>
* 若节点类型未注册返回默认兜底分析器 {@link UnsupportedExpressionAnalyzer} * 首先尝试节点类型的精确匹配若未注册向上递归查找其最近的已注册父类类型分析器
* 若依然未命中则返回默认的 {@link UnsupportedExpressionAnalyzer}保证分析器始终有返回值
* </p>
* *
* @param expr 要分析的表达式节点实例 * @param expr 表达式节点对象
* @param <E> 表达式类型推断自参数 * @param <E> 节点类型需为 {@link ExpressionNode} 子类
* @return 与该节点类型对应的分析器或默认兜底分析器 * @return 匹配的 {@link ExpressionAnalyzer} 实例若未注册则返回兜底分析器
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <E extends ExpressionNode> ExpressionAnalyzer<E> getExpressionAnalyzer(E expr) { public <E extends ExpressionNode> ExpressionAnalyzer<E> getExpressionAnalyzer(E expr) {
return (ExpressionAnalyzer<E>) Class<?> cls = expr.getClass();
exprAnalyzers.getOrDefault(expr.getClass(), defaultUnsupported); // 精确匹配
ExpressionAnalyzer<?> analyzer = exprAnalyzers.get(cls);
if (analyzer != null) {
return (ExpressionAnalyzer<E>) analyzer;
}
// 向上遍历父类尝试匹配
Class<?> current = cls.getSuperclass();
while (current != null && ExpressionNode.class.isAssignableFrom(current)) {
analyzer = exprAnalyzers.get(current);
if (analyzer != null) {
return (ExpressionAnalyzer<E>) analyzer;
}
current = current.getSuperclass();
}
// fallback
return (ExpressionAnalyzer<E>) defaultUnsupported;
} }
} }

View File

@ -0,0 +1,63 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.ArrayLiteralNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
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.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.ArrayType;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.List;
/**
* {@code ArrayLiteralAnalyzer} 用于分析数组字面量表达式ArrayLiteralNode
* <p>
* 主要负责
* <ul>
* <li>推断数组字面量的元素类型生成对应的 {@link ArrayType}</li>
* <li>检查所有元素类型是否一致不一致时报错并降级为 {@code int[]}</li>
* <li>若数组为空默认类型为 {@code int[]}并产生相应语义错误</li>
* </ul>
* </p>
*/
public class ArrayLiteralAnalyzer implements ExpressionAnalyzer<ArrayLiteralNode> {
/**
* 分析数组字面量表达式推断类型并检查元素类型一致性
*
* @param ctx 全局/当前语义分析上下文
* @param mi 所属模块信息
* @param fn 当前分析的函数节点
* @param locals 当前作用域符号表
* @param expr 当前数组字面量节点
* @return 推断出的数组类型若类型冲突或无法推断则为 {@code int[]}
*/
@Override
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, ArrayLiteralNode expr) {
List<ExpressionNode> elems = expr.elements();
if (elems.isEmpty()) {
ctx.getErrors().add(new SemanticError(expr, "空数组字面量的类型无法推断,已默认为 int[]"));
return new ArrayType(BuiltinType.INT);
}
// 以第一个元素为基准
Type first = ctx.getRegistry()
.getExpressionAnalyzer(elems.getFirst())
.analyze(ctx, mi, fn, locals, elems.getFirst());
for (int i = 1; i < elems.size(); i++) {
ExpressionNode e = elems.get(i);
Type t = ctx.getRegistry()
.getExpressionAnalyzer(e)
.analyze(ctx, mi, fn, locals, e);
if (!t.equals(first)) {
ctx.getErrors().add(new SemanticError(e, "数组元素类型不一致: 期望 " + first.name() + ",实际 " + t.name()));
return new ArrayType(BuiltinType.INT);
}
}
return new ArrayType(first);
}
}

View File

@ -0,0 +1,59 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
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.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.ArrayType;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
* {@code IndexExpressionAnalyzer} 负责下标访问表达式 {@code array[index]}的类型推断和检查
* <p>
* 语义规则
* <ol>
* <li>先分析 array 的类型必须为 {@link ArrayType}</li>
* <li>再分析 index 的类型必须为数值类型</li>
* <li>表达式结果类型为 array 的元素类型 array 非数组类型报错并返回 int 类型兜底</li>
* </ol>
*/
public class IndexExpressionAnalyzer implements ExpressionAnalyzer<IndexExpressionNode> {
/**
* 分析并类型检查下标访问表达式
*
* @param ctx 当前语义分析上下文
* @param mi 当前模块信息
* @param fn 当前函数节点
* @param locals 当前作用域符号表
* @param node 要分析的下标访问表达式节点
* @return array 合法则返回元素类型否则兜底为 int 类型
*/
@Override
public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IndexExpressionNode node) {
// 先分析被下标访问的数组表达式
Type arrType = ctx.getRegistry()
.getExpressionAnalyzer(node.array())
.analyze(ctx, mi, fn, locals, node.array());
if (arrType instanceof ArrayType(Type elementType)) {
// 再分析下标表达式
Type idxType = ctx.getRegistry()
.getExpressionAnalyzer(node.index())
.analyze(ctx, mi, fn, locals, node.index());
if (!idxType.isNumeric()) {
ctx.getErrors().add(new SemanticError(node, "数组下标必须是数值类型"));
}
// array[index] 的类型是数组元素类型
return elementType;
}
ctx.getErrors().add(new SemanticError(node, "仅数组类型支持下标访问"));
return BuiltinType.INT;
}
}

View File

@ -0,0 +1,72 @@
package org.jcnc.snow.compiler.semantic.analyzers.statement;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode;
import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
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.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.ArrayType;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
* {@code IndexAssignmentAnalyzer} 用于分析和类型检查数组下标赋值语句
* <p>
* 主要职责
* <ul>
* <li>检查左侧是否为数组类型</li>
* <li>检查下标是否为数值类型</li>
* <li>检查右值被赋值表达式类型是否与数组元素类型兼容</li>
* <li>如有类型不符将语义错误写入 {@link Context#getErrors()}</li>
* </ul>
*/
public class IndexAssignmentAnalyzer implements StatementAnalyzer<IndexAssignmentNode> {
/**
* 分析并类型检查数组下标赋值语句 {@code arr[i] = v}
* <ul>
* <li>左侧必须是数组类型</li>
* <li>下标必须为数值类型</li>
* <li>右侧赋值类型需与数组元素类型一致</li>
* <li>任何不合法情况均会产生 {@link SemanticError}</li>
* </ul>
*
* @param ctx 当前语义分析上下文
* @param mi 当前模块信息
* @param fn 所属函数节点
* @param locals 局部符号表
* @param node 赋值语句 AST 节点
*/
@Override
public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IndexAssignmentNode node) {
IndexExpressionNode target = node.target();
// 分析左侧类型必须为数组类型
Type arrT = ctx.getRegistry()
.getExpressionAnalyzer(target.array())
.analyze(ctx, mi, fn, locals, target.array());
if (!(arrT instanceof ArrayType(Type elementType))) {
ctx.getErrors().add(new SemanticError(node, "左侧不是数组,无法进行下标赋值"));
return;
}
// 分析下标类型必须为数值类型
Type idxT = ctx.getRegistry()
.getExpressionAnalyzer(target.index())
.analyze(ctx, mi, fn, locals, target.index());
if (!idxT.isNumeric()) {
ctx.getErrors().add(new SemanticError(node, "数组下标必须是数值类型"));
}
// 分析右值类型必须与元素类型一致
Type rhsT = ctx.getRegistry()
.getExpressionAnalyzer(node.value())
.analyze(ctx, mi, fn, locals, node.value());
if (!rhsT.equals(elementType)) {
ctx.getErrors().add(new SemanticError(node,
"数组元素赋值类型不匹配: 期望 " + elementType.name() + ",实际 " + rhsT.name()));
}
}
}

View File

@ -57,6 +57,11 @@ public final class AnalyzerRegistrar {
registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer()); registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer());
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer()); registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
registry.registerExpressionAnalyzer(ArrayLiteralNode.class, new ArrayLiteralAnalyzer());
registry.registerExpressionAnalyzer(IndexExpressionNode.class,new IndexExpressionAnalyzer()); // 关键行
registry.registerStatementAnalyzer(IndexAssignmentNode.class, new IndexAssignmentAnalyzer());
// ---------- 注册一元表达式分析器 ---------- // ---------- 注册一元表达式分析器 ----------
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer()); registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());

View File

@ -2,42 +2,54 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry; import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry;
import org.jcnc.snow.compiler.semantic.error.SemanticError; import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.type.ArrayType;
import org.jcnc.snow.compiler.semantic.type.Type; import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* {@code Context} 表示语义分析阶段的共享上下文环境 * {@code Context} 表示语义分析阶段的全局上下文环境
* <p> * <p>
* 它贯穿整个语义分析流程用于维护并提供以下核心服务: * 该类贯穿整个语义分析流程集中管理以下内容
* <ul> * <ul>
* <li>模块信息管理: 包含所有已加载模块源模块与内置模块</li> * <li>模块信息管理所有已加载模块包括源模块和内置模块</li>
* <li>错误收集: 集中存储语义分析期间产生的 {@link SemanticError}</li> * <li>错误收集集中存储语义分析期间产生的 {@link SemanticError}</li>
* <li>日志控制: 支持按需输出详细调试日志</li> * <li>日志输出控制可选支持调试信息</li>
* <li>分析器调度: 通过 {@link AnalyzerRegistry} 管理语句/表达式的分析器分发</li> * <li>分析器调度通过 {@link AnalyzerRegistry} 分发对应分析器</li>
* </ul> * </ul>
* <p>
* 提供便捷的 getter 方法和类型解析工具方法
* </p>
*/ */
public class Context { public class Context {
/** 模块表: 模块名 → {@link ModuleInfo},用于模块查找与跨模块引用 */ /**
* 模块表模块名 {@link ModuleInfo}用于模块查找与跨模块引用
*/
private final Map<String, ModuleInfo> modules; private final Map<String, ModuleInfo> modules;
/** 错误列表: 语义分析过程中收集的所有 {@link SemanticError} */ /**
* 错误列表语义分析过程中收集的所有 {@link SemanticError}
*/
private final List<SemanticError> errors; private final List<SemanticError> errors;
/** 日志开关: 若为 true将启用 {@link #log(String)} 输出日志信息 */ /**
* 日志开关若为 true将启用 {@link #log(String)} 输出日志信息
*/
private final boolean verbose; private final boolean verbose;
/** 语义分析器注册表: 用于按节点类型动态调度分析器 */ /**
* 语义分析器注册表用于按节点类型动态调度分析器
*/
private final AnalyzerRegistry registry; private final AnalyzerRegistry registry;
/** /**
* 构造语义分析上下文对象 * 构造语义分析上下文对象
* *
* @param modules 已注册的模块信息集合 * @param modules 已注册的模块信息集合
* @param errors 错误收集器分析器将所有语义错误写入此列表 * @param errors 错误收集器分析器将所有语义错误写入此列表
* @param verbose 是否启用调试日志输出 * @param verbose 是否启用调试日志输出
* @param registry 分析器注册表提供类型到分析器的映射与调度能力 * @param registry 分析器注册表提供类型到分析器的映射与调度能力
*/ */
public Context(Map<String, ModuleInfo> modules, public Context(Map<String, ModuleInfo> modules,
List<SemanticError> errors, List<SemanticError> errors,
@ -54,13 +66,17 @@ public class Context {
/** /**
* 获取所有模块信息映射表 * 获取所有模块信息映射表
* *
* @return 模块名 模块信息 {@link ModuleInfo} 的映射 * @return 模块名到模块信息{@link ModuleInfo}的映射表
*/ */
public Map<String, ModuleInfo> getModules() { public Map<String, ModuleInfo> getModules() {
return modules; return modules;
} }
/** @return 模块信息(快捷方式) */ /**
* 模块信息 getter快捷方式
*
* @return 模块名到模块信息{@link ModuleInfo}的映射表
*/
public Map<String, ModuleInfo> modules() { public Map<String, ModuleInfo> modules() {
return modules; return modules;
} }
@ -76,7 +92,11 @@ public class Context {
return errors; return errors;
} }
/** @return 错误列表(快捷方式) */ /**
* 错误列表 getter快捷方式
*
* @return 错误列表
*/
public List<SemanticError> errors() { public List<SemanticError> errors() {
return errors; return errors;
} }
@ -92,7 +112,11 @@ public class Context {
return registry; return registry;
} }
/** @return 注册表(快捷方式) */ /**
* 注册表 getter快捷方式
*
* @return {@link AnalyzerRegistry} 实例
*/
public AnalyzerRegistry registry() { public AnalyzerRegistry registry() {
return registry; return registry;
} }
@ -113,15 +137,26 @@ public class Context {
// ------------------ 工具函数 ------------------ // ------------------ 工具函数 ------------------
/** /**
* 将类型名称字符串解析为对应的内置 {@link Type} 实例 * 将类型名称字符串解析为对应的类型实例支持多维数组后缀
* <p> * <p>
* 若类型在 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在则返回对应类型 * 例如"int" int 类型"int[][]" 二维整型数组类型
* 否则返回 {@code null}调用方可据此决定是否降级处理 * </p>
* *
* @param name 类型名称 "int", "float", "void", "string" * @param name 类型名称支持 "[]" 数组后缀
* @return 匹配的 {@link Type}若无匹配项则返回 {@code null} * @return 对应的 {@link Type} 实例若无法识别返回 null
*/ */
public Type parseType(String name) { public Type parseType(String name) {
return BuiltinTypeRegistry.BUILTIN_TYPES.get(name); int dims = 0;
while (name.endsWith("[]")) {
name = name.substring(0, name.length() - 2);
dims++;
}
Type base = BuiltinTypeRegistry.BUILTIN_TYPES.get(name);
if (base == null) return null;
Type t = base;
for (int i = 0; i < dims; i++) {
t = new ArrayType(t);
}
return t;
} }
} }

View File

@ -0,0 +1,73 @@
package org.jcnc.snow.compiler.semantic.type;
import java.util.Objects;
/**
* {@code ArrayType} 表示数组类型每个数组类型包含其元素类型
* <p>
* 例如int[]string[] 等均可用本类表示内部通过 {@link #elementType()} 字段保存元素类型
* </p>
*/
public record ArrayType(
/*
数组元素的类型
*/
Type elementType
) implements Type {
/**
* 判断当前数组类型能否与另一类型兼容主要用于类型检查
* <p>
* 只有当 {@code other} 也是 ArrayType且元素类型兼容时返回 true
* </p>
*
* @param other 需判断的类型
* @return 类型兼容性结果
*/
@Override
public boolean isCompatible(Type other) {
if (!(other instanceof ArrayType(Type type))) return false;
return elementType.isCompatible(type);
}
/**
* 数组类型不是数值类型直接调用父接口默认实现
*
* @return 总为 false
*/
@Override
public boolean isNumeric() {
return Type.super.isNumeric();
}
/**
* 判断两个 ArrayType 是否等价元素类型完全一致即视为等价
*
* @param o 比较对象
* @return 是否等价
*/
@Override
public boolean equals(Object o) {
return o instanceof ArrayType(Type type) && Objects.equals(elementType, type);
}
/**
* 返回数组类型的字符串描述 "int[]"
*
* @return 类型名称
*/
@Override
public String toString() {
return name();
}
/**
* 获取数组类型的名称描述 "int[]"
*
* @return 类型名称
*/
@Override
public String name() {
return elementType.name() + "[]";
}
}

View File

@ -47,4 +47,6 @@ public interface Type {
int ia = order.indexOf(a), ib = order.indexOf(b); int ia = order.indexOf(a), ib = order.indexOf(b);
return order.get(Math.max(ia, ib)); return order.get(Math.max(ia, ib));
} }
/** 类型名字符串(如 int、double[] */
default String name() { return toString(); }
} }

View File

@ -6,42 +6,82 @@ import org.jcnc.snow.vm.module.LocalVariableStore;
import org.jcnc.snow.vm.module.OperandStack; import org.jcnc.snow.vm.module.OperandStack;
/** /**
* The {@code RPushCommand} class implements the {@link Command} interface and represents the * The {@code RPushCommand} class implements the {@link Command} interface
* reference push instruction ({@code R_PUSH}) in the virtual machine. * and represents the "reference push" instruction ({@code R_PUSH}) in the virtual machine.
* *
* <p> * <p>
* This instruction pushes a reference object, such as a String literal, onto the operand stack. * This instruction pushes a reference valuetypically a string literal or an array literalonto the operand stack.
* </p> * </p>
* *
* <p>Instruction format: {@code R_PUSH <literal>}</p> * <p><b>Instruction format:</b> {@code R_PUSH <literal>}</p>
* <ul> * <ul>
* <li>{@code <literal>}: The reference value (e.g., string) to be pushed onto the stack. * <li>{@code <literal>}: The reference value (e.g., string, boolean, integer, floating-point, or array literal)
* If the literal contains spaces, all parts after {@code R_PUSH} are joined into a single string.</li> * to be pushed onto the stack. If the literal contains spaces, all arguments after {@code R_PUSH} are joined with spaces.</li>
* </ul> * </ul>
* *
* <p>Behavior:</p> * <p><b>Behavior:</b></p>
* <ul> * <ul>
* <li>Checks that the instruction has at least one parameter after the operator.</li> * <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>Concatenates all parameters after {@code R_PUSH} into a single string, separated by spaces.</li>
* <li>Pushes the resulting string as a reference object onto the operand stack.</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>Increments the program counter to the next instruction.</li> * <li>Otherwise, pushes the literal as a string reference onto the operand stack.</li>
* <li>Throws an {@code IllegalStateException} if the instruction is missing required parameters.</li> * <li>Increments the program counter to advance to the next instruction.</li>
* </ul> * </ul>
*
* <p>
* Supported element types in array literals include integers, floating-point numbers, booleans,
* and quoted strings (surrounded by double quotes).
* </p>
*/ */
public final class RPushCommand implements Command { public final class RPushCommand implements Command {
/** /**
* Executes the {@code R_PUSH} instruction, pushing a reference (such as a string literal) * Parses a string element into its corresponding Java object.
* onto the operand stack. * <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 parts The instruction parameters. {@code parts[0]} is the operator ("R_PUSH"), * @param e The string to parse.
* {@code parts[1..]} are the parts of the literal to be concatenated and pushed. * @return The parsed object (String, Integer, Double, or Integer for boolean).
* @param pc The current program counter value, indicating the instruction address being executed. */
* @param stack The operand stack manager. The literal will be pushed onto this stack. private static Object getObject(String e) {
* @param lvs The local variable store. (Not used in this instruction.) String x = e.trim();
* @param cs The call stack manager. (Not used in this instruction.) Object v;
* @return The next program counter value ({@code pc + 1}), pointing to the next instruction. if (x.startsWith("\"") && x.endsWith("\"") && x.length() >= 2) {
* @throws IllegalStateException if the instruction is missing required parameters. // 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;
}
/**
* 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 @Override
public int execute(String[] parts, int pc, public int execute(String[] parts, int pc,
@ -52,12 +92,32 @@ public final class RPushCommand implements Command {
if (parts.length < 2) if (parts.length < 2)
throw new IllegalStateException("R_PUSH missing parameter"); throw new IllegalStateException("R_PUSH missing parameter");
// Join all arguments after R_PUSH into a single string, separated by spaces.
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 1; i < parts.length; i++) { for (int i = 1; i < parts.length; i++) {
if (i > 1) sb.append(' '); if (i > 1) sb.append(' ');
sb.append(parts[i]); sb.append(parts[i]);
} }
stack.push(sb.toString()); String literal = sb.toString().trim();
// 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);
}
}
// 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; return pc + 1;
} }
} }

View File

@ -226,6 +226,39 @@ public class SyscallCommand implements Command {
sel.close(); sel.close();
} }
// 数组元素访问arr[idx] -> 返回元素当前实现返回 int 或字符串/引用原样
case "ARR_GET" -> {
Object idxObj = stack.pop();
Object arrObj = stack.pop();
int idx;
if (idxObj instanceof Number n) idx = n.intValue();
else if (idxObj instanceof String s) idx = Integer.parseInt(s.trim());
else throw new IllegalArgumentException("ARR_GET: invalid index type " + idxObj);
Object elem;
if (arrObj instanceof java.util.List<?> list) {
if (idx < 0 || idx >= list.size())
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + list.size() + ")");
elem = list.get(idx);
} else if (arrObj != null && arrObj.getClass().isArray()) {
int len = java.lang.reflect.Array.getLength(arrObj);
if (idx < 0 || idx >= len)
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + len + ")");
elem = java.lang.reflect.Array.get(arrObj, idx);
} else {
throw new IllegalArgumentException("ARR_GET: not an array/list: " + arrObj);
}
if (elem instanceof Number n) {
stack.push(n.intValue());
} else if (elem instanceof Boolean b) {
stack.push(b ? 1 : 0);
} else {
// 对于字符串或其它引用类型直接返回引用由上层决定如何存储
stack.push(elem);
}
}
// 控制台输出 // 控制台输出
case "PRINT" -> { case "PRINT" -> {
Object dataObj = stack.pop(); Object dataObj = stack.pop();