10#define COMPILE_MESSAGE(msg) __pragma(message(msg))
11#elif defined(__GNUC__) || defined(__clang__)
12#define DO_PRAGMA(x) _Pragma(#x)
13#define COMPILE_MESSAGE(msg) DO_PRAGMA(message msg)
15#define COMPILE_MESSAGE(msg)
135 std::unordered_map<std::string, OpCode> map;
138 map[pair.second] = pair.first;
298 if (operandIndex == 0)
300 if (operandIndex == 1)
305 if (operandIndex == 0)
307 if (operandIndex == 1)
312 if (operandIndex == 0)
314 if (operandIndex == 1)
325 if (
static_cast<int>(op) >=
static_cast<int>(
OpCode::MOV))
350 throw std::runtime_error(
"Unknown opcode string: " + str);
355 std::stringstream ss;
386 for (
size_t i = 0; i < str.length(); ++i)
388 if (str[i] ==
'\\' && i + 1 < str.length())
409 result += str[i + 1];
424 std::stringstream ss;
427 ss <<
".PHIR 3.0.0" <<
"\n";
430 std::map<int, std::string> indexToVarName;
431 for (
const auto &[name, index] : bytecode.
variables)
433 indexToVarName[index] = name;
435 std::map<int, std::string> addressToFuncName;
438 addressToFuncName[address] = name;
442 ss <<
".CONSTANTS " << bytecode.
constants.size() <<
"\n";
443 for (
const auto &val : bytecode.
constants)
445 switch (val.getType())
451 ss <<
"BOOL " << (val.asBool() ?
"true" :
"false") <<
"\n";
454 ss <<
"INT " << val.asInt() <<
"\n";
457 ss <<
"FLOAT " << val.asFloat() <<
"\n";
460 ss <<
"STRING \"" <<
escapeString(val.asString()) <<
"\"\n";
463COMPILE_MESSAGE(
"Warning: PHS_01 Structs have not been fully implemented! Line " STR(__LINE__))
464 throw std::runtime_error(
"Structs not implemented!");
467COMPILE_MESSAGE(
"Warning: PHS_02 Arrays have not been implemented! Line " STR(__LINE__))
468 throw std::runtime_error(
"Arrays not implemented!");
474 for (
const auto &[name, index] : bytecode.
variables)
476 ss << name <<
" " << index <<
"\n";
483 ss << name <<
" " << address <<
"\n";
487 ss <<
".STRUCTS " << bytecode.
structs.size() <<
"\n";
488 for (
const auto &info : bytecode.
structs)
491 ss << info.name <<
" " << info.firstConstIndex <<
" " << info.fieldCount;
492 for (
const auto &fieldName : info.fieldNames)
494 ss <<
" " << fieldName;
500 ss <<
".INSTRUCTIONS " << bytecode.
instructions.size() <<
"\n";
503 std::stringstream instrLine;
507 int32_t operands[5] = {instr.operand1, instr.operand2, instr.operand3, instr.operand4, instr.operand5};
511 for (
int i = 0; i < operandCount; ++i)
523 instrLine <<
"r" << operands[i];
526 instrLine << operands[i];
528 if (operands[i] >= 0 && operands[i] <
static_cast<int>(bytecode.
constants.size()))
534 if (str.length() > 20)
535 str = str.substr(0, 20) +
"...";
536 comment =
"const[" + std::to_string(operands[i]) +
"]=\"" + str +
"\"";
540 comment =
"const[" + std::to_string(operands[i]) +
"]=" + std::to_string(val.
asInt());
545 instrLine << operands[i];
547 if (indexToVarName.count(operands[i]))
549 comment =
"var=" + indexToVarName[operands[i]];
553 instrLine << operands[i];
555 if (addressToFuncName.count(operands[i]))
557 comment =
"func=" + addressToFuncName[operands[i]];
561 instrLine << operands[i];
567 std::string lineStr = instrLine.str();
568 if (!comment.empty())
570 const size_t commentColumn = 40;
571 if (lineStr.length() < commentColumn)
573 lineStr.append(commentColumn - lineStr.length(),
' ');
582 ss << lineStr <<
"\n";
585 std::string textData = ss.str();
586 std::vector<uint8_t> buffer;
589 buffer.insert(buffer.end(), textData.begin(), textData.end());
598 throw std::runtime_error(
"Invalid Phasor IR file: too small");
602 std::string textData(data.begin() + 8, data.end());
603 std::stringstream ss(textData);
608 while (ss >> section)
610 if (section ==
".PHIR")
614 if (version <
"1.0.0")
616 throw std::runtime_error(
"Incompatible Phasor IR version");
619 if (section ==
".CONSTANTS")
624 for (
int i = 0; i < count; ++i)
632 else if (type ==
"BOOL")
638 else if (type ==
"INT")
644 else if (type ==
"FLOAT")
650 else if (type ==
"STRING")
655 while (ss.get(c) && c !=
'"')
657 std::getline(ss, valStr,
'"');
662 else if (section ==
".VARIABLES")
666 for (
int i = 0; i < count; ++i)
674 else if (section ==
".FUNCTIONS")
678 for (
int i = 0; i < count; ++i)
682 ss >> name >> address;
686 else if (section ==
".STRUCTS")
690 for (
int i = 0; i < count; ++i)
697 std::string fieldName;
701 int index =
static_cast<int>(bytecode.
structs.size());
702 bytecode.
structs.push_back(std::move(info));
706 else if (section ==
".INSTRUCTIONS")
711 for (
int i = 0; i < count; ++i)
718 int32_t operands[5] = {0, 0, 0, 0, 0};
720 for (
int j = 0; j < operandCount; ++j)
726 if (token.empty() || token[0] ==
';')
729 std::getline(ss, token);
734 if (!token.empty() && token.back() ==
',')
740 if (!token.empty() && token[0] ==
'r')
742 operands[j] = std::stoi(token.substr(1));
746 operands[j] = std::stoi(token);
752 while (ss.get(c) && c !=
'\n')
756 Instruction(op, operands[0], operands[1], operands[2], operands[3], operands[4]));
768 std::vector<uint8_t> data =
serialize(bytecode);
769 std::ofstream file(filename, std::ios::binary);
772 file.write(
reinterpret_cast<const char *
>(data.data()), data.size());
783 std::ifstream file(filename, std::ios::binary | std::ios::ate);
785 throw std::runtime_error(
"Cannot open file");
786 std::streamsize size = file.tellg();
787 file.seekg(0, std::ios::beg);
788 std::vector<uint8_t> buffer(size);
789 if (!file.read(
reinterpret_cast<char *
>(buffer.data()), size))
790 throw std::runtime_error(
"Cannot read file");
#define COMPILE_MESSAGE(msg)
static std::vector< uint8_t > serialize(const Bytecode &bytecode)
Serialize bytecode to Phasor IR format.
static std::string unescapeString(const std::string &str)
Helper to unescape strings from text format.
static const std::unordered_map< OpCode, std::string > opCodeToStringMap
static Bytecode deserialize(const std::vector< uint8_t > &data)
Deserialize Phasor IR format to bytecode.
static const std::unordered_map< std::string, OpCode > stringToOpCodeMap
static OperandType getOperandType(OpCode op, int operandIndex)
static Bytecode loadFromFile(const std::filesystem::path &filename)
Load bytecode from .phir file.
static std::string opCodeToString(OpCode op)
Helper to convert OpCode to string.
static int getOperandCount(OpCode op)
static std::string escapeString(const std::string &str)
Helper to escape strings for text format.
static bool saveToFile(const Bytecode &bytecode, const std::filesystem::path &filename)
Save bytecode to .phir file.
static OpCode stringToOpCode(const std::string &str)
Helper to convert string to OpCode.
A value in the Phasor VM.
std::string asString() const
Get the value as a string.
int64_t asInt() const
Get the value as an integer.
ValueType getType() const
Get the type of the value.
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.
std::vector< StructInfo > structs
List of struct descriptors.
std::vector< Value > constants
Constant pool.
std::map< std::string, int > functionEntries
Function name -> instruction index mapping.
int nextVarIndex
Next available variable index.
std::map< std::string, int > structEntries
Struct name -> index in structs.
std::map< std::string, int > variables
Variable name -> index mapping.
std::vector< Instruction > instructions
List of instructions.
Instruction with up to 5 operands Format: instruction operand1, operand2, operand3,...
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.