#pragma once #include "definitions.h" #include "pch.h" namespace bed::internal::scripting { struct RubyState { mrb_state *state = nullptr; int arena = 0; explicit RubyState(BEd *ctx) : state(mrb_open()) { if (!state) throw fatal_error("Failed to initialize mruby.", 1); state->ud = ctx; arena = mrb_gc_arena_save(state); } ~RubyState() { state->ud = nullptr; if (state) { mrb_gc_arena_restore(state, arena); mrb_full_gc(state); mrb_close(state); } } RubyState(const RubyState &) = delete; RubyState &operator=(const RubyState &) = delete; }; struct Block { mrb_state *mrb = nullptr; mrb_value proc = mrb_nil_value(); Block(mrb_state *mrb, mrb_value proc) noexcept : mrb(mrb), proc(proc) { mrb_gc_register(mrb, proc); } Block() noexcept = default; ~Block() noexcept { if (!mrb_nil_p(proc) && mrb) mrb_gc_unregister(mrb, proc); } Block(const Block &other) noexcept : mrb(other.mrb), proc(other.proc) { if (mrb && !mrb_nil_p(proc)) mrb_gc_register(mrb, proc); } Block &operator=(const Block &other) noexcept { if (this != &other) { if (mrb && !mrb_nil_p(proc)) mrb_gc_unregister(mrb, proc); mrb = other.mrb; proc = other.proc; if (mrb && !mrb_nil_p(proc)) mrb_gc_register(mrb, proc); } return *this; } Block(Block &&other) noexcept : mrb(other.mrb), proc(other.proc) { other.mrb = nullptr; other.proc = mrb_nil_value(); } Block &operator=(Block &&other) noexcept { if (this != &other) { if (mrb && !mrb_nil_p(proc)) mrb_gc_unregister(mrb, proc); mrb = other.mrb; proc = other.proc; other.mrb = nullptr; other.proc = mrb_nil_value(); } return *this; } void set_proc(mrb_state *mrb_, mrb_value new_proc) { if (!mrb_nil_p(proc) && mrb) mrb_gc_unregister(mrb, proc); mrb = mrb_; proc = new_proc; mrb_gc_register(mrb, proc); } mrb_value call(int argc = 0, mrb_value *argv = nullptr) const { if (mrb_nil_p(proc)) return mrb_nil_value(); mrb_value result = mrb_funcall_argv(mrb, proc, mrb_intern_cstr(mrb, "call"), argc, argv); if (!mrb->exc) return result; mrb_value exc = mrb_obj_value(mrb->exc); mrb_value msg = mrb_funcall(mrb, exc, "message", 0); std::string error; if (mrb_string_p(msg)) error.assign(RSTRING_PTR(msg), RSTRING_LEN(msg)); auto *fatal_class = mrb_class_get(mrb, "FatalError"); if (mrb_obj_is_kind_of(mrb, exc, fatal_class)) { mrb_value code = mrb_iv_get(mrb, exc, mrb_intern_lit(mrb, "@code")); mrb->exc = nullptr; int c = 1; if (mrb_fixnum_p(code)) c = mrb_fixnum(code); throw fatal_error(error, c); } auto *ed_class = mrb_class_get(mrb, "EdError"); if (mrb_obj_is_kind_of(mrb, exc, ed_class)) { mrb->exc = nullptr; throw ed_error(error); } mrb->exc = nullptr; throw ed_error("Ruby Exception: " + error); } }; void register_basic(BEd &ctx); void run(BEd &ctx, const std::string &str); }; // namespace bed::internal::scripting