27 const int &operand4,
const int &operand5)
29 uint8_t rA =
static_cast<uint8_t
>(operand1);
30 uint8_t rB =
static_cast<uint8_t
>(operand2);
31 uint8_t rC =
static_cast<uint8_t
>(operand3);
35 if (operand1 < 0 || operand1 >=
static_cast<int>(
bytecode->constants.size()))
36 throw std::runtime_error(
"Invalid constant index");
282 if (!
pop().isTruthy())
287 if (
pop().isTruthy())
296 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
297 throw std::runtime_error(
"Invalid variable index");
302 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
303 throw std::runtime_error(
"Invalid variable index");
323 std::getline(std::cin, s);
330 std::string path = pathVal.
asString();
334 throw std::runtime_error(
"Import handler not set");
343 std::string funcName = funcNameVal.
asString();
346 throw std::runtime_error(
"Unknown native function: " + funcName);
348 int argCount =
static_cast<int>(
pop().
asInt());
349 std::vector<Value> args(argCount);
350 for (
int i = argCount - 1; i >= 0; --i)
353 push(it->second(args,
this));
370 std::string funcName = funcNameVal.
asString();
371 auto it =
bytecode->functionEntries.find(funcName);
372 if (it ==
bytecode->functionEntries.end())
373 throw std::runtime_error(
"Unknown function: " + funcName);
413 int constIndex = operand2;
414 if (constIndex < 0 || constIndex >=
static_cast<int>(
bytecode->constants.size()))
415 throw std::runtime_error(
"Invalid constant index");
422 int varIndex = operand2;
423 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
424 throw std::runtime_error(
"Invalid variable index");
431 int varIndex = operand2;
432 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
433 throw std::runtime_error(
"Invalid variable index");
680 std::string s =
registers[rA].toString();
686 std::string s =
registers[rA].toString();
693 std::getline(std::cin, s);
731 idx = idxVal.
asInt();
733 idx =
static_cast<int64_t
>(idxVal.
asFloat());
739 idx = std::stoll(idxVal.
asString());
743 throw std::runtime_error(
"char_at() expects index convertible to integer");
747 throw std::runtime_error(
"char_at() expects string and integer");
749 if (idx < 0 || idx >=
static_cast<int64_t
>(s.length()))
752 push(
Value(std::string(1, s[
static_cast<size_t>(idx)])));
763 const std::string &s = strVal.
asString();
764 int64_t start = startVal.
asInt();
765 int64_t len = lenVal.
asInt();
767 if (start < 0 || start >=
static_cast<int64_t
>(s.length()))
778 throw std::runtime_error(
"substr() expects string, int, int");
785 if (operand1 < 0 || operand1 >=
static_cast<int>(
bytecode->structs.size()))
786 throw std::runtime_error(
"Invalid struct index for NEW_STRUCT_INSTANCE_STATIC");
794 if (constIndex < 0 || constIndex >=
static_cast<int>(
bytecode->constants.size()))
795 throw std::runtime_error(
"Invalid default constant index for struct field");
797 const std::string &fieldName = info.
fieldNames[i];
798 instance.
setField(fieldName, defVal);
806 if (operand1 < 0 || operand1 >=
static_cast<int>(
bytecode->structs.size()))
807 throw std::runtime_error(
"Invalid struct index for GET_FIELD_STATIC");
809 int fieldOffset = operand2;
810 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
811 throw std::runtime_error(
"Invalid field offset for GET_FIELD_STATIC");
812 const std::string &fieldName = info.
fieldNames[fieldOffset];
820 if (operand1 < 0 || operand1 >=
static_cast<int>(
bytecode->structs.size()))
821 throw std::runtime_error(
"Invalid struct index for SET_FIELD_STATIC");
823 int fieldOffset = operand2;
824 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
825 throw std::runtime_error(
"Invalid field offset for SET_FIELD_STATIC");
826 const std::string &fieldName = info.
fieldNames[fieldOffset];
835 if (operand1 < 0 || operand1 >=
static_cast<int>(
bytecode->constants.size()))
836 throw std::runtime_error(
"Invalid constant index for NEW_STRUCT");
838 std::string structName = nameVal.
asString();
844 if (operand1 < 0 || operand1 >=
static_cast<int>(
bytecode->constants.size()))
845 throw std::runtime_error(
"Invalid constant index for SET_FIELD");
846 std::string fieldName =
bytecode->constants[operand1].asString();
855 if (operand1 < 0 || operand1 >=
static_cast<int>(
bytecode->constants.size()))
856 throw std::runtime_error(
"Invalid constant index for GET_FIELD");
857 std::string fieldName =
bytecode->constants[operand1].asString();
864 throw std::runtime_error(
"Unknown opcode");
866 return Value(operand1);
881 stack.push_back(value);
888 throw std::runtime_error(
"Stack underflow");
899 throw std::runtime_error(
"Stack is empty");
904void VM::reset(
const bool &resetStack,
const bool &resetFunctions,
const bool &resetVariables)
941 throw std::runtime_error(
"Invalid variable index");
950 throw std::runtime_error(
"Invalid variable index");
983 size_t programCounter =
pc;
985 std::string info =
"Stack Top: ";
995 info +=
" | Current Program Counter: " + std::to_string(programCounter);
996 info +=
" | PC Stack Top: " + std::to_string(callStackTop);
char * asm_system_err(const char *cmd)
CRT system call, get err.
void asm_print_stdout(const char *s, int64_t len)
Native print function.
int64_t asm_system(const char *cmd)
CRT system call.
void asm_print_stderr(const char *s, int64_t len)
Native print error function.
char * asm_system_out(const char *cmd)
CRT system call, get out.
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_flneg(double a)
Native negation.
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_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.
ImportHandler importHandler
Import handler for loading modules.
std::vector< Value > variables
Variable storage indexed by variable index.
std::function< Value(const std::vector< Value > &args, VM *vm)> NativeFunction
Native function signature.
Value pop()
Pop a value from the stack.
std::function< void(const std::filesystem::path &path)> ImportHandler
void logerr(const Value &msg)
std::vector< Value > stack
Stack for function calls.
std::array< Value, 256 > registers
Virtual registers for register-based operations (v2.0).
std::vector< int > callStack
Call stack for function calls.
std::map< std::string, NativeFunction > nativeFunctions
Native function registry.
void registerNativeFunction(const std::string &name, NativeFunction fn)
Register a native function.
Value getRegister(uint8_t index)
Get a register value.
size_t getVariableCount()
Get the number of variables in the VM.
void log(const Value &msg)
Use the VM's logging via print opcode.
size_t addVariable(const Value &value)
Add a variable to the VM.
void freeVariable(const size_t index)
Free a variable in the VM.
size_t pc
Program counter.
void setVariable(const size_t index, const Value &value)
Set a variable in the VM.
void push(const Value &value)
Push a value onto the stack.
void setRegister(uint8_t index, const Value &value)
Set a register value.
void setImportHandler(ImportHandler handler)
void freeRegister(uint8_t index)
Free a register (reset to null).
void run(const Bytecode &bytecode)
Run the virtual machine.
const Bytecode * bytecode
Bytecode to execute.
Value getVariable(const size_t index)
Get a variable from the VM.
Value operation(const OpCode &op, const int &operand1=0, const int &operand2=0, const int &operand3=0, const int &operand4=0, const int &operand5=0)
Execute a single operation.
Value peek()
Peek at the top value on the stack.
size_t getRegisterCount()
Get the total number of registers.
void reset(const bool &resetStack=true, const bool &resetFunctions=true, const bool &resetVariables=true)
Reset the virtual machine.
std::string getInformation()
Get VM information for debugging.
A value in the Phasor VM.
bool isTruthy() const
Helper to determine truthiness.
std::string toString() const
Convert to string for printing.
double asFloat() const
Get the value as a double.
std::string asString() const
Get the value as a string.
void setField(const std::string &name, Value value)
int64_t asInt() const
Get the value as an integer.
bool isFloat() const
Check if the value is a double.
static Value createStruct(const std::string &name)
Value getField(const std::string &name) const
bool isString() const
Check if the value is a string.
bool isInt() const
Check if the value is an integer.
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 variable: 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].
Complete bytecode structure.
Instruction with up to 5 operands Format: instruction operand1, operand2, operand3,...
int32_t operand1
First operand.
int32_t operand2
Second operand.
int32_t operand4
Fourth operand.
int32_t operand3
Third operand.
int32_t operand5
Fifth 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.