Basics VM are working.
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "constants.hpp"
|
||||||
#include "godot_cpp/classes/ref_counted.hpp"
|
#include "godot_cpp/classes/ref_counted.hpp"
|
||||||
|
|
||||||
namespace CoborVM
|
namespace CoborVM
|
||||||
@@ -11,10 +12,28 @@ namespace CoborVM
|
|||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<ExecutableStep> program;
|
||||||
|
std::vector<int32_t> register_memory;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CoborVirtualMachine() = default;
|
CoborVirtualMachine() = default;
|
||||||
~CoborVirtualMachine() override = default;
|
~CoborVirtualMachine() override = default;
|
||||||
|
|
||||||
godot::PackedStringArray parse_source_code(godot::String source_code);
|
godot::PackedStringArray parse_source_code(godot::String source_code);
|
||||||
|
godot::String set_registers(int size, godot::PackedInt32Array preloaded_values);
|
||||||
|
godot::PackedInt32Array get_registers();
|
||||||
|
int get_program_counter();
|
||||||
|
godot::String run_step();
|
||||||
|
godot::String run_all();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Execution
|
||||||
|
godot::String run_executable_step(ExecutableStep step);
|
||||||
|
// Parsing
|
||||||
|
bool string_to_instruction(godot::String command_string, Instruction &outInstruction);
|
||||||
|
bool instruction_to_executable_step(Instruction instruction, godot::PackedStringArray line, ExecutableStep &outExecutableStep);
|
||||||
|
bool validate_and_parse_pointer(const godot::String &arg, int &outValue);
|
||||||
|
bool validate_and_parse_literal(const godot::String &arg, int &outValue);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
96
cobor_vm_extension/cobor_vm/include/constants.hpp
Normal file
96
cobor_vm_extension/cobor_vm/include/constants.hpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace CoborVM
|
||||||
|
{
|
||||||
|
enum Instruction
|
||||||
|
{
|
||||||
|
EMPTY,
|
||||||
|
|
||||||
|
SET,
|
||||||
|
CLR,
|
||||||
|
CPY,
|
||||||
|
SWP,
|
||||||
|
|
||||||
|
ADD,
|
||||||
|
SUB,
|
||||||
|
MUL,
|
||||||
|
DIV,
|
||||||
|
|
||||||
|
SKIP,
|
||||||
|
JMP,
|
||||||
|
JMPTO,
|
||||||
|
|
||||||
|
EQL,
|
||||||
|
BIGR,
|
||||||
|
SMLR,
|
||||||
|
ZERO,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExecutableStep
|
||||||
|
{
|
||||||
|
Instruction instruction = Instruction::EMPTY;
|
||||||
|
int arg1;
|
||||||
|
int arg2;
|
||||||
|
int arg3;
|
||||||
|
|
||||||
|
int lineNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map from string to instruction
|
||||||
|
const std::unordered_map<std::string, Instruction> stringToInstructionMap = {
|
||||||
|
{"SET", SET},
|
||||||
|
{"CLR", CLR},
|
||||||
|
{"CPY", CPY},
|
||||||
|
{"SWP", SWP},
|
||||||
|
{"ADD", ADD},
|
||||||
|
{"SUB", SUB},
|
||||||
|
{"MUL", MUL},
|
||||||
|
{"DIV", DIV},
|
||||||
|
{"SKIP", SKIP},
|
||||||
|
{"JMP", JMP},
|
||||||
|
{"JMPTO", JMPTO},
|
||||||
|
{"EQL", EQL},
|
||||||
|
{"BIGR", BIGR},
|
||||||
|
{"SMLR", SMLR},
|
||||||
|
{"ZERO", ZERO}};
|
||||||
|
|
||||||
|
// Map from instruction to string
|
||||||
|
const std::unordered_map<Instruction, std::string> InstructionToStringMap = {
|
||||||
|
{SET, "SET"},
|
||||||
|
{CLR, "CLR"},
|
||||||
|
{CPY, "CPY"},
|
||||||
|
{SWP, "SWP"},
|
||||||
|
{ADD, "ADD"},
|
||||||
|
{SUB, "SUB"},
|
||||||
|
{MUL, "MUL"},
|
||||||
|
{DIV, "DIV"},
|
||||||
|
{SKIP, "SKIP"},
|
||||||
|
{JMP, "JMP"},
|
||||||
|
{JMPTO, "JMPTO"},
|
||||||
|
{EQL, "EQL"},
|
||||||
|
{BIGR, "BIGR"},
|
||||||
|
{SMLR, "SMLR"},
|
||||||
|
{ZERO, "ZERO"}};
|
||||||
|
|
||||||
|
// Define the expected argument types for each instruction (true = pointer, false = literal)
|
||||||
|
const std::unordered_map<Instruction, std::vector<bool>> instructionPatterns = {
|
||||||
|
{Instruction::SKIP, {}}, // No arguments
|
||||||
|
{Instruction::EMPTY, {}},
|
||||||
|
{Instruction::JMP, {false}}, // One literal argument
|
||||||
|
{Instruction::JMPTO, {false}},
|
||||||
|
{Instruction::CLR, {true}}, // One pointer argument
|
||||||
|
{Instruction::ZERO, {true}},
|
||||||
|
{Instruction::SET, {false, true}}, // One literal and one pointer argument
|
||||||
|
{Instruction::CPY, {true, true}}, // Two pointer arguments
|
||||||
|
{Instruction::SWP, {true, true}},
|
||||||
|
{Instruction::EQL, {true, true}},
|
||||||
|
{Instruction::BIGR, {true, true}},
|
||||||
|
{Instruction::SMLR, {true, true}},
|
||||||
|
{Instruction::ADD, {true, true, true}}, // Three pointer arguments
|
||||||
|
{Instruction::SUB, {true, true, true}},
|
||||||
|
{Instruction::MUL, {true, true, true}},
|
||||||
|
{Instruction::DIV, {true, true, true}},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,224 +1,358 @@
|
|||||||
#include "cobor_virtual_machine.hpp"
|
#include "cobor_virtual_machine.hpp"
|
||||||
|
|
||||||
enum Instruction
|
using namespace CoborVM;
|
||||||
{
|
|
||||||
ERR,
|
|
||||||
|
|
||||||
SET,
|
|
||||||
CLR,
|
|
||||||
CPY,
|
|
||||||
SWP,
|
|
||||||
|
|
||||||
ADD,
|
|
||||||
SUB,
|
|
||||||
MUL,
|
|
||||||
DIV,
|
|
||||||
|
|
||||||
SKIP,
|
|
||||||
JMP,
|
|
||||||
JMPTO,
|
|
||||||
|
|
||||||
EQL,
|
|
||||||
BIGR,
|
|
||||||
SMLR,
|
|
||||||
ZERO,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ExecutableStep
|
|
||||||
{
|
|
||||||
Instruction instruction = Instruction::ERR;
|
|
||||||
int arg1;
|
|
||||||
int arg2;
|
|
||||||
int arg3;
|
|
||||||
|
|
||||||
int lineNumber;
|
|
||||||
};
|
|
||||||
|
|
||||||
void CoborVM::CoborVirtualMachine::_bind_methods()
|
void CoborVM::CoborVirtualMachine::_bind_methods()
|
||||||
{
|
{
|
||||||
godot::ClassDB::bind_method(godot::D_METHOD("parse_source_code", "source_code"), &CoborVirtualMachine::parse_source_code);
|
godot::ClassDB::bind_method(godot::D_METHOD("parse_source_code", "source_code"), &CoborVirtualMachine::parse_source_code);
|
||||||
|
godot::ClassDB::bind_method(godot::D_METHOD("set_registers", "size", "preloaded_values"), &CoborVirtualMachine::set_registers);
|
||||||
|
godot::ClassDB::bind_method(godot::D_METHOD("get_registers"), &CoborVirtualMachine::get_registers);
|
||||||
|
godot::ClassDB::bind_method(godot::D_METHOD("get_program_counter"), &CoborVirtualMachine::get_program_counter);
|
||||||
|
godot::ClassDB::bind_method(godot::D_METHOD("run_step"), &CoborVirtualMachine::run_step);
|
||||||
|
godot::ClassDB::bind_method(godot::D_METHOD("run_all"), &CoborVirtualMachine::run_all);
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction stringToInstruction(godot::String command_string);
|
|
||||||
ExecutableStep InstructionToExecutableStep(Instruction Instruction, godot::PackedStringArray line);
|
|
||||||
|
|
||||||
godot::PackedStringArray CoborVM::CoborVirtualMachine::parse_source_code(godot::String source_code)
|
godot::PackedStringArray CoborVM::CoborVirtualMachine::parse_source_code(godot::String source_code)
|
||||||
{
|
{
|
||||||
godot::PackedStringArray parseFaults;
|
godot::PackedStringArray parseFaults;
|
||||||
|
program.clear();
|
||||||
|
|
||||||
source_code = source_code.to_upper();
|
source_code = source_code.to_upper();
|
||||||
auto lines = source_code.split("\n");
|
auto lines = source_code.split("\n");
|
||||||
int lineNumber = 0;
|
int lineNumber = 0;
|
||||||
for (auto &line : lines)
|
for (auto &line : lines)
|
||||||
{
|
{
|
||||||
|
lineNumber++;
|
||||||
|
ExecutableStep step;
|
||||||
|
step.instruction = EMPTY;
|
||||||
|
step.lineNumber = lineNumber;
|
||||||
|
|
||||||
line = line.strip_edges();
|
line = line.strip_edges();
|
||||||
auto words = line.split(" ", false);
|
if (!line.is_empty())
|
||||||
Instruction command = stringToInstruction(words[0]);
|
|
||||||
if (command == ERR)
|
|
||||||
{
|
{
|
||||||
parseFaults.append(godot::String("Error: Invalid command at line ") +
|
auto words = line.split(" ", false);
|
||||||
godot::String::num_int64(lineNumber) +
|
Instruction instruction;
|
||||||
godot::String(": {") + line + godot::String("}"));
|
if (!string_to_instruction(words[0], instruction))
|
||||||
continue;
|
{
|
||||||
|
parseFaults.append(godot::String("Error: Invalid instruction at line ") +
|
||||||
|
godot::String::num_int64(lineNumber) +
|
||||||
|
godot::String(": {") + line + godot::String("}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!instruction_to_executable_step(instruction, words, step))
|
||||||
|
{
|
||||||
|
parseFaults.append(godot::String("Error: Invalid arguments at line ") +
|
||||||
|
godot::String::num_int64(lineNumber) +
|
||||||
|
godot::String(": {") + line + godot::String("}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lineNumber++;
|
program.emplace_back(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseFaults;
|
return parseFaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction stringToInstruction(godot::String command_string)
|
godot::String CoborVM::CoborVirtualMachine::set_registers(int size, godot::PackedInt32Array preloaded_values)
|
||||||
{
|
{
|
||||||
std::string cmd = command_string.utf8().get_data();
|
if (size <= 0)
|
||||||
if (cmd == "SET")
|
return godot::String("Size must be one or higher");
|
||||||
return SET;
|
register_memory = std::vector<int32_t>(size);
|
||||||
if (cmd == "CLR")
|
int i = 0;
|
||||||
return CLR;
|
for (int value : preloaded_values)
|
||||||
if (cmd == "MV")
|
{
|
||||||
return CPY;
|
if (i >= size)
|
||||||
if (cmd == "SWP")
|
break;
|
||||||
return SWP;
|
register_memory[i] = value;
|
||||||
if (cmd == "ADD")
|
i++;
|
||||||
return ADD;
|
}
|
||||||
if (cmd == "SUB")
|
return godot::String();
|
||||||
return SUB;
|
|
||||||
if (cmd == "MUL")
|
|
||||||
return MUL;
|
|
||||||
if (cmd == "DIV")
|
|
||||||
return DIV;
|
|
||||||
if (cmd == "SKIP")
|
|
||||||
return SKIP;
|
|
||||||
if (cmd == "JMP")
|
|
||||||
return JMP;
|
|
||||||
if (cmd == "JMPTO")
|
|
||||||
return JMPTO;
|
|
||||||
if (cmd == "EQL")
|
|
||||||
return EQL;
|
|
||||||
if (cmd == "BIGR")
|
|
||||||
return BIGR;
|
|
||||||
if (cmd == "SMLR")
|
|
||||||
return SMLR;
|
|
||||||
if (cmd == "ZERO")
|
|
||||||
return ZERO;
|
|
||||||
return ERR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutableStep InstructionToExecutableStep(Instruction instruction, godot::PackedStringArray line, int lineNumber)
|
godot::PackedInt32Array CoborVM::CoborVirtualMachine::get_registers()
|
||||||
{
|
{
|
||||||
ExecutableStep executableStep;
|
godot::PackedInt32Array ret;
|
||||||
executableStep.lineNumber = lineNumber;
|
for (int32_t value : register_memory)
|
||||||
|
|
||||||
godot::String arg1 = "0", arg2 = "0", arg3 = "0";
|
|
||||||
|
|
||||||
switch (instruction)
|
|
||||||
{
|
{
|
||||||
// Instructions with no arguments
|
ret.append(value);
|
||||||
case Instruction::SKIP:
|
}
|
||||||
if (line.size() != 1)
|
return ret;
|
||||||
return executableStep;
|
}
|
||||||
|
|
||||||
|
int CoborVM::CoborVirtualMachine::get_program_counter()
|
||||||
|
{
|
||||||
|
return register_memory[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
godot::String CoborVM::CoborVirtualMachine::run_step()
|
||||||
|
{
|
||||||
|
if (program.empty())
|
||||||
|
return godot::String("No program is compiled to run.");
|
||||||
|
if (register_memory.empty())
|
||||||
|
return godot::String("Registers are not set.");
|
||||||
|
|
||||||
|
int program_counter = register_memory[0];
|
||||||
|
if (program_counter < 0 || program_counter >= program.size())
|
||||||
|
return godot::String("Program counter pointing outside program scope: ") +
|
||||||
|
godot::String::num_int64(program_counter);
|
||||||
|
|
||||||
|
ExecutableStep step = program[program_counter];
|
||||||
|
auto err = run_executable_step(step);
|
||||||
|
if (!err.is_empty())
|
||||||
|
{
|
||||||
|
return godot::String("Execution failed at line: ") +
|
||||||
|
godot::String::num_int64(step.lineNumber) +
|
||||||
|
godot::String(" with error: ") +
|
||||||
|
err;
|
||||||
|
}
|
||||||
|
|
||||||
|
register_memory[0]++;
|
||||||
|
return godot::String();
|
||||||
|
}
|
||||||
|
|
||||||
|
godot::String CoborVM::CoborVirtualMachine::run_all()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int program_counter = register_memory[0];
|
||||||
|
if (program.size() <= program_counter)
|
||||||
|
break;
|
||||||
|
auto result = run_step();
|
||||||
|
if (!result.is_empty())
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return godot::String();
|
||||||
|
}
|
||||||
|
|
||||||
|
godot::String CoborVM::CoborVirtualMachine::run_executable_step(ExecutableStep step)
|
||||||
|
{
|
||||||
|
godot::String error_message;
|
||||||
|
|
||||||
|
auto check_register = [this, &error_message](int64_t reg) -> bool
|
||||||
|
{
|
||||||
|
if (reg >= register_memory.size())
|
||||||
|
{
|
||||||
|
error_message = godot::String("Pointing outside register memory at /") + godot::String::num_int64(reg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (step.instruction)
|
||||||
|
{
|
||||||
|
case EMPTY:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Instructions with one literal argument
|
case SET:
|
||||||
case Instruction::JMP:
|
{
|
||||||
case Instruction::JMPTO:
|
if (!check_register(step.arg2))
|
||||||
if (line.size() != 2)
|
return error_message;
|
||||||
return executableStep;
|
register_memory[step.arg2] = step.arg1;
|
||||||
arg1 = line[1];
|
|
||||||
if (!arg1.is_valid_int())
|
|
||||||
return executableStep;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Instructions with one pointer argument
|
|
||||||
case Instruction::CLR:
|
|
||||||
case Instruction::ZERO:
|
|
||||||
if (line.size() != 2)
|
|
||||||
return executableStep;
|
|
||||||
|
|
||||||
arg1 = line[1];
|
|
||||||
if (!arg1.begins_with("/"))
|
|
||||||
return executableStep;
|
|
||||||
arg1 = arg1.trim_prefix("/");
|
|
||||||
if (!arg1.is_valid_int())
|
|
||||||
return executableStep;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Instructions with one literal argument and one pointer argument
|
|
||||||
case Instruction::SET:
|
|
||||||
if (line.size() != 3)
|
|
||||||
return executableStep;
|
|
||||||
|
|
||||||
arg1 = line[1];
|
|
||||||
if (!arg1.is_valid_int())
|
|
||||||
return executableStep;
|
|
||||||
arg2 = line[2];
|
|
||||||
if (!arg2.begins_with("/"))
|
|
||||||
return executableStep;
|
|
||||||
arg2 = arg2.trim_prefix("/");
|
|
||||||
if (!arg2.is_valid_int())
|
|
||||||
return executableStep;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Instructions with two pointer argument
|
|
||||||
case Instruction::CPY:
|
|
||||||
case Instruction::SWP:
|
|
||||||
case Instruction::EQL:
|
|
||||||
case Instruction::BIGR:
|
|
||||||
case Instruction::SMLR:
|
|
||||||
if (line.size() != 3)
|
|
||||||
return executableStep;
|
|
||||||
|
|
||||||
arg1 = line[1];
|
|
||||||
if (!arg1.begins_with("/"))
|
|
||||||
return executableStep;
|
|
||||||
arg1 = arg1.trim_prefix("/");
|
|
||||||
if (!arg1.is_valid_int())
|
|
||||||
return executableStep;
|
|
||||||
arg2 = line[2];
|
|
||||||
if (!arg2.begins_with("/"))
|
|
||||||
return executableStep;
|
|
||||||
arg2 = arg2.trim_prefix("/");
|
|
||||||
if (!arg2.is_valid_int())
|
|
||||||
return executableStep;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Instructions with three pointer argument
|
|
||||||
case Instruction::ADD:
|
|
||||||
case Instruction::SUB:
|
|
||||||
case Instruction::MUL:
|
|
||||||
case Instruction::DIV:
|
|
||||||
if (line.size() != 4)
|
|
||||||
return executableStep;
|
|
||||||
|
|
||||||
arg1 = line[1];
|
|
||||||
if (!arg1.begins_with("/"))
|
|
||||||
return executableStep;
|
|
||||||
arg1 = arg1.trim_prefix("/");
|
|
||||||
if (!arg1.is_valid_int())
|
|
||||||
return executableStep;
|
|
||||||
arg2 = line[2];
|
|
||||||
if (!arg2.begins_with("/"))
|
|
||||||
return executableStep;
|
|
||||||
arg2 = arg2.trim_prefix("/");
|
|
||||||
if (!arg2.is_valid_int())
|
|
||||||
return executableStep;
|
|
||||||
arg3 = line[3];
|
|
||||||
if (!arg3.begins_with("/"))
|
|
||||||
return executableStep;
|
|
||||||
arg3 = arg3.trim_prefix("/");
|
|
||||||
if (!arg3.is_valid_int())
|
|
||||||
return executableStep;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return executableStep;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
executableStep.instruction = instruction;
|
|
||||||
executableStep.arg1 = arg1.to_int();
|
case CLR:
|
||||||
executableStep.arg2 = arg2.to_int();
|
{
|
||||||
executableStep.arg3 = arg3.to_int();
|
if (!check_register(step.arg1))
|
||||||
executableStep.lineNumber = lineNumber;
|
return error_message;
|
||||||
return executableStep;
|
register_memory[step.arg1] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CPY:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1) || !check_register(step.arg2))
|
||||||
|
return error_message;
|
||||||
|
register_memory[step.arg2] = register_memory[step.arg1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SWP:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1) || !check_register(step.arg2))
|
||||||
|
return error_message;
|
||||||
|
std::swap(register_memory[step.arg1], register_memory[step.arg2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ADD:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1) || !check_register(step.arg2) || !check_register(step.arg3))
|
||||||
|
return error_message;
|
||||||
|
register_memory[step.arg3] = register_memory[step.arg1] + register_memory[step.arg2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SUB:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1) || !check_register(step.arg2) || !check_register(step.arg3))
|
||||||
|
return error_message;
|
||||||
|
register_memory[step.arg3] = register_memory[step.arg1] - register_memory[step.arg2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MUL:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1) || !check_register(step.arg2) || !check_register(step.arg3))
|
||||||
|
return error_message;
|
||||||
|
register_memory[step.arg3] = register_memory[step.arg1] * register_memory[step.arg2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DIV:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1) || !check_register(step.arg2) || !check_register(step.arg3))
|
||||||
|
return error_message;
|
||||||
|
if (register_memory[step.arg2] == 0)
|
||||||
|
{
|
||||||
|
return godot::String("Dividing by zero! Check register: ") + godot::String::num_int64(step.arg2);
|
||||||
|
}
|
||||||
|
register_memory[step.arg3] = register_memory[step.arg1] / register_memory[step.arg2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SKIP:
|
||||||
|
{
|
||||||
|
register_memory[0]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JMP:
|
||||||
|
{
|
||||||
|
register_memory[0] += step.arg1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JMPTO:
|
||||||
|
{
|
||||||
|
register_memory[0] = step.arg1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EQL:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1) || !check_register(step.arg2))
|
||||||
|
return error_message;
|
||||||
|
if (register_memory[step.arg1] == register_memory[step.arg2])
|
||||||
|
register_memory[0]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BIGR:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1) || !check_register(step.arg2))
|
||||||
|
return error_message;
|
||||||
|
if (register_memory[step.arg1] < register_memory[step.arg2])
|
||||||
|
register_memory[0]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SMLR:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1) || !check_register(step.arg2))
|
||||||
|
return error_message;
|
||||||
|
if (register_memory[step.arg1] > register_memory[step.arg2])
|
||||||
|
register_memory[0]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ZERO:
|
||||||
|
{
|
||||||
|
if (!check_register(step.arg1))
|
||||||
|
return error_message;
|
||||||
|
if (register_memory[step.arg1] == 0)
|
||||||
|
register_memory[0]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return godot::String("Unknown instruction! This is not on you.");
|
||||||
|
}
|
||||||
|
return godot::String();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CoborVM::CoborVirtualMachine::string_to_instruction(godot::String command_string, Instruction &outInstruction)
|
||||||
|
{
|
||||||
|
std::string cmd = command_string.utf8().get_data();
|
||||||
|
|
||||||
|
auto it = stringToInstructionMap.find(cmd);
|
||||||
|
if (it != stringToInstructionMap.end())
|
||||||
|
{
|
||||||
|
outInstruction = it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoborVM::CoborVirtualMachine::instruction_to_executable_step(Instruction instruction, godot::PackedStringArray line, ExecutableStep &outExecutableStep)
|
||||||
|
{
|
||||||
|
outExecutableStep.instruction = instruction;
|
||||||
|
|
||||||
|
auto it = instructionPatterns.find(instruction);
|
||||||
|
if (it == instructionPatterns.end())
|
||||||
|
{
|
||||||
|
return false; // Unknown instruction
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<bool> &pattern = it->second;
|
||||||
|
if (line.size() != pattern.size() + 1)
|
||||||
|
{ // +1 for the instruction itself
|
||||||
|
return false; // Incorrect number of arguments
|
||||||
|
}
|
||||||
|
|
||||||
|
int arg1 = 0, arg2 = 0, arg3 = 0;
|
||||||
|
for (size_t i = 0; i < pattern.size(); ++i)
|
||||||
|
{
|
||||||
|
bool isPointer = pattern[i];
|
||||||
|
godot::String arg = line[i + 1]; // +1 to skip the instruction
|
||||||
|
|
||||||
|
if (isPointer)
|
||||||
|
{
|
||||||
|
if (!validate_and_parse_pointer(arg, (i == 0) ? arg1 : (i == 1) ? arg2
|
||||||
|
: arg3))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!validate_and_parse_literal(arg, (i == 0) ? arg1 : (i == 1) ? arg2
|
||||||
|
: arg3))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outExecutableStep.arg1 = arg1;
|
||||||
|
outExecutableStep.arg2 = arg2;
|
||||||
|
outExecutableStep.arg3 = arg3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoborVM::CoborVirtualMachine::validate_and_parse_pointer(const godot::String &arg, int &outValue)
|
||||||
|
{
|
||||||
|
if (!arg.begins_with("/"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
godot::String trimmedArg = arg.trim_prefix("/");
|
||||||
|
if (!trimmedArg.is_valid_int())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outValue = trimmedArg.to_int();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoborVM::CoborVirtualMachine::validate_and_parse_literal(const godot::String &arg, int &outValue)
|
||||||
|
{
|
||||||
|
if (!arg.is_valid_int())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outValue = arg.to_int();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user