Skip to content

Commit

Permalink
Support Enumerable#inject, Range#reject.
Browse files Browse the repository at this point in the history
  • Loading branch information
sisshiki1969 committed Jan 2, 2025
1 parent 30cad7a commit 5c6c084
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 11 deletions.
17 changes: 10 additions & 7 deletions monoruby/src/builtins/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub(super) fn init(globals: &mut Globals) {
globals.define_builtin_func(OBJECT_CLASS, "!=", ne, 1);
globals.define_builtin_func(OBJECT_CLASS, "class", class, 0);
globals.define_builtin_func(OBJECT_CLASS, "dup", dup, 0);
globals.define_builtin_func(OBJECT_CLASS, "enum_for", to_enum, 0);
globals.define_builtin_funcs_with(OBJECT_CLASS, "enum_for", &["to_enum"], to_enum, 0, 1, false);
globals.define_builtin_func(OBJECT_CLASS, "equal?", equal_, 1);
globals.define_builtin_func_rest(OBJECT_CLASS, "extend", extend);
globals.define_builtin_func(OBJECT_CLASS, "kind_of?", is_a, 1);
Expand All @@ -24,7 +24,6 @@ pub(super) fn init(globals: &mut Globals) {
);
globals.define_builtin_func_with(OBJECT_CLASS, "respond_to?", respond_to, 1, 2, false);
globals.define_builtin_func(OBJECT_CLASS, "singleton_class", singleton_class, 0);
globals.define_builtin_func(OBJECT_CLASS, "to_enum", to_enum, 0);
globals.define_builtin_func(OBJECT_CLASS, "to_s", to_s, 0);
globals.define_builtin_func(OBJECT_CLASS, "inspect", inspect, 0);
globals.define_builtin_func(OBJECT_CLASS, "instance_of?", instance_of, 1);
Expand Down Expand Up @@ -211,15 +210,19 @@ fn is_a(_vm: &mut Executor, globals: &mut Globals, lfp: Lfp) -> Result<Value> {
///
/// ### Object#enum_for
///
/// - to_enum([NOT SUPPORTED] method = :each, *args) -> Enumerator
/// - enum_for([NOT SUPPORTED] method = :each, *args) -> Enumerator
/// - to_enum([NOT SUPPORTED] method = :each, *args) {|*args| ... } -> Enumerator
/// - enum_for([NOT SUPPORTED] method = :each, *args) {|*args| ... } -> Enumerator
/// - to_enum(method = :each, [NOT SUPPORTED] *args) -> Enumerator
/// - enum_for(method = :each, [NOT SUPPORTED] *args) -> Enumerator
/// - to_enum(method = :each, [NOT SUPPORTED] *args) {|*args| ... } -> Enumerator
/// - enum_for(method = :each, [NOT SUPPORTED] *args) {|*args| ... } -> Enumerator
///
/// [https://docs.ruby-lang.org/ja/latest/method/Object/i/enum_for.html]
#[monoruby_builtin]
fn to_enum(vm: &mut Executor, _globals: &mut Globals, lfp: Lfp) -> Result<Value> {
vm.generate_enumerator(IdentId::EACH, lfp.self_val(), vec![])
let method = match lfp.try_arg(0) {
Some(m) => m.expect_symbol_or_string()?,
None => IdentId::EACH,
};
vm.generate_enumerator(method, lfp.self_val(), vec![])
}

///
Expand Down
33 changes: 33 additions & 0 deletions monoruby/src/builtins/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub(super) fn init(globals: &mut Globals) {
globals.define_builtin_func(RANGE_CLASS, "end", end, 0);
globals.define_builtin_func(RANGE_CLASS, "exclude_end?", exclude_end, 0);
globals.define_builtin_func(RANGE_CLASS, "each", each, 0);
//globals.define_builtin_func(RANGE_CLASS, "reject", reject, 0);
globals.define_builtin_func(RANGE_CLASS, "all?", all_, 0);
globals.define_builtin_func(RANGE_CLASS, "collect", map, 0);
globals.define_builtin_func(RANGE_CLASS, "map", map, 0);
Expand Down Expand Up @@ -89,6 +90,38 @@ fn each(vm: &mut Executor, globals: &mut Globals, lfp: Lfp) -> Result<Value> {
}
}

/*
///
/// ### Enumerable#reject
///
/// - reject {|item| ... } -> [object]
/// - reject -> Enumerator
///
/// [https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/reject.html]
#[monoruby_builtin]
fn reject(vm: &mut Executor, globals: &mut Globals, lfp: Lfp) -> Result<Value> {
let self_ = lfp.self_val();
let range = self_.as_range();
if let Some(bh) = lfp.block() {
let data = vm.get_block_data(globals, bh)?;
let mut res = vec![];
let mut elem = range.start;
let end = range.end;
while !vm
.invoke_method_inner(globals, IdentId::_EQ, elem, &[end], None)?
.as_bool()
{
if !vm.invoke_block(globals, &data, &[elem])?.as_bool() {
res.push(elem);
};
elem = vm.invoke_method_inner(globals, IdentId::get_id("succ"), elem, &[], None)?;
}
Ok(Value::array_from_vec(res))
} else {
vm.generate_enumerator(IdentId::get_id("reject"), lfp.self_val(), vec![])
}
}*/

///
/// ### Range#all
///
Expand Down
13 changes: 9 additions & 4 deletions monoruby/src/compiler/jitgen/slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ impl SlotContext {
Guarded::Fixnum => true,
Guarded::Float => true,
Guarded::Value => false,
Guarded::Class(_) => false,
Guarded::Class(class) => !class.is_falsy(),
},
}
}
Expand All @@ -169,7 +169,7 @@ impl SlotContext {
Guarded::Fixnum => false,
Guarded::Float => false,
Guarded::Value => false,
Guarded::Class(_) => false,
Guarded::Class(class) => class.is_falsy(),
},
}
}
Expand All @@ -179,7 +179,12 @@ impl SlotContext {
LinkMode::Xmm(_) => false,
LinkMode::Both(_) => false,
LinkMode::ConcreteValue(v) => v.is_nil(),
_ => false,
_ => match self.guarded(slot) {
Guarded::Fixnum => false,
Guarded::Float => false,
Guarded::Value => false,
Guarded::Class(class) => class.is_nil(),
},
}
}

Expand All @@ -192,7 +197,7 @@ impl SlotContext {
Guarded::Fixnum => true,
Guarded::Float => true,
Guarded::Value => false,
Guarded::Class(_) => true,
Guarded::Class(class) => !class.is_nil(),
},
}
}
Expand Down
8 changes: 8 additions & 0 deletions monoruby/src/globals/store/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ impl ClassId {
)
}

pub(crate) fn is_nil(&self) -> bool {
*self == NIL_CLASS
}

pub(crate) fn is_falsy(&self) -> bool {
*self == NIL_CLASS || *self == FALSE_CLASS
}

/// Get class name(IdentId) of *ClassId*.
pub(crate) fn get_name_id(self, store: &Store) -> IdentId {
let class = store.classes.get_module(self);
Expand Down
44 changes: 44 additions & 0 deletions monoruby/startup/startup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,52 @@ class Marshal
end

module Enumerable
def inject(init = nil)
acc = init
if init.nil?
flag = true
self.each do |x|
if flag
acc = x
flag = false
else
acc = yield(acc, x)
end
end
else
self.each do |x|
acc = yield(acc, x)
end
end
acc
end
alias reduce inject
end

class Range
include Enumerable

alias first begin

def reject
if block_given?
elem = self.begin
end_ = self.end
res = []
while elem != end_
if !yield(elem)
res << elem
end
elem = elem.succ
end
res
else
self.to_enum(:reject)
end
end
end


module Comparable
def ==(other)
case res = self <=> other
Expand Down
9 changes: 9 additions & 0 deletions monoruby/tests/enumerable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use monoruby::tests::*;

#[test]
fn inject() {
run_test(r##"[2, 3, 4, 5].inject {|result, item| result + item }"##);
run_test(r##"(1..5).inject {|result, item| result + item }"##);
run_test(r##"[2, 3, 4, 5].inject(0) {|result, item| result + item ** 2 }"##);
run_test(r##"(1..5).inject(0) {|result, item| result + item ** 2 }"##);
}

0 comments on commit 5c6c084

Please sign in to comment.