feat: 支持数组元素赋值操作

- 新增 __setindex_x 系列内置函数,用于数组元素赋值
- 实现了对 byte、short、int、long、float、double、boolean 和引用类型数组的支持
- 修改了 ExpressionBuilder 和 StatementBuilder以支持数组赋值语法
- 更新了 VirtualMachineEngine 和 SyscallCommand 以支持新的 ARR_SET系统调用
This commit is contained in:
Luke 2025-08-03 00:08:28 +08:00
parent 477591303a
commit d3a85a24bf
6 changed files with 383 additions and 295 deletions

View File

@ -77,7 +77,7 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
return;
}
// 各种一维数组类型byte/short/int/long/float/double/boolean
// 各种一维数组类型byte/short/int/long/float/double/boolean读取
switch (fn) {
case "__index_b" -> {
generateIndexInstruction(ins, out, slotMap, 'B');
@ -107,6 +107,35 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
generateIndexInstruction(ins, out, slotMap, 'R');
return;
}
case "__setindex_b" -> {
generateSetIndexInstruction(ins, out, slotMap, 'B');
return;
}
case "__setindex_s" -> {
generateSetIndexInstruction(ins, out, slotMap, 'S');
return;
}
case "__setindex_i" -> {
generateSetIndexInstruction(ins, out, slotMap, 'I');
return;
}
case "__setindex_l" -> {
generateSetIndexInstruction(ins, out, slotMap, 'L');
return;
}
case "__setindex_f" -> {
generateSetIndexInstruction(ins, out, slotMap, 'F');
return;
}
case "__setindex_d" -> {
generateSetIndexInstruction(ins, out, slotMap, 'D');
return;
}
case "__setindex_r" -> {
generateSetIndexInstruction(ins, out, slotMap, 'R');
return;
}
}
// 普通函数调用
@ -115,6 +144,54 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
// ========== 私有辅助方法 ==========
/**
* 生成数组元素赋值指令arr[idx] = value无返回值
* <p>
* 调用栈压栈顺序为arr (引用类型, R)idx (整型, I)value (元素类型, T)
* 执行 SYSCALL ARR_SET 指令以完成数组赋值操作
* </p>
*
* @param ins 当前的调用指令包含参数信息
* @param out VM 指令生成器用于输出 VM 指令
* @param slotMap 虚拟寄存器与槽位的映射表
* @param valType 待写入的 value 的类型标识'B': byte, 'S': short, 'I': int, 'L': long, 'F': float, 'D': double, 其余为引用类型'R'
* @throws IllegalStateException 如果参数数量不为3则抛出异常
*/
private void generateSetIndexInstruction(CallInstruction ins,
VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap,
char valType) {
List<IRValue> args = ins.getArguments();
if (args.size() != 3) {
// 参数数量错误抛出异常并输出实际参数列表
throw new IllegalStateException(
"[CallGenerator] __setindex_* 需要三个参数(arr, idx, value),实际: " + args);
}
// 第一个参数为数组对象压入引用类型寄存器'R' arr
loadArgument(out, slotMap, args.get(0), 'R', ins.getFunctionName());
// 第二个参数为索引值压入整型寄存器'I' idx
loadArgument(out, slotMap, args.get(1), 'I', ins.getFunctionName());
// 第三个参数为待赋值元素根据元素类型压入相应类型寄存器
// 支持类型'B'(byte), 'S'(short), 'I'(int), 'L'(long), 'F'(float), 'D'(double)
// 其他情况如引用类型'R'处理
switch (valType) {
case 'B' -> loadArgument(out, slotMap, args.get(2), 'B', ins.getFunctionName());
case 'S' -> loadArgument(out, slotMap, args.get(2), 'S', ins.getFunctionName());
case 'I' -> loadArgument(out, slotMap, args.get(2), 'I', ins.getFunctionName());
case 'L' -> loadArgument(out, slotMap, args.get(2), 'L', ins.getFunctionName());
case 'F' -> loadArgument(out, slotMap, args.get(2), 'F', ins.getFunctionName());
case 'D' -> loadArgument(out, slotMap, args.get(2), 'D', ins.getFunctionName());
default -> loadArgument(out, slotMap, args.get(2), 'R', ins.getFunctionName());
}
// 输出 VM 指令SYSCALL ARR_SET完成数组元素写入操作
out.emit(VMOpCode.SYSCALL + " " + "ARR_SET");
}
/**
* 解析 syscall 子命令第一个参数支持字符串常量与已绑定字符串的虚拟寄存器
*

View File

@ -222,7 +222,7 @@ public record ExpressionBuilder(IRContext ctx) {
* @param node 下标访问表达式节点
* @return 存放引用结果的虚拟寄存器
*/
private IRVirtualRegister buildIndexRef(IndexExpressionNode node) {
public IRVirtualRegister buildIndexRef(IndexExpressionNode node) {
// 1. 常量折叠如果 array index 都是编译期常量直接取值
Object arrConst = tryFoldConst(node.array());
Object idxConst = tryFoldConst(node.index());

View File

@ -103,6 +103,57 @@ public class StatementBuilder {
ctx.clearVarType();
return;
}
// ==== 支持数组下标赋值 arr[idx] = value ====
if (stmt instanceof IndexAssignmentNode idxAssign) {
IndexExpressionNode target = idxAssign.target();
// 1. 目标数组寄存器多维时用 buildIndexRef 拿引用
IRVirtualRegister arrReg = (target.array() instanceof IndexExpressionNode inner)
? expr.buildIndexRef(inner)
: expr.build(target.array());
// 2. 下标与右值
IRVirtualRegister idxReg = expr.build(target.index());
IRVirtualRegister valReg = expr.build(idxAssign.value());
// 3. 选择内置函数名 __setindex_x根据元素类型分派
String func = "__setindex_r";
org.jcnc.snow.compiler.parser.ast.base.ExpressionNode base = target.array();
while (base instanceof IndexExpressionNode innerIdx) base = innerIdx.array();
if (base instanceof IdentifierNode id) {
String arrType = ctx.getScope().lookupType(id.name());
if (arrType != null) {
String elemType = arrType.endsWith("[]") ? arrType.substring(0, arrType.length() - 2) : arrType;
switch (elemType) {
case "byte" -> func = "__setindex_b";
case "short" -> func = "__setindex_s";
case "int" -> func = "__setindex_i";
case "long" -> func = "__setindex_l";
case "float" -> func = "__setindex_f";
case "double" -> func = "__setindex_d";
case "boolean" -> func = "__setindex_i";
case "string" -> func = "__setindex_r";
default -> func = "__setindex_r";
}
}
}
// 4. 生成 CALL 指令
java.util.List<org.jcnc.snow.compiler.ir.core.IRValue> argv =
java.util.List.of(arrReg, idxReg, valReg);
ctx.addInstruction(new org.jcnc.snow.compiler.ir.instruction.CallInstruction(null, func, argv));
// 5. 赋值后清理常量绑定
try {
if (base instanceof IdentifierNode id2) {
ctx.getScope().clearConstValue(id2.name());
}
} catch (Throwable ignored) {}
return;
}
if (stmt instanceof DeclarationNode decl) {
// 变量声明语句 int a = 1;
if (decl.getInitializer().isPresent()) {

View File

@ -14,292 +14,35 @@ import java.util.List;
* and represents the "reference push" instruction ({@code R_PUSH}) in the virtual machine.
*
* <p>
* This instruction pushes a reference valuetypically a string literal or an array literalonto the operand stack.
* </p>
*
* <p><b>Instruction format:</b> {@code R_PUSH <literal>}</p>
*
* <p>
* Example usages:
* This instruction pushes a reference-type value onto the operand stack.
* The input is parsed from the textual instruction form, which can represent:
* <ul>
* <li>{@code R_PUSH "hello"} &rarr; push the string {@code "hello"} onto stack</li>
* <li>{@code R_PUSH [1,2,3]} &rarr; push an (unmodifiable) {@code List<Integer>} onto stack</li>
* <li>{@code R_PUSH [[1,2,3],[4,5,6]]} &rarr; push a nested (unmodifiable) {@code List<List<Integer>>} onto stack</li>
* <li>String literals</li>
* <li>Array literals (e.g., {@code [1, 2, 3]}), including nested arrays</li>
* </ul>
* </p>
*
* <p>
* Supported element types in array literals include integers, floating-point numbers (parsed as {@code Double}),
* booleans ({@code true}/{@code false} {@code 1}/{@code 0}), quoted strings (surrounded by double quotes),
* and further nested arrays.
* For array literals, a nested list structure is constructed. In this implementation,
* array literals are pushed as <b>mutable</b> {@link java.util.ArrayList} structures,
* so that subsequent system calls such as {@code ARR_SET} can modify elements in-place.
* </p>
*/
public final class RPushCommand implements Command {
// ======== Parsing helpers ========
public class RPushCommand implements Command {
/**
* Deeply wraps lists as unmodifiable; leaves scalars unchanged.
* Executes the R_PUSH command.
*
* @param v input object
* @return deeply unmodifiable version of the object
*/
private static Object deepUnmodifiableObject(Object v) {
if (v instanceof List<?> l) {
return deepUnmodifiable(l);
}
return v;
}
/**
* Recursively wraps all nested lists as unmodifiable.
*
* @param l input list
* @return deeply unmodifiable list
*/
private static List<?> deepUnmodifiable(List<?> l) {
List<Object> out = new ArrayList<>(l.size());
for (Object v : l) out.add(deepUnmodifiableObject(v));
return Collections.unmodifiableList(out);
}
/**
* Parses a value starting from the cursor.
* Skips whitespace, and delegates to the appropriate sub-parser depending on the character:
* array, quoted string, or atomic value.
*
* @param c cursor
* @return parsed value (Object)
*/
private static Object parseValue(Cursor c) {
skipWs(c);
if (c.end()) return "";
char ch = c.ch();
if (ch == '[') return parseArray(c);
if (ch == '\"') return parseQuoted(c);
return parseAtom(c);
}
/**
* Parses an array literal from the cursor, supporting nested structures.
* Assumes the current character is '['.
*
* @param c cursor
* @return List of parsed objects
*/
private static List<Object> parseArray(Cursor c) {
// assumes current char is '['
expect(c, '[');
skipWs(c);
List<Object> values = new ArrayList<>();
if (!peek(c, ']')) {
while (true) {
Object v = parseValue(c);
values.add(v);
skipWs(c);
if (peek(c, ',')) {
c.i++; // consume ','
skipWs(c);
continue;
}
break;
}
}
expect(c, ']');
return values;
}
// ======== Recursive-descent parser for array literals ========
/**
* Parses a string literal wrapped in double quotes (supports common escape sequences).
* <p>
* Assumes the cursor currently points to the starting quote character ("), and consumes the opening quote.
* Parses the string content from the cursor, stopping at the closing quote (").
* Supported escape sequences:
* <ul>
* <li>\n newline</li>
* <li>\r carriage return</li>
* <li>\t tab</li>
* <li>\" double quote itself</li>
* <li>\\ backslash</li>
* <li>Other characters are output as-is</li>
* </ul>
* If the string is not closed properly (i.e., no closing quote is found before the end), returns the currently parsed content.
*
* @param c cursor object (must support ch() for current char, i for index, end() for boundary check)
* @return parsed string content
*/
private static String parseQuoted(Cursor c) {
// Assume current position is the opening quote; consume it
expect(c, '\"');
StringBuilder sb = new StringBuilder();
// Traverse until the end or an unclosed string
while (!c.end()) {
char ch = c.ch();
c.i++;
if (ch == '\\') { // handle escape sequences
if (c.end()) break; // nothing after escape char
char nxt = c.ch();
c.i++;
// Common escapes
switch (nxt) {
case 'n' -> sb.append('\n'); // newline
case 'r' -> sb.append('\r'); // carriage return
case 't' -> sb.append('\t'); // tab
case '\"' -> sb.append('\"'); // double quote
case '\\' -> sb.append('\\'); // backslash
default -> sb.append(nxt); // any other char as-is
}
} else if (ch == '\"') {
// Found closing quote; end of string
return sb.toString();
} else {
// Regular character
sb.append(ch);
}
}
// Unclosed string, return parsed content
return sb.toString();
}
/**
* Parses an atomic constant ("atom"), supporting type-suffixed numbers and booleans.
* <p>
* Examples: 0.1f, 123L, 3.14d, 100, true, false<br>
* Parsing rules:
* <ul>
* <li>Supports float(f/F), long(l/L), double(d/D), short(s/S), byte(b/B) type suffixes</li>
* <li>Supports boolean true/false (case-insensitive, converted to 1/0)</li>
* <li>Decimals without suffix parsed as double; integers without suffix as int</li>
* <li>If parsing fails, returns the original string</li>
* </ul>
*
* @param c cursor, must support ch() for current char, i for index, end() for boundary check, s for the original string
* @return parsed Object
*/
private static Object parseAtom(Cursor c) {
int start = c.i;
// Read until a comma, ']' or whitespace
while (!c.end()) {
char ch = c.ch();
if (ch == ',' || ch == ']') break;
if (Character.isWhitespace(ch)) break;
c.i++;
}
// Extract current token
String token = c.s.substring(start, c.i).trim();
if (token.isEmpty()) return "";
// Boolean parsing (case-insensitive, convert to 1/0)
if ("true".equalsIgnoreCase(token)) return 1;
if ("false".equalsIgnoreCase(token)) return 0;
// Handle numeric type suffixes
try {
char last = token.charAt(token.length() - 1);
switch (last) {
case 'f':
case 'F':
// float suffix
return Float.parseFloat(token.substring(0, token.length() - 1));
case 'l':
case 'L':
// long suffix
return Long.parseLong(token.substring(0, token.length() - 1));
case 'd':
case 'D':
// double suffix
return Double.parseDouble(token.substring(0, token.length() - 1));
case 's':
case 'S':
// short suffix
return Short.parseShort(token.substring(0, token.length() - 1));
case 'b':
case 'B':
// byte suffix
return Byte.parseByte(token.substring(0, token.length() - 1));
default:
// No suffix, check for floating point or integer
if (token.contains(".") || token.contains("e") || token.contains("E")) {
return Double.parseDouble(token);
} else {
return Integer.parseInt(token);
}
}
} catch (NumberFormatException ex) {
// Parsing failed, fall back to original string (e.g. identifiers)
return token;
}
}
/**
* Skips all whitespace characters at the current cursor position until a non-whitespace or end of text is reached.
* <p>
* The cursor index is automatically incremented, so it will point to the next non-whitespace character (or end of text).
*
* @param c cursor object (must support ch() for current char, i for index, end() for boundary check)
*/
private static void skipWs(Cursor c) {
// Increment cursor while not at end and is whitespace
while (!c.end() && Character.isWhitespace(c.ch())) {
c.i++;
}
}
/**
* Checks if the current cursor position matches the specified character.
*
* @param c cursor object
* @param ch expected character
* @return true if not at end and character matches ch, otherwise false
*/
private static boolean peek(Cursor c, char ch) {
return !c.end() && c.ch() == ch;
}
/**
* Asserts that the current cursor position is the specified character; throws if not.
* If it matches, skips the character and any following whitespace.
*
* @param c cursor object
* @param ch expected character
* @throws IllegalArgumentException if current position is not the expected character
*/
private static void expect(Cursor c, char ch) {
if (c.end() || c.ch() != ch)
throw new IllegalArgumentException("R_PUSH array literal parse error: expected '" + ch + "' at position " + c.i);
c.i++; // Consume current character
skipWs(c); // Skip any subsequent whitespace
}
/**
* Executes the R_PUSH instruction: pushes a constant or array constant onto the operand stack.
* <p>
* Processing steps:
* <ul>
* <li>1. Checks parameter count, throws if insufficient.</li>
* <li>2. Concatenates all arguments (except opcode) into a raw literal string.</li>
* <li>3. Checks if the literal is an array (starts with [ and ends with ]).</li>
* <li>4. If array, recursively parses and pushes as a read-only List onto the operand stack.</li>
* <li>5. Otherwise, pushes the literal string as-is.</li>
* </ul>
*
* @param parts instruction and parameter strings (parts[0] is the opcode, others are params)
* @param pc current instruction index
* @param stack operand stack
* @param lvs local variable store
* @param cs call stack
* @return next instruction index
* @param parts The parts of the instruction, where {@code parts[1..n]} are concatenated as the literal.
* @param pc The current program counter.
* @param stack The operand stack where the result will be pushed.
* @param local The local variable store (unused in this instruction).
* @param callStack The call stack (unused in this instruction).
* @return The new program counter (typically {@code pc+1}).
* @throws IllegalStateException if no literal parameter is provided.
*/
@Override
public int execute(String[] parts, int pc,
OperandStack stack,
LocalVariableStore lvs,
CallStack cs) {
// Check parameter count
public int execute(String[] parts, int pc, OperandStack stack, LocalVariableStore local, CallStack callStack) {
if (parts.length < 2)
throw new IllegalStateException("R_PUSH missing parameter");
@ -318,8 +61,8 @@ public final class RPushCommand implements Command {
// Should not happen in theory; safety fallback
stack.push(parsed);
} else {
// Convert to read-only List before pushing to prevent modification
stack.push(deepUnmodifiable(list));
// Push a deep-mutable copy so ARR_SET can modify elements in-place
stack.push(deepMutable(list));
}
} else {
// Regular string, push as-is
@ -331,19 +74,29 @@ public final class RPushCommand implements Command {
/**
* A simple string cursor, supporting index increment and character reading, for use by the parser.
*/
private static final class Cursor {
final String s; // Original string
int i; // Current index
static class Cursor {
final String s;
int i;
/**
* Constructs a new {@code Cursor} for the given string.
*
* @param s The string to parse.
*/
Cursor(String s) {
this.s = s;
this.i = 0;
}
/**
* Checks if the cursor is at the end of the string.
*
* @return true if at end
* Advances the cursor by one character.
*/
void skip() {
i++;
}
/**
* @return {@code true} if the cursor has reached the end of the string.
*/
boolean end() {
return i >= s.length();
@ -353,10 +106,186 @@ public final class RPushCommand implements Command {
* Gets the character at the current cursor position.
*
* @return current character
* @throws StringIndexOutOfBoundsException if at end of string
*/
char ch() {
return s.charAt(i);
}
}
/**
* Parses a value from the input string at the current cursor position.
* This can be an array literal, a quoted string, or a simple atom (number, word).
*
* @param c The cursor for parsing.
* @return The parsed value (could be List, String, Number).
*/
Object parseValue(Cursor c) {
skipWs(c);
if (c.end()) return "";
char ch = c.ch();
if (ch == '[') return parseArray(c);
if (ch == '\"') return parseQuoted(c);
return parseAtom(c);
}
/**
* Skips whitespace characters in the input string.
*
* @param c The cursor to advance.
*/
private static void skipWs(Cursor c) {
while (!c.end()) {
char ch = c.ch();
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') c.skip();
else break;
}
}
/**
* Parses an array literal from the input, including nested arrays.
*
* @param c The cursor (positioned at '[' at entry).
* @return A List representing the parsed array.
*/
private Object parseArray(Cursor c) {
// assumes current char is '['
c.skip(); // skip '['
List<Object> out = new ArrayList<>();
skipWs(c);
while (!c.end()) {
if (c.ch() == ']') {
c.skip(); // skip ']'
break;
}
Object v = parseValue(c);
out.add(v);
skipWs(c);
if (!c.end() && c.ch() == ',') {
c.skip();
skipWs(c);
}
}
return out;
}
/**
* Parses a quoted string literal, handling escape characters.
*
* @param c The cursor (positioned at '"' at entry).
* @return The parsed string value.
*/
private static String parseQuoted(Cursor c) {
// assumes current char is '"'
c.skip(); // skip opening quote
StringBuilder sb = new StringBuilder();
while (!c.end()) {
char ch = c.ch();
c.skip();
if (ch == '\\') {
if (c.end()) break;
char esc = c.ch();
c.skip();
switch (esc) {
case 'n' -> sb.append('\n');
case 'r' -> sb.append('\r');
case 't' -> sb.append('\t');
case '\"' -> sb.append('\"');
case '\\' -> sb.append('\\');
default -> sb.append(esc);
}
} else if (ch == '\"') {
break;
} else {
sb.append(ch);
}
}
return sb.toString();
}
/**
* Parses an atom (number, hexadecimal, binary, or plain string token).
*
* @param c The cursor.
* @return An Integer, Double, or String, depending on the content.
*/
private static Object parseAtom(Cursor c) {
StringBuilder sb = new StringBuilder();
while (!c.end()) {
char ch = c.ch();
if (ch == ',' || ch == ']' || ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') break;
sb.append(ch);
c.skip();
}
String token = sb.toString();
// try number
try {
if (token.startsWith("0x") || token.startsWith("0X")) {
return Integer.parseInt(token.substring(2), 16);
}
if (token.startsWith("0b") || token.startsWith("0B")) {
return Integer.parseInt(token.substring(2), 2);
}
if (token.contains(".")) {
return Double.parseDouble(token);
}
return Integer.parseInt(token);
} catch (NumberFormatException e) {
// fallback as string
return token;
}
}
// ---------------------- helpers for immutability/mutability ----------------------
/**
* Recursively creates an unmodifiable copy of a list, with all nested lists also unmodifiable.
*
* @param l The list to make unmodifiable.
* @return An unmodifiable deep copy of the list.
*/
List<?> deepUnmodifiable(List<?> l) {
List<Object> out = new ArrayList<>(l.size());
for (Object v : l) out.add(deepUnmodifiableObject(v));
return Collections.unmodifiableList(out);
}
/**
* Helper method for {@link #deepUnmodifiable(List)}. Recursively processes each element.
*
* @param v The object to process.
* @return Unmodifiable list if input is a list, otherwise the value itself.
*/
Object deepUnmodifiableObject(Object v) {
if (v instanceof List<?> l) {
return deepUnmodifiable(l);
}
return v;
}
/**
* Create a deep mutable copy of a nested List structure, preserving element values.
* Nested lists are turned into {@link java.util.ArrayList} so they can be modified by ARR_SET.
*
* @param l The source list.
* @return Deep mutable copy of the list.
*/
private static java.util.List<?> deepMutable(java.util.List<?> l) {
java.util.List<Object> out = new java.util.ArrayList<>(l.size());
for (Object v : l) out.add(deepMutableObject(v));
return out;
}
/**
* Helper method for {@link #deepMutable(List)}. Recursively processes each element.
*
* @param v The object to process.
* @return Mutable list if input is a list, otherwise the value itself.
*/
private static Object deepMutableObject(Object v) {
if (v instanceof java.util.List<?> l) {
return deepMutable(l);
}
return v;
}
}

View File

@ -381,6 +381,40 @@ public class SyscallCommand implements Command {
}
}
case "ARR_SET" -> {
/*
arr[idx] = value
支持 List 和所有原生数组类型int[], double[], ...
参数顺序栈顶 valueidxarr
不返回值成功/失败由异常控制
*/
Object value = stack.pop();
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_SET: invalid index type " + idxObj);
if (arrObj instanceof java.util.List<?> list) {
// 必须是可变 List
@SuppressWarnings("unchecked")
java.util.List<Object> mlist = (java.util.List<Object>) list;
if (idx < 0 || idx >= mlist.size())
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + mlist.size() + ")");
mlist.set(idx, value);
} 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 + ")");
java.lang.reflect.Array.set(arrObj, idx, value);
} else {
throw new IllegalArgumentException("ARR_SET: not an array/list: " + arrObj);
}
// 操作成功push 0 作为 ok 信号不需要返回时可省略
stack.push(0);
}
// 控制台输出
case "PRINT" -> {

View File

@ -1,6 +1,5 @@
package org.jcnc.snow.vm.engine;
import org.jcnc.snow.common.Mode;
import org.jcnc.snow.vm.execution.CommandExecutionHandler;
import org.jcnc.snow.vm.module.*;
@ -51,8 +50,6 @@ public class VirtualMachineEngine {
/**
* Builds a VM engine with fresh runtime structures.
*
* @param vmMode execution mode (DEBUG / RUN)
*/
public VirtualMachineEngine() {
this.operandStack = new OperandStack();