Started a Cobor Virtual Machine implementation for running the code.

This commit is contained in:
douwe
2025-08-28 02:12:56 +02:00
parent fc137e2fb2
commit 28e224326c
197 changed files with 366838 additions and 0 deletions

View File

View File

@@ -0,0 +1,20 @@
#pragma once
#include "godot_cpp/classes/ref_counted.hpp"
namespace CoborVM
{
class CoborVirtualMachine : public godot::RefCounted
{
GDCLASS(CoborVirtualMachine, godot::RefCounted)
protected:
static void _bind_methods();
public:
CoborVirtualMachine() = default;
~CoborVirtualMachine() override = default;
godot::PackedStringArray parse_source_code(godot::String source_code);
};
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "godot_cpp/classes/ref_counted.hpp"
#include "godot_cpp/classes/wrapped.hpp"
#include "godot_cpp/variant/variant.hpp"
using namespace godot;
namespace CoborVM
{
class ExampleClass : public RefCounted
{
GDCLASS(ExampleClass, RefCounted)
protected:
static void _bind_methods();
public:
ExampleClass() = default;
~ExampleClass() override = default;
void print_type(const Variant &p_variant) const;
};
}

View File

@@ -0,0 +1,7 @@
#ifndef COBOR_VM_REGISTER_TYPES_H
#define COBOR_VM_REGISTER_TYPES_H
void initialize_gdextension_types();
void uninitialize_gdextension_types();
#endif // COBOR_VM_REGISTER_TYPES_H

View File

@@ -0,0 +1,224 @@
#include "cobor_virtual_machine.hpp"
enum Instruction
{
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()
{
godot::ClassDB::bind_method(godot::D_METHOD("parse_source_code", "source_code"), &CoborVirtualMachine::parse_source_code);
}
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 parseFaults;
source_code = source_code.to_upper();
auto lines = source_code.split("\n");
int lineNumber = 0;
for (auto &line : lines)
{
line = line.strip_edges();
auto words = line.split(" ", false);
Instruction command = stringToInstruction(words[0]);
if (command == ERR)
{
parseFaults.append(godot::String("Error: Invalid command at line ") +
godot::String::num_int64(lineNumber) +
godot::String(": {") + line + godot::String("}"));
continue;
}
lineNumber++;
}
return parseFaults;
}
Instruction stringToInstruction(godot::String command_string)
{
std::string cmd = command_string.utf8().get_data();
if (cmd == "SET")
return SET;
if (cmd == "CLR")
return CLR;
if (cmd == "MV")
return CPY;
if (cmd == "SWP")
return SWP;
if (cmd == "ADD")
return ADD;
if (cmd == "SUB")
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)
{
ExecutableStep executableStep;
executableStep.lineNumber = lineNumber;
godot::String arg1 = "0", arg2 = "0", arg3 = "0";
switch (instruction)
{
// Instructions with no arguments
case Instruction::SKIP:
if (line.size() != 1)
return executableStep;
break;
// Instructions with one literal argument
case Instruction::JMP:
case Instruction::JMPTO:
if (line.size() != 2)
return executableStep;
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;
}
executableStep.instruction = instruction;
executableStep.arg1 = arg1.to_int();
executableStep.arg2 = arg2.to_int();
executableStep.arg3 = arg3.to_int();
executableStep.lineNumber = lineNumber;
return executableStep;
}

View File

@@ -0,0 +1,9 @@
#include "example_class.hpp"
void CoborVM::ExampleClass::_bind_methods() {
godot::ClassDB::bind_method(D_METHOD("print_type", "variant"), &ExampleClass::print_type);
}
void CoborVM::ExampleClass::print_type(const Variant &p_variant) const {
print_line(vformat("Type: %d", p_variant.get_type()));
}

Binary file not shown.

View File

@@ -0,0 +1,20 @@
/* THIS FILE IS GENERATED DO NOT EDIT */
#include <godot_cpp/godot.hpp>
static const char *_doc_data_hash = "626471190447146946";
static const int _doc_data_uncompressed_size = 0;
static const int _doc_data_compressed_size = 8;
static const unsigned char _doc_data_compressed[] = {
120,
218,
3,
0,
0,
0,
0,
1,
};
static godot::internal::DocDataRegistration _doc_data_registration(_doc_data_hash, _doc_data_uncompressed_size, _doc_data_compressed_size, _doc_data_compressed);

Binary file not shown.

View File

@@ -0,0 +1,40 @@
#include "register_types.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
#include "example_class.hpp"
#include "cobor_virtual_machine.hpp"
using namespace godot;
void initialize_gdextension_types(ModuleInitializationLevel p_level)
{
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
GDREGISTER_CLASS(CoborVM::ExampleClass);
GDREGISTER_CLASS(CoborVM::CoborVirtualMachine);
}
void uninitialize_gdextension_types(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
extern "C"
{
// Initialization
GDExtensionBool GDE_EXPORT cobor_vm_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)
{
GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_gdextension_types);
init_obj.register_terminator(uninitialize_gdextension_types);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
}

Binary file not shown.