/**************************************************************************/ /* aethex_compiler.cpp */ /**************************************************************************/ /* This file is part of: */ /* AETHEX ENGINE */ /* https://aethex.foundation */ /**************************************************************************/ /* Copyright (c) 2026-present AeThex Labs. */ /**************************************************************************/ #include "aethex_compiler.h" #include "core/io/file_access.h" void AeThexCompiler::Chunk::write_int(int32_t value) { code.push_back((value >> 0) & 0xFF); code.push_back((value >> 8) & 0xFF); code.push_back((value >> 16) & 0xFF); code.push_back((value >> 24) & 0xFF); } void AeThexCompiler::Chunk::write_float(float value) { union { float f; int32_t i; } u; u.f = value; write_int(u.i); } int AeThexCompiler::Chunk::add_constant(const Variant &value) { constants.push_back(value); return constants.size() - 1; } int AeThexCompiler::Chunk::add_string(const String &str) { strings.push_back(str); return strings.size() - 1; } AeThexCompiler::AeThexCompiler() { } AeThexCompiler::~AeThexCompiler() { } void AeThexCompiler::_bind_methods() { BIND_ENUM_CONSTANT(TARGET_BYTECODE); BIND_ENUM_CONSTANT(TARGET_LUAU); BIND_ENUM_CONSTANT(TARGET_VERSE); BIND_ENUM_CONSTANT(TARGET_CSHARP); BIND_ENUM_CONSTANT(TARGET_JAVASCRIPT); } Error AeThexCompiler::compile(const AeThexParser::ASTNode *root, Target p_target) { had_error = false; current_target = p_target; result = CompiledScript(); result.target = p_target; if (!root) { had_error = true; return ERR_INVALID_PARAMETER; } if (p_target == TARGET_BYTECODE) { current_chunk = &result.main_chunk; compile_node(root); emit_return(); } else { switch (p_target) { case TARGET_LUAU: result.output_code = generate_luau(root); break; case TARGET_VERSE: result.output_code = generate_verse(root); break; case TARGET_CSHARP: result.output_code = generate_csharp(root); break; case TARGET_JAVASCRIPT: result.output_code = generate_javascript(root); break; default: break; } } return had_error ? ERR_COMPILATION_FAILED : OK; } void AeThexCompiler::compile_node(const AeThexParser::ASTNode *node) { if (!node) return; switch (node->type) { case AeThexParser::ASTNode::NODE_REALITY: compile_reality(node); break; case AeThexParser::ASTNode::NODE_JOURNEY: compile_journey(node); break; case AeThexParser::ASTNode::NODE_BEACON: compile_beacon(node); break; case AeThexParser::ASTNode::NODE_IF: compile_if(node); break; case AeThexParser::ASTNode::NODE_SYNC: compile_sync(node); break; case AeThexParser::ASTNode::NODE_BINARY_OP: compile_binary(node); break; case AeThexParser::ASTNode::NODE_UNARY_OP: compile_unary(node); break; case AeThexParser::ASTNode::NODE_CALL: compile_call(node); break; case AeThexParser::ASTNode::NODE_LITERAL: compile_literal(node); break; case AeThexParser::ASTNode::NODE_IDENTIFIER: compile_identifier(node); break; default: compile_statement(node); break; } } void AeThexCompiler::compile_reality(const AeThexParser::ASTNode *node) { result.reality_name = node->value; // Extract platforms from attributes if (node->attributes.has("platforms")) { String platforms = node->attributes["platforms"]; Vector parts = platforms.split(","); for (const String &p : parts) { result.supported_platforms.push_back(p.strip_edges()); } } // Compile children for (const AeThexParser::ASTNode *child : node->children) { compile_node(child); } } void AeThexCompiler::compile_journey(const AeThexParser::ASTNode *node) { // Create function chunk String func_name = node->value; result.functions[func_name] = Chunk(); Chunk *prev_chunk = current_chunk; current_chunk = &result.functions[func_name]; begin_scope(); // Parameters are first children until we hit a non-identifier int param_count = 0; for (const AeThexParser::ASTNode *child : node->children) { if (child->type == AeThexParser::ASTNode::NODE_IDENTIFIER && param_count < 10) { declare_local(child->value, false); param_count++; } else { compile_node(child); } } emit_return(); end_scope(); current_chunk = prev_chunk; } void AeThexCompiler::compile_beacon(const AeThexParser::ASTNode *node) { result.beacons.push_back(node->value); } void AeThexCompiler::compile_statement(const AeThexParser::ASTNode *node) { switch (node->type) { case AeThexParser::ASTNode::NODE_VARIABLE: { bool is_const = node->attributes.has("const") && node->attributes["const"] == "true"; declare_local(node->value, is_const); if (!node->children.is_empty()) { compile_node(node->children[0]); } else { emit_byte(OP_PUSH_NULL); } emit_bytes(OP_STORE_LOCAL, resolve_local(node->value)); break; } case AeThexParser::ASTNode::NODE_NOTIFY: { if (!node->children.is_empty()) { compile_node(node->children[0]); } emit_byte(OP_NOTIFY); break; } case AeThexParser::ASTNode::NODE_REVEAL: { if (!node->children.is_empty()) { compile_node(node->children[0]); } else { emit_byte(OP_PUSH_NULL); } emit_byte(OP_RETURN); break; } case AeThexParser::ASTNode::NODE_ASSIGNMENT: { if (node->children.size() >= 2) { compile_node(node->children[1]); // Value if (node->children[0]->type == AeThexParser::ASTNode::NODE_IDENTIFIER) { int local = resolve_local(node->children[0]->value); if (local >= 0) { emit_bytes(OP_STORE_LOCAL, local); } else { int idx = current_chunk->add_string(node->children[0]->value); emit_bytes(OP_STORE_GLOBAL, idx); } } } break; } default: compile_expression(node); emit_byte(OP_POP); break; } } void AeThexCompiler::compile_expression(const AeThexParser::ASTNode *node) { compile_node(node); } void AeThexCompiler::compile_binary(const AeThexParser::ASTNode *node) { if (node->children.size() < 2) return; compile_node(node->children[0]); compile_node(node->children[1]); String op = node->value; if (op == "+") emit_byte(OP_ADD); else if (op == "-") emit_byte(OP_SUB); else if (op == "*") emit_byte(OP_MUL); else if (op == "/") emit_byte(OP_DIV); else if (op == "%") emit_byte(OP_MOD); else if (op == "==") emit_byte(OP_EQ); else if (op == "!=") emit_byte(OP_NE); else if (op == "<") emit_byte(OP_LT); else if (op == "<=") emit_byte(OP_LE); else if (op == ">") emit_byte(OP_GT); else if (op == ">=") emit_byte(OP_GE); else if (op == "and") emit_byte(OP_AND); else if (op == "or") emit_byte(OP_OR); } void AeThexCompiler::compile_unary(const AeThexParser::ASTNode *node) { if (node->children.is_empty()) return; compile_node(node->children[0]); if (node->value == "-") emit_byte(OP_NEG); else if (node->value == "not") emit_byte(OP_NOT); } void AeThexCompiler::compile_call(const AeThexParser::ASTNode *node) { if (node->children.is_empty()) return; // First child is callee, rest are arguments for (int i = 1; i < node->children.size(); i++) { compile_node(node->children[i]); } compile_node(node->children[0]); emit_bytes(OP_CALL, node->children.size() - 1); } void AeThexCompiler::compile_literal(const AeThexParser::ASTNode *node) { String val = node->value; if (val == "null") { emit_byte(OP_PUSH_NULL); } else if (val == "true") { emit_bytes(OP_PUSH_BOOL, 1); } else if (val == "false") { emit_bytes(OP_PUSH_BOOL, 0); } else if (node->attributes.has("type") && node->attributes["type"] == "number") { if (val.contains(".")) { emit_byte(OP_PUSH_FLOAT); current_chunk->write_float(val.to_float()); } else { emit_byte(OP_PUSH_INT); current_chunk->write_int(val.to_int()); } } else { int idx = current_chunk->add_string(val); emit_bytes(OP_PUSH_STRING, idx); } } void AeThexCompiler::compile_identifier(const AeThexParser::ASTNode *node) { int local = resolve_local(node->value); if (local >= 0) { emit_bytes(OP_LOAD_LOCAL, local); } else { int idx = current_chunk->add_string(node->value); emit_bytes(OP_LOAD_GLOBAL, idx); } } void AeThexCompiler::compile_if(const AeThexParser::ASTNode *node) { if (node->children.size() < 2) return; // Condition compile_node(node->children[0]); int then_jump = emit_jump(OP_JUMP_IF_NOT); emit_byte(OP_POP); // Then block if (node->children[1]) { for (const AeThexParser::ASTNode *stmt : node->children[1]->children) { compile_node(stmt); } } int else_jump = emit_jump(OP_JUMP); patch_jump(then_jump); emit_byte(OP_POP); // Else block if (node->children.size() > 2 && node->children[2]) { for (const AeThexParser::ASTNode *stmt : node->children[2]->children) { compile_node(stmt); } } patch_jump(else_jump); } void AeThexCompiler::compile_sync(const AeThexParser::ASTNode *node) { if (!node->children.is_empty()) { compile_node(node->children[0]); } emit_byte(OP_SYNC); } void AeThexCompiler::emit_byte(uint8_t byte) { current_chunk->write(byte); } void AeThexCompiler::emit_bytes(uint8_t b1, uint8_t b2) { emit_byte(b1); emit_byte(b2); } void AeThexCompiler::emit_return() { emit_byte(OP_PUSH_NULL); emit_byte(OP_RETURN); } int AeThexCompiler::emit_jump(Opcode op) { emit_byte(op); emit_byte(0xFF); emit_byte(0xFF); return current_chunk->code.size() - 2; } void AeThexCompiler::patch_jump(int offset) { int jump = current_chunk->code.size() - offset - 2; current_chunk->code.write[offset] = (jump >> 0) & 0xFF; current_chunk->code.write[offset + 1] = (jump >> 8) & 0xFF; } void AeThexCompiler::emit_loop(int loop_start) { emit_byte(OP_LOOP); int offset = current_chunk->code.size() - loop_start + 2; emit_byte((offset >> 0) & 0xFF); emit_byte((offset >> 8) & 0xFF); } void AeThexCompiler::begin_scope() { scope_depth++; } void AeThexCompiler::end_scope() { scope_depth--; while (!locals.is_empty() && locals[locals.size() - 1].depth > scope_depth) { emit_byte(OP_POP); locals.resize(locals.size() - 1); } } void AeThexCompiler::declare_local(const String &name, bool is_const) { Local local; local.name = name; local.depth = scope_depth; local.is_const = is_const; locals.push_back(local); } int AeThexCompiler::resolve_local(const String &name) { for (int i = locals.size() - 1; i >= 0; i--) { if (locals[i].name == name) { return i; } } return -1; } String AeThexCompiler::indent_str(int level) const { String s; for (int i = 0; i < level; i++) { s += " "; } return s; } // ========================================== // Luau (Roblox) Code Generation // ========================================== String AeThexCompiler::export_to_luau(const AeThexParser::ASTNode *root) { return generate_luau(root); } String AeThexCompiler::generate_luau(const AeThexParser::ASTNode *root) { String code = "-- Generated by AeThex Compiler\n"; code += "-- Target: Roblox Luau\n\n"; if (root && root->type == AeThexParser::ASTNode::NODE_REALITY) { code += "local " + root->value + " = {}\n\n"; for (const AeThexParser::ASTNode *child : root->children) { code += luau_node(child, 0); } code += "\nreturn " + root->value + "\n"; } return code; } String AeThexCompiler::luau_node(const AeThexParser::ASTNode *node, int indent) { if (!node) return ""; String ind = indent_str(indent); switch (node->type) { case AeThexParser::ASTNode::NODE_JOURNEY: { String code = ind + "function " + result.reality_name + "." + node->value + "("; // Parameters bool first = true; for (const AeThexParser::ASTNode *child : node->children) { if (child->type == AeThexParser::ASTNode::NODE_IDENTIFIER) { if (!first) code += ", "; code += child->value; first = false; } } code += ")\n"; // Body for (const AeThexParser::ASTNode *child : node->children) { if (child->type != AeThexParser::ASTNode::NODE_IDENTIFIER) { code += luau_node(child, indent + 1); } } code += ind + "end\n\n"; return code; } case AeThexParser::ASTNode::NODE_BEACON: { return ind + "-- Beacon (Signal): " + node->value + "\n"; } case AeThexParser::ASTNode::NODE_VARIABLE: { String code = ind + "local " + node->value; if (!node->children.is_empty()) { code += " = " + luau_node(node->children[0], 0); } return code + "\n"; } case AeThexParser::ASTNode::NODE_NOTIFY: { String arg = node->children.is_empty() ? "\"\"" : luau_node(node->children[0], 0); return ind + "print(" + arg + ")\n"; } case AeThexParser::ASTNode::NODE_REVEAL: { String val = node->children.is_empty() ? "nil" : luau_node(node->children[0], 0); return ind + "return " + val + "\n"; } case AeThexParser::ASTNode::NODE_IF: { String code = ind + "if " + luau_node(node->children[0], 0) + " then\n"; if (node->children.size() > 1 && node->children[1]) { for (const AeThexParser::ASTNode *stmt : node->children[1]->children) { code += luau_node(stmt, indent + 1); } } if (node->children.size() > 2 && node->children[2]) { code += ind + "else\n"; for (const AeThexParser::ASTNode *stmt : node->children[2]->children) { code += luau_node(stmt, indent + 1); } } code += ind + "end\n"; return code; } case AeThexParser::ASTNode::NODE_SYNC: { String arg = node->children.is_empty() ? "nil" : luau_node(node->children[0], 0); return ind + "-- sync: " + arg + "\n" + ind + "task.wait()\n"; } case AeThexParser::ASTNode::NODE_BINARY_OP: { String left = node->children.size() > 0 ? luau_node(node->children[0], 0) : ""; String right = node->children.size() > 1 ? luau_node(node->children[1], 0) : ""; return "(" + left + " " + node->value + " " + right + ")"; } case AeThexParser::ASTNode::NODE_UNARY_OP: { String operand = node->children.is_empty() ? "" : luau_node(node->children[0], 0); return node->value + operand; } case AeThexParser::ASTNode::NODE_CALL: { String callee = node->children.is_empty() ? "" : luau_node(node->children[0], 0); String args; for (int i = 1; i < node->children.size(); i++) { if (i > 1) args += ", "; args += luau_node(node->children[i], 0); } return callee + "(" + args + ")"; } case AeThexParser::ASTNode::NODE_LITERAL: { if (node->value == "null") return "nil"; if (node->attributes.has("type") && node->attributes["type"] == "string") { return "\"" + node->value + "\""; } return node->value; } case AeThexParser::ASTNode::NODE_IDENTIFIER: return node->value; default: return ""; } } // ========================================== // Verse (UEFN) Code Generation // ========================================== String AeThexCompiler::export_to_verse(const AeThexParser::ASTNode *root) { return generate_verse(root); } String AeThexCompiler::generate_verse(const AeThexParser::ASTNode *root) { String code = "# Generated by AeThex Compiler\n"; code += "# Target: UEFN Verse\n\n"; code += "using { /Fortnite.com/Devices }\n"; code += "using { /Verse.org/Simulation }\n\n"; if (root && root->type == AeThexParser::ASTNode::NODE_REALITY) { code += root->value + " := class(creative_device):\n\n"; for (const AeThexParser::ASTNode *child : root->children) { code += verse_node(child, 1); } } return code; } String AeThexCompiler::verse_node(const AeThexParser::ASTNode *node, int indent) { if (!node) return ""; String ind = indent_str(indent); switch (node->type) { case AeThexParser::ASTNode::NODE_JOURNEY: { String code = ind + node->value + "("; // Parameters bool first = true; for (const AeThexParser::ASTNode *child : node->children) { if (child->type == AeThexParser::ASTNode::NODE_IDENTIFIER) { if (!first) code += ", "; code += child->value + " : any"; first = false; } } code += ") : void =\n"; // Body for (const AeThexParser::ASTNode *child : node->children) { if (child->type != AeThexParser::ASTNode::NODE_IDENTIFIER) { code += verse_node(child, indent + 1); } } return code + "\n"; } case AeThexParser::ASTNode::NODE_VARIABLE: { String is_var = (node->attributes.has("const") && node->attributes["const"] == "true") ? "" : "var "; String code = ind + is_var + node->value + " : any"; if (!node->children.is_empty()) { code += " = " + verse_node(node->children[0], 0); } return code + "\n"; } case AeThexParser::ASTNode::NODE_NOTIFY: { String arg = node->children.is_empty() ? "\"\"" : verse_node(node->children[0], 0); return ind + "Print(" + arg + ")\n"; } case AeThexParser::ASTNode::NODE_REVEAL: { return ind + "# return\n"; } case AeThexParser::ASTNode::NODE_LITERAL: { if (node->value == "null") return "false"; if (node->value == "true") return "true"; if (node->value == "false") return "false"; if (node->attributes.has("type") && node->attributes["type"] == "string") { return "\"" + node->value + "\""; } return node->value; } case AeThexParser::ASTNode::NODE_IDENTIFIER: return node->value; default: return ""; } } // ========================================== // C# (Unity) Code Generation // ========================================== String AeThexCompiler::export_to_csharp(const AeThexParser::ASTNode *root) { return generate_csharp(root); } String AeThexCompiler::generate_csharp(const AeThexParser::ASTNode *root) { String code = "// Generated by AeThex Compiler\n"; code += "// Target: Unity C#\n\n"; code += "using UnityEngine;\n"; code += "using System;\n\n"; if (root && root->type == AeThexParser::ASTNode::NODE_REALITY) { code += "public class " + root->value + " : MonoBehaviour\n{\n"; for (const AeThexParser::ASTNode *child : root->children) { code += csharp_node(child, 1); } code += "}\n"; } return code; } String AeThexCompiler::csharp_node(const AeThexParser::ASTNode *node, int indent) { if (!node) return ""; String ind = indent_str(indent); switch (node->type) { case AeThexParser::ASTNode::NODE_JOURNEY: { String code = ind + "public void " + node->value + "("; // Parameters bool first = true; for (const AeThexParser::ASTNode *child : node->children) { if (child->type == AeThexParser::ASTNode::NODE_IDENTIFIER) { if (!first) code += ", "; code += "object " + child->value; first = false; } } code += ")\n" + ind + "{\n"; // Body for (const AeThexParser::ASTNode *child : node->children) { if (child->type != AeThexParser::ASTNode::NODE_IDENTIFIER) { code += csharp_node(child, indent + 1); } } code += ind + "}\n\n"; return code; } case AeThexParser::ASTNode::NODE_BEACON: { return ind + "public event Action " + node->value + ";\n"; } case AeThexParser::ASTNode::NODE_VARIABLE: { String decl = (node->attributes.has("const") && node->attributes["const"] == "true") ? "const " : ""; String code = ind + decl + "var " + node->value; if (!node->children.is_empty()) { code += " = " + csharp_node(node->children[0], 0); } return code + ";\n"; } case AeThexParser::ASTNode::NODE_NOTIFY: { String arg = node->children.is_empty() ? "\"\"" : csharp_node(node->children[0], 0); return ind + "Debug.Log(" + arg + ");\n"; } case AeThexParser::ASTNode::NODE_REVEAL: { String val = node->children.is_empty() ? "" : csharp_node(node->children[0], 0); return ind + "return" + (val.is_empty() ? "" : " " + val) + ";\n"; } case AeThexParser::ASTNode::NODE_IF: { String code = ind + "if (" + csharp_node(node->children[0], 0) + ")\n"; code += ind + "{\n"; if (node->children.size() > 1 && node->children[1]) { for (const AeThexParser::ASTNode *stmt : node->children[1]->children) { code += csharp_node(stmt, indent + 1); } } code += ind + "}\n"; if (node->children.size() > 2 && node->children[2]) { code += ind + "else\n" + ind + "{\n"; for (const AeThexParser::ASTNode *stmt : node->children[2]->children) { code += csharp_node(stmt, indent + 1); } code += ind + "}\n"; } return code; } case AeThexParser::ASTNode::NODE_LITERAL: { if (node->value == "null") return "null"; if (node->attributes.has("type") && node->attributes["type"] == "string") { return "\"" + node->value + "\""; } return node->value; } case AeThexParser::ASTNode::NODE_IDENTIFIER: return node->value; default: return ""; } } // ========================================== // JavaScript (Web) Code Generation // ========================================== String AeThexCompiler::export_to_javascript(const AeThexParser::ASTNode *root) { return generate_javascript(root); } String AeThexCompiler::generate_javascript(const AeThexParser::ASTNode *root) { String code = "// Generated by AeThex Compiler\n"; code += "// Target: JavaScript (Web)\n\n"; if (root && root->type == AeThexParser::ASTNode::NODE_REALITY) { code += "const " + root->value + " = {\n"; bool first = true; for (const AeThexParser::ASTNode *child : root->children) { if (child->type == AeThexParser::ASTNode::NODE_JOURNEY) { if (!first) code += ",\n"; first = false; code += js_node(child, 1); } } code += "\n};\n\nexport default " + root->value + ";\n"; } return code; } String AeThexCompiler::js_node(const AeThexParser::ASTNode *node, int indent) { if (!node) return ""; String ind = indent_str(indent); switch (node->type) { case AeThexParser::ASTNode::NODE_JOURNEY: { String code = ind + node->value + "("; // Parameters bool first = true; for (const AeThexParser::ASTNode *child : node->children) { if (child->type == AeThexParser::ASTNode::NODE_IDENTIFIER) { if (!first) code += ", "; code += child->value; first = false; } } code += ") {\n"; // Body for (const AeThexParser::ASTNode *child : node->children) { if (child->type != AeThexParser::ASTNode::NODE_IDENTIFIER) { code += js_node(child, indent + 1); } } code += ind + "}"; return code; } case AeThexParser::ASTNode::NODE_VARIABLE: { String decl = (node->attributes.has("const") && node->attributes["const"] == "true") ? "const" : "let"; String code = ind + decl + " " + node->value; if (!node->children.is_empty()) { code += " = " + js_node(node->children[0], 0); } return code + ";\n"; } case AeThexParser::ASTNode::NODE_NOTIFY: { String arg = node->children.is_empty() ? "\"\"" : js_node(node->children[0], 0); return ind + "console.log(" + arg + ");\n"; } case AeThexParser::ASTNode::NODE_REVEAL: { String val = node->children.is_empty() ? "" : js_node(node->children[0], 0); return ind + "return" + (val.is_empty() ? "" : " " + val) + ";\n"; } case AeThexParser::ASTNode::NODE_LITERAL: { if (node->value == "null") return "null"; if (node->attributes.has("type") && node->attributes["type"] == "string") { return "\"" + node->value + "\""; } return node->value; } case AeThexParser::ASTNode::NODE_IDENTIFIER: return node->value; default: return ""; } } String AeThexCompiler::target_name(Target t) { switch (t) { case TARGET_BYTECODE: return "Bytecode"; case TARGET_LUAU: return "Roblox Luau"; case TARGET_VERSE: return "UEFN Verse"; case TARGET_CSHARP: return "Unity C#"; case TARGET_JAVASCRIPT: return "JavaScript"; default: return "Unknown"; } } String AeThexCompiler::opcode_name(Opcode op) { switch (op) { case OP_PUSH_NULL: return "PUSH_NULL"; case OP_PUSH_BOOL: return "PUSH_BOOL"; case OP_PUSH_INT: return "PUSH_INT"; case OP_PUSH_FLOAT: return "PUSH_FLOAT"; case OP_PUSH_STRING: return "PUSH_STRING"; case OP_POP: return "POP"; case OP_DUP: return "DUP"; case OP_LOAD_LOCAL: return "LOAD_LOCAL"; case OP_STORE_LOCAL: return "STORE_LOCAL"; case OP_LOAD_GLOBAL: return "LOAD_GLOBAL"; case OP_STORE_GLOBAL: return "STORE_GLOBAL"; case OP_ADD: return "ADD"; case OP_SUB: return "SUB"; case OP_MUL: return "MUL"; case OP_DIV: return "DIV"; case OP_CALL: return "CALL"; case OP_RETURN: return "RETURN"; case OP_SYNC: return "SYNC"; case OP_BEACON_EMIT: return "BEACON_EMIT"; case OP_NOTIFY: return "NOTIFY"; default: return "UNKNOWN"; } }