11#if defined(__GNUC__) || defined(__clang__)
12#pragma GCC diagnostic push
13#pragma GCC diagnostic ignored "-Wpedantic"
18#define TRACE_INSTR(_op) \
20 log(std::format("\nVM::evalLoop(): RUN (pc={})\n", pc - 1)); \
21 log(std::format("VM::evalLoop({}, {}, {}, {})\n", \
22 opCodeToString(_op), operand1, operand2, operand3)); \
26#define TRACE_INSTR(_op) do {} while (0)
29 static constexpr unsigned TABLE_SIZE = 512;
30 static void* s_table[TABLE_SIZE];
31 static bool s_ready =
false;
33 if (!s_ready) [[unlikely]]
35 for (
auto& e : s_table) e = &&LABEL_UNKNOWN;
168 int operand1 = 0, operand2 = 0, operand3 = 0;
169 u8 rA = 0, rB = 0, rC = 0;
173 if (pc >= m_bytecode->instructions.size()) [[unlikely]] return; \
175 const Instruction& _i = m_bytecode->instructions[pc++]; \
176 operand1 = _i.operand1; \
177 operand2 = _i.operand2; \
178 operand3 = _i.operand3; \
179 rA = static_cast<u8>(operand1); \
180 rB = static_cast<u8>(operand2); \
181 rC = static_cast<u8>(operand3); \
182 TRACE_INSTR(_i.op); \
183 const unsigned _op = static_cast<unsigned>(_i.op); \
184 goto *(_op < TABLE_SIZE ? s_table[_op] : &&LABEL_UNKNOWN); \
194 log(std::format(
"JUMP: {} -> {}\n",
pc - 1, operand1));
205 std::string funcName = funcNameVal.
asString();
206 auto it =
m_bytecode->functionEntries.find(funcName);
208 throw std::runtime_error(
"Unknown function: " + funcName);
210 log(std::format(
"CALL: {} -> {}: {}\n",
pc - 1, funcName, it->second));
229 throw std::runtime_error(
"Cannot return from outside a function");
244 std::string funcName = funcNameVal.
asString();
247 throw std::runtime_error(
"Unknown native function: " + funcName);
249 int argCount =
static_cast<int>(
pop().
asInt());
250 std::vector<Value> args(argCount);
251 for (
int i = argCount - 1; i >= 0; --i)
255 std::string argsText;
256 for (
auto& arg : args)
258 argsText += std::format(
"{:T}", arg);
259 if (arg != args.back()) argsText +=
", ";
261 log(std::format(
"CALL_NATIVE: {}({})\n", funcName, argsText));
264 push(it->second(args,
this));
272 log(std::format(
"JUMP_IF_FALSE: {} {} -> {}\n",
273 peek().isTruthy() ?
"TRUE" :
"FALSE",
pc - 1, operand1));
276 if (!
pop().isTruthy())
pc = operand1;
283 log(std::format(
"JUMP_IF_TRUE: {} {} -> {}\n",
284 peek().isTruthy() ?
"TRUE" :
"FALSE",
pc - 1, operand1));
287 if (
pop().isTruthy())
pc = operand1;
294 log(std::format(
"JUMP_BACK: {} -> {}\n",
pc - 1, operand1));
305 std::string path = pathVal.
asString();
309 throw std::runtime_error(
"Import handler not set");
322 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
323 throw std::runtime_error(
"Invalid constant index");
336 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
337 throw std::runtime_error(
"Invalid variable index");
344 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
345 throw std::runtime_error(
"Invalid variable index");
350 LABEL_TRUE_P: {
push(
Value(
true)); NEXT(); }
351 LABEL_FALSE_P: {
push(
Value(
false)); NEXT(); }
352 LABEL_NULL_VAL: {
push(
Value()); NEXT(); }
406 log(std::format(
"PRINT: (stdout) {:T}\n", v));
421 log(std::format(
"PRINTERROR: (stderr) {:T}\n", v));
438 std::getline(std::cin,
s);
440 log(std::format(
"\nREADLINE: {}\n",
s));
452 logerr(
"CANNOT ESCAPE SANDBOX");
459 log(std::format(
"SYSTEM: {:T} -> {}\n", cmd, ret));
472 logerr(
"CANNOT ESCAPE SANDBOX");
479 log(std::format(
"SYSTEM_OUT: {:T} -> {}\n", cmd, ret));
492 logerr(
"CANNOT ESCAPE SANDBOX");
499 log(std::format(
"SYSTEM_ERR: {:T} -> {}\n", cmd, ret));
543 try { idx = std::stoll(idxVal.
asString()); }
544 catch (...) {
throw std::runtime_error(
"char_at() expects index convertible to integer"); }
546 else throw std::runtime_error(
"char_at() expects string and integer");
548 if (idx < 0 || idx >=
static_cast<i64>(
s.length()))
551 push(
Value(std::string(1,
s[
static_cast<size_t>(idx)])));
564 const std::string&
s = strVal.
asString();
567 if (start < 0 || start >=
static_cast<i64>(
s.length()))
574 throw std::runtime_error(
"substr() expects string, int, int");
584 LABEL_NEW_STRUCT_INSTANCE_STATIC:
587 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
588 throw std::runtime_error(
"Invalid struct index for NEW_STRUCT_INSTANCE_STATIC");
594 if (constIndex < 0 || constIndex >=
static_cast<int>(
m_bytecode->constants.size()))
595 throw std::runtime_error(
"Invalid default constant index for struct field");
603 LABEL_GET_FIELD_STATIC:
606 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
607 throw std::runtime_error(
"Invalid struct index for GET_FIELD_STATIC");
609 int fieldOffset = operand2;
610 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
611 throw std::runtime_error(
"Invalid field offset for GET_FIELD_STATIC");
618 LABEL_SET_FIELD_STATIC:
621 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
622 throw std::runtime_error(
"Invalid struct index for SET_FIELD_STATIC");
624 int fieldOffset = operand2;
625 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
626 throw std::runtime_error(
"Invalid field offset for SET_FIELD_STATIC");
637 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
638 throw std::runtime_error(
"Invalid constant index for NEW_STRUCT");
646 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
647 throw std::runtime_error(
"Invalid constant index for SET_FIELD");
648 std::string fieldName =
m_bytecode->constants[operand1].asString();
660 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
661 throw std::runtime_error(
"Invalid constant index for GET_FIELD");
676 int constIndex = operand2;
677 if (constIndex < 0 || constIndex >=
static_cast<int>(
m_bytecode->constants.size()))
678 throw std::runtime_error(
"Invalid constant index");
685 int varIndex = operand2;
686 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
687 throw std::runtime_error(
"Invalid variable index");
694 int varIndex = operand2;
695 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
696 throw std::runtime_error(
"Invalid variable index");
761 log(std::format(
"PRINT_R: (stdout) {:T}\n",
registers[rA]));
775 log(std::format(
"PRINTERROR_R: (stderr) {:T}\n",
registers[rA]));
792 std::getline(std::cin,
s);
794 log(std::format(
"\nREADLINE_R: {}\n",
s));
806 logerr(
"CANNOT ESCAPE SANDBOX");
813 log(std::format(
"SYSTEM_R: {} -> {}\n", cmd, ret));
826 logerr(
"CANNOT ESCAPE SANDBOX");
833 log(std::format(
"SYSTEM_R: {:T} -> {}\n", cmd, ret));
846 logerr(
"CANNOT ESCAPE SANDBOX");
853 log(std::format(
"SYSTEM_ERR_R: {:T} -> {}\n", cmd, ret));
866 throw std::runtime_error(
"Unknown opcode");
876 log(std::format(
"\nVM::{}(): RUN (pc={})\n", __func__,
pc - 1));
882#pragma GCC diagnostic pop
888 u8 rA =
static_cast<u8>(operand1);
889 u8 rB =
static_cast<u8>(operand2);
890 u8 rC =
static_cast<u8>(operand3);
892 log(std::format(
"VM::{}({}, {}, {}, {})\n", __func__,
opCodeToString(op), operand1, operand2, operand3));
898#pragma region CONTROL FLOW
902 log(std::format(
"JUMP: {} -> {}\n",
pc - 1, operand1));
911 std::string funcName = funcNameVal.
asString();
912 auto it =
m_bytecode->functionEntries.find(funcName);
914 throw std::runtime_error(
"Unknown function: " + funcName);
916 log(std::format(
"CALL: {} -> {}: {}\n",
pc - 1, funcName, it->second));
933 throw std::runtime_error(
"Cannot return from outside a function");
947 std::string funcName = funcNameVal.
asString();
950 throw std::runtime_error(
"Unknown native function: " + funcName);
952 int argCount =
static_cast<int>(
pop().
asInt());
953 std::vector<Value> args(argCount);
954 for (
int i = argCount - 1; i >= 0; --i)
958 std::string argsText;
959 for (
auto &arg : args)
961 argsText += std::format(
"{:T}", arg);
962 if (arg != args.back())
965 log(std::format(
"CALL_NATIVE: {}({})\n", funcName, argsText));
969 push(it->second(args,
this));
976 log(std::format(
"JUMP_IF_FALSE: {} {} -> {}\n",
peek().isTruthy() ?
"TRUE" :
"FALSE",
pc - 1, operand1));
979 if (!
pop().isTruthy())
986 log(std::format(
"JUMP_IF_TRUE: {} {} -> {}\n",
peek().isTruthy() ?
"TRUE" :
"FALSE",
pc - 1, operand1));
989 if (
pop().isTruthy())
996 log(std::format(
"JUMP_BACK: {} -> {}\n",
pc - 1, operand1));
1005 std::string path = pathVal.
asString();
1009 throw std::runtime_error(
"Import handler not set");
1020#pragma region STACK CORE
1023 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
1024 throw std::runtime_error(
"Invalid constant index");
1035 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
1036 throw std::runtime_error(
"Invalid variable index");
1042 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
1043 throw std::runtime_error(
"Invalid variable index");
1064#pragma region STACK ARITHMETIC
1180#pragma region STACK LOGICAL
1305#pragma region STACK I/O
1311 log(std::format(
"PRINT: (stdout) {:T}\n", v));
1323 log(std::format(
"PRINTERROR: (stderr) {:T}\n", v));
1337 std::getline(std::cin,
s);
1339 log(std::format(
"\nREADLINE: {}\n",
s));
1346#pragma region STACK SYSTEM
1350 logerr(
"CANNOT ESCAPE SANDBOX");
1356 log(std::format(
"SYSTEM: {:T} -> {}\n", cmd, ret));
1367 logerr(
"CANNOT ESCAPE SANDBOX");
1373 log(std::format(
"SYSTEM_OUT: {:T} -> {}\n", cmd, ret));
1384 logerr(
"CANNOT ESCAPE SANDBOX");
1390 log(std::format(
"SYSTEM_ERR: {:T} -> {}\n", cmd, ret));
1400#pragma region STACK STRING
1427 idx = idxVal.
asInt();
1434 idx = std::stoll(idxVal.
asString());
1438 throw std::runtime_error(
"char_at() expects index convertible to integer");
1442 throw std::runtime_error(
"char_at() expects string and integer");
1444 if (idx < 0 || idx >=
static_cast<i64>(
s.length()))
1447 push(
Value(std::string(1,
s[
static_cast<size_t>(idx)])));
1458 const std::string &
s = strVal.
asString();
1462 if (start < 0 || start >=
static_cast<i64>(
s.length()))
1473 throw std::runtime_error(
"substr() expects string, int, int");
1479#pragma region STACK STRUCT
1482 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
1483 throw std::runtime_error(
"Invalid struct index for NEW_STRUCT_INSTANCE_STATIC");
1490 if (constIndex < 0 || constIndex >=
static_cast<int>(
m_bytecode->constants.size()))
1491 throw std::runtime_error(
"Invalid default constant index for struct field");
1493 const std::string &fieldName = info.
fieldNames[i];
1494 instance.
setField(fieldName, defVal);
1501 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
1502 throw std::runtime_error(
"Invalid struct index for GET_FIELD_STATIC");
1504 int fieldOffset = operand2;
1505 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
1506 throw std::runtime_error(
"Invalid field offset for GET_FIELD_STATIC");
1507 const std::string &fieldName = info.
fieldNames[fieldOffset];
1514 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
1515 throw std::runtime_error(
"Invalid struct index for SET_FIELD_STATIC");
1517 int fieldOffset = operand2;
1518 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
1519 throw std::runtime_error(
"Invalid field offset for SET_FIELD_STATIC");
1520 const std::string &fieldName = info.
fieldNames[fieldOffset];
1529 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
1530 throw std::runtime_error(
"Invalid constant index for NEW_STRUCT");
1532 std::string structName = nameVal.
asString();
1538 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
1539 throw std::runtime_error(
"Invalid constant index for SET_FIELD");
1540 std::string fieldName =
m_bytecode->constants[operand1].asString();
1549 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
1550 throw std::runtime_error(
"Invalid constant index for GET_FIELD");
1551 std::string fieldName =
m_bytecode->constants[operand1].asString();
1558#pragma region REGISTER CORE
1566 int constIndex = operand2;
1567 if (constIndex < 0 || constIndex >=
static_cast<int>(
m_bytecode->constants.size()))
1568 throw std::runtime_error(
"Invalid constant index");
1574 int varIndex = operand2;
1575 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
1576 throw std::runtime_error(
"Invalid variable index");
1582 int varIndex = operand2;
1583 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
1584 throw std::runtime_error(
"Invalid variable index");
1611#pragma region REG ARITHMETIC
1699#pragma region REG LOGICAL
1827#pragma region REG I/O
1832 log(std::format(
"PRINT_R: (stdout) {:T}\n",
registers[rA]));
1843 log(std::format(
"PRINTERROR_R: (stderr) {:T}\n",
registers[rA]));
1857 std::getline(std::cin,
s);
1859 log(std::format(
"\nREADLINE_R: {}\n",
s));
1866#pragma region REG SYSTEM
1870 logerr(
"CANNOT ESCAPE SANDBOX");
1876 log(std::format(
"SYSTEM_R: {} -> {}\n", cmd, ret));
1887 logerr(
"CANNOT ESCAPE SANDBOX");
1893 log(std::format(
"SYSTEM_R: {:T} -> {}\n", cmd, ret));
1904 logerr(
"CANNOT ESCAPE SANDBOX");
1910 log(std::format(
"SYSTEM_ERR_R: {:T} -> {}\n", cmd, ret));
1922#pragma region DEFAULT
1924 throw std::runtime_error(
"Unknown opcode");
1930 return Value(operand1);
void c_print_stderr(const char *s, int64_t len)
Native print error function.
int64_t c_system(const char *cmd)
CRT system call.
void c_print_stdout(const char *s, int64_t len)
Native print function.
char * c_system_out(const char *cmd)
CRT system call, get out.
char * c_system_err(const char *cmd)
CRT system call, get err.
double asm_tan(double a)
Native tangent.
double asm_log(double a)
Native natural logarithm.
int64_t asm_isub(int64_t a, int64_t b)
Native subtraction.
double asm_flmod(double a, double b)
double asm_sqrt(double a)
Native square root.
double asm_fladd(double a, double b)
double asm_sin(double a)
Native sine.
int64_t asm_imod(int64_t a, int64_t b)
Native modulus.
int64_t asm_idiv(int64_t a, int64_t b)
Native division.
double asm_fldiv(double a, double b)
double asm_flsub(double a, double b)
int64_t asm_iadd(int64_t a, int64_t b)
Native addition.
double asm_exp(double a)
Native exponential.
double asm_flneg(double a)
Native negation.
double asm_cos(double a)
Native cosine.
int64_t asm_imul(int64_t a, int64_t b)
Native multiplication.
double asm_flmul(double a, double b)
double asm_pow(double a, double b)
Native power.
std::size_t length() const noexcept
Throws when the HALT opcode is reached.
std::array< Value, MAX_REGISTERS > registers
Virtual registers for register-based operations (v2.0).
ImportHandler importHandler
Import handler for loading modules.
Value operation(const OpCode &op, const int &operand1=0, const int &operand2=0, const int &operand3=0)
Execute a single operation.
std::vector< Value > variables
Variable storage indexed by variable index, or simply: the managed heap.
Value pop()
Pop a value from the stack.
void logerr(const Value &msg)
Log a Value to stderr.
bool isDirectCall
is a direct call to a function
std::vector< int > callStack
Call stack for function calls.
void log(const Value &msg)
Log a Value to stdout.
void flush()
Flush stdout.
size_t pc
Program counter.
void push(const Value &value)
Push a value onto the stack.
std::map< std::string, NativeFunction > nativeFunctions
Native function registry.
const Bytecode * m_bytecode
Bytecode to execute.
Value peek()
Peek at the top value on the stack.
void flusherr()
Flush stderr.
A value in the Phasor VM.
PhsString asString() const noexcept
Get the value as a Small String.
const char * c_str() const
Convert to C Style String.
bool isTruthy() const noexcept
Helper to determine truthiness.
std::shared_ptr< ArrayInstance > asArray()
Get the value as an array.
bool isArray() const noexcept
Check if the value is an array.
bool isInt() const noexcept
static Value createStruct(const PhsString &name)
bool isFloat() const noexcept
void setField(const PhsString &name, Value value)
Value getField(const PhsString &name) const
f64 asFloat() const noexcept
Get the value as a f64.
i64 asInt() const noexcept
Get the value as an integer.
std::string toString() const noexcept
Convert to string for printing.
bool isString() const noexcept
int64_t asm_iless_than(int64_t a, int64_t b)
Native Less than comparison.
int64_t asm_flequal(double a, double b)
int64_t asm_flless_equal(double a, double b)
int64_t asm_flless_than(double a, double b)
int64_t asm_fland(double a, double b)
int64_t asm_flgreater_equal(double a, double b)
int64_t asm_igreater_equal(int64_t a, int64_t b)
Native Greater than or equal comparison.
int64_t asm_ior(int64_t a, int64_t b)
Native bitwise OR.
int64_t asm_igreater_than(int64_t a, int64_t b)
Native Greater than comparison.
int64_t asm_flnot_equal(double a, double b)
int64_t asm_flgreater_than(double a, double b)
int64_t asm_flnot(double a)
Native bitwise NOT.
int64_t asm_iand(int64_t a, int64_t b)
Native bitwise AND.
int64_t asm_flor(double a, double b)
int64_t asm_iequal(int64_t a, int64_t b)
Native Equality comparison.
int64_t asm_inot_equal(int64_t a, int64_t b)
Native Inequality comparison.
int64_t asm_iless_equal(int64_t a, int64_t b)
Native Less than or equal comparison.
The Phasor Programming Language and Runtime.
std::string opCodeToString(OpCode op)
@ IGREATER_THAN
Pop b, pop a, push a > b.
@ IEQUAL
Pop b, pop a, push a == b.
@ SYSTEM_OUT
Call system function and push stdout.
@ LOG_R
R[rA] = log(R[rB]).
@ FLMOD_R
R[rA] = R[rB] % R[rC].
@ SUBSTR
Pop len, pop start, pop s, push s.substr(start, len).
@ IAND
Pop b, pop a, push a && b.
@ SET_FIELD_STATIC
Pop value and struct instance, set field by static offset.
@ MOV
Copy register to register: R[rA] = R[rB].
@ PRINTERROR_R
Print register to stderr: printerror(R[rA]).
@ IAND_R
R[rA] = R[rB] && R[rC].
@ FLMUL_R
R[rA] = R[rB] * R[rC].
@ IADD
Pop b, pop a, push a + b.
@ PUSH2_R
Push 2 registers to stack: push2(R[rA], R[rB]).
@ FLGE_R
R[rA] = R[rB] >= R[rC].
@ SYSTEM_R
Run an operating system shell command: system(R[rA]).
@ PUSH_CONST
Push constant from constant pool.
@ JUMP_IF_TRUE
Jump if top of stack is true (pops value).
@ FLGT_R
R[rA] = R[rB] > R[rC].
@ PUSH_R
Push register to stack: push(R[rA]).
@ POP_R
Pop stack to register: R[rA] = pop().
@ FLEQUAL
Pop b, pop a, push a == b.
@ GET_FIELD_STATIC
Pop struct instance, push field by static offset (structIndex, fieldOffset).
@ FLNOT_EQUAL
Pop b, pop a, push a != b.
@ FLADD_R
R[rA] = R[rB] + R[rC].
@ POP2_R
Pop 2 values from stack to registers: pop2(R[rA], R[rB]).
@ LOAD_CONST_R
Load constant to register: R[rA] = constants[immediate].
@ FLADD
Pop b, pop a, push a + b.
@ JUMP
Unconditional jump to offset.
@ FLLT_R
R[rA] = R[rB] < R[rC].
@ SET_FIELD
Pop struct, pop field name, pop value, set field value.
@ IMULTIPLY
Pop b, pop a, push a * b.
@ READLINE_R
Read line into register: readline(R[rA]).
@ CHAR_AT
Pop index, pop s, push s[index].
@ IMUL_R
R[rA] = R[rB] * R[rC].
@ FLGREATER_EQUAL
Pop b, pop a, push a >= b.
@ FLLE_R
R[rA] = R[rB] <= R[rC].
@ IGREATER_EQUAL
Pop b, pop a, push a >= b.
@ SYSTEM_ERR
Call system function and push stderr.
@ SQRT_R
R[rA] = sqrt(R[rB]).
@ ISUB_R
R[rA] = R[rB] - R[rC].
@ FLDIV_R
R[rA] = R[rB] / R[rC].
@ COS_R
R[rA] = cos(R[rB]).
@ CALL_NATIVE
Call a native function: operand is index of function name in constants.
@ ISUBTRACT
Pop b, pop a, push a - b.
@ FLMODULO
Pop b, pop a, push a % b.
@ NEW_STRUCT_INSTANCE_STATIC
Create new struct instance using struct section metadata (structIndex).
@ IDIVIDE
Pop b, pop a, push a / b.
@ FLOR
Pop b, pop a, push a || b.
@ STORE_VAR
Pop top of stack, store in variable slot.
@ ILE_R
R[rA] = R[rB] <= R[rC].
@ FLDIVIDE
Pop b, pop a, push a / b.
@ POW_R
R[rA] = pow(R[rB], R[rC]).
@ IMODULO
Pop b, pop a, push a % b.
@ FLAND_R
R[rA] = R[rB] && R[rC].
@ JUMP_IF_FALSE
Jump if top of stack is false (pops value).
@ INOT_EQUAL
Pop b, pop a, push a != b.
@ FLEQ_R
R[rA] = R[rB] == R[rC].
@ LOAD_VAR
Push variable value onto stack.
@ IOR
Pop b, pop a, push a || b.
@ EXP_R
R[rA] = exp(R[rB]).
@ RETURN
Return from function.
@ STORE_VAR_R
Store register to varible: variables[immediate] = R[rA].
@ READLINE
Read line from input and push onto stack.
@ IGT_R
R[rA] = R[rB] > R[rC].
@ FLSUBTRACT
Pop b, pop a, push a - b.
@ IADD_R
R[rA] = R[rB] + R[rC].
@ IDIV_R
R[rA] = R[rB] / R[rC].
@ FLLESS_EQUAL
Pop b, pop a, push a <= b.
@ SYSTEM_ERR_R
Run shell command and get output: system_out(R[rA], R[rB]).
@ FLMULTIPLY
Pop b, pop a, push a * b.
@ ILESS_EQUAL
Pop b, pop a, push a <= b.
@ PRINT_R
Print register: print(R[rA]).
@ PRINTERROR
Pop top of stack and print to stderr.
@ TAN_R
R[rA] = tan(R[rB]).
@ FLSUB_R
R[rA] = R[rB] - R[rC].
@ JUMP_BACK
Jump backwards (for loops).
@ FLOR_R
R[rA] = R[rB] || R[rC].
@ CALL
Call a user function: operand is index of function name in constants.
@ INE_R
R[rA] = R[rB] != R[rC].
@ FLNE_R
R[rA] = R[rB] != R[rC].
@ FLGREATER_THAN
Pop b, pop a, push a > b.
@ ILESS_THAN
Pop b, pop a, push a < b.
@ GET_FIELD
Pop struct, pop field name, push field value.
@ SIN_R
R[rA] = sin(R[rB]).
@ IMPORT
Import a module: operand is index of module path in constants.
@ LOAD_VAR_R
Load variable to register: R[rA] = variables[immediate].
@ FLLESS_THAN
Pop b, pop a, push a < b.
@ NEW_STRUCT
Create new struct: operand is index of struct name in constants.
@ IOR_R
R[rA] = R[rB] || R[rC].
@ ILT_R
R[rA] = R[rB] < R[rC].
@ FLAND
Pop b, pop a, push a && b.
@ IMOD_R
R[rA] = R[rB] % R[rC].
@ PRINT
Pop top of stack and print.
@ SYSTEM
Call a system function: operand is index of function name in constants.
@ IEQ_R
R[rA] = R[rB] == R[rC].
@ IGE_R
R[rA] = R[rB] >= R[rC].
Instruction with up to 5 operands Format: instruction operand1, operand2, operand3 Each instruction u...
i32 operand1
First operand.
i32 operand2
Second operand.
i32 operand3
Third operand.
Struct metadata stored alongside bytecode (struct section).
int firstConstIndex
Index into constants for the first default value.
std::vector< std::string > fieldNames
Field names in declaration order.
int fieldCount
Number of fields in this struct.
std::string name
Struct name.