From 82f4ba1a6e5d361f6a4d4e2aa7b6f0e2054bbca1 Mon Sep 17 00:00:00 2001
From: Luke
Date: Fri, 1 Aug 2025 18:34:03 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=95=B0=E7=BB=84?=
=?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=8F=8A=E6=95=B0=E7=BB=84=E5=AD=97=E9=9D=A2?=
=?UTF-8?q?=E9=87=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 ArrayLiteralNode 表示数组字面量表达式节点
- 实现 ArrayLiteralAnalyzer 进行数组字面量的语义分析
- 添加 ArrayType 表示数组类型,并支持多维数组
- 修改 Context 类,增加对数组类型的支持
- 更新 DeclarationStatementParser,支持多维数组类型的声明
- 在 CallGenerator 中添加对特殊函数 __index_i 的处理,用于数组索引操作
---
.../backend/generator/CallGenerator.java | 102 +++++----
.../backend/generator/LoadConstGenerator.java | 3 +-
.../snow/compiler/backend/utils/OpHelper.java | 1 +
.../ir/builder/ExpressionBuilder.java | 171 +++++++++++++---
.../compiler/ir/builder/IRBuilderScope.java | 112 ++++++----
.../compiler/ir/builder/StatementBuilder.java | 193 +++++++++++++-----
.../lexer/scanners/SymbolTokenScanner.java | 4 +-
.../snow/compiler/lexer/token/TokenType.java | 10 +-
.../compiler/parser/ast/ArrayLiteralNode.java | 40 ++++
.../parser/ast/IndexAssignmentNode.java | 42 ++++
.../parser/ast/IndexExpressionNode.java | 41 ++++
.../expression/ArrayLiteralParselet.java | 80 ++++++++
.../parser/expression/IndexParselet.java | 51 +++++
.../expression/PrattExpressionParser.java | 103 +++++++---
.../parser/expression/Precedence.java | 28 +--
.../statement/DeclarationStatementParser.java | 52 +++--
.../statement/ExpressionStatementParser.java | 20 +-
.../semantic/analyzers/AnalyzerRegistry.java | 119 ++++++-----
.../expression/ArrayLiteralAnalyzer.java | 63 ++++++
.../expression/IndexExpressionAnalyzer.java | 59 ++++++
.../statement/IndexAssignmentAnalyzer.java | 72 +++++++
.../semantic/core/AnalyzerRegistrar.java | 5 +
.../snow/compiler/semantic/core/Context.java | 83 +++++---
.../compiler/semantic/type/ArrayType.java | 73 +++++++
.../snow/compiler/semantic/type/Type.java | 2 +
.../vm/commands/ref/control/RPushCommand.java | 106 +++++++---
.../system/control/SyscallCommand.java | 33 +++
27 files changed, 1339 insertions(+), 329 deletions(-)
create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/ast/ArrayLiteralNode.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/ast/IndexAssignmentNode.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/ast/IndexExpressionNode.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/expression/ArrayLiteralParselet.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/expression/IndexParselet.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/ArrayLiteralAnalyzer.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/IndexExpressionAnalyzer.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IndexAssignmentAnalyzer.java
create mode 100644 src/main/java/org/jcnc/snow/compiler/semantic/type/ArrayType.java
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
index 0f7c9c8..15957a1 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
@@ -13,42 +13,39 @@ import org.jcnc.snow.vm.engine.VMOpCode;
import java.util.*;
/**
- * CallGenerator - 将 IR {@code CallInstruction} 生成 VM 指令
- *
+ * {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令。
*
- * 本类负责将中间表示(IR)中的函数调用指令 {@link CallInstruction} 转换为虚拟机(VM)指令。
- * 支持普通函数调用和特殊的 syscall 指令转换。
- *
- *
- *
- * 能力说明:
+ * 支持:
*
- * - 支持识别 {@code IRConstant} 直接字面量或已绑定到虚拟寄存器的字符串常量,直接降级为 {@code SYSCALL <SUBCMD>} 指令。
- * - 对普通函数,完成参数加载、调用、返回值保存等指令生成。
+ * - 普通函数调用的参数压栈、调用、返回值保存
+ * - 特殊的 {@code syscall} 指令转为 VM 的 SYSCALL 指令
+ * - 数组访问内置函数 {@code __index_i(arr, idx)} 的专用指令序列
*
+ *
+ * 对于 syscall 子命令,支持常量字符串和字符串寄存器两种来源,并支持寄存器-字符串常量池注册机制。
*
*/
public class CallGenerator implements InstructionGenerator {
/**
- * <虚拟寄存器 id, 对应的字符串常量>
- * 用于记录虚拟寄存器与其绑定字符串常量的映射。由 {@link LoadConstGenerator} 在编译期间填充。
+ * 字符串常量池:用于绑定虚拟寄存器 id 到字符串值(供 syscall 子命令使用)。
*/
private static final Map STRING_CONST_POOL = new HashMap<>();
/**
- * 注册字符串常量到虚拟寄存器
- * 供 {@link LoadConstGenerator} 在加载字符串常量时调用。
+ * 注册一个字符串常量,绑定到虚拟寄存器 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 指令类型(CallInstruction)
+ * 返回当前指令生成器支持的 IR 指令类型(即 {@link CallInstruction})。
+ *
+ * @return {@code CallInstruction.class}
*/
@Override
public Class supportedClass() {
@@ -56,12 +53,12 @@ public class CallGenerator implements InstructionGenerator {
}
/**
- * 生成 VM 指令的主逻辑
+ * 生成 VM 指令序列,实现函数调用/特殊 syscall/数组索引等 IR 指令的转换。
*
- * @param ins 当前 IR 指令(函数调用)
- * @param out 指令输出构建器
- * @param slotMap IR 虚拟寄存器与物理槽位映射
- * @param currentFn 当前函数名
+ * @param ins 当前函数调用 IR 指令
+ * @param out VM 指令输出构建器
+ * @param slotMap IR 虚拟寄存器 → VM 槽位映射表
+ * @param currentFn 当前函数名
*/
@Override
public void generate(CallInstruction ins,
@@ -79,7 +76,7 @@ public class CallGenerator implements InstructionGenerator {
}
// ---------- 0. 解析 syscall 子命令 ----------
- // 子命令支持 IRConstant(直接字面量)或虚拟寄存器(需已绑定字符串)
+ // 支持 IRConstant 字面量或虚拟寄存器(需已绑定字符串)
String subcmd;
IRValue first = args.getFirst();
@@ -88,13 +85,11 @@ public class CallGenerator implements InstructionGenerator {
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
subcmd = s.toUpperCase(Locale.ROOT);
- } else if (first instanceof IRVirtualRegister vr) { // 虚拟寄存器
- // 从常量池中查找是否已绑定字符串
- subcmd = Optional.ofNullable(STRING_CONST_POOL.get(vr.id()))
- .orElseThrow(() ->
- new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr));
- subcmd = subcmd.toUpperCase(Locale.ROOT);
-
+ } else 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 第一个参数必须是字符串常量");
}
@@ -113,21 +108,54 @@ public class CallGenerator implements InstructionGenerator {
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 返回时用) ----------
char retType = 'I'; // 默认为整型
if (!ins.getArguments().isEmpty()) {
- int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst());
- retType = out.getSlotType(firstSlot);
- if (retType == '\0') retType = 'I';
+ // 简化:根据第一个参数类型推断返回类型,或者通过全局表拿到返回类型
+ String ret = GlobalFunctionTable.getReturnType(ins.getFunctionName());
+ if (ret != null) {
+ retType = Character.toUpperCase(ret.charAt(0));
+ }
}
- // ---------- 2. 加载全部实参 ----------
- for (var arg : ins.getArguments()) {
- int slotId = slotMap.get((IRVirtualRegister) arg);
+ // ---------- 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'; // 默认整型
+ if (t == '\0') t = 'I';
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
}
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java
index a24f6d9..28348a3 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java
@@ -63,7 +63,8 @@ public class LoadConstGenerator implements InstructionGenerator 'F'; // 单精度
case Boolean _ -> 'I'; // 布尔类型用 I 处理
case String _ -> 'R'; // 字符串常量
- case null, default -> // 其它类型异常
+ case java.util.List> _ -> 'R'; // 引用类型(如数组等)
+ case null, default ->
throw new IllegalStateException("未知的常量类型: "
+ (value != null ? value.getClass() : null));
};
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java b/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java
index 977c85e..2b5f8c8 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/utils/OpHelper.java
@@ -234,6 +234,7 @@ public final class OpHelper {
if (v instanceof Double) return "D";
if (v instanceof Float) return "F";
if (v instanceof String) return "R"; //引用类型
+ if (v instanceof java.util.List) return "R"; // 引用类型(数组等)
throw new IllegalStateException("Unknown const type: " + v.getClass());
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
index eab2d75..4a5d525 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
@@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.ir.builder;
import org.jcnc.snow.compiler.ir.core.IROpCode;
+import org.jcnc.snow.compiler.ir.core.IRValue;
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
@@ -14,37 +15,26 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import java.util.*;
/**
- * ExpressionBuilder - 表达式 → IR 构建器
- *
+ * {@code ExpressionBuilder} 表达式 → IR 构建器。
*
* 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作,并生成对应的 IR 指令序列。
- * 支持字面量、标识符、二元表达式、一元表达式、函数调用等多种类型表达式。
- *
- *
- *
- * 主要功能:
+ * 支持字面量、标识符、二元表达式、一元表达式、函数调用、数组下标等多种类型表达式。
*
- * - 将表达式节点映射为虚拟寄存器
- * - 为每种表达式类型生成对应 IR 指令
- * - 支持表达式嵌套的递归构建
- * - 支持写入指定目标寄存器,避免冗余的 move 指令
+ * - 将表达式节点映射为虚拟寄存器
+ * - 为每种表达式类型生成对应 IR 指令
+ * - 支持表达式嵌套的递归构建
+ * - 支持写入指定目标寄存器,避免冗余的 move 指令
+ * - 支持 IndexExpressionNode 的编译期折叠(arr[2]),并自动降级为运行时调用 __index_i
*
- *
*/
public record ExpressionBuilder(IRContext ctx) {
- /* ───────────────── 顶层入口 ───────────────── */
-
/**
- * 构建任意 AST 表达式节点,自动为其分配一个新的虚拟寄存器,并返回该寄存器。
+ * 构建表达式,返回存储其结果的虚拟寄存器。
*
- *
- * 这是表达式 IR 生成的核心入口。它会根据不同的表达式类型进行分派,递归构建 IR 指令。
- *
- *
- * @param expr 任意 AST 表达式节点
- * @return 存储该表达式结果的虚拟寄存器
- * @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符
+ * @param expr 要生成 IR 的表达式节点
+ * @return 存储表达式值的虚拟寄存器
+ * @throws IllegalStateException 不支持的表达式类型或未定义标识符
*/
public IRVirtualRegister build(ExpressionNode expr) {
return switch (expr) {
@@ -78,14 +68,20 @@ public record ExpressionBuilder(IRContext ctx) {
/* ───────────────── 写入指定寄存器 ───────────────── */
/**
- * 生成表达式,并将其结果直接写入目标寄存器,避免冗余的 move 操作。
- *
+ * 将表达式节点 {@link ExpressionNode} 的结果写入指定的虚拟寄存器 {@code dest}。
*
- * 某些简单表达式(如字面量、变量名)可以直接写入目标寄存器;复杂表达式则会先 build 到新寄存器,再 move 到目标寄存器。
+ * 按表达式类型分派处理,包括:
+ *
+ * - 字面量(数字、字符串、布尔、数组):生成 loadConst 指令直接写入目标寄存器
+ * - 变量标识符:查表获取源寄存器,并 move 到目标寄存器
+ * - 二元表达式与下标表达式:递归生成子表达式结果,并写入目标寄存器
+ * - 其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器
+ *
*
*
- * @param node 要生成的表达式节点
- * @param dest 目标虚拟寄存器(用于存储结果)
+ * @param node 要求值的表达式节点
+ * @param dest 结果目标虚拟寄存器
+ * @throws IllegalStateException 若标识符未定义(如变量未声明时引用)
*/
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
switch (node) {
@@ -103,8 +99,8 @@ public record ExpressionBuilder(IRContext ctx) {
case BoolLiteralNode b ->
InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
-
- // 标识符:查表获取原寄存器,然后 move 到目标寄存器
+ case ArrayLiteralNode arr ->
+ InstructionFactory.loadConstInto(ctx, dest, buildArrayConstant(arr));
case IdentifierNode id -> {
IRVirtualRegister src = ctx.getScope().lookup(id.name());
if (src == null)
@@ -114,8 +110,10 @@ public record ExpressionBuilder(IRContext ctx) {
// 二元表达式:递归生成并写入目标寄存器
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
-
- // 其它复杂情况:先 build 到新寄存器,再 move 到目标寄存器
+ case IndexExpressionNode idx -> {
+ IRVirtualRegister tmp = buildIndex(idx);
+ InstructionFactory.move(ctx, tmp, dest);
+ }
default -> {
IRVirtualRegister tmp = build(node);
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 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