!41 feat: 核心引擎与标准库重构及功能增强
Merge pull request !41 from Luke/feature/add-naitve-print
This commit is contained in:
commit
e788e9c437
10
.run/Demo14.run.xml
Normal file
10
.run/Demo14.run.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo14" type="Application" factoryName="Application" folderName="Demo">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo14 -o target/Demo14" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -3,7 +3,6 @@
|
||||
<toRun name="Demo1" type="Application" />
|
||||
<toRun name="Demo10" type="Application" />
|
||||
<toRun name="Demo11" type="Application" />
|
||||
<toRun name="Demo11" type="Application" />
|
||||
<toRun name="Demo12" type="Application" />
|
||||
<toRun name="Demo13" type="Application" />
|
||||
<toRun name="Demo2" type="Application" />
|
||||
|
||||
11
lib/os/OS.snow
Normal file
11
lib/os/OS.snow
Normal file
@ -0,0 +1,11 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -2,8 +2,7 @@ module: Main
|
||||
function: main
|
||||
return_type: void
|
||||
body:
|
||||
declare abc!:int =1
|
||||
|
||||
declare abc:int =1
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
|
||||
9
playground/Demo/Demo14/Main.snow
Normal file
9
playground/Demo/Demo14/Main.snow
Normal file
@ -0,0 +1,9 @@
|
||||
module: Main
|
||||
import: os
|
||||
function: main
|
||||
return_type: void
|
||||
body:
|
||||
print(222)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
11
playground/Demo/Demo14/OS.snow
Normal file
11
playground/Demo/Demo14/OS.snow
Normal file
@ -0,0 +1,11 @@
|
||||
module: os
|
||||
import: os
|
||||
function: print
|
||||
parameter:
|
||||
declare i1: int
|
||||
return_type: void
|
||||
body:
|
||||
syscall("PRINT",i1)
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -1,6 +1,7 @@
|
||||
package org.jcnc.snow.compiler.backend.builder;
|
||||
|
||||
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||
import org.jcnc.snow.compiler.backend.utils.OpHelper;
|
||||
import org.jcnc.snow.compiler.ir.core.IRFunction;
|
||||
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
@ -74,18 +75,26 @@ public final class VMCodeGenerator {
|
||||
*/
|
||||
public void generate(IRFunction fn) {
|
||||
this.currentFn = fn.name();
|
||||
out.beginFunction(currentFn); // 输出函数起始
|
||||
|
||||
/* 登记函数入口地址 —— 解决 CALL 未解析符号问题 */
|
||||
out.beginFunction(currentFn);
|
||||
|
||||
/* 逐条分发 IR 指令给对应的生成器 */
|
||||
for (IRInstruction ins : fn.body()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
// 取得与当前 IR 指令类型匹配的生成器(泛型强转消除类型警告)
|
||||
InstructionGenerator<IRInstruction> gen =
|
||||
(InstructionGenerator<IRInstruction>) registry.get(ins.getClass());
|
||||
if (gen == null) {
|
||||
throw new IllegalStateException("Unsupported IR: " + ins);
|
||||
}
|
||||
// 通过多态分发到实际生成器
|
||||
gen.generate(ins, out, slotMap, currentFn);
|
||||
}
|
||||
out.endFunction(); // 输出函数结束
|
||||
|
||||
/* 强制补上函数结尾的返回/终止指令 */
|
||||
String retOpcode = "main".equals(currentFn) ? "HALT" : "RET";
|
||||
out.emit(OpHelper.opcode(retOpcode));
|
||||
|
||||
/* 结束函数 */
|
||||
out.endFunction();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,53 +4,142 @@ import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||
import org.jcnc.snow.compiler.backend.utils.OpHelper;
|
||||
import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable;
|
||||
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
|
||||
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
import org.jcnc.snow.vm.engine.VMOpCode;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 将 IR CallInstruction 生成 VM 指令
|
||||
* <b>CallGenerator - 将 IR {@code CallInstruction} 生成 VM 指令</b>
|
||||
*
|
||||
* <p>
|
||||
* 本类负责将中间表示(IR)中的函数调用指令 {@link CallInstruction} 转换为虚拟机(VM)指令。
|
||||
* 支持普通函数调用和特殊的 syscall 指令转换。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>能力说明:</b>
|
||||
* <ul>
|
||||
* <li>支持识别 {@code IRConstant} 直接字面量或已绑定到虚拟寄存器的字符串常量,直接降级为 {@code SYSCALL <SUBCMD>} 指令。</li>
|
||||
* <li>对普通函数,完成参数加载、调用、返回值保存等指令生成。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public class CallGenerator implements InstructionGenerator<CallInstruction> {
|
||||
|
||||
/**
|
||||
* <虚拟寄存器 id, 对应的字符串常量>
|
||||
* <p>用于记录虚拟寄存器与其绑定字符串常量的映射。由 {@link LoadConstGenerator} 在编译期间填充。</p>
|
||||
*/
|
||||
private static final Map<Integer, String> STRING_CONST_POOL = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 注册字符串常量到虚拟寄存器
|
||||
* <p>供 {@link LoadConstGenerator} 在加载字符串常量时调用。</p>
|
||||
*
|
||||
* @param regId 虚拟寄存器 id
|
||||
* @param value 字符串常量值
|
||||
*/
|
||||
public static void registerStringConst(int regId, String value) {
|
||||
STRING_CONST_POOL.put(regId, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回本生成器支持的 IR 指令类型(CallInstruction)
|
||||
*/
|
||||
@Override
|
||||
public Class<CallInstruction> supportedClass() {
|
||||
return CallInstruction.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 VM 指令的主逻辑
|
||||
*
|
||||
* @param ins 当前 IR 指令(函数调用)
|
||||
* @param out 指令输出构建器
|
||||
* @param slotMap IR 虚拟寄存器与物理槽位映射
|
||||
* @param currentFn 当前函数名
|
||||
*/
|
||||
@Override
|
||||
public void generate(CallInstruction ins,
|
||||
VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
String currentFn) {
|
||||
|
||||
/* 1. 推断返回值类型(用于非 void 情况下的 I/F/D/L_STORE) */
|
||||
char retType = 'I';
|
||||
/* ========== 特殊处理 syscall 调用 ========== */
|
||||
if ("syscall".equals(ins.getFunctionName()) ||
|
||||
ins.getFunctionName().endsWith(".syscall")) {
|
||||
|
||||
List<IRValue> args = ins.getArguments();
|
||||
if (args.isEmpty()) {
|
||||
throw new IllegalStateException("syscall 需要子命令参数");
|
||||
}
|
||||
|
||||
// ---------- 0. 解析 syscall 子命令 ----------
|
||||
// 子命令支持 IRConstant(直接字面量)或虚拟寄存器(需已绑定字符串)
|
||||
String subcmd;
|
||||
IRValue first = args.getFirst();
|
||||
|
||||
if (first instanceof IRConstant(Object value)) { // 直接字面量
|
||||
if (!(value instanceof String s))
|
||||
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
|
||||
subcmd = s.toUpperCase(Locale.ROOT);
|
||||
|
||||
} else if (first instanceof IRVirtualRegister vr) { // 虚拟寄存器
|
||||
// 从常量池中查找是否已绑定字符串
|
||||
subcmd = Optional.ofNullable(STRING_CONST_POOL.get(vr.id()))
|
||||
.orElseThrow(() ->
|
||||
new IllegalStateException("未找到 syscall 字符串常量绑定: " + vr));
|
||||
subcmd = subcmd.toUpperCase(Locale.ROOT);
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("syscall 第一个参数必须是字符串常量");
|
||||
}
|
||||
|
||||
// ---------- 1. 压栈其余 syscall 参数(index 1 开始) ----------
|
||||
for (int i = 1; i < args.size(); i++) {
|
||||
IRVirtualRegister vr = (IRVirtualRegister) args.get(i);
|
||||
int slotId = slotMap.get(vr);
|
||||
char t = out.getSlotType(slotId);
|
||||
if (t == '\0') t = 'I'; // 默认整型
|
||||
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
|
||||
}
|
||||
|
||||
// ---------- 2. 生成 SYSCALL 指令 ----------
|
||||
out.emit(VMOpCode.SYSCALL + " " + subcmd);
|
||||
return; // syscall 无返回值,直接返回
|
||||
}
|
||||
|
||||
/* ========== 普通函数调用 ========== */
|
||||
|
||||
// ---------- 1. 推断返回值类型(非 void 返回时用) ----------
|
||||
char retType = 'I'; // 默认为整型
|
||||
if (!ins.getArguments().isEmpty()) {
|
||||
int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst());
|
||||
retType = out.getSlotType(firstSlot);
|
||||
if (retType == '\0') retType = 'I';
|
||||
}
|
||||
|
||||
/* 2. 依次加载实参 */
|
||||
// ---------- 2. 加载全部实参 ----------
|
||||
for (var arg : ins.getArguments()) {
|
||||
int slotId = slotMap.get((IRVirtualRegister) arg);
|
||||
char t = out.getSlotType(slotId);
|
||||
if (t == '\0') t = 'I';
|
||||
if (t == '\0') t = 'I'; // 默认整型
|
||||
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
|
||||
}
|
||||
|
||||
/* 3. 发出 CALL 指令 */
|
||||
// ---------- 3. 发出 CALL 指令 ----------
|
||||
out.emitCall(ins.getFunctionName(), ins.getArguments().size());
|
||||
|
||||
/* 3.5 若被调用函数返回 void,则无需保存返回值 */
|
||||
String rt = GlobalFunctionTable.getReturnType(ins.getFunctionName());
|
||||
if ("void".equals(rt)) {
|
||||
return; // 直接结束,无 _STORE
|
||||
// ---------- 3.5 如果为 void 返回直接结束 ----------
|
||||
if ("void".equals(GlobalFunctionTable.getReturnType(ins.getFunctionName()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 4. 保存返回值到目标槽 */
|
||||
// ---------- 4. 保存返回值 ----------
|
||||
int destSlot = slotMap.get(ins.getDest());
|
||||
out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
|
||||
out.setSlotType(destSlot, retType);
|
||||
|
||||
@ -10,16 +10,18 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 常量加载指令生成器
|
||||
* 该类用于生成将常量加载到虚拟机寄存器的指令,包括 PUSH 常量值和 STORE 到指定槽位,
|
||||
* 并为每个槽位设置正确的类型前缀(如 'I', 'L', 'F' 等)。
|
||||
* <b>LoadConstGenerator - 将 IR {@code LoadConstInstruction} 生成 VM 指令</b>
|
||||
*
|
||||
* <p>
|
||||
* 本类负责将 IR 层的常量加载指令 {@link LoadConstInstruction} 转换为对应的虚拟机指令。
|
||||
* 额外支持:如果常量类型为 {@code String},会同步登记到
|
||||
* {@link CallGenerator} 的字符串常量池,方便 syscall 降级场景使用。
|
||||
* </p>
|
||||
*/
|
||||
public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruction> {
|
||||
|
||||
/**
|
||||
* 返回本生成器支持的指令类型,即 LoadConstInstruction。
|
||||
*
|
||||
* @return 支持的指令类型的 Class 对象
|
||||
* 指定本生成器支持的 IR 指令类型(LoadConstInstruction)
|
||||
*/
|
||||
@Override
|
||||
public Class<LoadConstInstruction> supportedClass() {
|
||||
@ -27,49 +29,49 @@ public class LoadConstGenerator implements InstructionGenerator<LoadConstInstruc
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一条常量加载指令的目标虚拟机代码。
|
||||
* 生成 VM 指令主流程
|
||||
*
|
||||
* @param ins 当前要生成的 LoadConstInstruction 指令
|
||||
* @param out VMProgramBuilder,用于输出生成的虚拟机指令
|
||||
* @param slotMap IR 虚拟寄存器到实际槽位编号的映射表
|
||||
* @param currentFn 当前函数名(如有需要可使用)
|
||||
* @param ins 当前常量加载指令
|
||||
* @param out 指令输出构建器
|
||||
* @param slotMap 虚拟寄存器与物理槽位映射
|
||||
* @param currentFn 当前函数名
|
||||
*/
|
||||
@Override
|
||||
public void generate(LoadConstInstruction ins,
|
||||
VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
String currentFn) {
|
||||
// 1. 获取常量值(第一个操作数必为常量)
|
||||
|
||||
/* 1. 获取常量值 */
|
||||
IRConstant constant = (IRConstant) ins.operands().getFirst();
|
||||
Object value = constant.value();
|
||||
|
||||
// 2. 生成 PUSH 指令,将常量值推入操作数栈
|
||||
// 通过 OpHelper 辅助方法获取合适的数据类型前缀
|
||||
String pushOp = OpHelper.pushOpcodeFor(value);
|
||||
out.emit(pushOp + " " + value);
|
||||
/* 2. 生成 PUSH 指令,将常量值入栈 */
|
||||
out.emit(OpHelper.pushOpcodeFor(value) + " " + value);
|
||||
|
||||
// 3. 生成 STORE 指令,将栈顶的值存入对应槽位(寄存器)
|
||||
// 同样通过 OpHelper 获取对应类型的 STORE 指令
|
||||
String storeOp = OpHelper.storeOpcodeFor(value);
|
||||
// 获取目标虚拟寄存器对应的槽位编号
|
||||
/* 3. STORE 到目标槽位 */
|
||||
int slot = slotMap.get(ins.dest());
|
||||
out.emit(storeOp + " " + slot);
|
||||
out.emit(OpHelper.storeOpcodeFor(value) + " " + slot);
|
||||
|
||||
// 4. 根据常量的 Java 类型,为槽位设置正确的前缀字符
|
||||
// 这样在后续类型检查/运行时可用,常见前缀如 'I', 'L', 'F', 'D', 'S', 'B'
|
||||
/* 4. 标记槽位数据类型(用于后续类型推断和 LOAD/STORE 指令选择) */
|
||||
char prefix = switch (value) {
|
||||
case Integer _ -> 'I'; // 整型
|
||||
case Long _ -> 'L'; // 长整型
|
||||
case Short _ -> 'S'; // 短整型
|
||||
case Byte _ -> 'B'; // 字节型
|
||||
case Double _ -> 'D'; // 双精度浮点型
|
||||
case Float _ -> 'F'; // 单精度浮点型
|
||||
case Boolean _ -> 'I'; // 布尔(作为 0/1 整型存储)
|
||||
case null, default ->
|
||||
throw new IllegalStateException("Unknown const type: " + (value != null ? value.getClass() : null));
|
||||
case Integer _ -> 'I'; // 整型
|
||||
case Long _ -> 'L'; // 长整型
|
||||
case Short _ -> 'S'; // 短整型
|
||||
case Byte _ -> 'B'; // 字节型
|
||||
case Double _ -> 'D'; // 双精度
|
||||
case Float _ -> 'F'; // 单精度
|
||||
case Boolean _ -> 'I'; // 布尔类型用 I 处理
|
||||
case String _ -> 'R'; // 字符串常量
|
||||
case null, default -> // 其它类型异常
|
||||
throw new IllegalStateException("未知的常量类型: "
|
||||
+ (value != null ? value.getClass() : null));
|
||||
};
|
||||
|
||||
// 写入槽位类型映射表
|
||||
out.setSlotType(slot, prefix);
|
||||
|
||||
/* 5. 如果是字符串常量,则登记到 CallGenerator 的常量池,便于 syscall 字符串降级使用 */
|
||||
if (value instanceof String s) {
|
||||
CallGenerator.registerStringConst(ins.dest().id(), s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +167,9 @@ public final class OpHelper {
|
||||
map.put("D2I", Integer.toString(VMOpCode.D2I));
|
||||
map.put("D2L", Integer.toString(VMOpCode.D2L));
|
||||
map.put("D2F", Integer.toString(VMOpCode.D2F));
|
||||
map.put("R_PUSH", Integer.toString(VMOpCode.R_PUSH));
|
||||
map.put("R_LOAD", Integer.toString(VMOpCode.R_LOAD));
|
||||
map.put("R_STORE", Integer.toString(VMOpCode.R_STORE));
|
||||
map.put("POP", Integer.toString(VMOpCode.POP));
|
||||
map.put("DUP", Integer.toString(VMOpCode.DUP));
|
||||
map.put("SWAP", Integer.toString(VMOpCode.SWAP));
|
||||
@ -176,11 +179,19 @@ public final class OpHelper {
|
||||
map.put("MOV", Integer.toString(VMOpCode.MOV));
|
||||
map.put("HALT", Integer.toString(VMOpCode.HALT));
|
||||
map.put("SYSCALL", Integer.toString(VMOpCode.SYSCALL));
|
||||
map.put("DEBUG_TRAP", Integer.toString(VMOpCode.DEBUG_TRAP));
|
||||
// map.put("DEBUG_TRAP", Integer.toString(VMOpCode.DEBUG_TRAP));
|
||||
OPCODE_MAP = Collections.unmodifiableMap(map);
|
||||
|
||||
Map<Integer, String> revmap = new HashMap<>(); // reverse map
|
||||
OPCODE_MAP.forEach((key, value) -> revmap.put(Integer.parseInt(value), key));
|
||||
OPCODE_MAP.forEach((key, value) -> {
|
||||
int codeInt;
|
||||
if (value.startsWith("0x") || value.startsWith("0X")) {
|
||||
codeInt = Integer.parseInt(value.substring(2), 16);
|
||||
} else {
|
||||
codeInt = Integer.parseInt(value);
|
||||
}
|
||||
revmap.put(codeInt, key);
|
||||
});
|
||||
OPCODE_NAME_MAP = Collections.unmodifiableMap(revmap);
|
||||
}
|
||||
|
||||
@ -222,11 +233,13 @@ public final class OpHelper {
|
||||
if (v instanceof Byte) return "B";
|
||||
if (v instanceof Double) return "D";
|
||||
if (v instanceof Float) return "F";
|
||||
if (v instanceof String) return "R"; //引用类型
|
||||
throw new IllegalStateException("Unknown const type: " + v.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 opcode 数值的字符串形式获取指令名
|
||||
*
|
||||
* @param code 字符串形式的 opcode 数值
|
||||
* @return opcode 对应的指令名
|
||||
*/
|
||||
@ -236,6 +249,7 @@ public final class OpHelper {
|
||||
|
||||
/**
|
||||
* 根据 opcode 获取指令名
|
||||
*
|
||||
* @param code opcode
|
||||
* @return opcode 对应的指令名
|
||||
*/
|
||||
|
||||
@ -5,121 +5,117 @@ import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
|
||||
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
|
||||
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
|
||||
import org.jcnc.snow.compiler.ir.utils.ComparisonUtils;
|
||||
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
|
||||
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
|
||||
import org.jcnc.snow.compiler.parser.ast.*;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* <b>表达式构建器</b>
|
||||
* <b>ExpressionBuilder - 表达式 → IR 构建器</b>
|
||||
*
|
||||
* <p>
|
||||
* 该类负责将抽象语法树(AST)的表达式节点转换为中间表示(IR)指令和虚拟寄存器,
|
||||
* 是编译器IR生成阶段的核心工具。
|
||||
* <br/>
|
||||
* 主要职责包括:
|
||||
* 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作,并生成对应的 IR 指令序列。
|
||||
* 支持字面量、标识符、二元表达式、一元表达式、函数调用等多种类型表达式。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 主要功能:
|
||||
* <ul>
|
||||
* <li>将数字字面量、标识符、二元表达式、函数调用等AST表达式节点,翻译为对应的IR指令序列</li>
|
||||
* <li>管理并分配虚拟寄存器,保证IR操作的数据流正确</li>
|
||||
* <li>将表达式节点映射为虚拟寄存器</li>
|
||||
* <li>为每种表达式类型生成对应 IR 指令</li>
|
||||
* <li>支持表达式嵌套的递归构建</li>
|
||||
* <li>支持写入指定目标寄存器,避免冗余的 move 指令</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* </p>
|
||||
*/
|
||||
public record ExpressionBuilder(IRContext ctx) {
|
||||
|
||||
/**
|
||||
* 构建并返回某个表达式节点对应的虚拟寄存器。
|
||||
*
|
||||
* <p>会根据节点的实际类型分别处理:
|
||||
* <ul>
|
||||
* <li>数字字面量: 新建常量寄存器</li>
|
||||
* <li>布尔字面量: 生成值为 0 或 1 的常量寄存器</li>
|
||||
* <li>标识符: 查找当前作用域中的寄存器</li>
|
||||
* <li>二元表达式: 递归处理子表达式并进行相应运算</li>
|
||||
* <li>一元运算符:
|
||||
* <ul>
|
||||
* <li><code>-x</code>(取负,生成 <code>NEG_I32</code> 指令)与</li>
|
||||
* <li>code>!x</code>(逻辑非,转换为 <code>x == 0</code> 比较指令)</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>函数调用: 生成对应的Call指令</li>
|
||||
* <li>其它类型不支持,抛出异常</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param expr 要转换的表达式AST节点
|
||||
* @return 该表达式的计算结果寄存器
|
||||
* @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型
|
||||
*/
|
||||
/* ───────────────── 顶层入口 ───────────────── */
|
||||
|
||||
/**
|
||||
* 构建任意 AST 表达式节点,自动为其分配一个新的虚拟寄存器,并返回该寄存器。
|
||||
*
|
||||
* <p>
|
||||
* 这是表达式 IR 生成的核心入口。它会根据不同的表达式类型进行分派,递归构建 IR 指令。
|
||||
* </p>
|
||||
*
|
||||
* @param expr 任意 AST 表达式节点
|
||||
* @return 存储该表达式结果的虚拟寄存器
|
||||
* @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符
|
||||
*/
|
||||
public IRVirtualRegister build(ExpressionNode expr) {
|
||||
return switch (expr) {
|
||||
// 数字字面量
|
||||
// 数字字面量,例如 123、3.14
|
||||
case NumberLiteralNode n -> buildNumberLiteral(n.value());
|
||||
// 布尔字面量
|
||||
// 字符串字面量,例如 "abc"
|
||||
case StringLiteralNode s -> buildStringLiteral(s.value());
|
||||
// 布尔字面量,例如 true / false
|
||||
case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
|
||||
// 标识符
|
||||
// 标识符(变量名),如 a、b
|
||||
case IdentifierNode id -> {
|
||||
// 查找当前作用域中的变量寄存器
|
||||
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
|
||||
if (reg == null)
|
||||
throw new IllegalStateException("未定义标识符: " + id.name());
|
||||
yield reg;
|
||||
}
|
||||
// 二元表达式
|
||||
// 二元表达式(如 a+b, x==y)
|
||||
case BinaryExpressionNode bin -> buildBinary(bin);
|
||||
// 函数调用
|
||||
case CallExpressionNode call -> buildCall(call);
|
||||
case UnaryExpressionNode u -> buildUnary(u);
|
||||
// 函数/方法调用表达式
|
||||
case CallExpressionNode call -> buildCall(call);
|
||||
// 一元表达式(如 -a, !a)
|
||||
case UnaryExpressionNode un -> buildUnary(un);
|
||||
|
||||
// 默认分支:遇到未知表达式类型则直接抛异常
|
||||
default -> throw new IllegalStateException(
|
||||
"不支持的表达式类型: " + expr.getClass().getSimpleName());
|
||||
};
|
||||
}
|
||||
|
||||
/** 处理一元表达式 */
|
||||
private IRVirtualRegister buildUnary(UnaryExpressionNode un) {
|
||||
String op = un.operator();
|
||||
IRVirtualRegister val = build(un.operand());
|
||||
|
||||
// -x → NEG_*(根据类型自动选择位宽)
|
||||
if (op.equals("-")) {
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
IROpCode code = ExpressionUtils.negOp(un.operand());
|
||||
ctx.addInstruction(new UnaryOperationInstruction(code, dest, val));
|
||||
return dest;
|
||||
}
|
||||
|
||||
// !x → (x == 0)
|
||||
if (op.equals("!")) {
|
||||
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
|
||||
return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, val, zero);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("未知一元运算符: " + op);
|
||||
}
|
||||
/* ───────────────── 写入指定寄存器 ───────────────── */
|
||||
|
||||
/**
|
||||
* 直接将表达式计算结果写入指定的目标寄存器(dest)。
|
||||
* <p>
|
||||
* 与{@link #build(ExpressionNode)}类似,但支持目标寄存器复用(避免不必要的move)。
|
||||
* 生成表达式,并将其结果直接写入目标寄存器,避免冗余的 move 操作。
|
||||
*
|
||||
* @param node 表达式AST节点
|
||||
* @param dest 目标寄存器
|
||||
* @throws IllegalStateException 未定义标识符/不支持的表达式类型时报错
|
||||
* <p>
|
||||
* 某些简单表达式(如字面量、变量名)可以直接写入目标寄存器;复杂表达式则会先 build 到新寄存器,再 move 到目标寄存器。
|
||||
* </p>
|
||||
*
|
||||
* @param node 要生成的表达式节点
|
||||
* @param dest 目标虚拟寄存器(用于存储结果)
|
||||
*/
|
||||
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
|
||||
switch (node) {
|
||||
// 数字字面量,直接加载到目标寄存器
|
||||
// 数字字面量:生成 loadConst 指令写入目标寄存器
|
||||
case NumberLiteralNode n ->
|
||||
InstructionFactory.loadConstInto(ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
|
||||
// 标识符,查找并move到目标寄存器
|
||||
InstructionFactory.loadConstInto(
|
||||
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
|
||||
|
||||
// 字符串字面量:生成 loadConst 指令写入目标寄存器
|
||||
case StringLiteralNode s ->
|
||||
InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(s.value()));
|
||||
|
||||
// 布尔字面量:转换为 int 1/0,生成 loadConst
|
||||
case BoolLiteralNode b ->
|
||||
InstructionFactory.loadConstInto(
|
||||
ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
|
||||
|
||||
// 标识符:查表获取原寄存器,然后 move 到目标寄存器
|
||||
case IdentifierNode id -> {
|
||||
IRVirtualRegister src = ctx.getScope().lookup(id.name());
|
||||
if (src == null) throw new IllegalStateException("未定义标识符: " + id.name());
|
||||
if (src == null)
|
||||
throw new IllegalStateException("未定义标识符: " + id.name());
|
||||
InstructionFactory.move(ctx, src, dest);
|
||||
}
|
||||
// 二元表达式,直接写入目标寄存器
|
||||
|
||||
// 二元表达式:递归生成并写入目标寄存器
|
||||
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
|
||||
// 其他表达式,先递归生成寄存器,再move到目标寄存器
|
||||
|
||||
// 其它复杂情况:先 build 到新寄存器,再 move 到目标寄存器
|
||||
default -> {
|
||||
IRVirtualRegister tmp = build(node);
|
||||
InstructionFactory.move(ctx, tmp, dest);
|
||||
@ -127,41 +123,100 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ───────────────── 具体表达式类型 ───────────────── */
|
||||
|
||||
/**
|
||||
* 构建二元表达式的IR,生成新寄存器存储结果。
|
||||
* <p>
|
||||
* 先递归构建左右操作数,之后根据操作符类别(算术或比较)决定生成的IR操作码,
|
||||
* 并生成对应的二元运算指令。
|
||||
* 一元表达式构建
|
||||
*
|
||||
* @param bin 二元表达式节点
|
||||
* @return 存放结果的虚拟寄存器
|
||||
* <p>
|
||||
* 支持算术取负(-a)、逻辑非(!a)等一元运算符。
|
||||
* </p>
|
||||
*
|
||||
* @param un 一元表达式节点
|
||||
* @return 结果存储的新分配虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildBinary(BinaryExpressionNode bin) {
|
||||
String op = bin.operator();
|
||||
IRVirtualRegister left = build(bin.left());
|
||||
IRVirtualRegister right = build(bin.right());
|
||||
private IRVirtualRegister buildUnary(UnaryExpressionNode un) {
|
||||
// 递归生成操作数的寄存器
|
||||
IRVirtualRegister src = build(un.operand());
|
||||
// 分配目标寄存器
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
|
||||
// 1. 比较运算
|
||||
if (ComparisonUtils.isComparisonOperator(op)) {
|
||||
return InstructionFactory.binOp(
|
||||
ctx,
|
||||
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
|
||||
left, right);
|
||||
switch (un.operator()) {
|
||||
// 算术负号:生成取负指令
|
||||
case "-" -> ctx.addInstruction(
|
||||
new UnaryOperationInstruction(ExpressionUtils.negOp(un.operand()), dest, src));
|
||||
// 逻辑非:等价于 a == 0,生成比较指令
|
||||
case "!" -> {
|
||||
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
|
||||
return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, src, zero);
|
||||
}
|
||||
// 其它一元运算符不支持,抛异常
|
||||
default -> throw new IllegalStateException("未知一元运算符: " + un.operator());
|
||||
}
|
||||
|
||||
// 2. 其他算术 / 逻辑运算
|
||||
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||
return InstructionFactory.binOp(ctx, code, left, right);
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将二元表达式的结果直接写入指定寄存器dest。
|
||||
* <p>
|
||||
* 结构与{@link #buildBinary(BinaryExpressionNode)}类似,但不会新分配寄存器。
|
||||
* 构建函数或方法调用表达式。
|
||||
*
|
||||
* @param call AST 调用表达式节点
|
||||
* @return 存储调用结果的虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildCall(CallExpressionNode call) {
|
||||
// 递归生成所有参数(实参)对应的寄存器
|
||||
List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList();
|
||||
|
||||
// 解析被调用目标名,支持普通函数/成员方法
|
||||
String callee = switch (call.callee()) {
|
||||
// 成员方法调用,例如 obj.foo()
|
||||
case MemberExpressionNode m when m.object() instanceof IdentifierNode id
|
||||
-> id.name() + "." + m.member();
|
||||
// 普通函数调用
|
||||
case IdentifierNode id -> id.name();
|
||||
// 其它情况暂不支持
|
||||
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
};
|
||||
|
||||
// 为返回值分配新寄存器,生成 Call 指令
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 二元表达式构建,结果存储到新寄存器。
|
||||
* <br>
|
||||
* 支持算术、位运算与比较(==, !=, >, <, ...)。
|
||||
*
|
||||
* @param bin 二元表达式节点
|
||||
* @param dest 目标寄存器
|
||||
* @return 存储表达式结果的虚拟寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildBinary(BinaryExpressionNode bin) {
|
||||
// 递归生成左、右子表达式的寄存器
|
||||
IRVirtualRegister a = build(bin.left());
|
||||
IRVirtualRegister b = build(bin.right());
|
||||
String op = bin.operator();
|
||||
|
||||
// 比较运算符(==、!=、>、< 等),需要生成条件跳转或布尔值寄存器
|
||||
if (ComparisonUtils.isComparisonOperator(op)) {
|
||||
return InstructionFactory.binOp(
|
||||
ctx,
|
||||
// 通过比较工具获得合适的 IR 操作码
|
||||
ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
|
||||
a, b);
|
||||
}
|
||||
|
||||
// 其它算术/位运算
|
||||
IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
|
||||
if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
|
||||
return InstructionFactory.binOp(ctx, code, a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 二元表达式构建,结果直接写入目标寄存器(用于赋值左值等优化场景)。
|
||||
*
|
||||
* @param bin 二元表达式节点
|
||||
* @param dest 目标虚拟寄存器
|
||||
*/
|
||||
private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) {
|
||||
IRVirtualRegister a = build(bin.left());
|
||||
@ -180,54 +235,44 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理函数调用表达式,生成对应的Call指令和目标寄存器。
|
||||
* <p>
|
||||
* 支持普通标识符调用和成员调用(如 mod.func),会为每个参数依次生成子表达式的寄存器。
|
||||
*
|
||||
* @param call 调用表达式AST节点
|
||||
* @return 返回结果存放的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildCall(CallExpressionNode call) {
|
||||
// 递归构建所有参数的寄存器
|
||||
List<IRVirtualRegister> argv = call.arguments().stream()
|
||||
.map(this::build)
|
||||
.toList();
|
||||
// 获取完整调用目标名称(支持成员/模块调用和普通调用)
|
||||
String fullName = switch (call.callee()) {
|
||||
case MemberExpressionNode member when member.object() instanceof IdentifierNode _ ->
|
||||
((IdentifierNode)member.object()).name() + "." + member.member();
|
||||
case IdentifierNode id -> id.name();
|
||||
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
};
|
||||
// 申请目标寄存器
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
// 添加Call指令到IR上下文
|
||||
ctx.addInstruction(new CallInstruction(dest, fullName, new ArrayList<>(argv)));
|
||||
return dest;
|
||||
}
|
||||
/* ───────────────── 字面量辅助方法 ───────────────── */
|
||||
|
||||
/**
|
||||
* 处理数字字面量,生成常量寄存器和加载指令。
|
||||
* <p>
|
||||
* 会将字符串型字面量(如 "123", "1.0f")解析为具体的IRConstant,
|
||||
* 并分配一个新的虚拟寄存器来存放该常量。
|
||||
* 构建数字字面量表达式(如 123),分配新寄存器并生成 LoadConst 指令。
|
||||
*
|
||||
* @param value 字面量字符串
|
||||
* @return 存放该常量的寄存器
|
||||
* @param value 字面量文本(字符串格式)
|
||||
* @return 存储该字面量的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildNumberLiteral(String value) {
|
||||
IRConstant constant = ExpressionUtils.buildNumberConstant(ctx, value);
|
||||
IRVirtualRegister reg = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(reg, constant));
|
||||
return reg;
|
||||
IRConstant c = ExpressionUtils.buildNumberConstant(ctx, value);
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
|
||||
/** 布尔字面量 → CONST (true=1,false=0)*/
|
||||
private IRVirtualRegister buildBoolLiteral(boolean value) {
|
||||
IRConstant constant = new IRConstant(value ? 1 : 0);
|
||||
IRVirtualRegister reg = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(reg, constant));
|
||||
return reg;
|
||||
/**
|
||||
* 构建字符串字面量表达式,分配新寄存器并生成 LoadConst 指令。
|
||||
*
|
||||
* @param value 字符串内容
|
||||
* @return 存储该字符串的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildStringLiteral(String value) {
|
||||
IRConstant c = new IRConstant(value);
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建布尔字面量表达式(true/false),分配新寄存器并生成 LoadConst 指令(1 表示 true,0 表示 false)。
|
||||
*
|
||||
* @param v 布尔值
|
||||
* @return 存储 1/0 的寄存器
|
||||
*/
|
||||
private IRVirtualRegister buildBoolLiteral(boolean v) {
|
||||
IRConstant c = new IRConstant(v ? 1 : 0);
|
||||
IRVirtualRegister r = ctx.newRegister();
|
||||
ctx.addInstruction(new LoadConstInstruction(r, c));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ import java.util.List;
|
||||
* <p>
|
||||
* 它负责处理类似 {@code callee(arg1, arg2, ...)} 形式的调用表达式,执行如下操作:
|
||||
* <ul>
|
||||
* <li>识别调用目标(支持模块成员函数调用和当前模块函数调用);</li>
|
||||
* <li>识别调用目标(支持模块成员函数调用和当前模块函数调用,也支持自动在所有已导入模块中查找唯一同名函数);</li>
|
||||
* <li>根据被调用函数的参数签名检查实参数量和类型的兼容性;</li>
|
||||
* <li>支持数值参数的宽化转换(如 int → double);</li>
|
||||
* <li>支持数值到字符串的隐式转换(自动视为调用 {@code to_string});</li>
|
||||
@ -77,8 +77,38 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
|
||||
// 查找目标函数签名
|
||||
// 查找目标函数签名(先在当前模块/显式模块查找)
|
||||
FunctionType ft = target.getFunctions().get(functionName);
|
||||
|
||||
// 如果当前模块未找到,再自动遍历所有已导入模块寻找唯一同名函数
|
||||
if (ft == null && target == mi) {
|
||||
ModuleInfo foundModule = null;
|
||||
FunctionType foundType = null;
|
||||
for (String importName : mi.getImports()) {
|
||||
ModuleInfo imported = ctx.getModules().get(importName);
|
||||
if (imported == null) continue;
|
||||
FunctionType candidate = imported.getFunctions().get(functionName);
|
||||
if (candidate != null) {
|
||||
if (foundModule != null) {
|
||||
// 多个导入模块含有同名函数,二义性,报错
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"函数调用不明确: " + functionName +
|
||||
" 同时存在于模块 " + foundModule.getName() +
|
||||
" 和 " + imported.getName()));
|
||||
ctx.log("错误: 函数调用不明确 " + functionName);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
foundModule = imported;
|
||||
foundType = candidate;
|
||||
}
|
||||
}
|
||||
if (foundType != null) {
|
||||
target = foundModule;
|
||||
ft = foundType;
|
||||
}
|
||||
}
|
||||
|
||||
// 最终未找到则报错
|
||||
if (ft == null) {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"函数未定义: " + functionName));
|
||||
|
||||
@ -1,54 +1,77 @@
|
||||
package org.jcnc.snow.compiler.semantic.core;
|
||||
|
||||
import org.jcnc.snow.compiler.semantic.type.*;
|
||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.FunctionType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code BuiltinTypeRegistry} 是内置类型和内置模块的集中注册中心。
|
||||
* <b>BuiltinTypeRegistry - 语言全部内置类型/模块/函数注册中心</b>
|
||||
*
|
||||
* <p>
|
||||
* 本类主要负责:
|
||||
* 该类统一注册编译器需要用到的所有基础类型、标准库模块与内核函数,供语义分析及类型检查阶段使用。
|
||||
* <ul>
|
||||
* <li>定义语言中所有可识别的基础类型(如 int、float、string 等);</li>
|
||||
* <li>在语义分析初始化时,将内置模块(如 {@code BuiltinUtils})注册到上下文中;</li>
|
||||
* <li>提供对内置类型的快速查找支持。</li>
|
||||
* <li>所有基础类型(byte、short、int、long、float、double、string、boolean、void)</li>
|
||||
* <li>标准库模块 <b>BuiltinUtils</b>(仅注册函数签名,具体实现由 Snow 语言源码实现)</li>
|
||||
* <li>内核函数 <b>syscall</b>(供标准库内部实现调用)</li>
|
||||
* </ul>
|
||||
* 该类为纯工具类,所有成员均为静态,不可实例化。
|
||||
* </p>
|
||||
*/
|
||||
public final class BuiltinTypeRegistry {
|
||||
|
||||
/**
|
||||
* 内置类型映射表: 将类型名称字符串映射到对应的 {@link Type} 实例。
|
||||
* <b>基础类型表</b>:类型名称 → Type 实例
|
||||
* <p>
|
||||
* 用于类型解析过程(如解析变量声明或函数返回类型)中,
|
||||
* 将用户源码中的类型字符串转换为语义类型对象。
|
||||
* 本 Map 静态初始化,注册所有 Snow 语言基础类型,供类型检查与类型推断使用。
|
||||
* </p>
|
||||
*/
|
||||
public static final Map<String, Type> BUILTIN_TYPES = Map.of(
|
||||
"int", BuiltinType.INT,
|
||||
"long", BuiltinType.LONG,
|
||||
"short", BuiltinType.SHORT,
|
||||
"byte", BuiltinType.BYTE,
|
||||
"float", BuiltinType.FLOAT,
|
||||
"double", BuiltinType.DOUBLE,
|
||||
"string", BuiltinType.STRING,
|
||||
"boolean", BuiltinType.BOOLEAN,
|
||||
"void", BuiltinType.VOID
|
||||
);
|
||||
public static final Map<String, Type> BUILTIN_TYPES;
|
||||
static {
|
||||
Map<String, Type> t = new HashMap<>();
|
||||
t.put("byte", BuiltinType.BYTE); // 字节型
|
||||
t.put("short", BuiltinType.SHORT); // 短整型
|
||||
t.put("int", BuiltinType.INT); // 整型
|
||||
t.put("long", BuiltinType.LONG); // 长整型
|
||||
t.put("float", BuiltinType.FLOAT); // 单精度浮点
|
||||
t.put("double", BuiltinType.DOUBLE); // 双精度浮点
|
||||
t.put("string", BuiltinType.STRING); // 字符串
|
||||
t.put("void", BuiltinType.VOID); // 无返回
|
||||
t.put("boolean", BuiltinType.BOOLEAN); // 布尔类型
|
||||
|
||||
BUILTIN_TYPES = Collections.unmodifiableMap(t); // 不可变映射,防止被意外更改
|
||||
}
|
||||
|
||||
/**
|
||||
* 私有构造函数,禁止实例化。
|
||||
* 私有构造方法,禁止实例化
|
||||
*/
|
||||
private BuiltinTypeRegistry() { }
|
||||
|
||||
/**
|
||||
* 初始化语义上下文中与内置模块相关的内容。
|
||||
* <p>
|
||||
* 当前实现将内置模块 {@code BuiltinUtils} 注册至上下文模块表中,
|
||||
* 使其在用户代码中可被访问(如 {@code BuiltinUtils.to_string(...)})。
|
||||
* <b>初始化内置模块和函数声明</b>
|
||||
*
|
||||
* @param ctx 当前语义分析上下文
|
||||
* <p>
|
||||
* 语义分析阶段调用,将所有基础模块与函数声明注册到语义上下文中。
|
||||
* - 目前注册 BuiltinUtils 标准库模块(仅注册签名,不负责具体实现)。
|
||||
* - syscall 函数注册到 BuiltinUtils 内,供标准库内部调用。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 全局语义分析上下文,持有模块表
|
||||
*/
|
||||
public static void init(Context ctx) {
|
||||
/* ---------- 注册标准库 os ---------- */
|
||||
ModuleInfo utils = new ModuleInfo("os");
|
||||
|
||||
// syscall(string, int): void —— 供标准库内部使用的调用接口
|
||||
utils.getFunctions().put(
|
||||
"syscall",
|
||||
new FunctionType(
|
||||
Arrays.asList(BuiltinType.STRING, BuiltinType.INT),
|
||||
BuiltinType.VOID
|
||||
)
|
||||
);
|
||||
|
||||
// 注册 BuiltinUtils 到上下文的模块表(若已存在则不重复添加)
|
||||
ctx.getModules().putIfAbsent("os", utils);
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,6 +220,7 @@ public final class CompileTask implements Task {
|
||||
if (runAfterCompile) {
|
||||
System.out.println("\n=== Launching VM ===");
|
||||
VMLauncher.main(new String[]{outputFile.toString()});
|
||||
System.out.println("\n=== VM exited ===");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -5,12 +5,48 @@ import org.jcnc.snow.vm.interfaces.Command;
|
||||
import org.jcnc.snow.vm.module.*;
|
||||
|
||||
/**
|
||||
* CALL addr nArgs — pushes a new stack-frame, transfers the specified
|
||||
* argument count from the operand stack into the callee’s local slots
|
||||
* 0‥n-1 (left-to-right), then jumps to {@code addr}.
|
||||
* The CallCommand class implements the {@link Command} interface and represents a subroutine/function call
|
||||
* instruction in the virtual machine.
|
||||
* <p>
|
||||
* This command facilitates method invocation by creating a new stack frame, transferring arguments
|
||||
* from the operand stack to the callee's local variable store, and jumping to the specified target address.
|
||||
* </p>
|
||||
*
|
||||
* <p>Specific behavior:</p>
|
||||
* <ul>
|
||||
* <li>Parses the target address and the number of arguments from the instruction parameters.</li>
|
||||
* <li>Validates the operands and checks for correct argument count.</li>
|
||||
* <li>Builds a new local variable store for the callee and transfers arguments from the operand stack
|
||||
* (left-to-right order, where the top of the stack is the last argument).</li>
|
||||
* <li>Pushes a new stack frame onto the call stack, saving the return address and local variables.</li>
|
||||
* <li>Jumps to the specified target address to begin execution of the callee function.</li>
|
||||
* <li>If any error occurs (e.g., malformed operands, stack underflow), an exception is thrown.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class CallCommand implements Command {
|
||||
|
||||
/**
|
||||
* Executes the CALL instruction, initiating a subroutine/function call within the virtual machine.
|
||||
* <p>
|
||||
* This method handles the creation of a new stack frame for the callee, argument passing,
|
||||
* and control transfer to the target function address.
|
||||
* </p>
|
||||
*
|
||||
* @param parts The instruction parameters. Must include:
|
||||
* <ul>
|
||||
* <li>{@code parts[0]}: The "CALL" operator.</li>
|
||||
* <li>{@code parts[1]}: The target address of the callee function.</li>
|
||||
* <li>{@code parts[2]}: The number of arguments to pass.</li>
|
||||
* </ul>
|
||||
* @param currentPC The current program counter, used to record the return address for after the call.
|
||||
* @param operandStack The operand stack manager. Arguments are popped from this stack.
|
||||
* @param callerLVS The local variable store of the caller function (not directly modified here).
|
||||
* @param callStack The virtual machine's call stack manager, used to push the new stack frame.
|
||||
* @return The new program counter value, which is the address of the callee function (i.e., jump target).
|
||||
* The VM should transfer control to this address after setting up the call frame.
|
||||
* @throws IllegalArgumentException If the instruction parameters are malformed or missing.
|
||||
* @throws IllegalStateException If the operand stack does not contain enough arguments.
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] parts,
|
||||
int currentPC,
|
||||
@ -44,7 +80,7 @@ public class CallCommand implements Command {
|
||||
new MethodContext("subroutine@" + targetAddr, null));
|
||||
callStack.pushFrame(newFrame);
|
||||
|
||||
System.out.println("Calling function at address: " + targetAddr);
|
||||
System.out.println("\nCalling function at address: " + targetAddr);
|
||||
return targetAddr; // jump
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ public class RetCommand implements Command {
|
||||
finished.getLocalVariableStore().clearVariables();
|
||||
|
||||
int returnAddr = finished.getReturnAddress();
|
||||
System.out.println("Return " + returnAddr);
|
||||
System.out.println("\nReturn " + returnAddr);
|
||||
return returnAddr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
package org.jcnc.snow.vm.commands.ref.control;
|
||||
|
||||
import org.jcnc.snow.vm.interfaces.Command;
|
||||
import org.jcnc.snow.vm.module.CallStack;
|
||||
import org.jcnc.snow.vm.module.LocalVariableStore;
|
||||
import org.jcnc.snow.vm.module.OperandStack;
|
||||
|
||||
/**
|
||||
* The {@code RLoadCommand} class implements the {@link Command} interface and represents the
|
||||
* reference load instruction ({@code R_LOAD}) in the virtual machine.
|
||||
*
|
||||
* <p>
|
||||
* This instruction loads a reference object from the current stack frame’s local variable store
|
||||
* at the specified slot and pushes it onto the operand stack.
|
||||
* </p>
|
||||
*
|
||||
* <p>Instruction format: {@code R_LOAD <slot>}</p>
|
||||
* <ul>
|
||||
* <li>{@code <slot>}: The index in the local variable table to load the reference from.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Behavior:</p>
|
||||
* <ul>
|
||||
* <li>Parses the slot index from the instruction parameters.</li>
|
||||
* <li>Fetches the reference object stored at the specified slot in the current stack frame's local variable store.</li>
|
||||
* <li>Pushes the fetched reference onto the operand stack.</li>
|
||||
* <li>Increments the program counter to the next instruction.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class RLoadCommand implements Command {
|
||||
|
||||
/**
|
||||
* Executes the {@code R_LOAD} instruction, loading a reference from the local variable table and pushing it onto the operand stack.
|
||||
*
|
||||
* @param parts The instruction parameters. {@code parts[0]} is the operator ("R_LOAD"), {@code parts[1]} is the slot index.
|
||||
* @param pc The current program counter value, indicating the instruction address being executed.
|
||||
* @param stack The operand stack manager. The loaded reference will be pushed onto this stack.
|
||||
* @param lvs The local variable store. (Not used directly, as this command uses the store from the current stack frame.)
|
||||
* @param cs The call stack manager. The reference will be loaded from the local variable store of the top stack frame.
|
||||
* @return The next program counter value ({@code pc + 1}), pointing to the next instruction.
|
||||
* @throws NumberFormatException if the slot parameter cannot be parsed as an integer.
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] parts, int pc,
|
||||
OperandStack stack,
|
||||
LocalVariableStore lvs,
|
||||
CallStack cs) {
|
||||
|
||||
int slot = Integer.parseInt(parts[1]);
|
||||
Object v = cs.peekFrame().getLocalVariableStore().getVariable(slot);
|
||||
stack.push(v);
|
||||
return pc + 1;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package org.jcnc.snow.vm.commands.ref.control;
|
||||
|
||||
import org.jcnc.snow.vm.interfaces.Command;
|
||||
import org.jcnc.snow.vm.module.CallStack;
|
||||
import org.jcnc.snow.vm.module.LocalVariableStore;
|
||||
import org.jcnc.snow.vm.module.OperandStack;
|
||||
|
||||
/**
|
||||
* The {@code RPushCommand} class implements the {@link Command} interface and represents the
|
||||
* reference push instruction ({@code R_PUSH}) in the virtual machine.
|
||||
*
|
||||
* <p>
|
||||
* This instruction pushes a reference object, such as a String literal, onto the operand stack.
|
||||
* </p>
|
||||
*
|
||||
* <p>Instruction format: {@code R_PUSH <literal>}</p>
|
||||
* <ul>
|
||||
* <li>{@code <literal>}: The reference value (e.g., string) to be pushed onto the stack.
|
||||
* If the literal contains spaces, all parts after {@code R_PUSH} are joined into a single string.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Behavior:</p>
|
||||
* <ul>
|
||||
* <li>Checks that the instruction has at least one parameter after the operator.</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>Increments the program counter to the next instruction.</li>
|
||||
* <li>Throws an {@code IllegalStateException} if the instruction is missing required parameters.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class RPushCommand implements Command {
|
||||
|
||||
/**
|
||||
* Executes the {@code R_PUSH} instruction, pushing a reference (such as a string literal)
|
||||
* onto the operand stack.
|
||||
*
|
||||
* @param parts The instruction parameters. {@code parts[0]} is the operator ("R_PUSH"),
|
||||
* {@code parts[1..]} are the parts of the literal to be concatenated and pushed.
|
||||
* @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.
|
||||
* @param lvs The local variable store. (Not used in this instruction.)
|
||||
* @param cs The call stack manager. (Not used in this instruction.)
|
||||
* @return The next program counter value ({@code pc + 1}), pointing to the next instruction.
|
||||
* @throws IllegalStateException if the instruction is missing required parameters.
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] parts, int pc,
|
||||
OperandStack stack,
|
||||
LocalVariableStore lvs,
|
||||
CallStack cs) {
|
||||
|
||||
if (parts.length < 2)
|
||||
throw new IllegalStateException("R_PUSH missing parameter");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
if (i > 1) sb.append(' ');
|
||||
sb.append(parts[i]);
|
||||
}
|
||||
stack.push(sb.toString());
|
||||
return pc + 1;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package org.jcnc.snow.vm.commands.ref.control;
|
||||
|
||||
import org.jcnc.snow.vm.interfaces.Command;
|
||||
import org.jcnc.snow.vm.module.CallStack;
|
||||
import org.jcnc.snow.vm.module.LocalVariableStore;
|
||||
import org.jcnc.snow.vm.module.OperandStack;
|
||||
|
||||
/**
|
||||
* The {@code RStoreCommand} class implements the {@link Command} interface and represents the
|
||||
* reference store instruction ({@code R_STORE}) in the virtual machine.
|
||||
*
|
||||
* <p>
|
||||
* This instruction pops a reference object from the top of the operand stack
|
||||
* and stores it in the local variable table at the specified slot index of the current stack frame.
|
||||
* </p>
|
||||
*
|
||||
* <p>Instruction format: {@code R_STORE <slot>}</p>
|
||||
* <ul>
|
||||
* <li>{@code <slot>}: The index in the local variable table where the reference will be stored.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Behavior:</p>
|
||||
* <ul>
|
||||
* <li>Parses the slot index from the instruction parameters.</li>
|
||||
* <li>Pops a reference object from the operand stack.</li>
|
||||
* <li>Stores the popped reference object into the local variable table of the current stack frame at the specified slot.</li>
|
||||
* <li>Increments the program counter to the next instruction.</li>
|
||||
* <li>If the operand stack is empty, a {@code java.util.EmptyStackException} may be thrown.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class RStoreCommand implements Command {
|
||||
|
||||
/**
|
||||
* Executes the {@code R_STORE} instruction, storing a reference object from the top of the operand stack
|
||||
* into the local variable table of the current stack frame.
|
||||
*
|
||||
* @param parts The instruction parameters. {@code parts[0]} is the operator ("R_STORE"),
|
||||
* {@code parts[1]} is the slot index.
|
||||
* @param pc The current program counter value, indicating the instruction address being executed.
|
||||
* @param stack The operand stack manager. The reference object will be popped from this stack.
|
||||
* @param lvs The local variable store. (Not used directly, as the store from the current stack frame is used.)
|
||||
* @param cs The call stack manager. The reference will be stored in the local variable store of the top stack frame.
|
||||
* @return The next program counter value ({@code pc + 1}), pointing to the next instruction.
|
||||
* @throws NumberFormatException if the slot parameter cannot be parsed as an integer.
|
||||
* @throws java.util.EmptyStackException if the operand stack is empty when popping.
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] parts, int pc,
|
||||
OperandStack stack,
|
||||
LocalVariableStore lvs,
|
||||
CallStack cs) {
|
||||
|
||||
int slot = Integer.parseInt(parts[1]);
|
||||
Object v = stack.pop();
|
||||
cs.peekFrame().getLocalVariableStore().setVariable(slot, v);
|
||||
return pc + 1;
|
||||
}
|
||||
}
|
||||
@ -59,7 +59,7 @@ public class HaltCommand implements Command {
|
||||
@Override
|
||||
public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) {
|
||||
// Output the termination message
|
||||
LoggingUtils.logInfo("Process has ended", "\n");
|
||||
LoggingUtils.logInfo("\nProcess has ended", "");
|
||||
|
||||
// Return -1 to indicate the program termination, and the virtual machine will not continue executing subsequent instructions
|
||||
return -1;
|
||||
|
||||
@ -0,0 +1,340 @@
|
||||
package org.jcnc.snow.vm.commands.system.control;
|
||||
|
||||
import org.jcnc.snow.vm.interfaces.Command;
|
||||
import org.jcnc.snow.vm.io.FDTable;
|
||||
import org.jcnc.snow.vm.module.CallStack;
|
||||
import org.jcnc.snow.vm.module.LocalVariableStore;
|
||||
import org.jcnc.snow.vm.module.OperandStack;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
import static java.nio.file.StandardOpenOption.*;
|
||||
|
||||
/**
|
||||
* SyscallCommand —— 虚拟机系统调用(SYSCALL)指令实现。
|
||||
*
|
||||
* <p>
|
||||
* 本类负责将虚拟机指令集中的 SYSCALL 进行分派,模拟现实系统常见的文件、网络、管道、标准输出等操作,
|
||||
* 通过操作数栈完成参数、返回值传递,并借助文件描述符表(FDTable)进行底层资源管理。
|
||||
* 所有 I/O 相关功能均基于 Java NIO 实现,兼容多种 I/O 场景。
|
||||
* </p>
|
||||
*
|
||||
* <p>参数与栈约定:</p>
|
||||
* <ul>
|
||||
* <li>所有调用参数,均按“右值先入、左值后入”顺序压入 {@link OperandStack}。</li>
|
||||
* <li>SYSCALL 指令自动弹出参数并处理结果;返回值(如描述符、读取长度、是否成功等)压回栈顶。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>异常与失败处理:</p>
|
||||
* <ul>
|
||||
* <li>系统调用失败或遇到异常时,均向操作数栈压入 {@code -1},以便调用者统一检测。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>支持的子命令示例:</p>
|
||||
* <ul>
|
||||
* <li>PRINT / PRINTLN —— 控制台输出</li>
|
||||
* <li>OPEN / CLOSE / READ / WRITE / SEEK —— 文件相关操作</li>
|
||||
* <li>PIPE / DUP —— 管道与文件描述符复制</li>
|
||||
* <li>SOCKET / CONNECT / BIND / LISTEN / ACCEPT —— 网络通信</li>
|
||||
* <li>SELECT —— 多通道 I/O 就绪检测</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class SyscallCommand implements Command {
|
||||
|
||||
/**
|
||||
* 分发并执行 SYSCALL 子命令,根据子命令类型从操作数栈取出参数、操作底层资源,并将结果压回栈顶。
|
||||
*
|
||||
* @param parts 指令及子命令参数分割数组,parts[1]为子命令名
|
||||
* @param pc 当前指令计数器
|
||||
* @param stack 操作数栈
|
||||
* @param locals 局部变量表
|
||||
* @param callStack 调用栈
|
||||
* @return 下一条指令的 pc 值(通常为 pc+1)
|
||||
* @throws IllegalArgumentException 缺少子命令参数时抛出
|
||||
* @throws UnsupportedOperationException 不支持的 SYSCALL 子命令时抛出
|
||||
*/
|
||||
@Override
|
||||
public int execute(String[] parts, int pc,
|
||||
OperandStack stack,
|
||||
LocalVariableStore locals,
|
||||
CallStack callStack) {
|
||||
|
||||
if (parts.length < 2) {
|
||||
throw new IllegalArgumentException("SYSCALL missing subcommand");
|
||||
}
|
||||
|
||||
String cmd = parts[1].toUpperCase(Locale.ROOT);
|
||||
|
||||
try {
|
||||
switch (cmd) {
|
||||
// 文件相关操作
|
||||
case "OPEN" -> {
|
||||
int mode = (Integer) stack.pop();
|
||||
int flags = (Integer) stack.pop();
|
||||
String path = String.valueOf(stack.pop());
|
||||
FileChannel fc = FileChannel.open(Paths.get(path), flagsToOptions(flags));
|
||||
stack.push(FDTable.register(fc));
|
||||
}
|
||||
case "CLOSE" -> {
|
||||
int fd = (Integer) stack.pop();
|
||||
FDTable.close(fd);
|
||||
stack.push(0);
|
||||
}
|
||||
case "READ" -> {
|
||||
int count = (Integer) stack.pop();
|
||||
int fd = (Integer) stack.pop();
|
||||
Channel ch = FDTable.get(fd);
|
||||
if (!(ch instanceof ReadableByteChannel rch)) {
|
||||
stack.push(new byte[0]);
|
||||
break;
|
||||
}
|
||||
ByteBuffer buf = ByteBuffer.allocate(count);
|
||||
int n = rch.read(buf);
|
||||
if (n < 0) n = 0;
|
||||
buf.flip();
|
||||
byte[] out = new byte[n];
|
||||
buf.get(out);
|
||||
stack.push(out);
|
||||
}
|
||||
case "WRITE" -> {
|
||||
Object dataObj = stack.pop();
|
||||
int fd = (Integer) stack.pop();
|
||||
byte[] data = (dataObj instanceof byte[] b)
|
||||
? b
|
||||
: String.valueOf(dataObj).getBytes();
|
||||
Channel ch = FDTable.get(fd);
|
||||
if (!(ch instanceof WritableByteChannel wch)) {
|
||||
stack.push(-1);
|
||||
break;
|
||||
}
|
||||
int written = wch.write(ByteBuffer.wrap(data));
|
||||
stack.push(written);
|
||||
}
|
||||
case "SEEK" -> {
|
||||
int whence = (Integer) stack.pop();
|
||||
long off = ((Number) stack.pop()).longValue();
|
||||
int fd = (Integer) stack.pop();
|
||||
Channel ch = FDTable.get(fd);
|
||||
if (!(ch instanceof SeekableByteChannel sbc)) {
|
||||
stack.push(-1);
|
||||
break;
|
||||
}
|
||||
SeekableByteChannel newPos = switch (whence) {
|
||||
case 0 -> sbc.position(off);
|
||||
case 1 -> sbc.position(sbc.position() + off);
|
||||
case 2 -> sbc.position(sbc.size() + off);
|
||||
default -> throw new IllegalArgumentException("Invalid offset type");
|
||||
};
|
||||
stack.push(newPos);
|
||||
}
|
||||
|
||||
// 管道与描述符操作
|
||||
case "PIPE" -> {
|
||||
Pipe p = Pipe.open();
|
||||
stack.push(FDTable.register(p.sink()));
|
||||
stack.push(FDTable.register(p.source()));
|
||||
}
|
||||
case "DUP" -> {
|
||||
int oldfd = (Integer) stack.pop();
|
||||
stack.push(FDTable.dup(oldfd));
|
||||
}
|
||||
|
||||
// 网络相关
|
||||
case "SOCKET" -> {
|
||||
int proto = (Integer) stack.pop();
|
||||
int type = (Integer) stack.pop();
|
||||
int domain = (Integer) stack.pop();
|
||||
Channel ch = (type == 1)
|
||||
? SocketChannel.open()
|
||||
: DatagramChannel.open();
|
||||
stack.push(FDTable.register(ch));
|
||||
}
|
||||
case "CONNECT" -> {
|
||||
int port = (Integer) stack.pop();
|
||||
String host = String.valueOf(stack.pop());
|
||||
int fd = (Integer) stack.pop();
|
||||
Channel ch = FDTable.get(fd);
|
||||
if (ch instanceof SocketChannel sc) {
|
||||
sc.connect(new InetSocketAddress(host, port));
|
||||
stack.push(0);
|
||||
} else {
|
||||
stack.push(-1);
|
||||
}
|
||||
}
|
||||
case "BIND" -> {
|
||||
int port = (Integer) stack.pop();
|
||||
String host = String.valueOf(stack.pop());
|
||||
int fd = (Integer) stack.pop();
|
||||
Channel ch = FDTable.get(fd);
|
||||
if (ch instanceof ServerSocketChannel ssc) {
|
||||
ssc.bind(new InetSocketAddress(host, port));
|
||||
stack.push(0);
|
||||
} else {
|
||||
stack.push(-1);
|
||||
}
|
||||
}
|
||||
case "LISTEN" -> {
|
||||
int backlog = (Integer) stack.pop();
|
||||
int fd = (Integer) stack.pop();
|
||||
Channel ch = FDTable.get(fd);
|
||||
if (ch instanceof ServerSocketChannel) {
|
||||
stack.push(0);
|
||||
} else {
|
||||
stack.push(-1);
|
||||
}
|
||||
}
|
||||
case "ACCEPT" -> {
|
||||
int fd = (Integer) stack.pop();
|
||||
Channel ch = FDTable.get(fd);
|
||||
if (ch instanceof ServerSocketChannel ssc) {
|
||||
SocketChannel cli = ssc.accept();
|
||||
stack.push(FDTable.register(cli));
|
||||
} else {
|
||||
stack.push(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// 多路复用
|
||||
case "SELECT" -> {
|
||||
long timeout = ((Number) stack.pop()).longValue();
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Integer> fds = (List<Integer>) stack.pop();
|
||||
|
||||
Selector sel = Selector.open();
|
||||
for (int fd : fds) {
|
||||
Channel c = FDTable.get(fd);
|
||||
if (c instanceof SelectableChannel sc) {
|
||||
sc.configureBlocking(false);
|
||||
int ops = (c instanceof ReadableByteChannel ? SelectionKey.OP_READ : 0)
|
||||
| (c instanceof WritableByteChannel ? SelectionKey.OP_WRITE : 0);
|
||||
sc.register(sel, ops, fd);
|
||||
}
|
||||
}
|
||||
int ready = sel.select(timeout);
|
||||
List<Integer> readyFds = new ArrayList<>();
|
||||
if (ready > 0) {
|
||||
for (SelectionKey k : sel.selectedKeys()) {
|
||||
readyFds.add((Integer) k.attachment());
|
||||
}
|
||||
}
|
||||
stack.push(readyFds);
|
||||
sel.close();
|
||||
}
|
||||
|
||||
// 控制台输出
|
||||
case "PRINT" -> {
|
||||
Object dataObj = stack.pop();
|
||||
output(dataObj, false);
|
||||
stack.push(0);
|
||||
}
|
||||
case "PRINTLN" -> {
|
||||
Object dataObj = stack.pop();
|
||||
output(dataObj, true);
|
||||
stack.push(0);
|
||||
}
|
||||
|
||||
default -> throw new UnsupportedOperationException("Unsupported SYSCALL subcommand: " + cmd);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
pushErr(stack, e);
|
||||
}
|
||||
|
||||
return pc + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的文件打开标志,构造 NIO {@link OpenOption} 集合。
|
||||
* <p>
|
||||
* 本方法负责将底层虚拟机传递的 flags 整数型位域,转换为 Java NIO 标准的文件打开选项集合,
|
||||
* 以支持文件读、写、创建、截断、追加等多种访问场景。
|
||||
* 常用于 SYSCALL 的 OPEN 子命令。
|
||||
* </p>
|
||||
*
|
||||
* @param flags 文件打开模式标志。各标志可组合使用,具体含义请参见虚拟机文档。
|
||||
* @return 转换后的 OpenOption 集合,可直接用于 FileChannel.open 等 NIO 方法
|
||||
*/
|
||||
private static Set<OpenOption> flagsToOptions(int flags) {
|
||||
Set<OpenOption> opts = new HashSet<>();
|
||||
// 如果有写入标志,则添加WRITE,否则默认为READ。
|
||||
if ((flags & 0x1) != 0) opts.add(WRITE);
|
||||
else opts.add(READ);
|
||||
// 如果包含创建标志,允许创建文件。
|
||||
if ((flags & 0x40) != 0) opts.add(CREATE);
|
||||
// 包含截断标志,打开时清空内容。
|
||||
if ((flags & 0x200) != 0) opts.add(TRUNCATE_EXISTING);
|
||||
// 包含追加标志,文件写入时追加到末尾。
|
||||
if ((flags & 0x400) != 0) opts.add(APPEND);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获所有异常并统一处理,操作数栈压入 -1 代表本次系统调用失败。
|
||||
* <p>
|
||||
* 本方法是全局错误屏障,任何命令异常都会转换为虚拟机通用的失败信号,
|
||||
* 保证上层调用逻辑不会被异常打断。实际应用中可拓展错误码机制。
|
||||
* </p>
|
||||
*
|
||||
* @param stack 操作数栈,将失败信号写入此栈
|
||||
* @param e 抛出的异常对象,可在调试时输出日志
|
||||
*/
|
||||
private static void pushErr(OperandStack stack, Exception e) {
|
||||
stack.push(-1);
|
||||
System.err.println("Syscall exception: " + e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制台输出通用方法,支持基本类型、字节数组、任意数组、对象等。
|
||||
* <p>
|
||||
* 该方法用于 SYSCALL PRINT/PRINTLN,将任意类型对象转为易读字符串输出到标准输出流。
|
||||
* 字节数组自动按 UTF-8 解码,其它原生数组按格式化字符串输出。
|
||||
* </p>
|
||||
*
|
||||
* @param obj 待输出的内容,可以为任何类型(如基本类型、byte[]、数组、对象等)
|
||||
* @param newline 是否自动换行。如果为 true,则在输出后换行;否则直接输出。
|
||||
*/
|
||||
private static void output(Object obj, boolean newline) {
|
||||
String str;
|
||||
if (obj == null) {
|
||||
str = "null";
|
||||
} else if (obj instanceof byte[] bytes) {
|
||||
// 字节数组作为文本输出
|
||||
str = new String(bytes);
|
||||
} else if (obj.getClass().isArray()) {
|
||||
// 其它数组格式化输出
|
||||
str = arrayToString(obj);
|
||||
} else {
|
||||
str = obj.toString();
|
||||
}
|
||||
if (newline) System.out.println(str);
|
||||
else System.out.print(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将各种原生数组和对象数组转换为可读字符串,便于控制台输出和调试。
|
||||
* <p>
|
||||
* 本方法针对 int、long、double、float、short、char、byte、boolean 等所有原生数组类型
|
||||
* 以及对象数组都能正确格式化,统一输出格式风格,避免显示为类型 hashCode。
|
||||
* 若为不支持的类型,返回通用提示字符串。
|
||||
* </p>
|
||||
*
|
||||
* @param array 任意原生数组或对象数组
|
||||
* @return 该数组的可读字符串表示
|
||||
*/
|
||||
private static String arrayToString(Object array) {
|
||||
if (array instanceof int[] a) return Arrays.toString(a);
|
||||
if (array instanceof long[] a) return Arrays.toString(a);
|
||||
if (array instanceof double[] a) return Arrays.toString(a);
|
||||
if (array instanceof float[] a) return Arrays.toString(a);
|
||||
if (array instanceof short[] a) return Arrays.toString(a);
|
||||
if (array instanceof char[] a) return Arrays.toString(a);
|
||||
if (array instanceof byte[] a) return Arrays.toString(a);
|
||||
if (array instanceof boolean[] a) return Arrays.toString(a);
|
||||
if (array instanceof Object[] a) return Arrays.deepToString(a);
|
||||
return "Unsupported array";
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,9 @@
|
||||
package org.jcnc.snow.vm.engine;
|
||||
|
||||
import org.jcnc.snow.vm.commands.ref.control.RLoadCommand;
|
||||
import org.jcnc.snow.vm.commands.ref.control.RPushCommand;
|
||||
import org.jcnc.snow.vm.commands.ref.control.RStoreCommand;
|
||||
import org.jcnc.snow.vm.commands.system.control.SyscallCommand;
|
||||
import org.jcnc.snow.vm.commands.type.control.byte8.*;
|
||||
import org.jcnc.snow.vm.commands.type.control.double64.*;
|
||||
import org.jcnc.snow.vm.commands.type.control.float32.*;
|
||||
@ -2482,6 +2486,74 @@ public class VMOpCode {
|
||||
// endregion Double64
|
||||
// endregion Conversion
|
||||
|
||||
// region Reference Control (0x00E0-0x00EF)
|
||||
/**
|
||||
* R_PUSH Opcode: Represents an operation that pushes an object reference (such as a String or any reference type)
|
||||
* onto the operand stack.
|
||||
* <p>This opcode is implemented by the {@link RPushCommand} class, which defines its specific execution logic.</p>
|
||||
*
|
||||
* <p>Execution Steps:</p>
|
||||
* <ol>
|
||||
* <li>Parses the object reference literal (e.g., a string) from the instruction parameters.</li>
|
||||
* <li>Creates or interprets the reference as an object instance if necessary.</li>
|
||||
* <li>Pushes the reference object onto the operand stack.</li>
|
||||
* <li>Increments the program counter (PC) to proceed with the next sequential instruction.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>This opcode is commonly used for:</p>
|
||||
* <ul>
|
||||
* <li>Pushing string literals, objects, or other references needed for subsequent operations.</li>
|
||||
* <li>Supplying parameters for method calls that expect reference types.</li>
|
||||
* <li>Initializing the operand stack with reference values for computation or storage.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final int R_PUSH = 0x00E0;
|
||||
/**
|
||||
* R_LOAD Opcode: Represents an operation that loads an object reference from the local variable table
|
||||
* and pushes it onto the operand stack.
|
||||
* <p>This opcode is implemented by the {@link RLoadCommand} class, which defines its specific execution logic.</p>
|
||||
*
|
||||
* <p>Execution Steps:</p>
|
||||
* <ol>
|
||||
* <li>Parses the target slot index from the instruction parameters.</li>
|
||||
* <li>Retrieves the reference object stored at the specified slot in the local variable table
|
||||
* of the current stack frame.</li>
|
||||
* <li>Pushes the retrieved reference onto the operand stack.</li>
|
||||
* <li>Increments the program counter (PC) to proceed with the next sequential instruction.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>This opcode is commonly used for:</p>
|
||||
* <ul>
|
||||
* <li>Accessing local variables (such as function arguments or local object references) during method execution.</li>
|
||||
* <li>Preparing reference values for operations or method invocations.</li>
|
||||
* <li>Reusing objects previously stored in local variables.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final int R_LOAD = 0x00E1;
|
||||
/**
|
||||
* R_STORE Opcode: Represents an operation that pops an object reference from the top of the operand stack
|
||||
* and stores it into the local variable table at the specified slot index.
|
||||
* <p>This opcode is implemented by the {@link RStoreCommand} class, which defines its specific execution logic.</p>
|
||||
*
|
||||
* <p>Execution Steps:</p>
|
||||
* <ol>
|
||||
* <li>Parses the target slot index from the instruction parameters.</li>
|
||||
* <li>Pops the top reference object from the operand stack.</li>
|
||||
* <li>Stores the popped reference object into the specified slot in the local variable table
|
||||
* of the current stack frame.</li>
|
||||
* <li>Increments the program counter (PC) to proceed with the next sequential instruction.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>This opcode is commonly used for:</p>
|
||||
* <ul>
|
||||
* <li>Storing computation results or intermediate reference values for later use.</li>
|
||||
* <li>Passing object references to local variables in preparation for further operations or method calls.</li>
|
||||
* <li>Managing object lifetimes and ensuring correct referencing in scoped execution contexts.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final int R_STORE = 0x00E2;
|
||||
// endregion
|
||||
|
||||
// region Stack Control (0x0100-0x01FF)
|
||||
/**
|
||||
* POP Opcode: Represents a stack operation that removes the top element from the operand stack.
|
||||
@ -2646,8 +2718,18 @@ public class VMOpCode {
|
||||
* </ul>
|
||||
*/
|
||||
public static final int HALT = 0x0400;
|
||||
/**
|
||||
* SYSCALL Opcode: Represents a system call operation that invokes a system-level function or service.
|
||||
* <p>This opcode is implemented by the {@link SyscallCommand} class, which defines its specific execution logic.</p>
|
||||
*
|
||||
* <p>Execution Steps:</p>
|
||||
* <ol>
|
||||
* <li>Parses the system call identifier from the instruction parameters.</li>
|
||||
* <li>Invokes the corresponding system-level function or service based on the system call identifier.</li>
|
||||
* <li>Returns the result of the system call operation.</li>
|
||||
*/
|
||||
public static final int SYSCALL = 0x0401;
|
||||
public static final int DEBUG_TRAP = 0x0402;
|
||||
// public static final int DEBUG_TRAP = 0x0402;
|
||||
// endregion
|
||||
|
||||
/**
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
package org.jcnc.snow.vm.factories;
|
||||
|
||||
import org.jcnc.snow.vm.commands.system.control.SyscallCommand;
|
||||
import org.jcnc.snow.vm.commands.type.control.byte8.*;
|
||||
import org.jcnc.snow.vm.commands.type.control.double64.*;
|
||||
import org.jcnc.snow.vm.commands.type.control.float32.*;
|
||||
import org.jcnc.snow.vm.commands.type.control.int32.*;
|
||||
import org.jcnc.snow.vm.commands.type.control.long64.*;
|
||||
import org.jcnc.snow.vm.commands.ref.control.RLoadCommand;
|
||||
import org.jcnc.snow.vm.commands.ref.control.RPushCommand;
|
||||
import org.jcnc.snow.vm.commands.ref.control.RStoreCommand;
|
||||
import org.jcnc.snow.vm.commands.type.control.short16.*;
|
||||
import org.jcnc.snow.vm.commands.type.control.byte8.BAndCommand;
|
||||
import org.jcnc.snow.vm.commands.type.control.byte8.BOrCommand;
|
||||
@ -61,6 +65,7 @@ public class CommandFactory {
|
||||
|
||||
static {
|
||||
|
||||
|
||||
// region Type Control (0x0000-0x00BF)
|
||||
// region Byte8 (0x0000-0x001F)
|
||||
COMMANDS[VMOpCode.B_ADD] = new BAddCommand();
|
||||
@ -204,6 +209,7 @@ public class CommandFactory {
|
||||
COMMANDS[VMOpCode.D_CL] = new DCLCommand();
|
||||
COMMANDS[VMOpCode.D_CLE] = new DCLECommand();
|
||||
// endregion
|
||||
|
||||
// endregion
|
||||
|
||||
// region Type Conversion (0x00C0-0x00DF)
|
||||
@ -244,6 +250,12 @@ public class CommandFactory {
|
||||
COMMANDS[VMOpCode.D2F] = new D2FCommand();
|
||||
// endregion
|
||||
|
||||
// region Reference Control (0x00E0-0x00EF)
|
||||
COMMANDS[VMOpCode.R_PUSH] = new RPushCommand();
|
||||
COMMANDS[VMOpCode.R_LOAD] = new RLoadCommand();
|
||||
COMMANDS[VMOpCode.R_STORE] = new RStoreCommand();
|
||||
// endregion
|
||||
|
||||
// region Stack Control (0x0100-0x01FF)
|
||||
COMMANDS[VMOpCode.POP] = new PopCommand();
|
||||
COMMANDS[VMOpCode.DUP] = new DupCommand();
|
||||
@ -262,7 +274,7 @@ public class CommandFactory {
|
||||
|
||||
// region System Control (0x0400-0x04FF)
|
||||
COMMANDS[VMOpCode.HALT] = new HaltCommand();
|
||||
// COMMANDS[VMOpCode.SYSCALL] = new SyscallCommand();
|
||||
COMMANDS[VMOpCode.SYSCALL] = new SyscallCommand();
|
||||
// COMMANDS[VMOpCode.DEBUG_TRAP] = new DebugTrapCommand();
|
||||
// endregion
|
||||
|
||||
|
||||
62
src/main/java/org/jcnc/snow/vm/io/FDTable.java
Normal file
62
src/main/java/org/jcnc/snow/vm/io/FDTable.java
Normal file
@ -0,0 +1,62 @@
|
||||
package org.jcnc.snow.vm.io;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 维护 “虚拟 fd → Java NIO Channel” 的全局映射表。
|
||||
*
|
||||
* <pre>
|
||||
* 0 → stdin (ReadableByteChannel)
|
||||
* 1 → stdout (WritableByteChannel)
|
||||
* 2 → stderr (WritableByteChannel)
|
||||
* 3+ → 运行期动态分配
|
||||
* </pre>
|
||||
*/
|
||||
public final class FDTable {
|
||||
|
||||
private FDTable() {}
|
||||
|
||||
/** 下一次可用 fd(0‒2 保留给标准流) */
|
||||
private static final AtomicInteger NEXT_FD = new AtomicInteger(3);
|
||||
/** 主映射表:fd → Channel */
|
||||
private static final ConcurrentHashMap<Integer, Channel> MAP = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
// JVM 标准流包装成 NIO Channel 后放入表中
|
||||
MAP.put(0, Channels.newChannel(new BufferedInputStream(System.in)));
|
||||
MAP.put(1, Channels.newChannel(new BufferedOutputStream(System.out)));
|
||||
MAP.put(2, Channels.newChannel(new BufferedOutputStream(System.err)));
|
||||
}
|
||||
|
||||
/** 注册新 Channel,返回分配到的虚拟 fd */
|
||||
public static int register(Channel ch) {
|
||||
int fd = NEXT_FD.getAndIncrement();
|
||||
MAP.put(fd, ch);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/** 取得 Channel;如果 fd 不存在则返回 null */
|
||||
public static Channel get(int fd) {
|
||||
return MAP.get(fd);
|
||||
}
|
||||
|
||||
/** 关闭并移除 fd(0‒2 忽略) */
|
||||
public static void close(int fd) throws IOException {
|
||||
if (fd <= 2) return; // 标准流交由宿主 JVM 维护
|
||||
Channel ch = MAP.remove(fd);
|
||||
if (ch != null && ch.isOpen()) ch.close();
|
||||
}
|
||||
|
||||
/** 类似 dup(oldfd) —— 返回指向同一 Channel 的新 fd */
|
||||
public static int dup(int oldfd) {
|
||||
Channel ch = MAP.get(oldfd);
|
||||
if (ch == null)
|
||||
throw new IllegalArgumentException("Bad fd: " + oldfd);
|
||||
return register(ch); // 多个 fd 引用同一 Channel
|
||||
}
|
||||
}
|
||||
@ -84,7 +84,7 @@ public class OperandStack {
|
||||
* </p>
|
||||
*/
|
||||
public void printOperandStack() {
|
||||
LoggingUtils.logInfo("Operand Stack state:", stack + "\n");
|
||||
LoggingUtils.logInfo("\n\nOperand Stack state:", stack + "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user