10 uint8_t rA =
static_cast<uint8_t
>(operand1);
11 uint8_t rB =
static_cast<uint8_t
>(operand2);
12 uint8_t rC =
static_cast<uint8_t
>(operand3);
14 log(std::format(
"VM::{}({}, {}, {}, {})\n", __func__,
opCodeToString(op), operand1, operand2, operand3));
20#pragma region CONTROL FLOW
24 log(std::format(
"JUMP: {} -> {}\n",
pc - 1, operand1));
33 std::string funcName = funcNameVal.
asString();
34 auto it =
m_bytecode->functionEntries.find(funcName);
36 throw std::runtime_error(
"Unknown function: " + funcName);
38 log(std::format(
"CALL: {} -> {}: {}\n",
pc - 1, funcName, it->second));
54 throw std::runtime_error(
"Cannot return from outside a function");
68 std::string funcName = funcNameVal.
asString();
71 throw std::runtime_error(
"Unknown native function: " + funcName);
73 int argCount =
static_cast<int>(
pop().
asInt());
74 std::vector<Value> args(argCount);
75 for (
int i = argCount - 1; i >= 0; --i)
80 for (
auto &arg : args)
82 argsText += std::format(
"{:T}", arg);
83 if (arg != args.back())
86 log(std::format(
"CALL_NATIVE: {}({})\n", funcName, argsText));
90 push(it->second(args,
this));
97 log(std::format(
"JUMP_IF_FALSE: {} {} -> {}\n",
peek().isTruthy() ?
"TRUE" :
"FALSE",
pc - 1, operand1));
100 if (!
pop().isTruthy())
107 log(std::format(
"JUMP_IF_TRUE: {} {} -> {}\n",
peek().isTruthy() ?
"TRUE" :
"FALSE",
pc - 1, operand1));
110 if (
pop().isTruthy())
117 log(std::format(
"JUMP_BACK: {} -> {}\n",
pc - 1, operand1));
126 std::string path = pathVal.
asString();
130 throw std::runtime_error(
"Import handler not set");
141#pragma region STACK CORE
144 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
145 throw std::runtime_error(
"Invalid constant index");
156 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
157 throw std::runtime_error(
"Invalid variable index");
163 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
164 throw std::runtime_error(
"Invalid variable index");
185#pragma region STACK ARITHMETIC
301#pragma region STACK LOGICAL
426#pragma region STACK I/O
432 log(std::format(
"PRINT: (stdout) {:T}\n", v));
444 log(std::format(
"PRINTERROR: (stderr) {:T}\n", v));
458 std::getline(std::cin,
s);
460 log(std::format(
"\nREADLINE: {}\n",
s));
467#pragma region STACK SYSTEM
471 logerr(
"CANNOT ESCAPE SANDBOX");
477 log(std::format(
"SYSTEM: {:T} -> {}\n", cmd, ret));
488 logerr(
"CANNOT ESCAPE SANDBOX");
494 log(std::format(
"SYSTEM_OUT: {:T} -> {}\n", cmd, ret));
505 logerr(
"CANNOT ESCAPE SANDBOX");
511 log(std::format(
"SYSTEM_ERR: {:T} -> {}\n", cmd, ret));
521#pragma region STACK STRING
541 idx = idxVal.
asInt();
543 idx =
static_cast<int64_t
>(idxVal.
asFloat());
548 idx = std::stoll(idxVal.
asString());
552 throw std::runtime_error(
"char_at() expects index convertible to integer");
556 throw std::runtime_error(
"char_at() expects string and integer");
558 if (idx < 0 || idx >=
static_cast<int64_t
>(
s.length()))
561 push(
Value(std::string(1,
s[
static_cast<size_t>(idx)])));
572 const std::string &
s = strVal.
asString();
573 int64_t start = startVal.
asInt();
574 int64_t len = lenVal.
asInt();
576 if (start < 0 || start >=
static_cast<int64_t
>(
s.length()))
587 throw std::runtime_error(
"substr() expects string, int, int");
593#pragma region STACK STRUCT
596 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
597 throw std::runtime_error(
"Invalid struct index for NEW_STRUCT_INSTANCE_STATIC");
604 if (constIndex < 0 || constIndex >=
static_cast<int>(
m_bytecode->constants.size()))
605 throw std::runtime_error(
"Invalid default constant index for struct field");
607 const std::string &fieldName = info.
fieldNames[i];
608 instance.
setField(fieldName, defVal);
615 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
616 throw std::runtime_error(
"Invalid struct index for GET_FIELD_STATIC");
618 int fieldOffset = operand2;
619 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
620 throw std::runtime_error(
"Invalid field offset for GET_FIELD_STATIC");
621 const std::string &fieldName = info.
fieldNames[fieldOffset];
628 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
629 throw std::runtime_error(
"Invalid struct index for SET_FIELD_STATIC");
631 int fieldOffset = operand2;
632 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
633 throw std::runtime_error(
"Invalid field offset for SET_FIELD_STATIC");
634 const std::string &fieldName = info.
fieldNames[fieldOffset];
643 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
644 throw std::runtime_error(
"Invalid constant index for NEW_STRUCT");
646 std::string structName = nameVal.
asString();
652 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
653 throw std::runtime_error(
"Invalid constant index for SET_FIELD");
654 std::string fieldName =
m_bytecode->constants[operand1].asString();
663 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
664 throw std::runtime_error(
"Invalid constant index for GET_FIELD");
665 std::string fieldName =
m_bytecode->constants[operand1].asString();
673#pragma region REGISTER CORE
681 int constIndex = operand2;
682 if (constIndex < 0 || constIndex >=
static_cast<int>(
m_bytecode->constants.size()))
683 throw std::runtime_error(
"Invalid constant index");
689 int varIndex = operand2;
690 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
691 throw std::runtime_error(
"Invalid variable index");
697 int varIndex = operand2;
698 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
699 throw std::runtime_error(
"Invalid variable index");
726#pragma region REG ARITHMETIC
814#pragma region REG LOGICAL
942#pragma region REG I/O
947 log(std::format(
"PRINT_R: (stdout) {:T}\n",
registers[rA]));
958 log(std::format(
"PRINTERROR_R: (stderr) {:T}\n",
registers[rA]));
972 std::getline(std::cin,
s);
974 log(std::format(
"\nREADLINE_R: {}\n",
s));
981#pragma region REG SYSTEM
985 logerr(
"CANNOT ESCAPE SANDBOX");
991 log(std::format(
"SYSTEM_R: {} -> {}\n", cmd, ret));
1002 logerr(
"CANNOT ESCAPE SANDBOX");
1008 log(std::format(
"SYSTEM_R: {:T} -> {}\n", cmd, ret));
1019 logerr(
"CANNOT ESCAPE SANDBOX");
1025 log(std::format(
"SYSTEM_ERR_R: {:T} -> {}\n", cmd, ret));
1038#pragma region DEFAULT
1040 throw std::runtime_error(
"Unknown opcode");
1045 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.
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.
const char * c_str() const
Convert to C Style String.
bool isTruthy() const noexcept
Helper to determine truthiness.
bool isInt() const noexcept
int64_t asInt() const noexcept
Get the value as an integer.
void setField(const std::string &name, Value value)
bool isFloat() const noexcept
static Value createStruct(const std::string &name)
double asFloat() const noexcept
Get the value as a double.
std::string toString() const noexcept
Convert to string for printing.
bool isString() const noexcept
std::string asString() const noexcept
Get the value as a string.
Value getField(const std::string &name) const
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.
@ 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].
std::string opCodeToString(OpCode op)
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.