AeThex-Engine-Core/engine/modules/aethex_lang/aethex_vm.cpp
2026-02-24 01:55:30 -07:00

625 lines
15 KiB
C++

/**************************************************************************/
/* aethex_vm.cpp */
/**************************************************************************/
/* This file is part of: */
/* AETHEX ENGINE */
/* https://aethex.foundation */
/**************************************************************************/
/* Copyright (c) 2026-present AeThex Labs. */
/**************************************************************************/
#include "aethex_vm.h"
#include "core/io/file_access.h"
#include "core/os/os.h"
AeThexVM::AeThexVM() {
reset_stack();
register_builtin_functions();
}
AeThexVM::~AeThexVM() {
}
void AeThexVM::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_global", "name", "value"), &AeThexVM::set_global);
ClassDB::bind_method(D_METHOD("get_global", "name"), &AeThexVM::get_global);
ClassDB::bind_method(D_METHOD("has_global", "name"), &AeThexVM::has_global);
ClassDB::bind_method(D_METHOD("get_error"), &AeThexVM::get_error);
ClassDB::bind_method(D_METHOD("has_error"), &AeThexVM::has_error);
ClassDB::bind_method(D_METHOD("set_debug_trace", "trace"), &AeThexVM::set_debug_trace);
BIND_ENUM_CONSTANT(INTERPRET_OK);
BIND_ENUM_CONSTANT(INTERPRET_COMPILE_ERROR);
BIND_ENUM_CONSTANT(INTERPRET_RUNTIME_ERROR);
}
void AeThexVM::register_builtin_functions() {
// Register standard math constants and basic functions via globals
globals["PI"] = Math::PI;
globals["TAU"] = Math::TAU;
globals["INF"] = INFINITY;
globals["E"] = Math::E;
// Built-in types
globals["String"] = Variant();
globals["Array"] = Variant();
globals["Dictionary"] = Variant();
globals["Vector2"] = Variant();
globals["Vector3"] = Variant();
// Note: Native math functions like abs, floor, ceil, etc. are provided
// via the AeThex script library or through Godot's expression evaluator
}
void AeThexVM::reset_stack() {
stack_top = 0;
frame_count = 0;
runtime_error = "";
}
void AeThexVM::push(const Variant &value) {
if (stack_top >= STACK_MAX) {
runtime_error_at("Stack overflow");
return;
}
stack[stack_top++] = value;
}
Variant AeThexVM::pop() {
if (stack_top <= 0) {
runtime_error_at("Stack underflow");
return Variant();
}
return stack[--stack_top];
}
Variant AeThexVM::peek(int distance) const {
if (stack_top - 1 - distance < 0) {
return Variant();
}
return stack[stack_top - 1 - distance];
}
void AeThexVM::set_global(const String &name, const Variant &value) {
globals[name] = value;
}
Variant AeThexVM::get_global(const String &name) const {
if (globals.has(name)) {
return globals[name];
}
return Variant();
}
bool AeThexVM::has_global(const String &name) const {
return globals.has(name);
}
void AeThexVM::register_native_function(const String &name, const Callable &callable) {
native_functions[name] = callable;
}
AeThexVM::InterpretResult AeThexVM::execute(const AeThexCompiler::CompiledScript *p_script) {
if (!p_script) {
runtime_error = "No script to execute";
return INTERPRET_COMPILE_ERROR;
}
script = p_script;
reset_stack();
// Set up main frame
frames[0].chunk = &script->main_chunk;
frames[0].ip = 0;
frames[0].stack_base = 0;
frames[0].function_name = "main";
frame_count = 1;
return run();
}
AeThexVM::InterpretResult AeThexVM::call(const String &function_name, const Vector<Variant> &args) {
if (!script) {
runtime_error = "No script loaded";
return INTERPRET_COMPILE_ERROR;
}
if (!script->functions.has(function_name)) {
runtime_error = "Function not found: " + function_name;
return INTERPRET_RUNTIME_ERROR;
}
// Push arguments
for (const Variant &arg : args) {
push(arg);
}
// Set up call frame
CallFrame *frame = &frames[frame_count++];
frame->chunk = &script->functions[function_name];
frame->ip = 0;
frame->stack_base = stack_top - args.size();
frame->function_name = function_name;
return run();
}
uint8_t AeThexVM::read_byte() {
CallFrame *frame = &frames[frame_count - 1];
return frame->chunk->code[frame->ip++];
}
uint16_t AeThexVM::read_short() {
CallFrame *frame = &frames[frame_count - 1];
uint16_t value = frame->chunk->code[frame->ip] |
(frame->chunk->code[frame->ip + 1] << 8);
frame->ip += 2;
return value;
}
int32_t AeThexVM::read_int() {
CallFrame *frame = &frames[frame_count - 1];
int32_t value = frame->chunk->code[frame->ip] |
(frame->chunk->code[frame->ip + 1] << 8) |
(frame->chunk->code[frame->ip + 2] << 16) |
(frame->chunk->code[frame->ip + 3] << 24);
frame->ip += 4;
return value;
}
float AeThexVM::read_float() {
union { float f; int32_t i; } u;
u.i = read_int();
return u.f;
}
const String &AeThexVM::read_string() {
uint8_t idx = read_byte();
CallFrame *frame = &frames[frame_count - 1];
return frame->chunk->strings[idx];
}
Variant AeThexVM::read_constant() {
uint8_t idx = read_byte();
CallFrame *frame = &frames[frame_count - 1];
return frame->chunk->constants[idx];
}
bool AeThexVM::call_function(const String &name, int arg_count) {
if (script->functions.has(name)) {
if (frame_count >= FRAMES_MAX) {
runtime_error_at("Stack overflow (call frames)");
return false;
}
CallFrame *frame = &frames[frame_count++];
frame->chunk = &script->functions[name];
frame->ip = 0;
frame->stack_base = stack_top - arg_count;
frame->function_name = name;
return true;
}
return call_native(name, arg_count);
}
bool AeThexVM::call_native(const String &name, int arg_count) {
if (native_functions.has(name)) {
// Collect arguments
Array args;
for (int i = arg_count - 1; i >= 0; i--) {
args.push_front(peek(i));
}
// Pop arguments
for (int i = 0; i < arg_count; i++) {
pop();
}
// Call native function
Variant result;
Callable::CallError error;
const Variant *argv[16];
for (int i = 0; i < MIN(arg_count, 16); i++) {
argv[i] = &args[i];
}
native_functions[name].callp(argv, arg_count, result, error);
if (error.error != Callable::CallError::CALL_OK) {
runtime_error_at("Native function error: " + name);
return false;
}
push(result);
return true;
}
runtime_error_at("Undefined function: " + name);
return false;
}
void AeThexVM::runtime_error_at(const String &message) {
CallFrame *frame = &frames[frame_count - 1];
runtime_error = message + " at " + frame->function_name + ":" + itos(frame->ip);
}
AeThexVM::InterpretResult AeThexVM::run() {
CallFrame *frame = &frames[frame_count - 1];
#define READ_BYTE() (frame->chunk->code[frame->ip++])
#define READ_SHORT() (frame->ip += 2, (uint16_t)(frame->chunk->code[frame->ip - 2] | (frame->chunk->code[frame->ip - 1] << 8)))
#define BINARY_OP(op) \
do { \
Variant b = pop(); \
Variant a = pop(); \
push(Variant::evaluate(Variant::op, a, b)); \
} while (false)
while (true) {
if (frame->ip >= frame->chunk->code.size()) {
break;
}
if (debug_trace) {
print_stack();
}
uint8_t instruction = READ_BYTE();
switch (instruction) {
case AeThexCompiler::OP_PUSH_NULL:
push(Variant());
break;
case AeThexCompiler::OP_PUSH_BOOL:
push(READ_BYTE() != 0);
break;
case AeThexCompiler::OP_PUSH_INT: {
int32_t value = frame->chunk->code[frame->ip] |
(frame->chunk->code[frame->ip + 1] << 8) |
(frame->chunk->code[frame->ip + 2] << 16) |
(frame->chunk->code[frame->ip + 3] << 24);
frame->ip += 4;
push(value);
break;
}
case AeThexCompiler::OP_PUSH_FLOAT: {
union { float f; int32_t i; } u;
u.i = frame->chunk->code[frame->ip] |
(frame->chunk->code[frame->ip + 1] << 8) |
(frame->chunk->code[frame->ip + 2] << 16) |
(frame->chunk->code[frame->ip + 3] << 24);
frame->ip += 4;
push(u.f);
break;
}
case AeThexCompiler::OP_PUSH_STRING: {
uint8_t idx = READ_BYTE();
if (idx < frame->chunk->strings.size()) {
push(frame->chunk->strings[idx]);
} else {
push("");
}
break;
}
case AeThexCompiler::OP_POP:
pop();
break;
case AeThexCompiler::OP_DUP:
push(peek());
break;
case AeThexCompiler::OP_LOAD_LOCAL: {
uint8_t slot = READ_BYTE();
push(stack[frame->stack_base + slot]);
break;
}
case AeThexCompiler::OP_STORE_LOCAL: {
uint8_t slot = READ_BYTE();
stack[frame->stack_base + slot] = peek();
break;
}
case AeThexCompiler::OP_LOAD_GLOBAL: {
uint8_t idx = READ_BYTE();
String name = frame->chunk->strings[idx];
if (globals.has(name)) {
push(globals[name]);
} else {
push(Variant());
}
break;
}
case AeThexCompiler::OP_STORE_GLOBAL: {
uint8_t idx = READ_BYTE();
String name = frame->chunk->strings[idx];
globals[name] = peek();
break;
}
case AeThexCompiler::OP_ADD:
BINARY_OP(OP_ADD);
break;
case AeThexCompiler::OP_SUB:
BINARY_OP(OP_SUBTRACT);
break;
case AeThexCompiler::OP_MUL:
BINARY_OP(OP_MULTIPLY);
break;
case AeThexCompiler::OP_DIV:
BINARY_OP(OP_DIVIDE);
break;
case AeThexCompiler::OP_MOD:
BINARY_OP(OP_MODULE);
break;
case AeThexCompiler::OP_NEG: {
Variant a = pop();
push(Variant::evaluate(Variant::OP_NEGATE, a, Variant()));
break;
}
case AeThexCompiler::OP_EQ:
BINARY_OP(OP_EQUAL);
break;
case AeThexCompiler::OP_NE:
BINARY_OP(OP_NOT_EQUAL);
break;
case AeThexCompiler::OP_LT:
BINARY_OP(OP_LESS);
break;
case AeThexCompiler::OP_LE:
BINARY_OP(OP_LESS_EQUAL);
break;
case AeThexCompiler::OP_GT:
BINARY_OP(OP_GREATER);
break;
case AeThexCompiler::OP_GE:
BINARY_OP(OP_GREATER_EQUAL);
break;
case AeThexCompiler::OP_NOT: {
Variant a = pop();
push(Variant::evaluate(Variant::OP_NOT, a, Variant()));
break;
}
case AeThexCompiler::OP_AND:
BINARY_OP(OP_AND);
break;
case AeThexCompiler::OP_OR:
BINARY_OP(OP_OR);
break;
case AeThexCompiler::OP_JUMP: {
uint16_t offset = READ_SHORT();
frame->ip += offset;
break;
}
case AeThexCompiler::OP_JUMP_IF: {
uint16_t offset = READ_SHORT();
if (peek().booleanize()) {
frame->ip += offset;
}
break;
}
case AeThexCompiler::OP_JUMP_IF_NOT: {
uint16_t offset = READ_SHORT();
if (!peek().booleanize()) {
frame->ip += offset;
}
break;
}
case AeThexCompiler::OP_LOOP: {
uint16_t offset = READ_SHORT();
frame->ip -= offset;
break;
}
case AeThexCompiler::OP_CALL: {
uint8_t arg_count = READ_BYTE();
Variant callee = peek(arg_count);
if (callee.get_type() == Variant::STRING) {
if (!call_function(callee, arg_count)) {
return INTERPRET_RUNTIME_ERROR;
}
frame = &frames[frame_count - 1];
} else if (callee.get_type() == Variant::CALLABLE) {
// Direct callable
Array args;
for (int i = arg_count - 1; i >= 0; i--) {
args.push_front(peek(i));
}
for (int i = 0; i < arg_count + 1; i++) {
pop();
}
Variant result = callee.call("call_funcv", args);
push(result);
} else {
runtime_error_at("Can only call functions");
return INTERPRET_RUNTIME_ERROR;
}
break;
}
case AeThexCompiler::OP_RETURN: {
Variant result = pop();
// Pop locals
stack_top = frame->stack_base;
frame_count--;
if (frame_count == 0) {
push(result);
return INTERPRET_OK;
}
push(result);
frame = &frames[frame_count - 1];
break;
}
case AeThexCompiler::OP_NEW_ARRAY: {
uint8_t count = READ_BYTE();
Array arr;
for (int i = count - 1; i >= 0; i--) {
arr.push_front(peek(i));
}
for (int i = 0; i < count; i++) {
pop();
}
push(arr);
break;
}
case AeThexCompiler::OP_NEW_DICT: {
uint8_t count = READ_BYTE();
Dictionary dict;
for (int i = (count * 2) - 1; i >= 0; i -= 2) {
dict[peek(i)] = peek(i - 1);
}
for (int i = 0; i < count * 2; i++) {
pop();
}
push(dict);
break;
}
case AeThexCompiler::OP_INDEX_GET: {
Variant index = pop();
Variant container = pop();
bool valid = false;
Variant result = container.get(index, &valid);
if (!valid) {
runtime_error_at("Invalid index access");
return INTERPRET_RUNTIME_ERROR;
}
push(result);
break;
}
case AeThexCompiler::OP_INDEX_SET: {
Variant value = pop();
Variant index = pop();
Variant container = pop();
bool valid = false;
container.set(index, value, &valid);
if (!valid) {
runtime_error_at("Invalid index assignment");
return INTERPRET_RUNTIME_ERROR;
}
push(value);
break;
}
case AeThexCompiler::OP_SYNC: {
// Cross-platform sync operation
// In engine: just a barrier/wait point
Variant data = pop();
// TODO: Implement actual sync logic
push(data);
break;
}
case AeThexCompiler::OP_BEACON_EMIT: {
uint8_t idx = READ_BYTE();
String beacon_name = frame->chunk->strings[idx];
// TODO: Emit beacon signal
break;
}
case AeThexCompiler::OP_NOTIFY: {
Variant message = pop();
print_line("[AeThex] " + message.stringify());
break;
}
case AeThexCompiler::OP_AWAIT: {
// TODO: Implement async/await
break;
}
default:
runtime_error_at("Unknown opcode: " + itos(instruction));
return INTERPRET_RUNTIME_ERROR;
}
}
#undef READ_BYTE
#undef READ_SHORT
#undef BINARY_OP
return INTERPRET_OK;
}
void AeThexVM::print_stack() const {
print_line(" Stack: ");
for (int i = 0; i < stack_top; i++) {
print_line(" [ " + stack[i].stringify() + " ]");
}
}
void AeThexVM::disassemble_chunk(const AeThexCompiler::Chunk &chunk, const String &name) const {
print_line("== " + name + " ==");
int offset = 0;
while (offset < chunk.code.size()) {
uint8_t instruction = chunk.code[offset];
String line = vformat("%04d ", offset);
line += AeThexCompiler::opcode_name((AeThexCompiler::Opcode)instruction);
switch (instruction) {
case AeThexCompiler::OP_PUSH_BOOL:
case AeThexCompiler::OP_LOAD_LOCAL:
case AeThexCompiler::OP_STORE_LOCAL:
case AeThexCompiler::OP_LOAD_GLOBAL:
case AeThexCompiler::OP_STORE_GLOBAL:
case AeThexCompiler::OP_PUSH_STRING:
case AeThexCompiler::OP_CALL:
offset++;
line += " " + itos(chunk.code[offset]);
break;
case AeThexCompiler::OP_PUSH_INT:
case AeThexCompiler::OP_PUSH_FLOAT:
offset += 4;
break;
case AeThexCompiler::OP_JUMP:
case AeThexCompiler::OP_JUMP_IF:
case AeThexCompiler::OP_JUMP_IF_NOT:
case AeThexCompiler::OP_LOOP:
offset += 2;
break;
default:
break;
}
print_line(line);
offset++;
}
}