diff --git a/mrbgems/mruby-class-ext/mrblib/module.rb b/mrbgems/mruby-class-ext/mrblib/module.rb deleted file mode 100644 index 301585187..000000000 --- a/mrbgems/mruby-class-ext/mrblib/module.rb +++ /dev/null @@ -1,89 +0,0 @@ -class Module - - ## - # call-seq: - # mod < other -> true, false, or nil - # - # Returns true if `mod` is a subclass of `other`. Returns - # nil if there's no relationship between the two. - # (Think of the relationship in terms of the class definition: - # "class A < B" implies "A < B".) - # - def <(other) - if self.equal?(other) - false - else - self <= other - end - end - - ## - # call-seq: - # mod <= other -> true, false, or nil - # - # Returns true if `mod` is a subclass of `other` or - # is the same as `other`. Returns - # nil if there's no relationship between the two. - # (Think of the relationship in terms of the class definition: - # "class A < B" implies "A < B".) - def <=(other) - raise TypeError, 'compared with non class/module' unless other.is_a?(Module) - if self.ancestors.include?(other) - return true - elsif other.ancestors.include?(self) - return false - end - end - - ## - # call-seq: - # mod > other -> true, false, or nil - # - # Returns true if `mod` is an ancestor of `other`. Returns - # nil if there's no relationship between the two. - # (Think of the relationship in terms of the class definition: - # "class A < B" implies "B > A".) - # - def >(other) - if self.equal?(other) - false - else - self >= other - end - end - - ## - # call-seq: - # mod >= other -> true, false, or nil - # - # Returns true if `mod` is an ancestor of `other`, or the - # two modules are the same. Returns - # nil if there's no relationship between the two. - # (Think of the relationship in terms of the class definition: - # "class A < B" implies "B > A".) - # - def >=(other) - raise TypeError, 'compared with non class/module' unless other.is_a?(Module) - return other < self - end - - ## - # call-seq: - # module <=> other_module -> -1, 0, +1, or nil - # - # Comparison---Returns -1, 0, +1 or nil depending on whether `module` - # includes `other_module`, they are the same, or if `module` is included by - # `other_module`. - # - # Returns `nil` if `module` has no relationship with `other_module`, if - # `other_module` is not a module, or if the two values are incomparable. - # - def <=>(other) - return 0 if self.equal?(other) - return nil unless other.is_a?(Module) - cmp = self < other - return -1 if cmp - return 1 unless cmp.nil? - return nil - end -end diff --git a/mrbgems/mruby-class-ext/src/class.c b/mrbgems/mruby-class-ext/src/class.c index 46149b440..5a14a0c0f 100644 --- a/mrbgems/mruby-class-ext/src/class.c +++ b/mrbgems/mruby-class-ext/src/class.c @@ -121,16 +121,206 @@ class_attached_object(mrb_state *mrb, mrb_value self) return mrb_obj_iv_get(mrb, (struct RObject*)c, MRB_SYM(__attached__)); } +/* + * Check if a class/module is an ancestor of another. + * + * This function traverses the inheritance chain of `klass` upwards to determine + * if `super` appears anywhere in the hierarchy. It handles both regular classes/modules + * and included classes (ICLASS) which represent modules included in the inheritance chain. + * + * Args: + * klass: The class/module to check (potential descendant) + * super: The class/module to search for (potential ancestor) + * + * Returns: + * true: if `super` is found in `klass`'s inheritance chain + * false: if `super` is not an ancestor of `klass` + */ +static mrb_bool +is_ancestor(struct RClass *klass, struct RClass *super) +{ + struct RClass *c = klass; + while (c) { + if (c->tt == MRB_TT_ICLASS) { + if (c->c == super) return TRUE; + } + else { + if (c == super) return TRUE; + } + c = c->super; + } + return FALSE; +} + +/* + * Compare hierarchy relationship between two modules/classes. + * + * This function determines the ancestor relationship between `self` and `other`. + * It checks if one is an ancestor of the other by traversing the inheritance chain. + * + * Args: + * mrb: The mruby state + * self: The first module/class to compare + * other: The second module/class to compare + * + * Returns: + * true: if `self` is an ancestor of `other` (self > other) + * false: if `other` is an ancestor of `self` (self < other) + * nil: if there's no inheritance relationship between them + * + * Raises: + * TypeError: if `other` is not a class, module, or included class + */ +static mrb_value +mod_compare_hierarchy(mrb_state *mrb, mrb_value self, mrb_value other) +{ + if (!mrb_class_p(other) && !mrb_module_p(other) && !mrb_iclass_p(other)) { + mrb_raise(mrb, E_TYPE_ERROR, "compared with non class/module"); + } + + struct RClass *self_c = mrb_class_ptr(self); + struct RClass *other_c = mrb_class_ptr(other); + + if (is_ancestor(self_c, other_c)) { + return mrb_true_value(); + } + if (is_ancestor(other_c, self_c)) { + return mrb_false_value(); + } + return mrb_nil_value(); +} + +/* + * call-seq: + * mod <= other -> true, false, or nil + * + * Returns true if mod is a subclass of other or is the same as other. + * Returns nil if there's no relationship between the two. + */ +static mrb_value +mrb_mod_le(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + mrb_get_args(mrb, "o", &other); + return mod_compare_hierarchy(mrb, self, other); +} + +/* + * call-seq: + * mod < other -> true, false, or nil + * + * Returns true if mod is a subclass of other. Returns false if mod + * is the same as other. Returns nil if there's no relationship between the two. + */ +static mrb_value +mrb_mod_lt(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + mrb_get_args(mrb, "o", &other); + if (mrb_obj_equal(mrb, self, other)) { + return mrb_false_value(); + } + return mod_compare_hierarchy(mrb, self, other); +} + +/* + * call-seq: + * mod >= other -> true, false, or nil + * + * Returns true if mod is an ancestor of other, or the two modules are the same. + * Returns nil if there's no relationship between the two. + */ +static mrb_value +mrb_mod_ge(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + mrb_get_args(mrb, "o", &other); + return mod_compare_hierarchy(mrb, other, self); +} + +/* + * call-seq: + * mod > other -> true, false, or nil + * + * Returns true if mod is an ancestor of other. Returns false if mod + * is the same as other. Returns nil if there's no relationship between the two. + */ +static mrb_value +mrb_mod_gt(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + mrb_get_args(mrb, "o", &other); + if (mrb_obj_equal(mrb, self, other)) { + return mrb_false_value(); + } + return mod_compare_hierarchy(mrb, other, self); +} + +/* + * call-seq: + * module <=> other_module -> -1, 0, +1, or nil + * + * Comparison - Returns -1, 0, +1 or nil depending on whether module + * includes other_module, they are the same, or if module is included by + * other_module. + * + * Returns nil if module has no relationship with other_module, if + * other_module is not a module, or if the two values are incomparable. + */ +static mrb_value +mrb_mod_cmp(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + mrb_get_args(mrb, "o", &other); + + if (mrb_obj_equal(mrb, self, other)) { + return mrb_fixnum_value(0); + } + if (!mrb_class_p(other) && !mrb_module_p(other) && !mrb_iclass_p(other)) { + return mrb_nil_value(); + } + + mrb_value cmp = mod_compare_hierarchy(mrb, self, other); + + if (mrb_true_p(cmp)) { + return mrb_fixnum_value(-1); + } + else if (mrb_false_p(cmp)) { + return mrb_fixnum_value(1); + } + else { + return mrb_nil_value(); + } +} + +/* + * Initialize the mruby-class-ext gem. + * + * This function registers all the extension methods to the Module and Class classes. + * It's called automatically when the gem is loaded. + * + * Args: + * mrb: The mruby state + */ void mrb_mruby_class_ext_gem_init(mrb_state *mrb) { struct RClass *mod = mrb->module_class; + /* Module methods */ mrb_define_method_id(mrb, mod, MRB_SYM(name), mod_name, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mod, MRB_SYM_Q(singleton_class), mod_singleton_class_p, MRB_ARGS_NONE()); mrb_define_method_id(mrb, mod, MRB_SYM(module_exec), mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK()); mrb_define_method_id(mrb, mod, MRB_SYM(class_exec), mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK()); + /* Module comparison operators */ + mrb_define_method_id(mrb, mod, MRB_OPSYM(lt), mrb_mod_lt, MRB_ARGS_REQ(1)); + mrb_define_method_id(mrb, mod, MRB_OPSYM(le), mrb_mod_le, MRB_ARGS_REQ(1)); + mrb_define_method_id(mrb, mod, MRB_OPSYM(gt), mrb_mod_gt, MRB_ARGS_REQ(1)); + mrb_define_method_id(mrb, mod, MRB_OPSYM(ge), mrb_mod_ge, MRB_ARGS_REQ(1)); + mrb_define_method_id(mrb, mod, MRB_OPSYM(cmp), mrb_mod_cmp, MRB_ARGS_REQ(1)); + + /* Class-specific methods */ struct RClass *cls = mrb->class_class; mrb_define_method_id(mrb, cls, MRB_SYM(subclasses), class_subclasses, MRB_ARGS_NONE()); mrb_define_method_id(mrb, cls, MRB_SYM(attached_object), class_attached_object, MRB_ARGS_NONE());