From c325409dd02273733c5969832f0ccc8e818a3482 Mon Sep 17 00:00:00 2001 From: Syed Daanish Date: Tue, 8 Sep 2026 22:34:45 +0100 Subject: [PATCH] Add basic runtime function operations. --- include/bed.h | 4 +- include/internal/scripting/ruby.h | 83 +++++++++++++++++++++++----- include/internal/syntax/ruby/tries.h | 5 ++ src/internal/scripting/ruby.cc | 82 +++++++++++++++++++++++++-- 4 files changed, 154 insertions(+), 20 deletions(-) diff --git a/include/bed.h b/include/bed.h index 49b54eb..a211388 100644 --- a/include/bed.h +++ b/include/bed.h @@ -22,6 +22,8 @@ struct BEd { std::unordered_map languages; internal::io::IO io; internal::vase::AppendStorage append{"/tmp"}; + std::unordered_map buffers; + mrb_state *mrb = nullptr; int arena; @@ -37,8 +39,6 @@ struct BEd { std::string last_replacement = ""; std::string last_shell = ""; - std::unordered_map buffers; - internal::buffer::Range prev_1; internal::buffer::Range prev_2; internal::marks::MarksEngine marks; diff --git a/include/internal/scripting/ruby.h b/include/internal/scripting/ruby.h index 7625702..5030bc4 100644 --- a/include/internal/scripting/ruby.h +++ b/include/internal/scripting/ruby.h @@ -5,36 +5,93 @@ namespace bed::internal::scripting { struct Block { - mrb_value proc; - mrb_state *mrb; + mrb_state *mrb = nullptr; + mrb_value proc = mrb_nil_value(); - Block(mrb_state *mrb, mrb_value proc) - : proc(proc), mrb(mrb) { + Block(mrb_state *mrb, mrb_value proc) noexcept + : mrb(mrb), proc(proc) { mrb_gc_register(mrb, proc); } - Block() : proc(mrb_nil_value()) {} + Block() noexcept = default; - ~Block() { - if (!mrb_nil_p(proc)) + ~Block() noexcept { + if (!mrb_nil_p(proc) && mrb) mrb_gc_unregister(mrb, proc); } - void set_proc(mrb_value new_proc) { - if (!mrb_nil_p(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); } - Block(const Block &) = delete; - Block &operator=(const Block &) = delete; - 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); - return result; + 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); } }; diff --git a/include/internal/syntax/ruby/tries.h b/include/internal/syntax/ruby/tries.h index 09fb336..740a50e 100644 --- a/include/internal/syntax/ruby/tries.h +++ b/include/internal/syntax/ruby/tries.h @@ -68,6 +68,11 @@ const static std::vector builtins = { }; const static std::vector methods = { + // BEd methods. + "handle", + "register", + "unregister", + // Normal: "abort", "at_exit", "binding", diff --git a/src/internal/scripting/ruby.cc b/src/internal/scripting/ruby.cc index cce35bc..92e41ce 100644 --- a/src/internal/scripting/ruby.cc +++ b/src/internal/scripting/ruby.cc @@ -7,8 +7,7 @@ static mrb_value mrb_bed_exit(mrb_state *mrb, mrb_value) { return mrb_nil_value(); } -static void -raise_fatal(mrb_state *mrb, const fatal_error &e) { +static void raise_fatal(mrb_state *mrb, const fatal_error &e) { struct RClass *klass = mrb_class_get(mrb, "FatalError"); mrb_value exc = mrb_exc_new_str(mrb, klass, mrb_str_new_cstr(mrb, e.what())); mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "@code"), mrb_fixnum_value(e.code)); @@ -27,19 +26,92 @@ static mrb_value mrb_bed_handle(mrb_state *mrb, mrb_value) { mrb_raise(mrb, mrb_class_get(mrb, "EdError"), e.what()); } catch (const fatal_error &f) { raise_fatal(mrb, f); - } catch (...) { - mrb_raise(mrb, E_RUNTIME_ERROR, "Unexpected error."); } return mrb_nil_value(); } +static mrb_value hash_get(mrb_state *mrb, mrb_value hash, const char *name) { + if (mrb_nil_p(hash)) + return mrb_nil_value(); + return mrb_hash_get(mrb, hash, mrb_symbol_value(mrb_intern_cstr(mrb, name))); +} + +static functions::Function::AddressKind parse_address_kind(mrb_state *mrb, mrb_value value) { + if (mrb_nil_p(value)) + return functions::Function::AddressKind::None; + if (!mrb_symbol_p(value)) + mrb_raise(mrb, E_TYPE_ERROR, "address must be a Symbol"); + auto name = mrb_sym_name(mrb, mrb_symbol(value)); + if (strcmp(name, "none") == 0) + return functions::Function::AddressKind::None; + if (strcmp(name, "line") == 0) + return functions::Function::AddressKind::Line; + if (strcmp(name, "range") == 0) + return functions::Function::AddressKind::Range; + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid address type: :%s", name); + return functions::Function::AddressKind::None; +} + +static mrb_value mrb_bed_register(mrb_state *mrb, mrb_value) { + auto &ctx = *(BEd *)mrb->ud; + mrb_sym r_cmd_name; + mrb_value proc; + mrb_value opts = mrb_nil_value(); + mrb_get_args(mrb, "n&|H", &r_cmd_name, &proc, &opts); + mrb_int cmd_name_len; + const char *name = mrb_sym_name_len(mrb, r_cmd_name, &cmd_name_len); + std::string_view cmd_name(name, cmd_name_len); + std::string desc; + mrb_value r_desc = hash_get(mrb, opts, "desc"); + if (mrb_string_p(r_desc)) + desc.assign(RSTRING_PTR(r_desc), RSTRING_LEN(r_desc)); + std::string default_address; + mrb_value r_default_address = hash_get(mrb, opts, "default"); + if (mrb_string_p(r_default_address)) + default_address.assign(RSTRING_PTR(r_default_address), RSTRING_LEN(r_default_address)); + ctx.functions.insert( + cmd_name, + functions::Function{ + .address_kind = parse_address_kind(mrb, hash_get(mrb, opts, "address")), + .argument_kind = functions::Function::ArgumentKind::None, + .input_mode = functions::Function::InputMode::None, + .desc = desc, + .default_address = default_address, + .accept_zero = true, + .pre_text_mode = nullptr, + .handle = [b = Block(mrb, proc)]( + BEd &, + const buffer::Address &, + vase::Shard *, + const functions::Function::Argument &, + std::vector * + ) { + b.call(); + } + } + ); + return mrb_nil_value(); +} + +static mrb_value mrb_bed_unregister(mrb_state *mrb, mrb_value) { + auto &ctx = *(BEd *)mrb->ud; + mrb_sym r_cmd_name; + mrb_get_args(mrb, "n", &r_cmd_name); + mrb_int cmd_name_len; + const char *name = mrb_sym_name_len(mrb, r_cmd_name, &cmd_name_len); + std::string_view cmd_name(name, cmd_name_len); + ctx.functions.remove(cmd_name); + return mrb_nil_value(); +} + void register_basic(BEd &ctx) { auto *bed_error = mrb_define_class(ctx.mrb, "EdError", mrb_exc_get_id(ctx.mrb, MRB_ERROR_SYM(RuntimeError))); mrb_define_class(ctx.mrb, "FatalError", bed_error); - mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "exit", mrb_bed_exit, MRB_ARGS_NONE()); mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "handle", mrb_bed_handle, MRB_ARGS_REQ(1)); + mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "register", mrb_bed_register, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1) | MRB_ARGS_BLOCK()); + mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "unregister", mrb_bed_unregister, MRB_ARGS_REQ(1)); } void run(BEd &ctx, const std::string &str) {