1#ifndef OPS_ARE_INCLUDED
7Value VM::operation(
const OpCode &op,
const int &operand1,
const int &operand2,
const int &operand3,
const int &,
const int &)
9 uint8_t rA =
static_cast<uint8_t
>(operand1);
10 uint8_t rB =
static_cast<uint8_t
>(operand2);
11 uint8_t rC =
static_cast<uint8_t
>(operand3);
13 log(std::format(
"VM::{}({}, {}, {}, {})\n", __func__,
opCodeToString(op), operand1, operand2, operand3));
19 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
20 throw std::runtime_error(
"Invalid constant index");
263 log(std::format(
"JUMP: {} -> {}\n",
pc, operand1));
271 if (!
pop().isTruthy())
276 if (
pop().isTruthy())
285 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
286 throw std::runtime_error(
"Invalid variable index");
291 if (operand1 < 0 || operand1 >=
static_cast<int>(
variables.size()))
292 throw std::runtime_error(
"Invalid variable index");
300 log(std::format(
"PRINT: (stdout) {:T}\n", v));
311 log(std::format(
"PRINTERROR: (stderr) {:T}\n", v));
325 std::getline(std::cin, s);
327 log(std::format(
"\nREADLINE: {}\n", s));
335 std::string path = pathVal.
asString();
339 throw std::runtime_error(
"Import handler not set");
349 std::string funcName = funcNameVal.
asString();
352 throw std::runtime_error(
"Unknown native function: " + funcName);
354 int argCount =
static_cast<int>(
pop().
asInt());
355 std::vector<Value> args(argCount);
356 for (
int i = argCount - 1; i >= 0; --i)
360 std::string argsText;
361 for (
auto &arg : args)
363 argsText += std::format(
"{:T}", arg);
364 if (arg != args.back())
367 log(std::format(
"CALL_NATIVE: {}({})\n", funcName, argsText));
371 push(it->second(args,
this));
389 std::string funcName = funcNameVal.
asString();
390 auto it =
m_bytecode->functionEntries.find(funcName);
392 throw std::runtime_error(
"Unknown function: " + funcName);
411 logerr(
"CANNOT ESCAPE SANDBOX");
417 log(std::format(
"SYSTEM: {:T} -> {}\n", cmd, ret));
428 logerr(
"CANNOT ESCAPE SANDBOX");
434 log(std::format(
"SYSTEM_OUT: {:T} -> {}\n", cmd, ret));
445 logerr(
"CANNOT ESCAPE SANDBOX");
451 log(std::format(
"SYSTEM_ERR: {:T} -> {}\n", cmd, ret));
468 auto constIndex = operand2;
469 if (constIndex < 0 || constIndex >=
static_cast<int>(
m_bytecode->constants.size()))
470 throw std::runtime_error(
"Invalid constant index");
477 auto varIndex = operand2;
478 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
479 throw std::runtime_error(
"Invalid variable index");
486 auto varIndex = operand2;
487 if (varIndex < 0 || varIndex >=
static_cast<int>(
variables.size()))
488 throw std::runtime_error(
"Invalid variable index");
735 std::string s =
registers[rA].toString();
737 log(std::format(
"PRINT_R: (stdout) {:T}\n",
registers[rA]));
745 std::string s =
registers[rA].toString();
747 log(std::format(
"PRINTERROR_R: (stderr) {:T}\n",
registers[rA]));
761 std::getline(std::cin, s);
763 log(std::format(
"\nREADLINE_R: {}\n", s));
771 logerr(
"CANNOT ESCAPE SANDBOX");
777 log(std::format(
"SYSTEM_R: {} -> {}\n", cmd, ret));
788 logerr(
"CANNOT ESCAPE SANDBOX");
794 log(std::format(
"SYSTEM_R: {:T} -> {}\n", cmd, ret));
805 logerr(
"CANNOT ESCAPE SANDBOX");
811 log(std::format(
"SYSTEM_ERR_R: {:T} -> {}\n", cmd, ret));
838 idx = idxVal.
asInt();
840 idx =
static_cast<int64_t
>(idxVal.
asFloat());
845 idx = std::stoll(idxVal.
asString());
849 throw std::runtime_error(
"char_at() expects index convertible to integer");
853 throw std::runtime_error(
"char_at() expects string and integer");
855 if (idx < 0 || idx >=
static_cast<int64_t
>(s.length()))
858 push(
Value(std::string(1, s[
static_cast<size_t>(idx)])));
869 const std::string &s = strVal.
asString();
870 int64_t start = startVal.
asInt();
871 int64_t len = lenVal.
asInt();
873 if (start < 0 || start >=
static_cast<int64_t
>(s.length()))
884 throw std::runtime_error(
"substr() expects string, int, int");
890 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
891 throw std::runtime_error(
"Invalid struct index for NEW_STRUCT_INSTANCE_STATIC");
898 if (constIndex < 0 || constIndex >=
static_cast<int>(
m_bytecode->constants.size()))
899 throw std::runtime_error(
"Invalid default constant index for struct field");
901 const std::string &fieldName = info.
fieldNames[i];
902 instance.
setField(fieldName, defVal);
909 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
910 throw std::runtime_error(
"Invalid struct index for GET_FIELD_STATIC");
912 auto fieldOffset = operand2;
913 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
914 throw std::runtime_error(
"Invalid field offset for GET_FIELD_STATIC");
915 const std::string &fieldName = info.
fieldNames[fieldOffset];
922 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->structs.size()))
923 throw std::runtime_error(
"Invalid struct index for SET_FIELD_STATIC");
925 auto fieldOffset = operand2;
926 if (fieldOffset < 0 || fieldOffset >= info.
fieldCount)
927 throw std::runtime_error(
"Invalid field offset for SET_FIELD_STATIC");
928 const std::string &fieldName = info.
fieldNames[fieldOffset];
937 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
938 throw std::runtime_error(
"Invalid constant index for NEW_STRUCT");
940 std::string structName = nameVal.
asString();
946 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
947 throw std::runtime_error(
"Invalid constant index for SET_FIELD");
948 std::string fieldName =
m_bytecode->constants[operand1].asString();
957 if (operand1 < 0 || operand1 >=
static_cast<int>(
m_bytecode->constants.size()))
958 throw std::runtime_error(
"Invalid constant index for GET_FIELD");
959 std::string fieldName =
m_bytecode->constants[operand1].asString();
965 throw std::runtime_error(
"Unknown opcode");
968 return Value(operand1);
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_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.
ImportHandler importHandler
Import handler for loading modules.
std::vector< Value > variables
Variable storage indexed by variable index.
Value pop()
Pop a value from the stack.
void logerr(const Value &msg)
Log a Value to stderr.
const Bytecode * m_bytecode
Bytecode to execute.
std::vector< int > callStack
Call stack for function calls.
std::map< std::string, NativeFunction > nativeFunctions
Native function registry.
void log(const Value &msg)
Log a Value to stdout.
std::array< Value, MAX_REGISTERS > registers
Virtual registers for register-based operations (v2.0).
void flush()
Flush stdout.
size_t pc
Program counter.
void push(const Value &value)
Push a value onto the stack.
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.
void flusherr()
Flush stderr.
A value in the Phasor VM.
bool isTruthy() const
Helper to determine truthiness.
const char * c_str() const
Convert to C Style String.
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 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.