feat: 支持数组类型及数组字面量
- 新增 ArrayLiteralNode 表示数组字面量表达式节点 - 实现 ArrayLiteralAnalyzer 进行数组字面量的语义分析 - 添加 ArrayType 表示数组类型,并支持多维数组 - 修改 Context 类,增加对数组类型的支持 - 更新 DeclarationStatementParser,支持多维数组类型的声明 - 在 CallGenerator 中添加对特殊函数 __index_i 的处理,用于数组索引操作
This commit is contained in:
parent
f1069d6e5d
commit
82f4ba1a6e
@ -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 <SUBCMD>} 指令。</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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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));
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 对象,用于申请新的虚拟寄存器。
|
* 变量名到编译期常量值的映射表。
|
||||||
|
* 用于常量折叠优化(如 int、string、数组等常量)。
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 指令,并管理作用域和控制流标签。
|
* 负责将各类语句(如循环、分支、表达式、赋值、声明、返回、break、continue 等)
|
||||||
|
* 转换为对应的 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>
|
||||||
* 根据节点类型分发到对应的处理方法。
|
* 根据节点类型分发到对应的处理方法。
|
||||||
|
* 支持:循环、分支、表达式语句、赋值、声明、返回、break、continue。
|
||||||
|
* 不支持的语句类型会抛出异常。
|
||||||
|
* </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>如果是布尔字面量,返回 1(true)或 0(false)。</li>
|
||||||
|
* <li>如果是数组字面量,会递归所有元素全部是常量时,返回包含常量元素的 List。</li>
|
||||||
|
* <li>如果是标识符节点,尝试从作用域查找是否登记为常量,如果找到则返回。</li>
|
||||||
|
* <li>其余类型或无法确定常量时,返回 null。</li>
|
||||||
|
* </ul>
|
||||||
|
* 用于全局/局部常量传播优化与类型推断。
|
||||||
|
*
|
||||||
|
* @param expr 待折叠的表达式节点,允许为 null
|
||||||
|
* @return 如果可折叠则返回其常量值(如 Integer、Double、String、List),否则返回 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 返回 1,false 返回 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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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,
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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());
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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());
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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() + "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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(); }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 value—typically a string literal or an array literal—onto 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user