summaryrefslogtreecommitdiffhomepage
path: root/mrbgems
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2019-07-17 10:35:41 +0900
committerGitHub <[email protected]>2019-07-17 10:35:41 +0900
commitd605b72c1d6fa4564a0a5e88535504b6850463b5 (patch)
tree774fc0de56002abb3bb2b1c3387ff08f91876d17 /mrbgems
parent2af92d0ebcbeca6d3d85a27c8193273080a63090 (diff)
parent9af3b7c6258de327218dd04e69d76ae68caf17b1 (diff)
downloadmruby-d605b72c1d6fa4564a0a5e88535504b6850463b5.tar.gz
mruby-d605b72c1d6fa4564a0a5e88535504b6850463b5.zip
Merge branch 'master' into i110/inspect-recursion
Diffstat (limited to 'mrbgems')
-rw-r--r--mrbgems/default.gembox6
-rw-r--r--mrbgems/mruby-array-ext/mrblib/array.rb107
-rw-r--r--mrbgems/mruby-array-ext/src/array.c51
-rw-r--r--mrbgems/mruby-array-ext/test/array.rb39
-rw-r--r--mrbgems/mruby-bin-config/mrbgem.rake23
-rw-r--r--mrbgems/mruby-bin-config/mruby-config (renamed from mrbgems/mruby-bin-mruby-config/mruby-config)0
-rw-r--r--mrbgems/mruby-bin-config/mruby-config.bat (renamed from mrbgems/mruby-bin-mruby-config/mruby-config.bat)0
-rw-r--r--mrbgems/mruby-bin-debugger/bintest/print.rb2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c16
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c7
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c6
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c8
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c25
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c13
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h4
-rw-r--r--mrbgems/mruby-bin-mirb/bintest/mirb.rb4
-rw-r--r--mrbgems/mruby-bin-mirb/tools/mirb/mirb.c50
-rw-r--r--mrbgems/mruby-bin-mrbc/mrbgem.rake2
-rw-r--r--mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c9
-rw-r--r--mrbgems/mruby-bin-mruby-config/mrbgem.rake30
-rw-r--r--mrbgems/mruby-bin-mruby/bintest/mruby.rb67
-rw-r--r--mrbgems/mruby-bin-mruby/mrbgem.rake1
-rw-r--r--mrbgems/mruby-bin-mruby/tools/mruby/mruby.c44
-rw-r--r--mrbgems/mruby-bin-strip/bintest/mruby-strip.rb6
-rw-r--r--mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c18
-rw-r--r--mrbgems/mruby-class-ext/mrblib/module.rb89
-rw-r--r--mrbgems/mruby-class-ext/src/class.c3
-rw-r--r--mrbgems/mruby-class-ext/test/module.rb56
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c1657
-rw-r--r--mrbgems/mruby-compiler/core/node.h24
-rw-r--r--mrbgems/mruby-compiler/core/parse.y1103
-rw-r--r--mrbgems/mruby-compiler/mrbgem.rake6
-rw-r--r--mrbgems/mruby-complex/mrbgem.rake10
-rw-r--r--mrbgems/mruby-complex/mrblib/complex.rb123
-rw-r--r--mrbgems/mruby-complex/src/complex.c150
-rw-r--r--mrbgems/mruby-complex/test/complex.rb136
-rw-r--r--mrbgems/mruby-enum-chain/mrbgem.rake6
-rw-r--r--mrbgems/mruby-enum-chain/mrblib/chain.rb60
-rw-r--r--mrbgems/mruby-enum-chain/test/enum_chain.rb76
-rw-r--r--mrbgems/mruby-enum-ext/mrblib/enum.rb53
-rw-r--r--mrbgems/mruby-enum-ext/test/enum.rb6
-rw-r--r--mrbgems/mruby-enum-lazy/mrblib/lazy.rb2
-rw-r--r--mrbgems/mruby-enumerator/mrblib/enumerator.rb46
-rw-r--r--mrbgems/mruby-enumerator/test/enumerator.rb29
-rw-r--r--mrbgems/mruby-eval/src/eval.c134
-rw-r--r--mrbgems/mruby-eval/test/eval.rb37
-rw-r--r--mrbgems/mruby-fiber/src/fiber.c25
-rw-r--r--mrbgems/mruby-fiber/test/fiber.rb2
-rw-r--r--mrbgems/mruby-hash-ext/mrblib/hash.rb89
-rw-r--r--mrbgems/mruby-hash-ext/src/hash-ext.c51
-rw-r--r--mrbgems/mruby-hash-ext/test/hash.rb6
-rw-r--r--mrbgems/mruby-inline-struct/test/inline.c22
-rw-r--r--mrbgems/mruby-inline-struct/test/inline.rb67
-rw-r--r--mrbgems/mruby-io/mrbgem.rake3
-rw-r--r--mrbgems/mruby-io/src/file.c40
-rw-r--r--mrbgems/mruby-io/src/file_test.c21
-rw-r--r--mrbgems/mruby-io/src/io.c217
-rw-r--r--mrbgems/mruby-io/test/file.rb45
-rw-r--r--mrbgems/mruby-io/test/file_test.rb21
-rw-r--r--mrbgems/mruby-io/test/gc_filedes.sh4
-rw-r--r--mrbgems/mruby-io/test/io.rb130
-rw-r--r--mrbgems/mruby-io/test/mruby_io_test.c6
-rw-r--r--mrbgems/mruby-kernel-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-kernel-ext/mrblib/kernel.rb15
-rw-r--r--mrbgems/mruby-kernel-ext/src/kernel.c61
-rw-r--r--mrbgems/mruby-kernel-ext/test/kernel.rb8
-rw-r--r--mrbgems/mruby-math/src/math.c21
-rw-r--r--mrbgems/mruby-math/test/math.rb82
-rw-r--r--mrbgems/mruby-metaprog/mrbgem.rake5
-rw-r--r--mrbgems/mruby-metaprog/src/metaprog.c724
-rw-r--r--mrbgems/mruby-metaprog/test/metaprog.rb400
-rw-r--r--mrbgems/mruby-method/mrblib/kernel.rb5
-rw-r--r--mrbgems/mruby-method/mrblib/method.rb8
-rw-r--r--mrbgems/mruby-method/src/method.c31
-rw-r--r--mrbgems/mruby-method/test/method.rb38
-rw-r--r--mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb6
-rw-r--r--mrbgems/mruby-numeric-ext/src/numeric_ext.c17
-rw-r--r--mrbgems/mruby-numeric-ext/test/numeric.rb3
-rw-r--r--mrbgems/mruby-object-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-object-ext/mrblib/object.rb16
-rw-r--r--mrbgems/mruby-object-ext/src/object.c20
-rw-r--r--mrbgems/mruby-object-ext/test/nil.rb3
-rw-r--r--mrbgems/mruby-objectspace/src/mruby_objectspace.c2
-rw-r--r--mrbgems/mruby-objectspace/test/objectspace.rb4
-rw-r--r--mrbgems/mruby-pack/src/pack.c214
-rw-r--r--mrbgems/mruby-pack/test/pack.rb92
-rw-r--r--mrbgems/mruby-print/mrblib/print.rb5
-rw-r--r--mrbgems/mruby-print/src/print.c1
-rw-r--r--mrbgems/mruby-proc-ext/mrblib/proc.rb10
-rw-r--r--mrbgems/mruby-proc-ext/src/proc.c57
-rw-r--r--mrbgems/mruby-proc-ext/test/proc.rb44
-rw-r--r--mrbgems/mruby-random/src/random.c18
-rw-r--r--mrbgems/mruby-random/test/random.rb102
-rw-r--r--mrbgems/mruby-range-ext/mrblib/range.rb44
-rw-r--r--mrbgems/mruby-range-ext/src/range.c31
-rw-r--r--mrbgems/mruby-range-ext/test/range.rb102
-rw-r--r--mrbgems/mruby-rational/mrbgem.rake5
-rw-r--r--mrbgems/mruby-rational/mrblib/rational.rb116
-rw-r--r--mrbgems/mruby-rational/src/rational.c206
-rw-r--r--mrbgems/mruby-rational/test/rational.rb308
-rw-r--r--mrbgems/mruby-sleep/.gitignore4
-rw-r--r--mrbgems/mruby-sleep/.travis.yml29
-rw-r--r--mrbgems/mruby-sleep/.travis_build_config.rb6
-rw-r--r--mrbgems/mruby-sleep/README.md27
-rw-r--r--mrbgems/mruby-sleep/Rakefile29
-rw-r--r--mrbgems/mruby-sleep/example/sleep.rb3
-rw-r--r--mrbgems/mruby-sleep/mrbgem.rake5
-rw-r--r--mrbgems/mruby-sleep/src/mrb_sleep.c135
-rw-r--r--mrbgems/mruby-sleep/test/sleep_test.rb29
-rw-r--r--mrbgems/mruby-socket/mrbgem.rake1
-rw-r--r--mrbgems/mruby-socket/src/socket.c62
-rw-r--r--mrbgems/mruby-socket/test/addrinfo.rb10
-rw-r--r--mrbgems/mruby-socket/test/socket.rb4
-rw-r--r--mrbgems/mruby-socket/test/sockettest.c1
-rw-r--r--mrbgems/mruby-sprintf/src/sprintf.c27
-rw-r--r--mrbgems/mruby-sprintf/test/sprintf.rb9
-rw-r--r--mrbgems/mruby-string-ext/mrblib/string.rb33
-rw-r--r--mrbgems/mruby-string-ext/src/string.c663
-rw-r--r--mrbgems/mruby-string-ext/test/range.rb26
-rw-r--r--mrbgems/mruby-string-ext/test/string.rb226
-rw-r--r--mrbgems/mruby-struct/mrblib/struct.rb31
-rw-r--r--mrbgems/mruby-struct/src/struct.c105
-rw-r--r--mrbgems/mruby-struct/test/struct.rb24
-rw-r--r--mrbgems/mruby-symbol-ext/mrblib/symbol.rb6
-rw-r--r--mrbgems/mruby-symbol-ext/src/symbol.c19
-rw-r--r--mrbgems/mruby-symbol-ext/test/symbol.rb34
-rw-r--r--mrbgems/mruby-test/driver.c200
-rw-r--r--mrbgems/mruby-test/init_mrbtest.c39
-rw-r--r--mrbgems/mruby-test/mrbgem.rake44
-rw-r--r--mrbgems/mruby-time/include/mruby/time.h25
-rw-r--r--mrbgems/mruby-time/src/time.c94
-rw-r--r--mrbgems/mruby-time/test/time.rb126
-rw-r--r--mrbgems/mruby-toplevel-ext/test/toplevel.rb4
137 files changed, 7226 insertions, 2783 deletions
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox
index 7ddbb16d1..9859c7d52 100644
--- a/mrbgems/default.gembox
+++ b/mrbgems/default.gembox
@@ -1,4 +1,7 @@
MRuby::GemBox.new do |conf|
+ # Meta-programming features
+ conf.gem :core => "mruby-metaprog"
+
# Use standard IO/File class
conf.gem :core => "mruby-io"
@@ -83,6 +86,9 @@ MRuby::GemBox.new do |conf|
# Use class/module extension
conf.gem :core => "mruby-class-ext"
+ # Use Method/UnboundMethod class
+ conf.gem :core => "mruby-method"
+
# Use mruby-compiler to build other mrbgems
conf.gem :core => "mruby-compiler"
end
diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb
index c0995bb99..59b6087d2 100644
--- a/mrbgems/mruby-array-ext/mrblib/array.rb
+++ b/mrbgems/mruby-array-ext/mrblib/array.rb
@@ -1,31 +1,6 @@
class Array
##
# call-seq:
- # Array.try_convert(obj) -> array or nil
- #
- # Tries to convert +obj+ into an array, using +to_ary+ method.
- # converted array or +nil+ if +obj+ cannot be converted for any reason.
- # This method can be used to check if an argument is an array.
- #
- # Array.try_convert([1]) #=> [1]
- # Array.try_convert("1") #=> nil
- #
- # if tmp = Array.try_convert(arg)
- # # the argument is an array
- # elsif tmp = String.try_convert(arg)
- # # the argument is a string
- # end
- #
- def self.try_convert(obj)
- if obj.respond_to?(:to_ary)
- obj.to_ary
- else
- nil
- end
- end
-
- ##
- # call-seq:
# ary.uniq! -> ary or nil
# ary.uniq! { |item| ... } -> ary or nil
#
@@ -41,23 +16,19 @@ class Array
# c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
#
def uniq!(&block)
- ary = self.dup
- result = []
+ hash = {}
if block
- hash = {}
- while ary.size > 0
- val = ary.shift
+ self.each do |val|
key = block.call(val)
- hash[key] = val unless hash.has_key?(key)
- end
- hash.each_value do |value|
- result << value
+ hash[key] = val unless hash.key?(key)
end
+ result = hash.values
else
- while ary.size > 0
- result << ary.shift
- ary.delete(result.last)
+ hash = {}
+ self.each do |val|
+ hash[val] = val
end
+ result = hash.keys
end
if result.size == self.size
nil
@@ -136,6 +107,25 @@ class Array
##
# call-seq:
+ # ary.union(other_ary,...) -> new_ary
+ #
+ # Set Union---Returns a new array by joining this array with
+ # <i>other_ary</i>, removing duplicates.
+ #
+ # ["a", "b", "c"].union(["c", "d", "a"], ["a", "c", "e"])
+ # #=> ["a", "b", "c", "d", "e"]
+ #
+ def union(*args)
+ ary = self.dup
+ args.each do |x|
+ ary.concat(x)
+ ary.uniq!
+ end
+ ary
+ end
+
+ ##
+ # call-seq:
# ary & other_ary -> new_ary
#
# Set Intersection---Returns a new array
@@ -276,7 +266,6 @@ class Array
self
end
- NONE=Object.new
##
# call-seq:
# ary.fetch(index) -> obj
@@ -301,7 +290,7 @@ class Array
# #=> "100 is out of bounds"
#
- def fetch(n=nil, ifnone=NONE, &block)
+ def fetch(n, ifnone=NONE, &block)
warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
idx = n
@@ -783,16 +772,6 @@ class Array
end
##
- # call-seq:
- # ary.to_ary -> ary
- #
- # Returns +self+.
- #
- def to_ary
- self
- end
-
- ##
# call-seq:
# ary.dig(idx, ...) -> object
#
@@ -911,7 +890,7 @@ class Array
#
# Assumes that self is an array of arrays and transposes the rows and columns.
#
- # If the length of the subarrays don’t match, an IndexError is raised.
+ # If the length of the subarrays don't match, an IndexError is raised.
#
# Examples:
#
@@ -932,4 +911,32 @@ class Array
self.map { |row| row[column_index] }
end
end
+
+ ##
+ # call-seq:
+ # ary.to_h -> Hash
+ # ary.to_h{|item| ... } -> Hash
+ #
+ # Returns the result of interpreting <i>aray</i> as an array of
+ # <tt>[key, value]</tt> pairs. If a block is given, it should
+ # return <tt>[key, value]</tt> pairs to construct a hash.
+ #
+ # [[:foo, :bar], [1, 2]].to_h
+ # # => {:foo => :bar, 1 => 2}
+ # [1, 2].to_h{|x| [x, x*2]}
+ # # => {1 => 2, 2 => 4}
+ #
+ def to_h(&blk)
+ h = {}
+ self.each do |v|
+ v = blk.call(v) if blk
+ raise TypeError, "wrong element type #{v.class}" unless Array === v
+ raise ArgumentError, "wrong array length (expected 2, was #{v.length})" unless v.length == 2
+ h[v[0]] = v[1]
+ end
+ h
+ end
+
+ alias append push
+ alias prepend unshift
end
diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c
index 169f968f9..cb4798d49 100644
--- a/mrbgems/mruby-array-ext/src/array.c
+++ b/mrbgems/mruby-array-ext/src/array.c
@@ -106,49 +106,6 @@ mrb_ary_values_at(mrb_state *mrb, mrb_value self)
return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref);
}
-/*
- * call-seq:
- * ary.to_h -> Hash
- *
- * Returns the result of interpreting <i>aray</i> as an array of
- * <tt>[key, value]</tt> paris.
- *
- * [[:foo, :bar], [1, 2]].to_h
- * # => {:foo => :bar, 1 => 2}
- *
- */
-
-static mrb_value
-mrb_ary_to_h(mrb_state *mrb, mrb_value ary)
-{
- mrb_int i;
- mrb_value v, hash;
-
- hash = mrb_hash_new_capa(mrb, 0);
-
- for (i = 0; i < RARRAY_LEN(ary); ++i) {
- mrb_value elt = RARRAY_PTR(ary)[i];
- v = mrb_check_array_type(mrb, elt);
-
- if (mrb_nil_p(v)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)",
- mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, elt)),
- mrb_fixnum_value(i)
- );
- }
-
- if (RARRAY_LEN(v) != 2) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong array length at %S (expected 2, was %S)",
- mrb_fixnum_value(i),
- mrb_fixnum_value(RARRAY_LEN(v))
- );
- }
-
- mrb_hash_set(mrb, hash, RARRAY_PTR(v)[0], RARRAY_PTR(v)[1]);
- }
-
- return hash;
-}
/*
* call-seq:
@@ -175,7 +132,7 @@ static mrb_value
mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_int i, j, k, len, alen = ARY_LEN(a);
+ mrb_int i, j, k, len, alen;
mrb_value val;
mrb_value *ptr;
mrb_value ary;
@@ -188,7 +145,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
mrb_get_args(mrb, "o|i", &index, &len);
switch (mrb_type(index)) {
case MRB_TT_RANGE:
- if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
+ if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) {
goto delete_pos_len;
}
else {
@@ -205,6 +162,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
mrb_get_args(mrb, "ii", &i, &len);
delete_pos_len:
+ alen = ARY_LEN(a);
if (i < 0) i += alen;
if (i < 0 || alen < i) return mrb_nil_value();
if (len < 0) return mrb_nil_value();
@@ -236,8 +194,7 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1));
mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1));
mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY());
- mrb_define_method(mrb, a, "to_h", mrb_ary_to_h, MRB_ARGS_REQ(0));
- mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ANY());
+ mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ARG(1,1));
}
void
diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb
index 7d810acc2..82f09297a 100644
--- a/mrbgems/mruby-array-ext/test/array.rb
+++ b/mrbgems/mruby-array-ext/test/array.rb
@@ -1,13 +1,6 @@
##
# Array(Ext) Test
-assert("Array.try_convert") do
- assert_nil Array.try_convert(0)
- assert_nil Array.try_convert(nil)
- assert_equal [], Array.try_convert([])
- assert_equal [1,2,3], Array.try_convert([1,2,3])
-end
-
assert("Array#assoc") do
s1 = [ "colors", "red", "blue", "green" ]
s2 = [ "letters", "a", "b", "c" ]
@@ -75,6 +68,14 @@ assert("Array#|") do
assert_equal [1, 2, 3, 1], a
end
+assert("Array#union") do
+ a = [1, 2, 3, 1]
+ b = [1, 4]
+ c = [1, 5]
+
+ assert_equal [1, 2, 3, 4, 5], a.union(b,c)
+end
+
assert("Array#&") do
a = [1, 2, 3, 1]
b = [1, 4]
@@ -267,9 +268,9 @@ assert("Array#bsearch") do
end
end
-assert("Array#bsearch_index") do
- # tested through Array#bsearch
-end
+# tested through Array#bsearch
+#assert("Array#bsearch_index") do
+#end
assert("Array#delete_if") do
a = [1, 2, 3, 4, 5]
@@ -330,27 +331,11 @@ assert('Array#to_h') do
assert_raise(ArgumentError) { [[1]].to_h }
end
-assert('Array#to_h (Modified)') do
- class A
- def to_ary
- $a.clear
- nil
- end
- end
- $a = [A.new]
- assert_raise(TypeError) { $a.to_h }
-end
-
assert("Array#index (block)") do
assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
end
-assert("Array#to_ary") do
- assert_equal [], [].to_ary
- assert_equal [1,2,3], [1,2,3].to_ary
-end
-
assert("Array#dig") do
h = [[[1]], 0]
assert_equal(1, h.dig(0, 0, 0))
@@ -418,5 +403,5 @@ assert('Array#transpose') do
assert_equal([[1], [2], [3]].transpose, [[1,2,3]])
assert_equal([[1,2], [3,4], [5,6]].transpose, [[1,3,5], [2,4,6]])
assert_raise(TypeError) { [1].transpose }
- assert_raise(IndexError) { [[1], [2,3,4]].transpose }
+ assert_raise(IndexError) { [[1], [2,3,4]].transpose }
end
diff --git a/mrbgems/mruby-bin-config/mrbgem.rake b/mrbgems/mruby-bin-config/mrbgem.rake
new file mode 100644
index 000000000..3a0a1b897
--- /dev/null
+++ b/mrbgems/mruby-bin-config/mrbgem.rake
@@ -0,0 +1,23 @@
+unless MRuby::Build.current.kind_of?(MRuby::CrossBuild)
+ MRuby::Gem::Specification.new('mruby-bin-config') do |spec|
+ name = 'mruby-config'
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = "#{name} command"
+
+ mruby_config = name + (ENV['OS'] == 'Windows_NT' ? '.bat' : '')
+ mruby_config_path = "#{build.build_dir}/bin/#{mruby_config}"
+ make_cfg = "#{build.build_dir}/lib/libmruby.flags.mak"
+ tmplt_path = "#{__dir__}/#{mruby_config}"
+ build.bins << mruby_config
+
+ file mruby_config_path => [make_cfg, tmplt_path] do |t|
+ config = Hash[File.readlines(make_cfg).map!(&:chomp).map! {|l|
+ l.gsub('\\"', '"').split(' = ', 2).map! {|s| s.sub(/^(?=.)/, 'echo ')}
+ }]
+ tmplt = File.read(tmplt_path)
+ File.write(t.name, tmplt.gsub(/(#{Regexp.union(*config.keys)})\b/, config))
+ FileUtils.chmod(0755, t.name)
+ end
+ end
+end
diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config b/mrbgems/mruby-bin-config/mruby-config
index 57346c03f..57346c03f 100644
--- a/mrbgems/mruby-bin-mruby-config/mruby-config
+++ b/mrbgems/mruby-bin-config/mruby-config
diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config.bat b/mrbgems/mruby-bin-config/mruby-config.bat
index a1f7bfdd1..a1f7bfdd1 100644
--- a/mrbgems/mruby-bin-mruby-config/mruby-config.bat
+++ b/mrbgems/mruby-bin-config/mruby-config.bat
diff --git a/mrbgems/mruby-bin-debugger/bintest/print.rb b/mrbgems/mruby-bin-debugger/bintest/print.rb
index 0d4aad011..6675392b8 100644
--- a/mrbgems/mruby-bin-debugger/bintest/print.rb
+++ b/mrbgems/mruby-bin-debugger/bintest/print.rb
@@ -317,7 +317,7 @@ TestConstNameSubClass.new.m()
bp = nil
SRC
- # todo: wait for 'break' to be implimented
+ # todo: wait for 'break' to be implemented
tc = []
9.times { tc << {:cmd=>"s"} }
tc << {:cmd=>"p CONST", :exp=>"super class"}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
index d3ccf08ae..513db4ded 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
@@ -84,7 +84,7 @@ free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp)
}
static uint16_t
-check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
+check_file_lineno(mrb_state *mrb, struct mrb_irep *irep, const char *file, uint16_t lineno)
{
mrb_irep_debug_info_file *info_file;
uint16_t result = 0;
@@ -93,8 +93,10 @@ check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
uint16_t i;
for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
+ const char *filename;
info_file = irep->debug_info->files[f_idx];
- if (!strcmp(info_file->filename, file)) {
+ filename = mrb_sym2name_len(mrb, info_file->filename_sym, NULL);
+ if (!strcmp(filename, file)) {
result = MRB_DEBUG_BP_FILE_OK;
fix_lineno = check_lineno(info_file, lineno);
@@ -103,7 +105,7 @@ check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
}
}
for (i=0; i < irep->rlen; ++i) {
- result |= check_file_lineno(irep->reps[i], file, lineno);
+ result |= check_file_lineno(mrb, irep->reps[i], file, lineno);
if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
return result;
}
@@ -185,7 +187,7 @@ mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *fil
}
/* file and lineno check (line type mrb_debug_line_ary only.) */
- result = check_file_lineno(dbg->root_irep, file, lineno);
+ result = check_file_lineno(mrb, dbg->root_irep, file, lineno);
if (result == 0) {
return MRB_DEBUG_BREAK_INVALID_FILE;
}
@@ -426,10 +428,10 @@ mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
}
static mrb_bool
-check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line)
+check_start_pc_for_line(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, uint16_t line)
{
if (pc > irep->iseq) {
- if (line == mrb_debug_get_line(irep, pc - irep->iseq - 1)) {
+ if (line == mrb_debug_get_line(mrb, irep, pc - irep->iseq - 1)) {
return FALSE;
}
}
@@ -447,7 +449,7 @@ mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const ch
return MRB_DEBUG_INVALID_ARGUMENT;
}
- if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
+ if (!check_start_pc_for_line(mrb, dbg->irep, dbg->pc, line)) {
return MRB_DEBUG_OK;
}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
index 21fe64127..66ddfa783 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
@@ -181,7 +181,7 @@ mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, cons
else srcname = filename;
search_path[0] = srcpath;
- search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->irep, 0));
+ search_path[1] = dirname(mrb, mrb_debug_get_filename(mrb, mrdb->dbg->irep, 0));
search_path[2] = ".";
for (i = 0; i < 3; i++) {
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
index c8700530f..f888d1430 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
@@ -31,7 +31,7 @@ mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size
}
mrb_value
-mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc)
+mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc, int direct_eval)
{
void (*tmp)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *);
mrb_value ruby_code;
@@ -48,6 +48,11 @@ mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t
v = mrb_obj_value(mrb->exc);
mrb->exc = 0;
}
+ else if (direct_eval) {
+ recv = dbg->regs[0];
+
+ v = mrb_funcall(mrb, recv, expr, 0);
+ }
else {
/*
* begin
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
index e256f6262..ab8c08869 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
@@ -8,6 +8,6 @@
#include <mruby.h>
#include "mrdb.h"
-mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*);
+mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*, int);
#endif /* APIPRINT_H_ */
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
index 8e5901754..bc9937e94 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
@@ -242,7 +242,7 @@ info_break_select(mrb_state *mrb, mrdb_state *mrdb)
}
mrb_debug_bptype
-parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
+parse_breakcommand(mrb_state *mrb, mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
{
mrb_debug_context *dbg = mrdb->dbg;
char *args;
@@ -274,7 +274,7 @@ parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **c
STRTOUL(l, body);
if (l <= 65535) {
*line = l;
- *file = (body == args)? mrb_debug_get_filename(dbg->irep, dbg->pc - dbg->irep->iseq): args;
+ *file = (body == args)? mrb_debug_get_filename(mrb, dbg->irep, dbg->pc - dbg->irep->iseq): args;
}
else {
puts(BREAK_ERR_MSG_RANGEOVER);
@@ -332,7 +332,7 @@ dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
char *method = NULL;
int32_t ret;
- type = parse_breakcommand(mrdb, &file, &line, &cname, &method);
+ type = parse_breakcommand(mrb, mrdb, &file, &line, &cname, &method);
switch (type) {
case MRB_DEBUG_BPTYPE_LINE:
ret = mrb_debug_set_break_line(mrb, dbg, file, line);
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
index 0a864567d..db28e4229 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
@@ -82,6 +82,12 @@ static help_msg help_msg_list[] = {
"Arguments are breakpoint numbers with spaces in between.\n"
},
{
+ "i[nfo]", "l[ocals]", "Print name of local variables",
+ "Usage: info locals\n"
+ "\n"
+ "Print name of local variables.\n"
+ },
+ {
"l[ist]", NULL, "List specified line",
"Usage: list\n"
" list first[,last]\n"
@@ -495,7 +501,7 @@ dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb)
if (mrdb->dbg->xm == DBG_QUIT) {
struct RClass *exc;
- exc = mrb_define_class(mrb, "DebuggerExit", mrb_class_get(mrb, "Exception"));
+ exc = mrb_define_class(mrb, "DebuggerExit", mrb->eException_class);
mrb_raise(mrb, exc, "Exit mrdb.");
}
return DBGST_PROMPT;
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
index cca636711..25f071589 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
@@ -36,7 +36,7 @@ dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb)
expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]);
}
- result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL);
+ result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL, 0);
/* $print_no = result */
s = mrb_str_cat_lit(mrb, result, "\0");
@@ -56,3 +56,26 @@ dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb)
{
return dbgcmd_print(mrb, mrdb);
}
+
+dbgcmd_state
+dbgcmd_info_local(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_value result;
+ mrb_value s;
+ int ai;
+
+ ai = mrb_gc_arena_save(mrb);
+
+ result = mrb_debug_eval(mrb, mrdb->dbg, "local_variables", 0, NULL, 1);
+
+ s = mrb_str_cat_lit(mrb, result, "\0");
+ printf("$%lu = %s\n", (unsigned long)mrdb->print_no++, RSTRING_PTR(s));
+
+ if (mrdb->print_no == 0) {
+ mrdb->print_no = 1;
+ }
+
+ mrb_gc_arena_restore(mrb, ai);
+
+ return DBGST_PROMPT;
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
index cb4c738fc..233c86cef 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
@@ -19,7 +19,7 @@ dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb)
if (dbg->xphase == DBG_PHASE_RUNNING){
struct RClass *exc;
puts("Start it from the beginning.");
- exc = mrb_define_class(mrb, "DebuggerRestart", mrb_class_get(mrb, "Exception"));
+ exc = mrb_define_class(mrb, "DebuggerRestart", mrb->eException_class);
mrb_raise(mrb, exc, "Restart mrdb.");
}
}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
index 0588dfca5..003406172 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
@@ -52,6 +52,7 @@ static const debug_command debug_command_list[] = {
{"eval", NULL, 2, 0, 0, DBGCMD_EVAL, dbgcmd_eval}, /* ev[al] */
{"help", NULL, 1, 0, 1, DBGCMD_HELP, dbgcmd_help}, /* h[elp] */
{"info", "breakpoints", 1, 1, 1, DBGCMD_INFO_BREAK, dbgcmd_info_break}, /* i[nfo] b[reakpoints] */
+ {"info", "locals", 1, 1, 0, DBGCMD_INFO_LOCAL, dbgcmd_info_local}, /* i[nfo] l[ocals] */
{"list", NULL, 1, 0, 1, DBGCMD_LIST, dbgcmd_list}, /* l[ist] */
{"print", NULL, 1, 0, 0, DBGCMD_PRINT, dbgcmd_print}, /* p[rint] */
{"quit", NULL, 1, 0, 0, DBGCMD_QUIT, dbgcmd_quit}, /* q[uit] */
@@ -510,6 +511,7 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
mrb_sym sym;
int32_t bpno;
mrb_bool isCfunc;
+ struct mrb_insn_data insn;
mrb_debug_context *dbg = mrb_debug_context_get(mrb);
@@ -517,11 +519,12 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
bpno = dbg->method_bpno;
dbg->method_bpno = 0;
- switch(GET_OPCODE(*pc)) {
+ insn = mrb_decode_insn(pc);
+ switch(insn.insn) {
case OP_SEND:
case OP_SENDB:
- c = mrb_class(mrb, regs[GETARG_A(*pc)]);
- sym = irep->syms[GETARG_B(*pc)];
+ c = mrb_class(mrb, regs[insn.a]);
+ sym = irep->syms[insn.b];
break;
case OP_SUPER:
c = mrb->c->ci->target_class->super;
@@ -566,8 +569,8 @@ mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *reg
dbg->xphase = DBG_PHASE_RUNNING;
}
- file = mrb_debug_get_filename(irep, pc - irep->iseq);
- line = mrb_debug_get_line(irep, pc - irep->iseq);
+ file = mrb_debug_get_filename(mrb, irep, pc - irep->iseq);
+ line = mrb_debug_get_line(mrb, irep, pc - irep->iseq);
switch (dbg->xm) {
case DBG_STEP:
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
index 5ac12c1cd..7b14a899f 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
@@ -23,6 +23,7 @@ typedef enum debug_command_id {
DBGCMD_STEP,
DBGCMD_BREAK,
DBGCMD_INFO_BREAK,
+ DBGCMD_INFO_LOCAL,
DBGCMD_WATCH,
DBGCMD_INFO_WATCH,
DBGCMD_ENABLE,
@@ -151,6 +152,7 @@ dbgcmd_state dbgcmd_next(mrb_state*, mrdb_state*);
/* cmdbreak.c */
dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_info_break(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_info_local(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_delete(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_enable(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_disable(mrb_state*, mrdb_state*);
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
index f17f9c57d..de2f90144 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
@@ -6,6 +6,10 @@
#ifndef MRDBCONF_H
#define MRDBCONF_H
+#ifndef MRB_ENABLE_DEBUG_HOOK
+# error Need 'MRB_ENABLE_DEBUG_HOOK' configuration in your 'build_config.rb'
+#endif
+
/* configuration options: */
/* maximum size for command buffer */
#define MAX_COMMAND_LINE 1024
diff --git a/mrbgems/mruby-bin-mirb/bintest/mirb.rb b/mrbgems/mruby-bin-mirb/bintest/mirb.rb
index 0515cb136..0058896f1 100644
--- a/mrbgems/mruby-bin-mirb/bintest/mirb.rb
+++ b/mrbgems/mruby-bin-mirb/bintest/mirb.rb
@@ -12,9 +12,9 @@ assert('regression for #1563') do
end
assert('mirb -d option') do
- o, _ = Open3.capture2('bin/mirb', :stdin_data => "p $DEBUG\n")
+ o, _ = Open3.capture2('bin/mirb', :stdin_data => "$DEBUG\n")
assert_true o.include?('=> false')
- o, _ = Open3.capture2('bin/mirb -d', :stdin_data => "p $DEBUG\n")
+ o, _ = Open3.capture2('bin/mirb -d', :stdin_data => "$DEBUG\n")
assert_true o.include?('=> true')
end
diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
index b494b13c9..17b2ca16c 100644
--- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
+++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
@@ -6,6 +6,15 @@
** immediately. It's a REPL...
*/
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/proc.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/throw.h>
+
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -49,14 +58,6 @@
#define SIGJMP_BUF jmp_buf
#endif
-#include <mruby.h>
-#include <mruby/array.h>
-#include <mruby/proc.h>
-#include <mruby/compile.h>
-#include <mruby/dump.h>
-#include <mruby/string.h>
-#include <mruby/variable.h>
-
#ifdef ENABLE_READLINE
static const char history_file_name[] = ".mirb_history";
@@ -111,7 +112,7 @@ p(mrb_state *mrb, mrb_value obj, int prompt)
if (!mrb_string_p(val)) {
val = mrb_obj_as_string(mrb, obj);
}
- msg = mrb_locale_from_utf8(RSTRING_PTR(val), RSTRING_LEN(val));
+ msg = mrb_locale_from_utf8(RSTRING_PTR(val), (int)RSTRING_LEN(val));
fwrite(msg, strlen(msg), 1, stdout);
mrb_locale_free(msg);
putc('\n', stdout);
@@ -126,10 +127,6 @@ is_code_block_open(struct mrb_parser_state *parser)
/* check for heredoc */
if (parser->parsing_heredoc != NULL) return TRUE;
- if (parser->heredoc_end_now) {
- parser->heredoc_end_now = FALSE;
- return FALSE;
- }
/* check for unterminated string */
if (parser->lex_strterm) return TRUE;
@@ -233,7 +230,7 @@ usage(const char *name)
{
static const char *const usage_msg[] = {
"switches:",
- "-d set $DEBUG to true (same as `mruby -d`)"
+ "-d set $DEBUG to true (same as `mruby -d`)",
"-r library same as `mruby -r`",
"-v print version number, then run in verbose mode",
"--verbose run in verbose mode",
@@ -376,7 +373,7 @@ check_keyword(const char *buf, const char *word)
size_t len = strlen(word);
/* skip preceding spaces */
- while (*p && isspace((unsigned char)*p)) {
+ while (*p && ISSPACE(*p)) {
p++;
}
/* check keyword */
@@ -386,7 +383,7 @@ check_keyword(const char *buf, const char *word)
p += len;
/* skip trailing spaces */
while (*p) {
- if (!isspace((unsigned char)*p)) return 0;
+ if (!ISSPACE(*p)) return 0;
p++;
}
return 1;
@@ -495,7 +492,10 @@ main(int argc, char **argv)
while (TRUE) {
char *utf8;
+ struct mrb_jmpbuf c_jmp;
+ MRB_TRY(&c_jmp);
+ mrb->jmp = &c_jmp;
if (args.rfp) {
if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL)
goto done;
@@ -559,8 +559,7 @@ main(int argc, char **argv)
MIRB_LINE_FREE(line);
#endif
-done:
-
+ done:
if (code_block_open) {
if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) {
fputs("concatenated input string too long\n", stderr);
@@ -599,13 +598,13 @@ done:
/* warning */
char* msg = mrb_locale_from_utf8(parser->warn_buffer[0].message, -1);
printf("line %d: %s\n", parser->warn_buffer[0].lineno, msg);
- mrb_utf8_free(msg);
+ mrb_locale_free(msg);
}
if (0 < parser->nerr) {
/* syntax error */
char* msg = mrb_locale_from_utf8(parser->error_buffer[0].message, -1);
printf("line %d: %s\n", parser->error_buffer[0].lineno, msg);
- mrb_utf8_free(msg);
+ mrb_locale_free(msg);
}
else {
/* generate bytecode */
@@ -652,6 +651,11 @@ done:
}
mrb_parser_free(parser);
cxt->lineno++;
+ MRB_CATCH(&c_jmp) {
+ p(mrb, mrb_obj_value(mrb->exc), 0);
+ mrb->exc = 0;
+ }
+ MRB_END_EXC(&c_jmp);
}
#ifdef ENABLE_READLINE
@@ -661,6 +665,12 @@ done:
if (args.rfp) fclose(args.rfp);
mrb_free(mrb, args.argv);
+ if (args.libv) {
+ for (i = 0; i < args.libc; ++i) {
+ mrb_free(mrb, args.libv[i]);
+ }
+ mrb_free(mrb, args.libv);
+ }
mrbc_context_free(mrb, cxt);
mrb_close(mrb);
diff --git a/mrbgems/mruby-bin-mrbc/mrbgem.rake b/mrbgems/mruby-bin-mrbc/mrbgem.rake
index e710b5a49..48b67aedb 100644
--- a/mrbgems/mruby-bin-mrbc/mrbgem.rake
+++ b/mrbgems/mruby-bin-mrbc/mrbgem.rake
@@ -8,7 +8,7 @@ MRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec|
exec = exefile("#{build.build_dir}/bin/mrbc")
mrbc_objs = Dir.glob("#{spec.dir}/tools/mrbc/*.c").map { |f| objfile(f.pathmap("#{spec.build_dir}/tools/mrbc/%n")) }.flatten
- file exec => mrbc_objs + [libfile("#{build.build_dir}/lib/libmruby_core")] do |t|
+ file exec => mrbc_objs + [build.libmruby_core_static] do |t|
build.linker.run t.name, t.prerequisites
end
diff --git a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
index 580c2e25b..2fd9da77d 100644
--- a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
+++ b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
@@ -18,6 +18,7 @@ struct mrbc_args {
const char *initname;
mrb_bool check_syntax : 1;
mrb_bool verbose : 1;
+ mrb_bool remove_lv : 1;
unsigned int flags : 4;
};
@@ -33,6 +34,7 @@ usage(const char *name)
"-B<symbol> binary <symbol> output in C language format",
"-e generate little endian iseq data",
"-E generate big endian iseq data",
+ "--remove-lv remove local variables",
"--verbose run at verbose mode",
"--version print the version",
"--copyright print the copyright",
@@ -142,6 +144,10 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
mrb_show_copyright(mrb);
exit(EXIT_SUCCESS);
}
+ else if (strcmp(argv[i] + 2, "remove-lv") == 0) {
+ args->remove_lv = TRUE;
+ break;
+ }
return -1;
default:
return i;
@@ -232,6 +238,9 @@ dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, st
int n = MRB_DUMP_OK;
mrb_irep *irep = proc->body.irep;
+ if (args->remove_lv) {
+ mrb_irep_remove_lv(mrb, irep);
+ }
if (args->initname) {
n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname);
if (n == MRB_DUMP_INVALID_ARGUMENT) {
diff --git a/mrbgems/mruby-bin-mruby-config/mrbgem.rake b/mrbgems/mruby-bin-mruby-config/mrbgem.rake
deleted file mode 100644
index 66d6ef80b..000000000
--- a/mrbgems/mruby-bin-mruby-config/mrbgem.rake
+++ /dev/null
@@ -1,30 +0,0 @@
-module MRuby
- class Build
- def exefile(name)
- if name.is_a?(Array)
- name.flatten.map { |n| exefile(n) }
- elsif name !~ /\./
- "#{name}#{exts.executable}"
- else
- name
- end
- end
- end
-end
-
-MRuby.each_target do
- next if kind_of? MRuby::CrossBuild
-
- mruby_config = 'mruby-config' + (ENV['OS'] == 'Windows_NT' ? '.bat' : '')
- mruby_config_path = "#{build_dir}/bin/#{mruby_config}"
- @bins << mruby_config
-
- file mruby_config_path => libfile("#{build_dir}/lib/libmruby") do |t|
- FileUtils.copy "#{File.dirname(__FILE__)}/#{mruby_config}", t.name
- config = Hash[open("#{build_dir}/lib/libmruby.flags.mak").read.split("\n").map {|x| a = x.split(/\s*=\s*/, 2); [a[0], a[1].gsub('\\"', '"') ]}]
- IO.write(t.name, File.open(t.name) {|f|
- f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS|MRUBY_LIBMRUBY_PATH)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"}
- })
- FileUtils.chmod(0755, t.name)
- end
-end
diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
index a7fb63fa2..5dbbc5592 100644
--- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb
+++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
@@ -1,10 +1,18 @@
require 'tempfile'
+require 'open3'
+
+def assert_mruby(exp_out, exp_err, exp_success, args)
+ out, err, stat = Open3.capture3(cmd("mruby"), *args)
+ assert do
+ assert_operator(exp_out, :===, out, "standard output")
+ assert_operator(exp_err, :===, err, "standard error")
+ assert_equal(exp_success, stat.success?, "exit success?")
+ end
+end
assert('regression for #1564') do
- o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1`
- assert_include o, "-e:1:2: syntax error"
- o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1`
- assert_include o, "-e:1:3: syntax error"
+ assert_mruby("", /\A-e:1:2: syntax error, .*\n\z/, false, %w[-e <<])
+ assert_mruby("", /\A-e:1:3: syntax error, .*\n\z/, false, %w[-e <<-])
end
assert('regression for #1572') do
@@ -12,7 +20,7 @@ assert('regression for #1572') do
File.write script.path, 'p "ok"'
system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}"
o = `#{cmd('mruby')} -b #{bin.path}`.strip
- assert_equal o, '"ok"'
+ assert_equal '"ok"', o
end
assert '$0 value' do
@@ -31,6 +39,13 @@ assert '$0 value' do
assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp
end
+assert('float literal') do
+ script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb')
+ File.write script.path, 'p [3.21, 2e308.infinite?, -2e308.infinite?]'
+ system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}"
+ assert_equal "[3.21, 1, -1]", `#{cmd('mruby')} -b #{bin.path}`.chomp!
+end
+
assert '__END__', '8.6' do
script = Tempfile.new('test.rb')
@@ -59,6 +74,11 @@ RUBY
assert_equal 0, $?.exitstatus
end
+assert('mruby -c option') do
+ assert_mruby("Syntax OK\n", "", true, ["-c", "-e", "p 1"])
+ assert_mruby("", /\A-e:1:7: syntax error, .*\n\z/, false, ["-c", "-e", "p 1; 1."])
+end
+
assert('mruby -d option') do
o = `#{cmd('mruby')} -e #{shellquote('p $DEBUG')}`
assert_equal "false\n", o
@@ -66,6 +86,14 @@ assert('mruby -d option') do
assert_equal "true\n", o
end
+assert('mruby -e option (no code specified)') do
+ assert_mruby("", /\A.*: No code specified for -e\n\z/, false, %w[-e])
+end
+
+assert('mruby -h option') do
+ assert_mruby(/\AUsage: #{Regexp.escape cmd("mruby")} .*/m, "", true, %w[-h])
+end
+
assert('mruby -r option') do
lib = Tempfile.new('lib.rb')
lib.write <<EOS
@@ -88,3 +116,32 @@ EOS
assert_equal 'hogeClass', `#{cmd('mruby')} -r #{lib.path} -r #{script.path} -e #{shellquote('print Hoge.class')}`
assert_equal 0, $?.exitstatus
end
+
+assert('mruby -r option (no library specified)') do
+ assert_mruby("", /\A.*: No library specified for -r\n\z/, false, %w[-r])
+end
+
+assert('mruby -r option (file not found)') do
+ assert_mruby("", /\A.*: Cannot open library file: .*\n\z/, false, %w[-r _no_exists_])
+end
+
+assert('mruby invalid short option') do
+ assert_mruby("", /\A.*: invalid option -1 .*\n\z/, false, %w[-1])
+end
+
+assert('mruby invalid long option') do
+ assert_mruby("", /\A.*: invalid option --longopt .*\n\z/, false, %w[--longopt])
+end
+
+assert('unhandled exception') do
+ assert_mruby("", /\bEXCEPTION\b.*\n\z/, false, %w[-e raise("EXCEPTION")])
+end
+
+assert('program file not found') do
+ assert_mruby("", /\A.*: Cannot open program file: .*\n\z/, false, %w[_no_exists_])
+end
+
+assert('codegen error') do
+ code = "def f(#{(1..100).map{|n| "a#{n}"} * ","}); end"
+ assert_mruby("", /\Acodegen error:.*\n\z/, false, ["-e", code])
+end
diff --git a/mrbgems/mruby-bin-mruby/mrbgem.rake b/mrbgems/mruby-bin-mruby/mrbgem.rake
index fbec13847..280621e31 100644
--- a/mrbgems/mruby-bin-mruby/mrbgem.rake
+++ b/mrbgems/mruby-bin-mruby/mrbgem.rake
@@ -5,6 +5,7 @@ MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec|
spec.bins = %w(mruby)
spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
spec.add_dependency('mruby-error', :core => 'mruby-error')
+ spec.add_test_dependency('mruby-print', :core => 'mruby-print')
if build.cxx_exception_enabled?
build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx")
diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
index d9f90c5e1..29ab4c17c 100644
--- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
+++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
@@ -7,19 +7,6 @@
#include <mruby/dump.h>
#include <mruby/variable.h>
-#ifdef MRB_DISABLE_STDIO
-static void
-p(mrb_state *mrb, mrb_value obj)
-{
- mrb_value val = mrb_inspect(mrb, obj);
-
- fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout);
- putc('\n', stdout);
-}
-#else
-#define p(mrb,obj) mrb_p(mrb,obj)
-#endif
-
struct _args {
FILE *rfp;
char* cmdline;
@@ -41,7 +28,7 @@ usage(const char *name)
"switches:",
"-b load and execute RiteBinary (mrb) file",
"-c check syntax only",
- "-d set debugging flags (set $DEBUG to true)"
+ "-d set debugging flags (set $DEBUG to true)",
"-e 'command' one line of script",
"-r library load the library before executing your script",
"-v print version number, then run in verbose mode",
@@ -119,14 +106,17 @@ append_cmdline:
}
}
else {
- printf("%s: No code specified for -e\n", *origargv);
- return EXIT_SUCCESS;
+ fprintf(stderr, "%s: No code specified for -e\n", *origargv);
+ return EXIT_FAILURE;
}
break;
+ case 'h':
+ usage(*origargv);
+ exit(EXIT_SUCCESS);
case 'r':
if (!item[0]) {
if (argc <= 1) {
- printf("%s: No library specified for -r\n", *origargv);
+ fprintf(stderr, "%s: No library specified for -r\n", *origargv);
return EXIT_FAILURE;
}
argc--; argv++;
@@ -158,6 +148,7 @@ append_cmdline:
exit(EXIT_SUCCESS);
}
default:
+ fprintf(stderr, "%s: invalid option %s (-h will show valid options)\n", *origargv, *argv);
return EXIT_FAILURE;
}
}
@@ -167,7 +158,7 @@ append_cmdline:
else {
args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r");
if (args->rfp == NULL) {
- printf("%s: Cannot open program file. (%s)\n", *origargv, *argv);
+ fprintf(stderr, "%s: Cannot open program file: %s\n", *origargv, *argv);
return EXIT_FAILURE;
}
args->fname = TRUE;
@@ -212,14 +203,13 @@ main(int argc, char **argv)
mrb_sym zero_sym;
if (mrb == NULL) {
- fputs("Invalid mrb_state, exiting mruby\n", stderr);
+ fprintf(stderr, "%s: Invalid mrb_state, exiting mruby\n", *argv);
return EXIT_FAILURE;
}
n = parse_args(mrb, argc, argv, &args);
if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) {
cleanup(mrb, &args);
- usage(argv[0]);
return n;
}
else {
@@ -258,7 +248,8 @@ main(int argc, char **argv)
for (i = 0; i < args.libc; i++) {
FILE *lfp = fopen(args.libv[i], args.mrbfile ? "rb" : "r");
if (lfp == NULL) {
- printf("Cannot open library file. (%s)\n", args.libv[i]);
+ fprintf(stderr, "%s: Cannot open library file: %s\n", *argv, args.libv[i]);
+ mrbc_context_free(mrb, c);
cleanup(mrb, &args);
return EXIT_FAILURE;
}
@@ -288,19 +279,16 @@ main(int argc, char **argv)
mrb_gc_arena_restore(mrb, ai);
mrbc_context_free(mrb, c);
if (mrb->exc) {
- if (mrb_undef_p(v)) {
- mrb_p(mrb, mrb_obj_value(mrb->exc));
- }
- else {
+ if (!mrb_undef_p(v)) {
mrb_print_error(mrb);
}
- n = -1;
+ n = EXIT_FAILURE;
}
else if (args.check_syntax) {
- printf("Syntax OK\n");
+ puts("Syntax OK");
}
}
cleanup(mrb, &args);
- return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ return n;
}
diff --git a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
index bb664a2b1..2db3c10b1 100644
--- a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
+++ b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
@@ -67,7 +67,7 @@ EOS
`#{cmd('mruby-strip')} -l #{without_lv.path}`
assert_true without_lv.size < with_lv.size
-
- assert_equal '[:a, :b]', `#{cmd('mruby')} -b #{with_lv.path}`.chomp
- assert_equal '[]', `#{cmd('mruby')} -b #{without_lv.path}`.chomp
+#
+# assert_equal '[:a, :b]', `#{cmd('mruby')} -b #{with_lv.path}`.chomp
+# assert_equal '[]', `#{cmd('mruby')} -b #{without_lv.path}`.chomp
end
diff --git a/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c b/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
index deb66d54c..fb78b0c3b 100644
--- a/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
+++ b/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
@@ -12,22 +12,6 @@ struct strip_args {
mrb_bool lvar;
};
-
-static void
-irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
-{
- int i;
-
- if (irep->lv) {
- mrb_free(mrb, irep->lv);
- irep->lv = NULL;
- }
-
- for (i = 0; i < irep->rlen; ++i) {
- irep_remove_lv(mrb, irep->reps[i]);
- }
-}
-
static void
print_usage(const char *f)
{
@@ -99,7 +83,7 @@ strip(mrb_state *mrb, struct strip_args *args)
/* clear lv if --lvar is enabled */
if (args->lvar) {
- irep_remove_lv(mrb, irep);
+ mrb_irep_remove_lv(mrb, irep);
}
wfile = fopen(filename, "wb");
diff --git a/mrbgems/mruby-class-ext/mrblib/module.rb b/mrbgems/mruby-class-ext/mrblib/module.rb
new file mode 100644
index 000000000..301585187
--- /dev/null
+++ b/mrbgems/mruby-class-ext/mrblib/module.rb
@@ -0,0 +1,89 @@
+class Module
+
+ ##
+ # call-seq:
+ # mod < other -> true, false, or nil
+ #
+ # Returns true if `mod` is a subclass of `other`. Returns
+ # <code>nil</code> 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
+ # <code>nil</code> 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
+ # <code>nil</code> 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
+ # <code>nil</code> 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 705db9949..fdd56bcc3 100644
--- a/mrbgems/mruby-class-ext/src/class.c
+++ b/mrbgems/mruby-class-ext/src/class.c
@@ -5,8 +5,7 @@
static mrb_value
mrb_mod_name(mrb_state *mrb, mrb_value self)
{
- mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self));
- return mrb_nil_p(name)? name : mrb_str_dup(mrb, name);
+ return mrb_class_path(mrb, mrb_class_ptr(self));
}
static mrb_value
diff --git a/mrbgems/mruby-class-ext/test/module.rb b/mrbgems/mruby-class-ext/test/module.rb
index ed6713aac..52e04ab37 100644
--- a/mrbgems/mruby-class-ext/test/module.rb
+++ b/mrbgems/mruby-class-ext/test/module.rb
@@ -1,3 +1,57 @@
+assert 'Module#<' do
+ a = Class.new
+ b = Class.new(a)
+ c = Class.new(a)
+ d = Module.new
+ e = Class.new { include d }
+ f = Module.new { include d }
+
+ # compare class to class
+ assert_true b < a
+ assert_false b < b
+ assert_false a < b
+ assert_nil c < b
+
+ # compare class to module
+ assert_true e < d
+ assert_false d < e
+ assert_nil a < d
+
+ # compare module to module
+ assert_true f < d
+ assert_false f < f
+ assert_false d < f
+
+ assert_raise(TypeError) { a < Object.new }
+end
+
+assert 'Module#<=' do
+ a = Class.new
+ b = Class.new(a)
+ c = Class.new(a)
+ d = Module.new
+ e = Class.new { include d }
+ f = Module.new { include d }
+
+ # compare class to class
+ assert_true b <= a
+ assert_true b <= b
+ assert_false a <= b
+ assert_nil c <= b
+
+ # compare class to module
+ assert_true e <= d
+ assert_false d <= e
+ assert_nil a <= d
+
+ # compare module to module
+ assert_true f <= d
+ assert_true f <= f
+ assert_false d <= f
+
+ assert_raise(TypeError) { a <= Object.new }
+end
+
assert 'Module#name' do
module Outer
class Inner; end
@@ -26,7 +80,7 @@ end
assert 'Module#singleton_class?' do
mod = Module.new
cls = Class.new
- scl = cls.singleton_class
+ scl = (class <<cls; self; end)
assert_false mod.singleton_class?
assert_false cls.singleton_class?
diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c
index f71de9b4b..ed8fc3150 100644
--- a/mrbgems/mruby-compiler/core/codegen.c
+++ b/mrbgems/mruby-compiler/core/codegen.c
@@ -8,6 +8,7 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include <mruby.h>
#include <mruby/compile.h>
#include <mruby/proc.h>
@@ -23,6 +24,8 @@
#define MRB_CODEGEN_LEVEL_MAX 1024
#endif
+#define MAXARG_S (1<<16)
+
typedef mrb_ast_node node;
typedef struct mrb_parser_state parser_state;
@@ -36,7 +39,7 @@ enum looptype {
struct loopinfo {
enum looptype type;
- int pc1, pc2, pc3, acc;
+ int pc0, pc1, pc2, pc3, acc;
int ensure_level;
struct loopinfo *prev;
};
@@ -50,23 +53,24 @@ typedef struct scope {
node *lv;
- int sp;
- int pc;
- int lastlabel;
+ uint16_t sp;
+ uint16_t pc;
+ uint16_t lastpc;
+ uint16_t lastlabel;
int ainfo:15;
mrb_bool mscope:1;
struct loopinfo *loop;
int ensure_level;
- char const *filename;
+ mrb_sym filename_sym;
uint16_t lineno;
mrb_code *iseq;
uint16_t *lines;
- int icapa;
+ uint32_t icapa;
mrb_irep *irep;
- int pcapa, scapa, rcapa;
+ uint32_t pcapa, scapa, rcapa;
uint16_t nlocals;
uint16_t nregs;
@@ -98,12 +102,14 @@ codegen_error(codegen_scope *s, const char *message)
while (s->prev) {
codegen_scope *tmp = s->prev;
mrb_free(s->mrb, s->iseq);
+ mrb_free(s->mrb, s->lines);
mrb_pool_close(s->mpool);
s = tmp;
}
#ifndef MRB_DISABLE_STDIO
- if (s->filename && s->lineno) {
- fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message);
+ if (s->filename_sym && s->lineno) {
+ const char *filename = mrb_sym2name_len(s->mrb, s->filename_sym, NULL);
+ fprintf(stderr, "codegen error:%s:%d: %s\n", filename, s->lineno, message);
}
else {
fprintf(stderr, "codegen error: %s\n", message);
@@ -122,15 +128,6 @@ codegen_palloc(codegen_scope *s, size_t len)
}
static void*
-codegen_malloc(codegen_scope *s, size_t len)
-{
- void *p = mrb_malloc_simple(s->mrb, len);
-
- if (!p) codegen_error(s, "mrb_malloc");
- return p;
-}
-
-static void*
codegen_realloc(codegen_scope *s, void *p, size_t len)
{
p = mrb_realloc_simple(s->mrb, p, len);
@@ -142,32 +139,135 @@ codegen_realloc(codegen_scope *s, void *p, size_t len)
static int
new_label(codegen_scope *s)
{
- s->lastlabel = s->pc;
- return s->pc;
+ return s->lastlabel = s->pc;
}
-static inline int
-genop(codegen_scope *s, mrb_code i)
+static void
+emit_B(codegen_scope *s, uint32_t pc, uint8_t i)
{
- if (s->pc >= s->icapa) {
+ if (pc >= MAXARG_S || s->icapa >= MAXARG_S) {
+ codegen_error(s, "too big code block");
+ }
+ if (pc >= s->icapa) {
s->icapa *= 2;
- if (s->pc >= MAXARG_sBx) {
- codegen_error(s, "too big code block");
- }
- if (s->icapa > MAXARG_sBx) {
- s->icapa = MAXARG_sBx;
+ if (s->icapa > MAXARG_S) {
+ s->icapa = MAXARG_S;
}
s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
if (s->lines) {
- s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa);
- s->irep->lines = s->lines;
+ s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->icapa);
}
}
- s->iseq[s->pc] = i;
if (s->lines) {
- s->lines[s->pc] = s->lineno;
+ if (s->lineno > 0 || pc == 0)
+ s->lines[pc] = s->lineno;
+ else
+ s->lines[pc] = s->lines[pc-1];
+ }
+ s->iseq[pc] = i;
+}
+
+static void
+emit_S(codegen_scope *s, int pc, uint16_t i)
+{
+ uint8_t hi = i>>8;
+ uint8_t lo = i&0xff;
+
+ emit_B(s, pc, hi);
+ emit_B(s, pc+1, lo);
+}
+
+static void
+gen_B(codegen_scope *s, uint8_t i)
+{
+ emit_B(s, s->pc, i);
+ s->pc++;
+}
+
+static void
+gen_S(codegen_scope *s, uint16_t i)
+{
+ emit_S(s, s->pc, i);
+ s->pc += 2;
+}
+
+static void
+genop_0(codegen_scope *s, mrb_code i)
+{
+ s->lastpc = s->pc;
+ gen_B(s, i);
+}
+
+static void
+genop_1(codegen_scope *s, mrb_code i, uint16_t a)
+{
+ s->lastpc = s->pc;
+ if (a > 0xff) {
+ gen_B(s, OP_EXT1);
+ gen_B(s, i);
+ gen_S(s, a);
}
- return s->pc++;
+ else {
+ gen_B(s, i);
+ gen_B(s, (uint8_t)a);
+ }
+}
+
+static void
+genop_2(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
+{
+ s->lastpc = s->pc;
+ if (a > 0xff && b > 0xff) {
+ gen_B(s, OP_EXT3);
+ gen_B(s, i);
+ gen_S(s, a);
+ gen_S(s, b);
+ }
+ else if (b > 0xff) {
+ gen_B(s, OP_EXT2);
+ gen_B(s, i);
+ gen_B(s, (uint8_t)a);
+ gen_S(s, b);
+ }
+ else if (a > 0xff) {
+ gen_B(s, OP_EXT1);
+ gen_B(s, i);
+ gen_S(s, a);
+ gen_B(s, (uint8_t)b);
+ }
+ else {
+ gen_B(s, i);
+ gen_B(s, (uint8_t)a);
+ gen_B(s, (uint8_t)b);
+ }
+}
+
+static void
+genop_3(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b, uint8_t c)
+{
+ genop_2(s, i, a, b);
+ gen_B(s, c);
+}
+
+static void
+genop_2S(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
+{
+ genop_1(s, i, a);
+ gen_S(s, b);
+}
+
+static void
+genop_W(codegen_scope *s, mrb_code i, uint32_t a)
+{
+ uint8_t a1 = (a>>16) & 0xff;
+ uint8_t a2 = (a>>8) & 0xff;
+ uint8_t a3 = a & 0xff;
+
+ s->lastpc = s->pc;
+ gen_B(s, i);
+ gen_B(s, a1);
+ gen_B(s, a2);
+ gen_B(s, a3);
}
#define NOVAL 0
@@ -181,298 +281,270 @@ no_optimize(codegen_scope *s)
return FALSE;
}
-static int
-genop_peep(codegen_scope *s, mrb_code i, int val)
+static
+mrb_bool
+on_eval(codegen_scope *s)
{
- /* peephole optimization */
- if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) {
- mrb_code i0 = s->iseq[s->pc-1];
- int c1 = GET_OPCODE(i);
- int c0 = GET_OPCODE(i0);
+ if (s && s->parser && s->parser->on_eval)
+ return TRUE;
+ return FALSE;
+}
- switch (c1) {
- case OP_MOVE:
- if (GETARG_A(i) == GETARG_B(i)) {
- /* skip useless OP_MOVE */
- return 0;
- }
- if (val) break;
- switch (c0) {
- case OP_MOVE:
- if (GETARG_A(i) == GETARG_A(i0)) {
- /* skip overriden OP_MOVE */
- s->pc--;
- s->iseq[s->pc] = i;
- }
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) {
- /* skip swapping OP_MOVE */
- return 0;
- }
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->pc--;
- return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val);
- }
- break;
- case OP_LOADI:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0));
- return 0;
- }
- break;
- case OP_ARRAY:
- case OP_HASH:
- case OP_RANGE:
- case OP_AREF:
- case OP_GETUPVAR:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0));
- return 0;
- }
- break;
- case OP_LOADSYM:
- case OP_GETGLOBAL:
- case OP_GETIV:
- case OP_GETCV:
- case OP_GETCONST:
- case OP_GETSPECIAL:
- case OP_LOADL:
- case OP_STRING:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0));
- return 0;
- }
- break;
- case OP_SCLASS:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0));
- return 0;
- }
- break;
- case OP_LOADNIL:
- case OP_LOADSELF:
- case OP_LOADT:
- case OP_LOADF:
- case OP_OCLASS:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i));
- return 0;
- }
- break;
- default:
- break;
- }
- break;
- case OP_SETIV:
- case OP_SETCV:
- case OP_SETCONST:
- case OP_SETMCNST:
- case OP_SETGLOBAL:
- if (val) break;
- if (c0 == OP_MOVE) {
- if (GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i));
- return 0;
- }
- }
- break;
- case OP_SETUPVAR:
- if (val) break;
- if (c0 == OP_MOVE) {
- if (GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i));
- return 0;
- }
- }
- break;
- case OP_GETUPVAR:
- if (c0 == OP_SETUPVAR) {
- if (GETARG_B(i) == GETARG_B(i0) && GETARG_C(i) == GETARG_C(i0)) {
- if (GETARG_A(i) == GETARG_A(i0)) {
- /* just skip OP_SETUPVAR */
- return 0;
- }
- else {
- return genop(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_A(i0)));
- }
- }
- }
- break;
- case OP_EPOP:
- if (c0 == OP_EPOP) {
- s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i));
- return 0;
- }
- break;
- case OP_POPERR:
- if (c0 == OP_POPERR) {
- s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i));
- return 0;
- }
- break;
- case OP_RETURN:
- switch (c0) {
- case OP_RETURN:
- return 0;
- case OP_MOVE:
- if (GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL);
- return 0;
- }
- break;
- case OP_SETIV:
- case OP_SETCV:
- case OP_SETCONST:
- case OP_SETMCNST:
- case OP_SETUPVAR:
- case OP_SETGLOBAL:
- s->pc--;
- genop_peep(s, i0, NOVAL);
- i0 = s->iseq[s->pc-1];
- return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL));
-#if 0
- case OP_SEND:
- if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0));
- return;
- }
- break;
-#endif
- default:
- break;
- }
- break;
- case OP_ADD:
- case OP_SUB:
- if (c0 == OP_LOADI) {
- int c = GETARG_sBx(i0);
-
- if (c1 == OP_SUB) c = -c;
- if (c > 127 || c < -127) break;
- if (0 <= c)
- s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c);
- else
- s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c);
- return 0;
- }
- break;
- case OP_ARYCAT:
- case OP_ARYPUSH:
- if (c0 == OP_MOVE && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AB(c1, GETARG_A(i), GETARG_B(i0));
- return 0;
- }
- break;
- case OP_STRCAT:
- if (c0 == OP_STRING) {
- mrb_value v = s->irep->pool[GETARG_Bx(i0)];
+struct mrb_insn_data
+mrb_decode_insn(mrb_code *pc)
+{
+ struct mrb_insn_data data = { 0 };
+ mrb_code insn = READ_B();
+ uint16_t a = 0;
+ uint16_t b = 0;
+ uint8_t c = 0;
+
+ switch (insn) {
+#define FETCH_Z() /* empty */
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ switch (insn) {
+ case OP_EXT1:
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+ case OP_EXT2:
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+ case OP_EXT3:
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+ default:
+ break;
+ }
+ data.insn = insn;
+ data.a = a;
+ data.b = b;
+ data.c = c;
+ return data;
+}
- if (mrb_string_p(v) && RSTRING_LEN(v) == 0) {
- s->pc--;
- return 0;
- }
- }
- if (c0 == OP_LOADNIL) {
- if (GETARG_B(i) == GETARG_A(i0)) {
- s->pc--;
- return 0;
- }
- }
+static struct mrb_insn_data
+mrb_last_insn(codegen_scope *s)
+{
+ if (s->pc == s->lastpc) {
+ struct mrb_insn_data data;
+
+ data.insn = OP_NOP;
+ return data;
+ }
+ return mrb_decode_insn(&s->iseq[s->lastpc]);
+}
+
+static mrb_bool
+no_peephole(codegen_scope *s)
+{
+ return no_optimize(s) || s->lastlabel == s->pc || s->pc == 0 || s->pc == s->lastpc;
+}
+
+static uint16_t
+genjmp(codegen_scope *s, mrb_code i, uint16_t pc)
+{
+ uint16_t pos;
+
+ s->lastpc = s->pc;
+ gen_B(s, i);
+ pos = s->pc;
+ gen_S(s, pc);
+ return pos;
+}
+
+static uint16_t
+genjmp2(codegen_scope *s, mrb_code i, uint16_t a, int pc, int val)
+{
+ uint16_t pos;
+
+ if (!no_peephole(s) && !val) {
+ struct mrb_insn_data data = mrb_last_insn(s);
+
+ if (data.insn == OP_MOVE && data.a == a) {
+ s->pc = s->lastpc;
+ a = data.b;
+ }
+ }
+
+ s->lastpc = s->pc;
+ if (a > 0xff) {
+ gen_B(s, OP_EXT1);
+ gen_B(s, i);
+ gen_S(s, a);
+ pos = s->pc;
+ gen_S(s, pc);
+ }
+ else {
+ gen_B(s, i);
+ gen_B(s, (uint8_t)a);
+ pos = s->pc;
+ gen_S(s, pc);
+ }
+ return pos;
+}
+
+static void
+gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
+{
+ if (no_peephole(s)) {
+ normal:
+ genop_2(s, OP_MOVE, dst, src);
+ if (on_eval(s)) {
+ genop_0(s, OP_NOP);
+ }
+ return;
+ }
+ else {
+ struct mrb_insn_data data = mrb_last_insn(s);
+
+ switch (data.insn) {
+ case OP_MOVE:
+ if (dst == src) return; /* remove useless MOVE */
+ if (data.b == dst && data.a == src) /* skip swapping MOVE */
+ return;
+ goto normal;
+ case OP_LOADNIL: case OP_LOADSELF: case OP_LOADT: case OP_LOADF:
+ case OP_LOADI__1:
+ case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3:
+ case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7:
+ if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
+ s->pc = s->lastpc;
+ genop_1(s, data.insn, dst);
break;
- case OP_JMPIF:
- case OP_JMPNOT:
- if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i));
- return s->pc-1;
- }
+ case OP_LOADI: case OP_LOADINEG: case OP_LOADL: case OP_LOADSYM:
+ case OP_GETGV: case OP_GETSV: case OP_GETIV: case OP_GETCV:
+ case OP_GETCONST: case OP_STRING:
+ case OP_LAMBDA: case OP_BLOCK: case OP_METHOD: case OP_BLKPUSH:
+ if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
+ s->pc = s->lastpc;
+ genop_2(s, data.insn, dst, data.b);
break;
default:
- break;
+ goto normal;
}
}
- return genop(s, i);
}
static void
-scope_error(codegen_scope *s)
+gen_return(codegen_scope *s, uint8_t op, uint16_t src)
{
- exit(EXIT_FAILURE);
+ if (no_peephole(s)) {
+ genop_1(s, op, src);
+ }
+ else {
+ struct mrb_insn_data data = mrb_last_insn(s);
+
+ if (data.insn == OP_MOVE && src == data.a) {
+ s->pc = s->lastpc;
+ genop_1(s, op, data.b);
+ }
+ else if (data.insn != OP_RETURN) {
+ genop_1(s, op, src);
+ }
+ }
}
static void
-distcheck(codegen_scope *s, int diff)
+gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst)
{
- if (diff > MAXARG_sBx || diff < -MAXARG_sBx) {
- codegen_error(s, "too distant jump address");
+ if (no_peephole(s)) {
+ normal:
+ genop_1(s, op, dst);
+ return;
+ }
+ else {
+ struct mrb_insn_data data = mrb_last_insn(s);
+
+ switch (data.insn) {
+ case OP_LOADI__1:
+ if (op == OP_ADD) op = OP_SUB;
+ else op = OP_ADD;
+ data.b = 1;
+ goto replace;
+ case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3:
+ case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7:
+ data.b = data.insn - OP_LOADI_0;
+ /* fall through */
+ case OP_LOADI:
+ replace:
+ if (data.b >= 128) goto normal;
+ s->pc = s->lastpc;
+ if (op == OP_ADD) {
+ genop_2(s, OP_ADDI, dst, (uint8_t)data.b);
+ }
+ else {
+ genop_2(s, OP_SUBI, dst, (uint8_t)data.b);
+ }
+ break;
+ default:
+ goto normal;
+ }
}
}
-static inline void
-dispatch(codegen_scope *s, int pc)
+static int
+dispatch(codegen_scope *s, uint16_t pos0)
{
- int diff = s->pc - pc;
- mrb_code i = s->iseq[pc];
- int c = GET_OPCODE(i);
+ uint16_t newpos;
s->lastlabel = s->pc;
- switch (c) {
- case OP_JMP:
- case OP_JMPIF:
- case OP_JMPNOT:
- case OP_ONERR:
- break;
- default:
-#ifndef MRB_DISABLE_STDIO
- fprintf(stderr, "bug: dispatch on non JMP op\n");
-#endif
- scope_error(s);
- break;
- }
- distcheck(s, diff);
- s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
+ newpos = PEEK_S(s->iseq+pos0);
+ emit_S(s, pos0, s->pc);
+ return newpos;
}
static void
-dispatch_linked(codegen_scope *s, int pc)
+dispatch_linked(codegen_scope *s, uint16_t pos)
{
- mrb_code i;
- int pos;
-
- if (!pc) return;
+ if (pos==0) return;
for (;;) {
- i = s->iseq[pc];
- pos = GETARG_sBx(i);
- dispatch(s, pc);
- if (!pos) break;
- pc = pos;
+ pos = dispatch(s, pos);
+ if (pos==0) break;
}
}
#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
static void
-push_(codegen_scope *s)
+push_n_(codegen_scope *s, int n)
{
- if (s->sp > 511) {
+ if (s->sp+n >= 0xffff) {
codegen_error(s, "too complex expression");
}
- s->sp++;
+ s->sp+=n;
nregs_update;
}
static void
-push_n_(codegen_scope *s, int n)
+pop_n_(codegen_scope *s, int n)
{
- if (s->sp+n > 511) {
- codegen_error(s, "too complex expression");
+ if ((int)s->sp-n < 0) {
+ codegen_error(s, "stack pointer underflow");
}
- s->sp+=n;
- nregs_update;
+ s->sp-=n;
}
-#define push() push_(s)
+#define push() push_n_(s,1)
#define push_n(n) push_n_(s,n)
-#define pop_(s) ((s)->sp--)
-#define pop() pop_(s)
-#define pop_n(n) (s->sp-=(n))
+#define pop() pop_n_(s,1)
+#define pop_n(n) pop_n_(s,n)
#define cursp() (s->sp)
static inline int
@@ -496,9 +568,12 @@ new_lit(codegen_scope *s, mrb_value val)
#ifndef MRB_WITHOUT_FLOAT
case MRB_TT_FLOAT:
for (i=0; i<s->irep->plen; i++) {
+ mrb_float f1, f2;
pv = &s->irep->pool[i];
if (mrb_type(*pv) != MRB_TT_FLOAT) continue;
- if (mrb_float(*pv) == mrb_float(val)) return i;
+ f1 = mrb_float(*pv);
+ f2 = mrb_float(val);
+ if (f1 == f2 && !signbit(f1) == !signbit(f2)) return i;
}
break;
#endif
@@ -545,52 +620,23 @@ new_lit(codegen_scope *s, mrb_value val)
return i;
}
-/* method symbols should be fit in 9 bits */
-#define MAXMSYMLEN 512
/* maximum symbol numbers */
-#define MAXSYMLEN 65536
+#define MAXSYMLEN 0x10000
static int
-new_msym(codegen_scope *s, mrb_sym sym)
+new_sym(codegen_scope *s, mrb_sym sym)
{
int i, len;
mrb_assert(s->irep);
len = s->irep->slen;
- if (len > MAXMSYMLEN) len = MAXMSYMLEN;
for (i=0; i<len; i++) {
if (s->irep->syms[i] == sym) return i;
- if (s->irep->syms[i] == 0) break;
}
- if (i == MAXMSYMLEN) {
- codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")");
- }
- s->irep->syms[i] = sym;
- if (i == s->irep->slen) s->irep->slen++;
- return i;
-}
-
-static int
-new_sym(codegen_scope *s, mrb_sym sym)
-{
- int i;
-
- for (i=0; i<s->irep->slen; i++) {
- if (s->irep->syms[i] == sym) return i;
- }
- if (s->irep->slen == MAXSYMLEN) {
- codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")");
- }
-
- if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) {
- s->scapa = MAXSYMLEN;
- s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN);
- for (i = s->irep->slen; i < MAXMSYMLEN; i++) {
- static const mrb_sym mrb_sym_zero = { 0 };
- s->irep->syms[i] = mrb_sym_zero;
- }
- s->irep->slen = MAXMSYMLEN;
+ if (s->irep->slen >= s->scapa) {
+ s->scapa *= 2;
+ s->irep->syms = (mrb_sym*)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*s->scapa);
}
s->irep->syms[s->irep->slen] = sym;
return s->irep->slen++;
@@ -608,8 +654,12 @@ node_len(node *tree)
return n;
}
+#define nint(x) ((int)(intptr_t)(x))
+#define nchar(x) ((char)(intptr_t)(x))
#define nsym(x) ((mrb_sym)(intptr_t)(x))
+
#define lv_name(lv) nsym((lv)->car)
+
static int
lv_idx(codegen_scope *s, mrb_sym id)
{
@@ -631,7 +681,6 @@ for_body(codegen_scope *s, node *tree)
int idx;
struct loopinfo *lp;
node *n2;
- mrb_code c;
/* generate receiver */
codegen(s, tree->cdr->car, VAL);
@@ -645,7 +694,7 @@ for_body(codegen_scope *s, node *tree)
/* generate loop variable */
n2 = tree->car;
- genop(s, MKOP_Ax(OP_ENTER, 0x40000));
+ genop_W(s, OP_ENTER, 0x40000);
if (n2->car && !n2->car->cdr && !n2->cdr) {
gen_assignment(s, n2->car->car, 1, NOVAL);
}
@@ -659,25 +708,20 @@ for_body(codegen_scope *s, node *tree)
/* loop body */
codegen(s, tree->cdr->cdr->car, VAL);
pop();
- if (s->pc > 0) {
- c = s->iseq[s->pc-1];
- if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel)
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
- }
+ gen_return(s, OP_RETURN, cursp());
loop_pop(s, NOVAL);
scope_finish(s);
s = prev;
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK));
+ genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1);
push();pop(); /* space for a block */
pop();
- idx = new_msym(s, mrb_intern_lit(s->mrb, "each"));
- genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
+ idx = new_sym(s, mrb_intern_lit(s->mrb, "each"));
+ genop_3(s, OP_SENDB, cursp(), idx, 0);
}
static int
lambda_body(codegen_scope *s, node *tree, int blk)
{
- mrb_code c;
codegen_scope *parent = s;
s = scope_new(s->mrb, s, tree->car);
if (s == NULL) {
@@ -688,78 +732,150 @@ lambda_body(codegen_scope *s, node *tree, int blk)
if (blk) {
struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
- lp->pc1 = new_label(s);
+ lp->pc0 = new_label(s);
}
tree = tree->cdr;
- if (tree->car) {
+ if (tree->car == NULL) {
+ genop_W(s, OP_ENTER, 0);
+ }
+ else {
mrb_aspec a;
int ma, oa, ra, pa, ka, kd, ba;
int pos, i;
- node *n, *opt;
+ node *opt;
+ node *margs, *pargs;
+ node *tail;
+ /* mandatory arguments */
ma = node_len(tree->car->car);
- n = tree->car->car;
- while (n) {
- n = n->cdr;
- }
+ margs = tree->car->car;
+ tail = tree->car->cdr->cdr->cdr->cdr;
+
+ /* optional arguments */
oa = node_len(tree->car->cdr->car);
+ /* rest argument? */
ra = tree->car->cdr->cdr->car ? 1 : 0;
+ /* mandatory arugments after rest argument */
pa = node_len(tree->car->cdr->cdr->cdr->car);
- ka = kd = 0;
- ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
+ pargs = tree->car->cdr->cdr->cdr->car;
+ /* keyword arguments */
+ ka = tail? node_len(tail->cdr->car) : 0;
+ /* keyword dictionary? */
+ kd = tail && tail->cdr->cdr->car? 1 : 0;
+ /* block argument? */
+ ba = tail && tail->cdr->cdr->cdr->car ? 1 : 0;
if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
codegen_error(s, "too many formal arguments");
}
- a = ((mrb_aspec)(ma & 0x1f) << 18)
- | ((mrb_aspec)(oa & 0x1f) << 13)
- | ((ra & 1) << 12)
- | ((pa & 0x1f) << 7)
- | ((ka & 0x1f) << 2)
- | ((kd & 1)<< 1)
- | (ba & 1);
- s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */
- | ((ra & 1) << 5)
- | (pa & 0x1f);
- genop(s, MKOP_Ax(OP_ENTER, a));
+ a = MRB_ARGS_REQ(ma)
+ | MRB_ARGS_OPT(oa)
+ | (ra? MRB_ARGS_REST() : 0)
+ | MRB_ARGS_POST(pa)
+ | MRB_ARGS_KEY(ka, kd)
+ | (ba? MRB_ARGS_BLOCK() : 0);
+ s->ainfo = (((ma+oa) & 0x3f) << 7) /* (12bits = 5:1:5:1) */
+ | ((ra & 0x1) << 6)
+ | ((pa & 0x1f) << 1)
+ | (kd & 0x1);
+ genop_W(s, OP_ENTER, a);
+ /* generate jump table for optional arguments initializer */
pos = new_label(s);
for (i=0; i<oa; i++) {
new_label(s);
- genop(s, MKOP_sBx(OP_JMP, 0));
+ genjmp(s, OP_JMP, 0);
}
if (oa > 0) {
- genop(s, MKOP_sBx(OP_JMP, 0));
+ genjmp(s, OP_JMP, 0);
}
opt = tree->car->cdr->car;
i = 0;
while (opt) {
int idx;
- dispatch(s, pos+i);
+ dispatch(s, pos+i*3+1);
codegen(s, opt->car->cdr, VAL);
- idx = lv_idx(s, nsym(opt->car->car));
pop();
- genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
+ idx = lv_idx(s, nsym(opt->car->car));
+ gen_move(s, idx, cursp(), 0);
i++;
opt = opt->cdr;
}
if (oa > 0) {
- dispatch(s, pos+i);
+ dispatch(s, pos+i*3+1);
+ }
+
+ /* keyword arguments */
+ if (tail) {
+ node *kwds = tail->cdr->car;
+ int kwrest = 0;
+
+ if (tail->cdr->cdr->car) {
+ kwrest = 1;
+ }
+ mrb_assert(nint(tail->car) == NODE_ARGS_TAIL);
+ mrb_assert(node_len(tail) == 4);
+
+ while (kwds) {
+ int jmpif_key_p, jmp_def_set = -1;
+ node *kwd = kwds->car, *def_arg = kwd->cdr->cdr->car;
+ mrb_sym kwd_sym = nsym(kwd->cdr->car);
+
+ mrb_assert(nint(kwd->car) == NODE_KW_ARG);
+
+ if (def_arg) {
+ genop_2(s, OP_KEY_P, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
+ jmpif_key_p = genjmp2(s, OP_JMPIF, lv_idx(s, kwd_sym), 0, 0);
+ codegen(s, def_arg, VAL);
+ pop();
+ gen_move(s, lv_idx(s, kwd_sym), cursp(), 0);
+ jmp_def_set = genjmp(s, OP_JMP, 0);
+ dispatch(s, jmpif_key_p);
+ }
+ genop_2(s, OP_KARG, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
+ if (jmp_def_set != -1) {
+ dispatch(s, jmp_def_set);
+ }
+ i++;
+
+ kwds = kwds->cdr;
+ }
+ if (tail->cdr->car && !kwrest) {
+ genop_0(s, OP_KEYEND);
+ }
+ }
+
+ /* argument destructuring */
+ if (margs) {
+ node *n = margs;
+
+ pos = 1;
+ while (n) {
+ if (nint(n->car->car) == NODE_MASGN) {
+ gen_vmassignment(s, n->car->cdr->car, pos, NOVAL);
+ }
+ pos++;
+ n = n->cdr;
+ }
+ }
+ if (pargs) {
+ node *n = margs;
+
+ pos = ma+oa+ra+1;
+ while (n) {
+ if (nint(n->car->car) == NODE_MASGN) {
+ gen_vmassignment(s, n->car->cdr->car, pos, NOVAL);
+ }
+ pos++;
+ n = n->cdr;
+ }
}
}
+
codegen(s, tree->cdr->car, VAL);
pop();
if (s->pc > 0) {
- c = s->iseq[s->pc-1];
- if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) {
- if (s->nregs == 0) {
- genop(s, MKOP_A(OP_LOADNIL, 0));
- genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
- }
- else {
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
- }
- }
+ gen_return(s, OP_RETURN, cursp());
}
if (blk) {
loop_pop(s, NOVAL);
@@ -773,24 +889,13 @@ scope_body(codegen_scope *s, node *tree, int val)
{
codegen_scope *scope = scope_new(s->mrb, s, tree->car);
if (scope == NULL) {
- raise_error(s, "unexpected scope");
+ codegen_error(s, "unexpected scope");
}
codegen(scope, tree->cdr, VAL);
+ gen_return(scope, OP_RETURN, scope->sp-1);
if (!s->iseq) {
- genop(scope, MKOP_A(OP_STOP, 0));
- }
- else if (!val) {
- genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
- }
- else {
- if (scope->nregs == 0) {
- genop(scope, MKOP_A(OP_LOADNIL, 0));
- genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
- }
- else {
- genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL);
- }
+ genop_0(scope, OP_STOP);
}
scope_finish(scope);
if (!s->irep) {
@@ -800,9 +905,6 @@ scope_body(codegen_scope *s, node *tree, int val)
return s->irep->rlen - 1;
}
-#define nint(x) ((int)(intptr_t)(x))
-#define nchar(x) ((char)(intptr_t)(x))
-
static mrb_bool
nosplat(node *t)
{
@@ -854,15 +956,15 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
}
else {
pop_n(n);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ genop_2(s, OP_ARRAY, cursp(), n);
push();
codegen(s, t->car, VAL);
pop(); pop();
if (is_splat) {
- genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL);
+ genop_1(s, OP_ARYCAT, cursp());
}
else {
- genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL);
+ genop_1(s, OP_ARYPUSH, cursp());
}
}
t = t->cdr;
@@ -871,10 +973,10 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
codegen(s, t->car, VAL);
pop(); pop();
if (nint(t->car->car) == NODE_SPLAT) {
- genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL);
+ genop_1(s, OP_ARYCAT, cursp());
}
else {
- genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL);
+ genop_1(s, OP_ARYPUSH, cursp());
}
t = t->cdr;
}
@@ -899,22 +1001,15 @@ static void
gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
{
mrb_sym sym = name ? name : nsym(tree->cdr->car);
- int idx, skip = 0;
+ int skip = 0;
int n = 0, noop = 0, sendv = 0, blk = 0;
codegen(s, tree->car, VAL); /* receiver */
if (safe) {
int recv = cursp()-1;
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- genop(s, MKOP_AB(OP_MOVE, cursp(), recv));
- push_n(2); pop_n(2); /* space for one arg and a block */
- pop();
- idx = new_msym(s, mrb_intern_lit(s->mrb, "=="));
- genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1));
- skip = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ gen_move(s, cursp(), recv, 1);
+ skip = genjmp2(s, OP_JMPNIL, cursp(), 0, val);
}
- idx = new_msym(s, sym);
tree = tree->cdr->cdr->car;
if (tree) {
n = gen_values(s, tree->car, VAL, sp?1:0);
@@ -923,14 +1018,15 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
push();
}
}
- if (sp) {
+ if (sp) { /* last argument pushed (attr=) */
if (sendv) {
+ gen_move(s, cursp(), sp, 0);
pop();
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp));
+ genop_1(s, OP_ARYPUSH, cursp());
push();
}
else {
- genop(s, MKOP_AB(OP_MOVE, cursp(), sp));
+ gen_move(s, cursp(), sp, 0);
push();
n++;
}
@@ -939,9 +1035,7 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
noop = 1;
codegen(s, tree->cdr, VAL);
pop();
- }
- else {
- blk = cursp();
+ blk = 1;
}
push();pop();
pop_n(n+1);
@@ -950,39 +1044,40 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
if (!noop && symlen == 1 && symname[0] == '+' && n == 1) {
- genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val);
+ gen_addsub(s, OP_ADD, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '-' && n == 1) {
- genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
+ gen_addsub(s, OP_SUB, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '*' && n == 1) {
- genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
+ genop_1(s, OP_MUL, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '/' && n == 1) {
- genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
+ genop_1(s, OP_DIV, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '<' && n == 1) {
- genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
+ genop_1(s, OP_LT, cursp());
}
else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1) {
- genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
+ genop_1(s, OP_LE, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '>' && n == 1) {
- genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
+ genop_1(s, OP_GT, cursp());
}
else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1) {
- genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
+ genop_1(s, OP_GE, cursp());
}
else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1) {
- genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
+ genop_1(s, OP_EQ, cursp());
}
else {
- if (sendv) n = CALL_MAXARGS;
- if (blk > 0) { /* no block */
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
+ int idx = new_sym(s, sym);
+
+ if (sendv) {
+ genop_2(s, blk ? OP_SENDVB : OP_SENDV, cursp(), idx);
}
else {
- genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n));
+ genop_3(s, blk ? OP_SENDB : OP_SEND, cursp(), idx, n);
}
}
}
@@ -1004,13 +1099,15 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
switch (type) {
case NODE_GVAR:
idx = new_sym(s, nsym(tree));
- genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
+ genop_2(s, OP_SETGV, sp, idx);
break;
+ case NODE_ARG:
case NODE_LVAR:
idx = lv_idx(s, nsym(tree));
if (idx > 0) {
if (idx != sp) {
- genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
+ gen_move(s, idx, sp, val);
+ if (val && on_eval(s)) genop_0(s, OP_NOP);
}
break;
}
@@ -1021,7 +1118,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
while (up) {
idx = lv_idx(up, nsym(tree));
if (idx > 0) {
- genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
+ genop_3(s, OP_SETUPVAR, sp, idx, lv);
break;
}
lv++;
@@ -1031,23 +1128,23 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
break;
case NODE_IVAR:
idx = new_sym(s, nsym(tree));
- genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
+ genop_2(s, OP_SETIV, sp, idx);
break;
case NODE_CVAR:
idx = new_sym(s, nsym(tree));
- genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
+ genop_2(s, OP_SETCV, sp, idx);
break;
case NODE_CONST:
idx = new_sym(s, nsym(tree));
- genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
+ genop_2(s, OP_SETCONST, sp, idx);
break;
case NODE_COLON2:
- idx = new_sym(s, nsym(tree->cdr));
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
+ gen_move(s, cursp(), sp, 0);
push();
codegen(s, tree->car, VAL);
pop_n(2);
- genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
+ idx = new_sym(s, nsym(tree->cdr));
+ genop_2(s, OP_SETMCNST, sp, idx);
break;
case NODE_CALL:
@@ -1057,7 +1154,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
type == NODE_SCALL);
pop();
if (val) {
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
+ gen_move(s, cursp(), sp, 0);
}
break;
@@ -1090,7 +1187,7 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
while (t) {
int sp = cursp();
- genop(s, MKOP_ABC(OP_AREF, sp, rhs, n));
+ genop_3(s, OP_AREF, sp, rhs, n);
push();
gen_assignment(s, t->car, sp, NOVAL);
pop();
@@ -1107,12 +1204,12 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
p = p->cdr;
}
}
- genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
+ gen_move(s, cursp(), rhs, val);
push_n(post+1);
pop_n(post+1);
- genop(s, MKOP_ABC(OP_APOST, cursp(), n, post));
+ genop_3(s, OP_APOST, cursp(), n, post);
n = 1;
- if (t->car) { /* rest */
+ if (t->car && t->car != (node*)-1) { /* rest */
gen_assignment(s, t->car, cursp(), NOVAL);
}
if (t->cdr && t->cdr->car) {
@@ -1124,19 +1221,19 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
}
}
if (val) {
- genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
+ gen_move(s, cursp(), rhs, 0);
}
}
}
static void
-gen_send_intern(codegen_scope *s)
+gen_intern(codegen_scope *s)
{
- push();pop(); /* space for a block */
pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0));
+ genop_1(s, OP_INTERN, cursp());
push();
}
+
static void
gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
{
@@ -1159,25 +1256,25 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
j = 0;
++i;
if (sym)
- gen_send_intern(s);
+ gen_intern(s);
}
break;
}
- if (j >= 2) {
+ while (j >= 2) {
pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
- j = 1;
+ j--;
}
tree = tree->cdr;
}
if (j > 0) {
++i;
if (sym)
- gen_send_intern(s);
+ gen_intern(s);
}
pop_n(i);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i));
+ genop_2(s, OP_ARRAY, cursp(), i);
push();
}
else {
@@ -1196,7 +1293,7 @@ raise_error(codegen_scope *s, const char *msg)
{
int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg));
- genop(s, MKOP_ABx(OP_ERR, 1, idx));
+ genop_1(s, OP_ERR, idx);
}
#ifndef MRB_WITHOUT_FLOAT
@@ -1274,11 +1371,9 @@ static void
gen_retval(codegen_scope *s, node *tree)
{
if (nint(tree->car) == NODE_SPLAT) {
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0));
- push();
codegen(s, tree, VAL);
- pop(); pop();
- genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ pop();
+ genop_1(s, OP_ARYDUP, cursp());
}
else {
codegen(s, tree, VAL);
@@ -1294,7 +1389,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (!tree) {
if (val) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
return;
@@ -1305,11 +1400,14 @@ codegen(codegen_scope *s, node *tree, int val)
codegen_error(s, "too complex expression");
}
if (s->irep && s->filename_index != tree->filename_index) {
- s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
- mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc);
+ mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
+ const char *filename = mrb_sym2name_len(s->mrb, fname, NULL);
+
+ mrb_debug_info_append_file(s->mrb, s->irep->debug_info,
+ filename, s->lines, s->debug_start_pos, s->pc);
s->debug_start_pos = s->pc;
s->filename_index = tree->filename_index;
- s->filename = mrb_parser_get_filename(s->parser, tree->filename_index);
+ s->filename_sym = mrb_parser_get_filename(s->parser, tree->filename_index);
}
nt = nint(tree->car);
@@ -1318,7 +1416,7 @@ codegen(codegen_scope *s, node *tree, int val)
switch (nt) {
case NODE_BEGIN:
if (val && !tree) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
while (tree) {
@@ -1329,18 +1427,18 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_RESCUE:
{
- int onerr, noexc, exend, pos1, pos2, tmp;
+ int noexc, exend, pos1, pos2, tmp;
struct loopinfo *lp;
if (tree->car == NULL) goto exit;
- onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
lp = loop_push(s, LOOP_BEGIN);
- lp->pc1 = onerr;
+ lp->pc0 = new_label(s);
+ lp->pc1 = genjmp(s, OP_ONERR, 0);
codegen(s, tree->car, VAL);
pop();
lp->type = LOOP_RESCUE;
- noexc = genop(s, MKOP_Bx(OP_JMP, 0));
- dispatch(s, onerr);
+ noexc = genjmp(s, OP_JMP, 0);
+ dispatch(s, lp->pc1);
tree = tree->cdr;
exend = 0;
pos1 = 0;
@@ -1348,7 +1446,7 @@ codegen(codegen_scope *s, node *tree, int val)
node *n2 = tree->car;
int exc = cursp();
- genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
+ genop_1(s, OP_EXCEPT, exc);
push();
while (n2) {
node *n3 = n2->car;
@@ -1359,30 +1457,29 @@ codegen(codegen_scope *s, node *tree, int val)
do {
if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) {
codegen(s, n4->car, VAL);
- genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
+ gen_move(s, cursp(), exc, 0);
push_n(2); pop_n(2); /* space for one arg and a block */
pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+ genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1);
}
else {
if (n4) {
codegen(s, n4->car, VAL);
}
else {
- genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError"))));
+ genop_2(s, OP_GETCONST, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "StandardError")));
push();
}
pop();
- genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1));
+ genop_2(s, OP_RESCUE, exc, cursp());
}
- distcheck(s, pos2);
- tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, val);
pos2 = tmp;
if (n4) {
n4 = n4->cdr;
}
} while (n4);
- pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ pos1 = genjmp(s, OP_JMP, 0);
dispatch_linked(s, pos2);
pop();
@@ -1393,21 +1490,20 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, n3->cdr->cdr->car, val);
if (val) pop();
}
- distcheck(s, exend);
- tmp = genop(s, MKOP_sBx(OP_JMP, exend));
+ tmp = genjmp(s, OP_JMP, exend);
exend = tmp;
n2 = n2->cdr;
push();
}
if (pos1) {
dispatch(s, pos1);
- genop(s, MKOP_A(OP_RAISE, exc));
+ genop_1(s, OP_RAISE, exc);
}
}
pop();
tree = tree->cdr;
dispatch(s, noexc);
- genop(s, MKOP_A(OP_POPERR, 1));
+ genop_1(s, OP_POPERR, 1);
if (tree->car) {
codegen(s, tree->car, val);
}
@@ -1424,15 +1520,13 @@ codegen(codegen_scope *s, node *tree, int val)
(nint(tree->cdr->cdr->car) == NODE_BEGIN &&
tree->cdr->cdr->cdr)) {
int idx;
- int epush = s->pc;
- genop(s, MKOP_Bx(OP_EPUSH, 0));
s->ensure_level++;
- codegen(s, tree->car, val);
idx = scope_body(s, tree->cdr, NOVAL);
- s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
+ genop_1(s, OP_EPUSH, idx);
+ codegen(s, tree->car, val);
s->ensure_level--;
- genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
+ genop_1(s, OP_EPOP, 1);
}
else { /* empty ensure ignored */
codegen(s, tree->car, val);
@@ -1443,7 +1537,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (val) {
int idx = lambda_body(s, tree, 1);
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
+ genop_2(s, OP_LAMBDA, cursp(), idx);
push();
}
break;
@@ -1452,7 +1546,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (val) {
int idx = lambda_body(s, tree, 1);
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
+ genop_2(s, OP_BLOCK, cursp(), idx);
push();
}
break;
@@ -1460,10 +1554,10 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_IF:
{
int pos1, pos2;
- node *e = tree->cdr->cdr->car;
+ node *elsepart = tree->cdr->cdr->car;
if (!tree->car) {
- codegen(s, e, val);
+ codegen(s, elsepart, val);
goto exit;
}
switch (nint(tree->car->car)) {
@@ -1474,27 +1568,27 @@ codegen(codegen_scope *s, node *tree, int val)
goto exit;
case NODE_FALSE:
case NODE_NIL:
- codegen(s, e, val);
+ codegen(s, elsepart, val);
goto exit;
}
codegen(s, tree->car, VAL);
pop();
- pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
+ pos1 = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
codegen(s, tree->cdr->car, val);
- if (e) {
+ if (elsepart) {
if (val) pop();
- pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+ pos2 = genjmp(s, OP_JMP, 0);
dispatch(s, pos1);
- codegen(s, e, val);
+ codegen(s, elsepart, val);
dispatch(s, pos2);
}
else {
if (val) {
pop();
- pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+ pos2 = genjmp(s, OP_JMP, 0);
dispatch(s, pos1);
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
dispatch(s, pos2);
push();
}
@@ -1511,7 +1605,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
+ pos = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
codegen(s, tree->cdr, val);
dispatch(s, pos);
}
@@ -1523,7 +1617,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ pos = genjmp2(s, OP_JMPIF, cursp(), 0, val);
codegen(s, tree->cdr, val);
dispatch(s, pos);
}
@@ -1533,14 +1627,14 @@ codegen(codegen_scope *s, node *tree, int val)
{
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
- lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ lp->pc0 = new_label(s);
+ lp->pc1 = genjmp(s, OP_JMP, 0);
lp->pc2 = new_label(s);
codegen(s, tree->cdr, NOVAL);
dispatch(s, lp->pc1);
codegen(s, tree->car, VAL);
pop();
- distcheck(s, lp->pc2 - s->pc);
- genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc));
+ genjmp2(s, OP_JMPIF, cursp(), lp->pc2, NOVAL);
loop_pop(s, val);
}
@@ -1550,14 +1644,14 @@ codegen(codegen_scope *s, node *tree, int val)
{
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
- lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ lp->pc0 = new_label(s);
+ lp->pc1 = genjmp(s, OP_JMP, 0);
lp->pc2 = new_label(s);
codegen(s, tree->cdr, NOVAL);
dispatch(s, lp->pc1);
codegen(s, tree->car, VAL);
pop();
- distcheck(s, lp->pc2 - s->pc);
- genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc));
+ genjmp2(s, OP_JMPNOT, cursp(), lp->pc2, NOVAL);
loop_pop(s, val);
}
@@ -1586,43 +1680,40 @@ codegen(codegen_scope *s, node *tree, int val)
while (n) {
codegen(s, n->car, VAL);
if (head) {
- genop(s, MKOP_AB(OP_MOVE, cursp(), head));
- push_n(2); pop_n(2); /* space for one arg and a block */
- pop();
+ gen_move(s, cursp(), head, 0);
+ push(); push(); pop(); pop(); pop();
if (nint(n->car->car) == NODE_SPLAT) {
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+ genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1);
}
else {
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1));
+ genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "===")), 1);
}
}
else {
pop();
}
- distcheck(s, pos2);
- tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, NOVAL);
pos2 = tmp;
n = n->cdr;
}
if (tree->car->car) {
- pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ pos1 = genjmp(s, OP_JMP, 0);
dispatch_linked(s, pos2);
}
codegen(s, tree->car->cdr, val);
if (val) pop();
- distcheck(s, pos3);
- tmp = genop(s, MKOP_sBx(OP_JMP, pos3));
+ tmp = genjmp(s, OP_JMP, pos3);
pos3 = tmp;
if (pos1) dispatch(s, pos1);
tree = tree->cdr;
}
if (val) {
int pos = cursp();
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
if (pos3) dispatch_linked(s, pos3);
if (head) pop();
if (cursp() != pos) {
- genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+ gen_move(s, cursp(), pos, 0);
}
push();
}
@@ -1654,7 +1745,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->cdr, val);
if (val) {
pop(); pop();
- genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE));
+ genop_1(s, OP_RANGE_INC, cursp());
push();
}
break;
@@ -1664,7 +1755,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->cdr, val);
if (val) {
pop(); pop();
- genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE));
+ genop_1(s, OP_RANGE_EXC, cursp());
push();
}
break;
@@ -1675,7 +1766,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ genop_2(s, OP_GETMCNST, cursp(), sym);
if (val) push();
}
break;
@@ -1684,8 +1775,8 @@ codegen(codegen_scope *s, node *tree, int val)
{
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ genop_1(s, OP_OCLASS, cursp());
+ genop_2(s, OP_GETMCNST, cursp(), sym);
if (val) push();
}
break;
@@ -1698,7 +1789,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (n >= 0) {
if (val) {
pop_n(n);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ genop_2(s, OP_ARRAY, cursp(), n);
push();
}
}
@@ -1709,21 +1800,47 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_HASH:
+ case NODE_KW_HASH:
{
int len = 0;
mrb_bool update = FALSE;
while (tree) {
- codegen(s, tree->car->car, val);
- codegen(s, tree->car->cdr, val);
- len++;
+ if (nint(tree->car->car->car) == NODE_KW_REST_ARGS) {
+ if (len > 0) {
+ pop_n(len*2);
+ if (!update) {
+ genop_2(s, OP_HASH, cursp(), len);
+ }
+ else {
+ pop();
+ genop_2(s, OP_HASHADD, cursp(), len);
+ }
+ push();
+ }
+ codegen(s, tree->car->cdr, VAL);
+ if (len > 0 || update) {
+ pop(); pop();
+ genop_1(s, OP_HASHCAT, cursp());
+ push();
+ }
+ update = TRUE;
+ len = 0;
+ }
+ else {
+ codegen(s, tree->car->car, val);
+ codegen(s, tree->car->cdr, val);
+ len++;
+ }
tree = tree->cdr;
- if (val && len == 126) {
+ if (val && len == 255) {
pop_n(len*2);
- genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
- if (update) {
+ if (!update) {
+ genop_2(s, OP_HASH, cursp(), len);
+ }
+ else {
pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+ genop_2(s, OP_HASHADD, cursp(), len);
}
push();
update = TRUE;
@@ -1732,10 +1849,14 @@ codegen(codegen_scope *s, node *tree, int val)
}
if (val) {
pop_n(len*2);
- genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
- if (update) {
+ if (!update) {
+ genop_2(s, OP_HASH, cursp(), len);
+ }
+ else {
pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+ if (len > 0) {
+ genop_2(s, OP_HASHADD, cursp(), len);
+ }
}
push();
}
@@ -1776,7 +1897,7 @@ codegen(codegen_scope *s, node *tree, int val)
n++;
}
else {
- genop(s, MKOP_A(OP_LOADNIL, rhs+n));
+ genop_1(s, OP_LOADNIL, rhs+n);
gen_assignment(s, t->car, rhs+n, NOVAL);
}
t = t->cdr;
@@ -1800,7 +1921,7 @@ codegen(codegen_scope *s, node *tree, int val)
else {
rn = len - post - n;
}
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
+ genop_3(s, OP_ARRAY2, cursp(), rhs+n, rn);
gen_assignment(s, t->car, cursp(), NOVAL);
n += rn;
}
@@ -1815,7 +1936,7 @@ codegen(codegen_scope *s, node *tree, int val)
}
pop_n(len);
if (val) {
- genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len));
+ genop_2(s, OP_ARRAY, rhs, len);
push();
}
}
@@ -1843,17 +1964,17 @@ codegen(codegen_scope *s, node *tree, int val)
int onerr, noexc, exc;
struct loopinfo *lp;
- onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
+ onerr = genjmp(s, OP_ONERR, 0);
lp = loop_push(s, LOOP_BEGIN);
lp->pc1 = onerr;
exc = cursp();
codegen(s, tree->car, VAL);
lp->type = LOOP_RESCUE;
- genop(s, MKOP_A(OP_POPERR, 1));
- noexc = genop(s, MKOP_Bx(OP_JMP, 0));
+ genop_1(s, OP_POPERR, 1);
+ noexc = genjmp(s, OP_JMP, 0);
dispatch(s, onerr);
- genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
- genop(s, MKOP_A(OP_LOADF, exc));
+ genop_1(s, OP_EXCEPT, exc);
+ genop_1(s, OP_LOADF, exc);
dispatch(s, noexc);
loop_pop(s, NOVAL);
}
@@ -1867,7 +1988,7 @@ codegen(codegen_scope *s, node *tree, int val)
push();
}
codegen(s, n->car, VAL); /* receiver */
- idx = new_msym(s, nsym(n->cdr->car));
+ idx = new_sym(s, nsym(n->cdr->car));
base = cursp()-1;
if (n->cdr->cdr->car) {
nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
@@ -1881,12 +2002,12 @@ codegen(codegen_scope *s, node *tree, int val)
}
}
/* copy receiver and arguments */
- genop(s, MKOP_AB(OP_MOVE, cursp(), base));
+ gen_move(s, cursp(), base, 1);
for (i=0; i<nargs; i++) {
- genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
+ gen_move(s, cursp()+i+1, base+i+1, 1);
}
push_n(nargs+2);pop_n(nargs+2); /* space for receiver, arguments and a block */
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ genop_3(s, OP_SEND, cursp(), idx, callargs);
push();
}
else {
@@ -1900,30 +2021,30 @@ codegen(codegen_scope *s, node *tree, int val)
pop();
if (val) {
if (vsp >= 0) {
- genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ gen_move(s, vsp, cursp(), 1);
}
- pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0));
+ pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
}
else {
- pos = genop_peep(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0), NOVAL);
+ pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
}
codegen(s, tree->cdr->cdr->car, VAL);
pop();
if (val && vsp >= 0) {
- genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ gen_move(s, vsp, cursp(), 1);
}
if (nint(tree->car->car) == NODE_CALL) {
if (callargs == CALL_MAXARGS) {
pop();
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ genop_1(s, OP_ARYPUSH, cursp());
}
else {
pop_n(callargs);
callargs++;
}
pop();
- idx = new_msym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ idx = new_sym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
+ genop_3(s, OP_SEND, cursp(), idx, callargs);
}
else {
gen_assignment(s, tree->car, cursp(), val);
@@ -1935,52 +2056,52 @@ codegen(codegen_scope *s, node *tree, int val)
push(); pop();
pop(); pop();
- idx = new_msym(s, sym);
if (len == 1 && name[0] == '+') {
- genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val);
+ gen_addsub(s, OP_ADD, cursp());
}
else if (len == 1 && name[0] == '-') {
- genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val);
+ gen_addsub(s, OP_SUB, cursp());
}
else if (len == 1 && name[0] == '*') {
- genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1));
+ genop_1(s, OP_MUL, cursp());
}
else if (len == 1 && name[0] == '/') {
- genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1));
+ genop_1(s, OP_DIV, cursp());
}
else if (len == 1 && name[0] == '<') {
- genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1));
+ genop_1(s, OP_LT, cursp());
}
else if (len == 2 && name[0] == '<' && name[1] == '=') {
- genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1));
+ genop_1(s, OP_LE, cursp());
}
else if (len == 1 && name[0] == '>') {
- genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1));
+ genop_1(s, OP_GT, cursp());
}
else if (len == 2 && name[0] == '>' && name[1] == '=') {
- genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1));
+ genop_1(s, OP_GE, cursp());
}
else {
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1));
+ idx = new_sym(s, sym);
+ genop_3(s, OP_SEND, cursp(), idx, 1);
}
if (callargs < 0) {
gen_assignment(s, tree->car, cursp(), val);
}
else {
if (val && vsp >= 0) {
- genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ gen_move(s, vsp, cursp(), 0);
}
if (callargs == CALL_MAXARGS) {
pop();
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ genop_1(s, OP_ARYPUSH, cursp());
}
else {
pop_n(callargs);
callargs++;
}
pop();
- idx = new_msym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ idx = new_sym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
+ genop_3(s, OP_SEND, cursp(), idx, callargs);
}
}
break;
@@ -1997,7 +2118,7 @@ codegen(codegen_scope *s, node *tree, int val)
s2 = s2->prev;
if (!s2) break;
}
- genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf)));
+ genop_2S(s, OP_ARGARY, cursp(), (lv & 0xf));
push(); push(); /* ARGARY pushes two values */
pop(); pop();
if (tree) {
@@ -2015,12 +2136,12 @@ codegen(codegen_scope *s, node *tree, int val)
pop();
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push(); pop();
}
pop_n(n+1);
if (sendv) n = CALL_MAXARGS;
- genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n));
+ genop_2(s, OP_SUPER, cursp(), n);
if (val) push();
}
break;
@@ -2037,14 +2158,14 @@ codegen(codegen_scope *s, node *tree, int val)
if (!s2) break;
}
if (s2) ainfo = s2->ainfo;
- genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)));
+ genop_2S(s, OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf));
push(); push(); pop(); /* ARGARY pushes two values */
if (tree && tree->cdr) {
codegen(s, tree->cdr, VAL);
pop();
}
pop(); pop();
- genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS));
+ genop_2(s, OP_SUPER, cursp(), CALL_MAXARGS);
if (val) push();
}
break;
@@ -2054,13 +2175,13 @@ codegen(codegen_scope *s, node *tree, int val)
gen_retval(s, tree);
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
if (s->loop) {
- genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN));
+ gen_return(s, OP_RETURN_BLK, cursp());
}
else {
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ gen_return(s, OP_RETURN, cursp());
}
if (val) push();
break;
@@ -2087,9 +2208,9 @@ codegen(codegen_scope *s, node *tree, int val)
}
push();pop(); /* space for a block */
pop_n(n+1);
- genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
+ genop_2S(s, OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf));
if (sendv) n = CALL_MAXARGS;
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n));
+ genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "call")), n);
if (val) push();
}
break;
@@ -2105,11 +2226,10 @@ codegen(codegen_scope *s, node *tree, int val)
}
else if (s->loop->type == LOOP_NORMAL) {
if (s->ensure_level > s->loop->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
}
codegen(s, tree, NOVAL);
- distcheck(s, s->loop->pc1 - s->pc);
- genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
+ genjmp(s, OP_JMP, s->loop->pc0);
}
else {
if (tree) {
@@ -2117,9 +2237,9 @@ codegen(codegen_scope *s, node *tree, int val)
pop();
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ gen_return(s, OP_RETURN, cursp());
}
if (val) push();
break;
@@ -2130,10 +2250,9 @@ codegen(codegen_scope *s, node *tree, int val)
}
else {
if (s->ensure_level > s->loop->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
}
- distcheck(s, s->loop->pc2 - s->pc);
- genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
+ genjmp(s, OP_JMP, s->loop->pc2);
}
if (val) push();
break;
@@ -2160,13 +2279,12 @@ codegen(codegen_scope *s, node *tree, int val)
}
else {
if (n > 0) {
- genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+ genop_1(s, OP_POPERR, n);
}
if (s->ensure_level > lp->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
+ genop_1(s, OP_EPOP, s->ensure_level - lp->ensure_level);
}
- distcheck(s, s->loop->pc1 - s->pc);
- genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
+ genjmp(s, OP_JMP, lp->pc0);
}
}
if (val) push();
@@ -2178,7 +2296,8 @@ codegen(codegen_scope *s, node *tree, int val)
int idx = lv_idx(s, nsym(tree));
if (idx > 0) {
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL);
+ gen_move(s, cursp(), idx, val);
+ if (val && on_eval(s)) genop_0(s, OP_NOP);
}
else {
int lv = 0;
@@ -2187,7 +2306,7 @@ codegen(codegen_scope *s, node *tree, int val)
while (up) {
idx = lv_idx(up, nsym(tree));
if (idx > 0) {
- genop_peep(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv), VAL);
+ genop_3(s, OP_GETUPVAR, cursp(), idx, lv);
break;
}
lv++;
@@ -2199,29 +2318,29 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_GVAR:
- if (val) {
+ {
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
- push();
+ genop_2(s, OP_GETGV, cursp(), sym);
+ if (val) push();
}
break;
case NODE_IVAR:
- if (val) {
+ {
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
- push();
+ genop_2(s, OP_GETIV, cursp(), sym);
+ if (val) push();
}
break;
case NODE_CVAR:
- if (val) {
+ {
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
- push();
+ genop_2(s, OP_GETCV, cursp(), sym);
+ if (val) push();
}
break;
@@ -2229,27 +2348,21 @@ codegen(codegen_scope *s, node *tree, int val)
{
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
- if (val) {
- push();
- }
+ genop_2(s, OP_GETCONST, cursp(), sym);
+ if (val) push();
}
break;
case NODE_DEFINED:
- codegen(s, tree, VAL);
+ codegen(s, tree, val);
break;
case NODE_BACK_REF:
if (val) {
- char buf[3];
- int sym;
+ char buf[] = {'$', nchar(tree)};
+ int sym = new_sym(s, mrb_intern(s->mrb, buf, sizeof(buf)));
- buf[0] = '$';
- buf[1] = nchar(tree);
- buf[2] = 0;
- sym = new_sym(s, mrb_intern_cstr(s->mrb, buf));
- genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ genop_2(s, OP_GETGV, cursp(), sym);
push();
}
break;
@@ -2262,7 +2375,7 @@ codegen(codegen_scope *s, node *tree, int val)
str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree)));
sym = new_sym(s, mrb_intern_str(mrb, str));
- genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ genop_2(s, OP_GETGV, cursp(), sym);
push();
}
break;
@@ -2272,7 +2385,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_BLOCK_ARG:
- codegen(s, tree, VAL);
+ codegen(s, tree, val);
break;
case NODE_INT:
@@ -2280,7 +2393,6 @@ codegen(codegen_scope *s, node *tree, int val)
char *p = (char*)tree->car;
int base = nint(tree->cdr->car);
mrb_int i;
- mrb_code co;
mrb_bool overflow;
i = readint_mrb_int(s, p, base, FALSE, &overflow);
@@ -2289,19 +2401,19 @@ codegen(codegen_scope *s, node *tree, int val)
double f = readint_float(s, p, base);
int off = new_lit(s, mrb_float_value(s->mrb, f));
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ genop_2(s, OP_LOADL, cursp(), off);
}
else
#endif
{
- if (i < MAXARG_sBx && i > -MAXARG_sBx) {
- co = MKOP_AsBx(OP_LOADI, cursp(), i);
- }
+ if (i == -1) genop_1(s, OP_LOADI__1, cursp());
+ else if (i < 0) genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
+ else if (i < 8) genop_1(s, OP_LOADI_0 + (uint8_t)i, cursp());
+ else if (i <= 0xffff) genop_2(s, OP_LOADI, cursp(), (uint16_t)i);
else {
int off = new_lit(s, mrb_fixnum_value(i));
- co = MKOP_ABx(OP_LOADL, cursp(), off);
+ genop_2(s, OP_LOADL, cursp(), off);
}
- genop(s, co);
}
push();
}
@@ -2314,7 +2426,7 @@ codegen(codegen_scope *s, node *tree, int val)
mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, f));
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ genop_2(s, OP_LOADL, cursp(), off);
push();
}
break;
@@ -2323,16 +2435,15 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_NEGATE:
{
nt = nint(tree->car);
- tree = tree->cdr;
switch (nt) {
#ifndef MRB_WITHOUT_FLOAT
case NODE_FLOAT:
if (val) {
- char *p = (char*)tree;
+ char *p = (char*)tree->cdr;
mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, -f));
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ genop_2(s, OP_LOADL, cursp(), off);
push();
}
break;
@@ -2340,10 +2451,9 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_INT:
if (val) {
- char *p = (char*)tree->car;
- int base = nint(tree->cdr->car);
+ char *p = (char*)tree->cdr->car;
+ int base = nint(tree->cdr->cdr->car);
mrb_int i;
- mrb_code co;
mrb_bool overflow;
i = readint_mrb_int(s, p, base, TRUE, &overflow);
@@ -2352,18 +2462,18 @@ codegen(codegen_scope *s, node *tree, int val)
double f = readint_float(s, p, base);
int off = new_lit(s, mrb_float_value(s->mrb, -f));
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ genop_2(s, OP_LOADL, cursp(), off);
}
else {
#endif
- if (i < MAXARG_sBx && i > -MAXARG_sBx) {
- co = MKOP_AsBx(OP_LOADI, cursp(), i);
+ if (i == -1) genop_1(s, OP_LOADI__1, cursp());
+ else if (i >= -0xffff) {
+ genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
}
else {
int off = new_lit(s, mrb_fixnum_value(i));
- co = MKOP_ABx(OP_LOADL, cursp(), off);
+ genop_2(s, OP_LOADL, cursp(), off);
}
- genop(s, co);
#ifndef MRB_WITHOUT_FLOAT
}
#endif
@@ -2373,13 +2483,11 @@ codegen(codegen_scope *s, node *tree, int val)
default:
if (val) {
- int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
-
- genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
- push();
+ int sym = new_sym(s, mrb_intern_lit(s->mrb, "-@"));
codegen(s, tree, VAL);
- pop(); pop();
- genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
+ pop();
+ genop_3(s, OP_SEND, cursp(), sym, 0);
+ push();
}
else {
codegen(s, tree, NOVAL);
@@ -2397,7 +2505,7 @@ codegen(codegen_scope *s, node *tree, int val)
int off = new_lit(s, mrb_str_new(s->mrb, p, len));
mrb_gc_arena_restore(s->mrb, ai);
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
}
break;
@@ -2410,7 +2518,7 @@ codegen(codegen_scope *s, node *tree, int val)
node *n = tree;
if (!n) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
break;
}
@@ -2419,7 +2527,7 @@ codegen(codegen_scope *s, node *tree, int val)
while (n) {
codegen(s, n->car, VAL);
pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
n = n->cdr;
}
@@ -2450,7 +2558,7 @@ codegen(codegen_scope *s, node *tree, int val)
int ai = mrb_gc_arena_save(s->mrb);
int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
- genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ genop_1(s, OP_LOADSELF, cursp());
push();
codegen(s, tree->car, VAL);
n = tree->cdr;
@@ -2461,14 +2569,14 @@ codegen(codegen_scope *s, node *tree, int val)
}
codegen(s, n->car, VAL);
pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
n = n->cdr;
}
push(); /* for block */
pop_n(3);
sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
+ genop_3(s, OP_SEND, cursp(), sym, 1);
if (val) push();
mrb_gc_arena_restore(s->mrb, ai);
}
@@ -2482,13 +2590,13 @@ codegen(codegen_scope *s, node *tree, int val)
int off = new_lit(s, mrb_str_new(s->mrb, p, len));
int sym;
- genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ genop_1(s, OP_LOADSELF, cursp());
push();
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push(); push();
pop_n(3);
sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
+ genop_3(s, OP_SEND, cursp(), sym, 1);
if (val) push();
mrb_gc_arena_restore(s->mrb, ai);
}
@@ -2504,24 +2612,24 @@ codegen(codegen_scope *s, node *tree, int val)
int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1));
int argc = 1;
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ genop_1(s, OP_OCLASS, cursp());
+ genop_2(s, OP_GETMCNST, cursp(), sym);
push();
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
if (p2 || p3) {
if (p2) { /* opt */
off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
push();
argc++;
if (p3) { /* enc */
off = new_lit(s, mrb_str_new(s->mrb, p3, 1));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
argc++;
}
@@ -2529,7 +2637,7 @@ codegen(codegen_scope *s, node *tree, int val)
push(); /* space for a block */
pop_n(argc+2);
sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+ genop_3(s, OP_SEND, cursp(), sym, argc);
mrb_gc_arena_restore(s->mrb, ai);
push();
}
@@ -2544,15 +2652,15 @@ codegen(codegen_scope *s, node *tree, int val)
int off;
char *p;
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ genop_1(s, OP_OCLASS, cursp());
+ genop_2(s, OP_GETMCNST, cursp(), sym);
push();
codegen(s, n->car, VAL);
n = n->cdr;
while (n) {
codegen(s, n->car, VAL);
pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
n = n->cdr;
}
@@ -2561,29 +2669,29 @@ codegen(codegen_scope *s, node *tree, int val)
p = (char*)n->car;
off = new_lit(s, mrb_str_new_cstr(s->mrb, p));
codegen(s, tree->car, VAL);
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
}
if (n->cdr->car) { /* opt */
char *p2 = (char*)n->cdr->car;
off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
argc++;
}
if (n->cdr->cdr) { /* enc */
char *p2 = (char*)n->cdr->cdr;
off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
argc++;
}
push(); /* space for a block */
pop_n(argc+2);
sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+ genop_3(s, OP_SEND, cursp(), sym, argc);
mrb_gc_arena_restore(s->mrb, ai);
push();
}
@@ -2603,7 +2711,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (val) {
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ genop_2(s, OP_LOADSYM, cursp(), sym);
push();
}
break;
@@ -2611,55 +2719,46 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_DSYM:
codegen(s, tree, val);
if (val) {
- gen_send_intern(s);
+ gen_intern(s);
}
break;
case NODE_SELF:
if (val) {
- genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ genop_1(s, OP_LOADSELF, cursp());
push();
}
break;
case NODE_NIL:
if (val) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
break;
case NODE_TRUE:
if (val) {
- genop(s, MKOP_A(OP_LOADT, cursp()));
+ genop_1(s, OP_LOADT, cursp());
push();
}
break;
case NODE_FALSE:
if (val) {
- genop(s, MKOP_A(OP_LOADF, cursp()));
+ genop_1(s, OP_LOADF, cursp());
push();
}
break;
case NODE_ALIAS:
{
- int a = new_msym(s, nsym(tree->car));
- int b = new_msym(s, nsym(tree->cdr));
- int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method"));
+ int a = new_sym(s, nsym(tree->car));
+ int b = new_sym(s, nsym(tree->cdr));
- genop(s, MKOP_A(OP_TCLASS, cursp()));
- push();
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a));
- push();
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b));
- push();
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push(); /* space for a block */
- pop_n(4);
- genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
+ genop_2(s, OP_ALIAS, a, b);
if (val) {
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
}
@@ -2667,41 +2766,15 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_UNDEF:
{
- int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method"));
- int num = 0;
node *t = tree;
- genop(s, MKOP_A(OP_TCLASS, cursp()));
- push();
while (t) {
- int symbol;
- if (num >= CALL_MAXARGS - 1) {
- pop_n(num);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num));
- while (t) {
- symbol = new_msym(s, nsym(t->car));
- push();
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
- pop();
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
- t = t->cdr;
- }
- num = CALL_MAXARGS;
- break;
- }
- symbol = new_msym(s, nsym(t->car));
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
- push();
+ int symbol = new_sym(s, nsym(t->car));
+ genop_1(s, OP_UNDEF, symbol);
t = t->cdr;
- num++;
- }
- push();pop(); /* space for a block */
- pop();
- if (num < CALL_MAXARGS) {
- pop_n(num);
}
- genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num));
if (val) {
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
}
@@ -2710,13 +2783,14 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_CLASS:
{
int idx;
+ node *body;
if (tree->car->car == (node*)0) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
else if (tree->car->car == (node*)1) {
- genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop_1(s, OP_OCLASS, cursp());
push();
}
else {
@@ -2726,14 +2800,20 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->cdr->car, VAL);
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
pop(); pop();
- idx = new_msym(s, nsym(tree->car->cdr));
- genop(s, MKOP_AB(OP_CLASS, cursp(), idx));
- idx = scope_body(s, tree->cdr->cdr->car, val);
- genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ idx = new_sym(s, nsym(tree->car->cdr));
+ genop_2(s, OP_CLASS, cursp(), idx);
+ body = tree->cdr->cdr->car;
+ if (nint(body->cdr->car) == NODE_BEGIN && body->cdr->cdr == NULL) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
+ idx = scope_body(s, body, val);
+ genop_2(s, OP_EXEC, cursp(), idx);
+ }
if (val) {
push();
}
@@ -2745,21 +2825,27 @@ codegen(codegen_scope *s, node *tree, int val)
int idx;
if (tree->car->car == (node*)0) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
else if (tree->car->car == (node*)1) {
- genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop_1(s, OP_OCLASS, cursp());
push();
}
else {
codegen(s, tree->car->car, VAL);
}
pop();
- idx = new_msym(s, nsym(tree->car->cdr));
- genop(s, MKOP_AB(OP_MODULE, cursp(), idx));
- idx = scope_body(s, tree->cdr->car, val);
- genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ idx = new_sym(s, nsym(tree->car->cdr));
+ genop_2(s, OP_MODULE, cursp(), idx);
+ if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
+ tree->cdr->car->cdr->cdr == NULL) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
+ idx = scope_body(s, tree->cdr->car, val);
+ genop_2(s, OP_EXEC, cursp(), idx);
+ }
if (val) {
push();
}
@@ -2772,9 +2858,15 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
- idx = scope_body(s, tree->cdr->car, val);
- genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ genop_1(s, OP_SCLASS, cursp());
+ if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
+ tree->cdr->car->cdr->cdr == NULL) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
+ idx = scope_body(s, tree->cdr->car, val);
+ genop_2(s, OP_EXEC, cursp(), idx);
+ }
if (val) {
push();
}
@@ -2783,17 +2875,17 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_DEF:
{
- int sym = new_msym(s, nsym(tree->car));
+ int sym = new_sym(s, nsym(tree->car));
int idx = lambda_body(s, tree->cdr, 0);
- genop(s, MKOP_A(OP_TCLASS, cursp()));
+ genop_1(s, OP_TCLASS, cursp());
push();
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ genop_2(s, OP_METHOD, cursp(), idx);
push(); pop();
pop();
- genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ genop_2(s, OP_DEF, cursp(), sym);
if (val) {
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ genop_2(s, OP_LOADSYM, cursp(), sym);
push();
}
}
@@ -2802,18 +2894,18 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_SDEF:
{
node *recv = tree->car;
- int sym = new_msym(s, nsym(tree->cdr->car));
+ int sym = new_sym(s, nsym(tree->cdr->car));
int idx = lambda_body(s, tree->cdr->cdr, 0);
codegen(s, recv, VAL);
pop();
- genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
+ genop_1(s, OP_SCLASS, cursp());
push();
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ genop_2(s, OP_METHOD, cursp(), idx);
pop();
- genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ genop_2(s, OP_DEF, cursp(), sym);
if (val) {
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ genop_2(s, OP_LOADSYM, cursp(), sym);
push();
}
}
@@ -2875,7 +2967,7 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
p->irep->plen = 0;
- p->scapa = MAXMSYMLEN;
+ p->scapa = 256;
p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa);
p->irep->slen = 0;
@@ -2900,18 +2992,16 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
}
p->ai = mrb_gc_arena_save(mrb);
- p->filename = prev->filename;
- if (p->filename) {
+ p->filename_sym = prev->filename_sym;
+ if (p->filename_sym) {
p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa);
}
p->lineno = prev->lineno;
/* debug setting */
p->debug_start_pos = 0;
- if (p->filename) {
+ if (p->filename_sym) {
mrb_debug_info_alloc(mrb, p->irep);
- p->irep->filename = p->filename;
- p->irep->lines = p->lines;
}
else {
p->irep->debug_info = NULL;
@@ -2929,34 +3019,26 @@ scope_finish(codegen_scope *s)
{
mrb_state *mrb = s->mrb;
mrb_irep *irep = s->irep;
- size_t fname_len;
- char *fname;
+ if (s->nlocals >= 0x3ff) {
+ codegen_error(s, "too many local variables");
+ }
irep->flags = 0;
if (s->iseq) {
irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc);
irep->ilen = s->pc;
- if (s->lines) {
- irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc);
- }
- else {
- irep->lines = 0;
- }
}
irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen);
irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen);
irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen);
- if (s->filename) {
- irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
- mrb_debug_info_append_file(mrb, irep, s->debug_start_pos, s->pc);
+ if (s->filename_sym) {
+ mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
+ const char *filename = mrb_sym2name_len(s->mrb, fname, NULL);
- fname_len = strlen(s->filename);
- fname = (char*)codegen_malloc(s, fname_len + 1);
- memcpy(fname, s->filename, fname_len);
- fname[fname_len] = '\0';
- irep->filename = fname;
- irep->own_filename = TRUE;
+ mrb_debug_info_append_file(s->mrb, s->irep->debug_info,
+ filename, s->lines, s->debug_start_pos, s->pc);
}
+ mrb_free(s->mrb, s->lines);
irep->nlocals = s->nlocals;
irep->nregs = s->nregs;
@@ -2971,7 +3053,7 @@ loop_push(codegen_scope *s, enum looptype t)
struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo));
p->type = t;
- p->pc1 = p->pc2 = p->pc3 = 0;
+ p->pc0 = p->pc1 = p->pc2 = p->pc3 = 0;
p->prev = s->loop;
p->ensure_level = s->ensure_level;
p->acc = cursp();
@@ -3013,27 +3095,26 @@ loop_break(codegen_scope *s, node *tree)
return;
}
if (n > 0) {
- genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+ genop_1(s, OP_POPERR, n);
}
if (loop->type == LOOP_NORMAL) {
int tmp;
if (s->ensure_level > s->loop->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
}
if (tree) {
- genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL);
+ gen_move(s, loop->acc, cursp(), 0);
}
- distcheck(s, s->loop->pc3);
- tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3));
+ tmp = genjmp(s, OP_JMP, loop->pc3);
loop->pc3 = tmp;
}
else {
if (!tree) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
- genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
+ gen_return(s, OP_BREAK, cursp());
}
}
}
@@ -3042,7 +3123,7 @@ static void
loop_pop(codegen_scope *s, int val)
{
if (val) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
dispatch_linked(s, s->loop->pc3);
s->loop = s->loop->prev;
@@ -3061,11 +3142,11 @@ generate_code(mrb_state *mrb, parser_state *p, int val)
}
scope->mrb = mrb;
scope->parser = p;
- scope->filename = p->filename;
+ scope->filename_sym = p->filename_sym;
scope->filename_index = p->current_filename_index;
MRB_TRY(&scope->jmp) {
- mrb->jmp = &scope->jmp;
+ mrb->jmp = &scope->jmp;
/* prepare irep */
codegen(scope, p->tree, val);
proc = mrb_proc_new(mrb, scope->irep);
@@ -3092,3 +3173,71 @@ mrb_generate_code(mrb_state *mrb, parser_state *p)
{
return generate_code(mrb, p, VAL);
}
+
+void
+mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
+{
+ int i;
+
+ if (irep->lv) {
+ mrb_free(mrb, irep->lv);
+ irep->lv = NULL;
+ }
+
+ for (i = 0; i < irep->rlen; ++i) {
+ mrb_irep_remove_lv(mrb, irep->reps[i]);
+ }
+}
+
+#undef OPCODE
+#define Z 1
+#define S 3
+#define W 4
+/* instruction sizes */
+uint8_t mrb_insn_size[] = {
+#define B 2
+#define BB 3
+#define BBB 4
+#define BS 4
+#define SB 4
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+#undef OPCODE
+#undef B
+#undef BB
+#undef BS
+#undef SB
+#undef BBB
+};
+/* EXT1 instruction sizes */
+uint8_t mrb_insn_size1[] = {
+#define B 3
+#define BB 4
+#define BBB 5
+#define BS 5
+#define SB 5
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+#undef OPCODE
+#undef B
+};
+/* EXT2 instruction sizes */
+uint8_t mrb_insn_size2[] = {
+#define B 2
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+#undef OPCODE
+#undef BB
+#undef BBB
+#undef BS
+#undef SB
+};
+/* EXT3 instruction sizes */
+#define BB 5
+#define BBB 6
+#define BS 4
+#define SB 5
+uint8_t mrb_insn_size3[] = {
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+};
diff --git a/mrbgems/mruby-compiler/core/node.h b/mrbgems/mruby-compiler/core/node.h
index 9636dd759..219bddab0 100644
--- a/mrbgems/mruby-compiler/core/node.h
+++ b/mrbgems/mruby-compiler/core/node.h
@@ -9,14 +9,11 @@
enum node_type {
NODE_METHOD,
- NODE_FBODY,
- NODE_CFUNC,
NODE_SCOPE,
NODE_BLOCK,
NODE_IF,
NODE_CASE,
NODE_WHEN,
- NODE_OPT_N,
NODE_WHILE,
NODE_UNTIL,
NODE_ITER,
@@ -40,12 +37,12 @@ enum node_type {
NODE_CALL,
NODE_SCALL,
NODE_FCALL,
- NODE_VCALL,
NODE_SUPER,
NODE_ZSUPER,
NODE_ARRAY,
NODE_ZARRAY,
NODE_HASH,
+ NODE_KW_HASH,
NODE_RETURN,
NODE_YIELD,
NODE_LVAR,
@@ -57,8 +54,6 @@ enum node_type {
NODE_NTH_REF,
NODE_BACK_REF,
NODE_MATCH,
- NODE_MATCH2,
- NODE_MATCH3,
NODE_INT,
NODE_FLOAT,
NODE_NEGATE,
@@ -71,10 +66,10 @@ enum node_type {
NODE_REGX,
NODE_DREGX,
NODE_DREGX_ONCE,
- NODE_LIST,
NODE_ARG,
- NODE_ARGSCAT,
- NODE_ARGSPUSH,
+ NODE_ARGS_TAIL,
+ NODE_KW_ARG,
+ NODE_KW_REST_ARGS,
NODE_SPLAT,
NODE_TO_ARY,
NODE_SVALUE,
@@ -88,26 +83,15 @@ enum node_type {
NODE_SCLASS,
NODE_COLON2,
NODE_COLON3,
- NODE_CREF,
NODE_DOT2,
NODE_DOT3,
- NODE_FLIP2,
- NODE_FLIP3,
- NODE_ATTRSET,
NODE_SELF,
NODE_NIL,
NODE_TRUE,
NODE_FALSE,
NODE_DEFINED,
- NODE_NEWLINE,
NODE_POSTEXE,
- NODE_ALLOCA,
- NODE_DMETHOD,
- NODE_BMETHOD,
- NODE_MEMO,
- NODE_IFUNC,
NODE_DSYM,
- NODE_ATTRASGN,
NODE_HEREDOC,
NODE_LITERAL_DELIM,
NODE_WORDS,
diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y
index 7f848b4df..ac59a8b0b 100644
--- a/mrbgems/mruby-compiler/core/parse.y
+++ b/mrbgems/mruby-compiler/core/parse.y
@@ -10,13 +10,7 @@
# define YYDEBUG 1
#endif
#define YYERROR_VERBOSE 1
-/*
- * Force yacc to use our memory management. This is a little evil because
- * the macros assume that "parser_state *p" is in scope
- */
-#define YYMALLOC(n) mrb_malloc(p->mrb, (n))
-#define YYFREE(o) mrb_free(p->mrb, (o))
-#define YYSTACK_USE_ALLOCA 0
+#define YYSTACK_USE_ALLOCA 1
#include <ctype.h>
#include <errno.h>
@@ -76,6 +70,24 @@ typedef unsigned int stack_type;
#define nint(x) ((node*)(intptr_t)(x))
#define intn(x) ((int)(intptr_t)(x))
+#if defined(MRB_COMPLEX_NUMBERS) || defined(MRB_RATIONAL_NUMBERS)
+ #define MRB_SUFFIX_SUPPORT
+
+ #ifdef MRB_RATIONAL_NUMBERS
+ #define NUM_SUFFIX_R (1<<0)
+ #else
+ #define NUM_SUFFIX_R 0
+ #endif
+
+ #ifdef MRB_COMPLEX_NUMBERS
+ #define NUM_SUFFIX_I (1<<1)
+ #else
+ #define NUM_SUFFIX_I 0
+ #endif
+
+ #define NUM_SUFFIX_ALL (NUM_SUFFIX_R | NUM_SUFFIX_I)
+#endif
+
static inline mrb_sym
intern_cstr_gen(parser_state *p, const char *s)
{
@@ -133,6 +145,10 @@ cons_gen(parser_state *p, node *car, node *cdr)
c->cdr = cdr;
c->lineno = p->lineno;
c->filename_index = p->current_filename_index;
+ /* beginning of next partial file; need to point the previous file */
+ if (p->lineno == 0 && p->current_filename_index > 0) {
+ c->filename_index-- ;
+ }
return c;
}
#define cons(a,b) cons_gen(p,(a),(b))
@@ -216,6 +232,26 @@ parser_strdup(parser_state *p, const char *s)
#undef strdup
#define strdup(s) parser_strdup(p, s)
+static void
+dump_int(uint16_t i, char *s)
+{
+ char *p = s;
+ char *t = s;
+
+ while (i > 0) {
+ *p++ = (i % 10)+'0';
+ i /= 10;
+ }
+ if (p == s) *p++ = '0';
+ *p = 0;
+ p--; /* point the last char */
+ while (t < p) {
+ char c = *t;
+ *t++ = *p;
+ *p-- = c;
+ }
+}
+
/* xxx ----------------------------- */
static node*
@@ -279,6 +315,20 @@ local_add(parser_state *p, mrb_sym sym)
}
}
+static void
+local_add_blk(parser_state *p, mrb_sym blk)
+{
+ /* allocate register for block */
+ local_add_f(p, blk ? blk : mrb_intern_lit(p->mrb, "&"));
+}
+
+static void
+local_add_kw(parser_state *p, mrb_sym kwd)
+{
+ /* allocate register for keywords hash */
+ local_add_f(p, kwd ? kwd : mrb_intern_lit(p->mrb, "**"));
+}
+
static node*
locals_node(parser_state *p)
{
@@ -568,6 +618,13 @@ new_hash(parser_state *p, node *a)
return cons((node*)NODE_HASH, a);
}
+/* (:kw_hash (k . v) (k . v)...) */
+static node*
+new_kw_hash(parser_state *p, node *a)
+{
+ return cons((node*)NODE_KW_HASH, a);
+}
+
/* (:sym . a) */
static node*
new_sym(parser_state *p, mrb_sym sym)
@@ -671,23 +728,80 @@ new_arg(parser_state *p, mrb_sym sym)
return cons((node*)NODE_ARG, nsym(sym));
}
-/* (m o r m2 b) */
+static void
+local_add_margs(parser_state *p, node *n)
+{
+ while (n) {
+ if (n->car->car == (node*)NODE_MASGN) {
+ node *t = n->car->cdr->cdr;
+
+ n->car->cdr->cdr = NULL;
+ while (t) {
+ local_add_f(p, sym(t->car));
+ t = t->cdr;
+ }
+ local_add_margs(p, n->car->cdr->car->car);
+ local_add_margs(p, n->car->cdr->car->cdr->cdr->car);
+ }
+ n = n->cdr;
+ }
+}
+
+/* (m o r m2 tail) */
/* m: (a b c) */
/* o: ((a . e1) (b . e2)) */
/* r: a */
/* m2: (a b c) */
/* b: a */
static node*
-new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk)
+new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, node *tail)
{
node *n;
- n = cons(m2, nsym(blk));
+ local_add_margs(p, m);
+ local_add_margs(p, m2);
+ n = cons(m2, tail);
n = cons(nsym(rest), n);
n = cons(opt, n);
return cons(m, n);
}
+/* (:args_tail keywords rest_keywords_sym block_sym) */
+static node*
+new_args_tail(parser_state *p, node *kws, node *kwrest, mrb_sym blk)
+{
+ node *k;
+
+ if (kws || kwrest) {
+ local_add_kw(p, (kwrest && kwrest->cdr)? sym(kwrest->cdr) : 0);
+ }
+
+ local_add_blk(p, blk);
+
+ // allocate register for keywords arguments
+ // order is for Proc#parameters
+ for (k = kws; k; k = k->cdr) {
+ if (!k->car->cdr->cdr->car) { // allocate required keywords
+ local_add_f(p, sym(k->car->cdr->car));
+ }
+ }
+ for (k = kws; k; k = k->cdr) {
+ if (k->car->cdr->cdr->car) { // allocate keywords with default
+ local_add_f(p, sym(k->car->cdr->car));
+ }
+ }
+
+ return list4((node*)NODE_ARGS_TAIL, kws, kwrest, nsym(blk));
+}
+
+/* (:kw_arg kw_sym def_arg) */
+static node*
+new_kw_arg(parser_state *p, mrb_sym kw, node *def_arg)
+{
+ mrb_assert(kw);
+ return list3((node*)NODE_KW_ARG, nsym(kw), def_arg);
+}
+
/* (:block_arg . a) */
static node*
new_block_arg(parser_state *p, node *a)
@@ -725,6 +839,13 @@ new_masgn(parser_state *p, node *a, node *b)
return cons((node*)NODE_MASGN, cons(a, b));
}
+/* (:masgn mlhs mrhs) no check */
+static node*
+new_masgn_param(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_MASGN, cons(a, b));
+}
+
/* (:asgn lhs rhs) */
static node*
new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
@@ -733,19 +854,57 @@ new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
return list4((node*)NODE_OP_ASGN, a, nsym(op), b);
}
+#ifdef MRB_COMPLEX_NUMBERS
+static node*
+new_imaginary(parser_state *p, node *imaginary)
+{
+ return new_call(p, new_const(p, intern_cstr("Kernel")), intern_cstr("Complex"), list1(list2(list3((node*)NODE_INT, (node*)strdup("0"), nint(10)), imaginary)), 1);
+}
+#endif
+
+#ifdef MRB_RATIONAL_NUMBERS
+static node*
+new_rational(parser_state *p, node *rational)
+{
+ return new_call(p, new_const(p, intern_cstr("Kernel")), intern_cstr("Rational"), list1(list1(rational)), 1);
+}
+#endif
+
/* (:int . i) */
static node*
-new_int(parser_state *p, const char *s, int base)
+new_int(parser_state *p, const char *s, int base, int suffix)
{
- return list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+ node* result = list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+#ifdef MRB_RATIONAL_NUMBERS
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+#endif
+#ifdef MRB_COMPLEX_NUMBERS
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+#endif
+ return result;
}
#ifndef MRB_WITHOUT_FLOAT
/* (:float . i) */
static node*
-new_float(parser_state *p, const char *s)
+new_float(parser_state *p, const char *s, int suffix)
{
- return cons((node*)NODE_FLOAT, (node*)strdup(s));
+ node* result = cons((node*)NODE_FLOAT, (node*)strdup(s));
+#ifdef MRB_RATIONAL_NUMBERS
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+#endif
+#ifdef MRB_COMPLEX_NUMBERS
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+#endif
+ return result;
}
#endif
@@ -763,6 +922,86 @@ new_dstr(parser_state *p, node *a)
return cons((node*)NODE_DSTR, a);
}
+static int
+string_node_p(node *n)
+{
+ return (int)((enum node_type)(intptr_t)n->car == NODE_STR);
+}
+
+static node*
+composite_string_node(parser_state *p, node *a, node *b)
+{
+ size_t newlen = (size_t)a->cdr + (size_t)b->cdr;
+ char *str = (char*)mrb_pool_realloc(p->pool, a->car, (size_t)a->cdr + 1, newlen + 1);
+ memcpy(str + (size_t)a->cdr, b->car, (size_t)b->cdr);
+ str[newlen] = '\0';
+ a->car = (node*)str;
+ a->cdr = (node*)newlen;
+ cons_free(b);
+ return a;
+}
+
+static node*
+concat_string(parser_state *p, node *a, node *b)
+{
+ if (string_node_p(a)) {
+ if (string_node_p(b)) {
+ /* a == NODE_STR && b == NODE_STR */
+ composite_string_node(p, a->cdr, b->cdr);
+ cons_free(b);
+ return a;
+ }
+ else {
+ /* a == NODE_STR && b == NODE_DSTR */
+
+ if (string_node_p(b->cdr->car)) {
+ /* a == NODE_STR && b->[NODE_STR, ...] */
+ composite_string_node(p, a->cdr, b->cdr->car->cdr);
+ cons_free(b->cdr->car);
+ b->cdr->car = a;
+ return b;
+ }
+ }
+ }
+ else {
+ node *c; /* last node of a */
+ for (c = a; c->cdr != NULL; c = c->cdr) ;
+
+ if (string_node_p(b)) {
+ /* a == NODE_DSTR && b == NODE_STR */
+ if (string_node_p(c->car)) {
+ /* a->[..., NODE_STR] && b == NODE_STR */
+ composite_string_node(p, c->car->cdr, b->cdr);
+ cons_free(b);
+ return a;
+ }
+
+ push(a, b);
+ return a;
+ }
+ else {
+ /* a == NODE_DSTR && b == NODE_DSTR */
+ if (string_node_p(c->car) && string_node_p(b->cdr->car)) {
+ /* a->[..., NODE_STR] && b->[NODE_STR, ...] */
+ node *d = b->cdr;
+ cons_free(b);
+ composite_string_node(p, c->car->cdr, d->car->cdr);
+ cons_free(d->car);
+ c->cdr = d->cdr;
+ cons_free(d);
+ return a;
+ }
+ else {
+ c->cdr = b->cdr;
+ cons_free(b);
+ return a;
+ }
+ }
+ }
+
+ return new_dstr(p, list2(a, b));
+}
+
/* (:str . (s . len)) */
static node*
new_xstr(parser_state *p, const char *s, int len)
@@ -1029,7 +1268,6 @@ heredoc_end(parser_state *p)
end_strterm(p);
p->lex_strterm = p->lex_strterm_before_heredoc;
p->lex_strterm_before_heredoc = NULL;
- p->heredoc_end_now = TRUE;
}
else {
/* next heredoc */
@@ -1112,7 +1350,7 @@ heredoc_end(parser_state *p)
%token <nd> tNTH_REF tBACK_REF
%token <num> tREGEXP_END
-%type <nd> singleton string string_rep string_interp xstring regexp
+%type <nd> singleton string string_fragment string_rep string_interp xstring regexp
%type <nd> literal numeric cpath symbol
%type <nd> top_compstmt top_stmts top_stmt
%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
@@ -1123,7 +1361,7 @@ heredoc_end(parser_state *p)
%type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs
%type <nd> command_asgn command_rhs mrhs superclass block_call block_command
%type <nd> f_block_optarg f_block_opt
-%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
+%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_margs
%type <nd> assoc_list assocs assoc undef_list backref for_var
%type <nd> block_param opt_block_param block_param_def f_opt
%type <nd> bv_decls opt_bv_decl bvar f_larglist lambda_body
@@ -1134,6 +1372,10 @@ heredoc_end(parser_state *p)
%type <nd> heredoc words symbols
%type <num> call_op call_op2 /* 0:'&.', 1:'.', 2:'::' */
+%type <nd> args_tail opt_args_tail f_kwarg f_kw f_kwrest
+%type <nd> f_block_kwarg f_block_kw block_args_tail opt_block_args_tail
+%type <id> f_label
+
%token tUPLUS /* unary+ */
%token tUMINUS /* unary- */
%token tPOW /* ** */
@@ -1159,6 +1401,7 @@ heredoc_end(parser_state *p)
%token tLBRACE /* { */
%token tLBRACE_ARG /* { */
%token tSTAR /* * */
+%token tDSTAR /* ** */
%token tAMPER /* & */
%token tLAMBDA /* -> */
%token tANDDOT /* &. */
@@ -1736,6 +1979,7 @@ op : '|' { $$ = intern_c('|'); }
| '/' { $$ = intern_c('/'); }
| '%' { $$ = intern_c('%'); }
| tPOW { $$ = intern("**",2); }
+ | tDSTAR { $$ = intern("**",2); }
| '!' { $$ = intern_c('!'); }
| '~' { $$ = intern_c('~'); }
| tUPLUS { $$ = intern("+@",2); }
@@ -1944,11 +2188,11 @@ aref_args : none
}
| args comma assocs trailer
{
- $$ = push($1, new_hash(p, $3));
+ $$ = push($1, new_kw_hash(p, $3));
}
| assocs trailer
{
- $$ = cons(new_hash(p, $1), 0);
+ $$ = cons(new_kw_hash(p, $1), 0);
NODE_LINENO($$, $1);
}
;
@@ -1984,12 +2228,12 @@ opt_call_args : none
}
| args comma assocs ','
{
- $$ = cons(push($1, new_hash(p, $3)), 0);
+ $$ = cons(push($1, new_kw_hash(p, $3)), 0);
NODE_LINENO($$, $1);
}
| assocs ','
{
- $$ = cons(list1(new_hash(p, $1)), 0);
+ $$ = cons(list1(new_kw_hash(p, $1)), 0);
NODE_LINENO($$, $1);
}
;
@@ -2007,12 +2251,12 @@ call_args : command
}
| assocs opt_block_arg
{
- $$ = cons(list1(new_hash(p, $1)), $2);
+ $$ = cons(list1(new_kw_hash(p, $1)), $2);
NODE_LINENO($$, $1);
}
| args comma assocs opt_block_arg
{
- $$ = cons(push($1, new_hash(p, $3)), $4);
+ $$ = cons(push($1, new_kw_hash(p, $3)), $4);
NODE_LINENO($$, $1);
}
| block_arg
@@ -2393,43 +2637,24 @@ for_var : lhs
| mlhs
;
-f_marg : f_norm_arg
- {
- $$ = new_arg(p, $1);
- }
- | tLPAREN f_margs rparen
- {
- $$ = new_masgn(p, $2, 0);
- }
- ;
-
-f_marg_list : f_marg
- {
- $$ = list1($1);
- }
- | f_marg_list ',' f_marg
- {
- $$ = push($1, $3);
- }
- ;
-
-f_margs : f_marg_list
+f_margs : f_arg
{
$$ = list3($1,0,0);
}
- | f_marg_list ',' tSTAR f_norm_arg
+ | f_arg ',' tSTAR f_norm_arg
{
$$ = list3($1, new_arg(p, $4), 0);
}
- | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list
+ | f_arg ',' tSTAR f_norm_arg ',' f_arg
{
$$ = list3($1, new_arg(p, $4), $6);
}
- | f_marg_list ',' tSTAR
+ | f_arg ',' tSTAR
{
+ local_add_f(p, 0);
$$ = list3($1, (node*)-1, 0);
}
- | f_marg_list ',' tSTAR ',' f_marg_list
+ | f_arg ',' tSTAR ',' f_arg
{
$$ = list3($1, (node*)-1, $5);
}
@@ -2437,96 +2662,134 @@ f_margs : f_marg_list
{
$$ = list3(0, new_arg(p, $2), 0);
}
- | tSTAR f_norm_arg ',' f_marg_list
+ | tSTAR f_norm_arg ',' f_arg
{
$$ = list3(0, new_arg(p, $2), $4);
}
| tSTAR
{
+ local_add_f(p, 0);
$$ = list3(0, (node*)-1, 0);
}
- | tSTAR ',' f_marg_list
+ | tSTAR ','
{
- $$ = list3(0, (node*)-1, $3);
+ local_add_f(p, 0);
+ }
+ f_arg
+ {
+ $$ = list3(0, (node*)-1, $4);
}
;
-block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg
+block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg
+ {
+ $$ = new_args_tail(p, $1, $3, $4);
+ }
+ | f_block_kwarg opt_f_block_arg
+ {
+ $$ = new_args_tail(p, $1, 0, $2);
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ $$ = new_args_tail(p, 0, $1, $2);
+ }
+ | f_block_arg
+ {
+ $$ = new_args_tail(p, 0, 0, $1);
+ }
+ ;
+
+opt_block_args_tail : ',' block_args_tail
+ {
+ $$ = $2;
+ }
+ | /* none */
+ {
+ $$ = new_args_tail(p, 0, 0, 0);
+ }
+ ;
+
+block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail
{
$$ = new_args(p, $1, $3, $5, 0, $6);
}
- | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, $1, $3, $5, $7, $8);
}
- | f_arg ',' f_block_optarg opt_f_block_arg
+ | f_arg ',' f_block_optarg opt_block_args_tail
{
$$ = new_args(p, $1, $3, 0, 0, $4);
}
- | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_block_optarg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, $1, $3, 0, $5, $6);
}
- | f_arg ',' f_rest_arg opt_f_block_arg
+ | f_arg ',' f_rest_arg opt_block_args_tail
{
$$ = new_args(p, $1, 0, $3, 0, $4);
}
- | f_arg ','
+ | f_arg ',' opt_block_args_tail
{
- $$ = new_args(p, $1, 0, 0, 0, 0);
+ $$ = new_args(p, $1, 0, 0, 0, $3);
}
- | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, $1, 0, $3, $5, $6);
}
- | f_arg opt_f_block_arg
+ | f_arg opt_block_args_tail
{
$$ = new_args(p, $1, 0, 0, 0, $2);
}
- | f_block_optarg ',' f_rest_arg opt_f_block_arg
+ | f_block_optarg ',' f_rest_arg opt_block_args_tail
{
$$ = new_args(p, 0, $1, $3, 0, $4);
}
- | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, 0, $1, $3, $5, $6);
}
- | f_block_optarg opt_f_block_arg
+ | f_block_optarg opt_block_args_tail
{
$$ = new_args(p, 0, $1, 0, 0, $2);
}
- | f_block_optarg ',' f_arg opt_f_block_arg
+ | f_block_optarg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, 0, $1, 0, $3, $4);
}
- | f_rest_arg opt_f_block_arg
+ | f_rest_arg opt_block_args_tail
{
$$ = new_args(p, 0, 0, $1, 0, $2);
}
- | f_rest_arg ',' f_arg opt_f_block_arg
+ | f_rest_arg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, 0, 0, $1, $3, $4);
}
- | f_block_arg
+ | block_args_tail
{
$$ = new_args(p, 0, 0, 0, 0, $1);
}
;
opt_block_param : none
- | block_param_def
{
+ local_add_blk(p, 0);
+ $$ = 0;
+ }
+ | block_param_def
+ {
p->cmd_start = TRUE;
$$ = $1;
}
;
-block_param_def : '|' opt_bv_decl '|'
+block_param_def : '|' {local_add_blk(p, 0);} opt_bv_decl '|'
{
$$ = 0;
}
| tOROP
{
+ local_add_blk(p, 0);
$$ = 0;
}
| '|' block_param opt_bv_decl '|'
@@ -2739,7 +3002,14 @@ literal : numeric
| symbols
;
-string : tCHAR
+string : string_fragment
+ | string string_fragment
+ {
+ $$ = concat_string(p, $1, $2);
+ }
+ ;
+
+string_fragment : tCHAR
| tSTRING
| tSTRING_BEG tSTRING
{
@@ -2961,7 +3231,7 @@ var_ref : variable
}
| keyword__FILE__
{
- const char *fn = p->filename;
+ const char *fn = mrb_sym2name_len(p->mrb, p->filename_sym, NULL);
if (!fn) {
fn = "(null)";
}
@@ -2971,8 +3241,8 @@ var_ref : variable
{
char buf[16];
- snprintf(buf, sizeof(buf), "%d", p->lineno);
- $$ = new_int(p, buf, 10);
+ dump_int(p->lineno, buf);
+ $$ = new_int(p, buf, 10, 0);
}
| keyword__ENCODING__
{
@@ -3021,65 +3291,151 @@ f_arglist : '(' f_args rparen
}
;
-f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg
+f_label : tIDENTIFIER tLABEL_TAG
+ ;
+
+f_kw : f_label arg
+ {
+ void_expr_error(p, $2);
+ $$ = new_kw_arg(p, $1, $2);
+ }
+ | f_label
+ {
+ $$ = new_kw_arg(p, $1, 0);
+ }
+ ;
+
+f_block_kw : f_label primary_value
+ {
+ $$ = new_kw_arg(p, $1, $2);
+ }
+ | f_label
+ {
+ $$ = new_kw_arg(p, $1, 0);
+ }
+ ;
+
+f_block_kwarg : f_block_kw
+ {
+ $$ = list1($1);
+ }
+ | f_block_kwarg ',' f_block_kw
+ {
+ $$ = push($1, $3);
+ }
+ ;
+
+f_kwarg : f_kw
+ {
+ $$ = list1($1);
+ }
+ | f_kwarg ',' f_kw
+ {
+ $$ = push($1, $3);
+ }
+ ;
+
+kwrest_mark : tPOW
+ | tDSTAR
+ ;
+
+f_kwrest : kwrest_mark tIDENTIFIER
+ {
+ $$ = cons((node*)NODE_KW_REST_ARGS, nsym($2));
+ }
+ | kwrest_mark
+ {
+ $$ = cons((node*)NODE_KW_REST_ARGS, 0);
+ }
+ ;
+
+args_tail : f_kwarg ',' f_kwrest opt_f_block_arg
+ {
+ $$ = new_args_tail(p, $1, $3, $4);
+ }
+ | f_kwarg opt_f_block_arg
+ {
+ $$ = new_args_tail(p, $1, 0, $2);
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ $$ = new_args_tail(p, 0, $1, $2);
+ }
+ | f_block_arg
+ {
+ $$ = new_args_tail(p, 0, 0, $1);
+ }
+ ;
+
+opt_args_tail : ',' args_tail
+ {
+ $$ = $2;
+ }
+ | /* none */
+ {
+ $$ = new_args_tail(p, 0, 0, 0);
+ }
+ ;
+
+f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
{
$$ = new_args(p, $1, $3, $5, 0, $6);
}
- | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
{
$$ = new_args(p, $1, $3, $5, $7, $8);
}
- | f_arg ',' f_optarg opt_f_block_arg
+ | f_arg ',' f_optarg opt_args_tail
{
$$ = new_args(p, $1, $3, 0, 0, $4);
}
- | f_arg ',' f_optarg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_optarg ',' f_arg opt_args_tail
{
$$ = new_args(p, $1, $3, 0, $5, $6);
}
- | f_arg ',' f_rest_arg opt_f_block_arg
+ | f_arg ',' f_rest_arg opt_args_tail
{
$$ = new_args(p, $1, 0, $3, 0, $4);
}
- | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_rest_arg ',' f_arg opt_args_tail
{
$$ = new_args(p, $1, 0, $3, $5, $6);
}
- | f_arg opt_f_block_arg
+ | f_arg opt_args_tail
{
$$ = new_args(p, $1, 0, 0, 0, $2);
}
- | f_optarg ',' f_rest_arg opt_f_block_arg
+ | f_optarg ',' f_rest_arg opt_args_tail
{
$$ = new_args(p, 0, $1, $3, 0, $4);
}
- | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
{
$$ = new_args(p, 0, $1, $3, $5, $6);
}
- | f_optarg opt_f_block_arg
+ | f_optarg opt_args_tail
{
$$ = new_args(p, 0, $1, 0, 0, $2);
}
- | f_optarg ',' f_arg opt_f_block_arg
+ | f_optarg ',' f_arg opt_args_tail
{
$$ = new_args(p, 0, $1, 0, $3, $4);
}
- | f_rest_arg opt_f_block_arg
+ | f_rest_arg opt_args_tail
{
$$ = new_args(p, 0, 0, $1, 0, $2);
}
- | f_rest_arg ',' f_arg opt_f_block_arg
+ | f_rest_arg ',' f_arg opt_args_tail
{
$$ = new_args(p, 0, 0, $1, $3, $4);
}
- | f_block_arg
+ | args_tail
{
$$ = new_args(p, 0, 0, 0, 0, $1);
}
| /* none */
{
- local_add_f(p, 0);
+ local_add_f(p, mrb_intern_lit(p->mrb, "&"));
$$ = new_args(p, 0, 0, 0, 0, 0);
}
;
@@ -3121,9 +3477,15 @@ f_arg_item : f_norm_arg
{
$$ = new_arg(p, $1);
}
- | tLPAREN f_margs rparen
+ | tLPAREN
+ {
+ $<nd>$ = local_switch(p);
+ }
+ f_margs rparen
{
- $$ = new_masgn(p, $2, 0);
+ $$ = new_masgn_param(p, $3, p->locals->car);
+ local_resume(p, $<nd>2);
+ local_add_f(p, 0);
}
;
@@ -3189,7 +3551,7 @@ f_rest_arg : restarg_mark tIDENTIFIER
}
| restarg_mark
{
- local_add_f(p, 0);
+ local_add_f(p, mrb_intern_lit(p->mrb, "*"));
$$ = -1;
}
;
@@ -3200,7 +3562,6 @@ blkarg_mark : '&'
f_block_arg : blkarg_mark tIDENTIFIER
{
- local_add_f(p, $2);
$$ = $2;
}
;
@@ -3211,7 +3572,6 @@ opt_f_block_arg : ',' f_block_arg
}
| none
{
- local_add_f(p, 0);
$$ = 0;
}
;
@@ -3275,7 +3635,7 @@ assoc : arg tASSOC arg
void_expr_error(p, $3);
$$ = cons(new_sym(p, $1), $3);
}
- | string tLABEL_TAG arg
+ | string_fragment tLABEL_TAG arg
{
void_expr_error(p, $3);
if ($1->car == (node*)NODE_DSTR) {
@@ -3285,6 +3645,11 @@ assoc : arg tASSOC arg
$$ = cons(new_sym(p, new_strsym(p, $1)), $3);
}
}
+ | tDSTAR arg
+ {
+ void_expr_error(p, $2);
+ $$ = cons(cons((node*)NODE_KW_REST_ARGS, 0), $2);
+ }
;
operation : tIDENTIFIER
@@ -3375,8 +3740,9 @@ yyerror(parser_state *p, const char *s)
if (! p->capture_errors) {
#ifndef MRB_DISABLE_STDIO
- if (p->filename) {
- fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
+ if (p->filename_sym) {
+ const char *filename = mrb_sym2name_len(p->mrb, p->filename_sym, NULL);
+ fprintf(stderr, "%s:%d:%d: %s\n", filename, p->lineno, p->column, s);
}
else {
fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
@@ -3395,11 +3761,13 @@ yyerror(parser_state *p, const char *s)
}
static void
-yyerror_i(parser_state *p, const char *fmt, int i)
+yyerror_c(parser_state *p, const char *msg, char c)
{
char buf[256];
- snprintf(buf, sizeof(buf), fmt, i);
+ strncpy(buf, msg, sizeof(buf) - 2);
+ buf[sizeof(buf) - 2] = '\0';
+ strncat(buf, &c, 1);
yyerror(p, buf);
}
@@ -3411,11 +3779,12 @@ yywarn(parser_state *p, const char *s)
if (! p->capture_errors) {
#ifndef MRB_DISABLE_STDIO
- if (p->filename) {
- fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
+ if (p->filename_sym) {
+ const char *filename = mrb_sym2name_len(p->mrb, p->filename_sym, NULL);
+ fprintf(stderr, "%s:%d:%d: warning: %s\n", filename, p->lineno, p->column, s);
}
else {
- fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
+ fprintf(stderr, "line %d:%d: warning: %s\n", p->lineno, p->column, s);
}
#endif
}
@@ -3437,11 +3806,14 @@ yywarning(parser_state *p, const char *s)
}
static void
-yywarning_s(parser_state *p, const char *fmt, const char *s)
+yywarning_s(parser_state *p, const char *msg, const char *s)
{
char buf[256];
- snprintf(buf, sizeof(buf), fmt, s);
+ strncpy(buf, msg, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ strncat(buf, ": ", sizeof(buf) - strlen(buf) - 1);
+ strncat(buf, s, sizeof(buf) - strlen(buf) - 1);
yywarning(p, buf);
}
@@ -3450,13 +3822,13 @@ backref_error(parser_state *p, node *n)
{
int c;
- c = (int)(intptr_t)n->car;
+ c = intn(n->car);
if (c == NODE_NTH_REF) {
- yyerror_i(p, "can't set variable $%" MRB_PRId, (int)(intptr_t)n->cdr);
+ yyerror_c(p, "can't set variable $", (char)intn(n->cdr)+'0');
}
else if (c == NODE_BACK_REF) {
- yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr);
+ yyerror_c(p, "can't set variable $", (char)intn(n->cdr));
}
else {
mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c));
@@ -3469,7 +3841,7 @@ void_expr_error(parser_state *p, node *n)
int c;
if (n == NULL) return;
- c = (int)(intptr_t)n->car;
+ c = intn(n->car);
switch (c) {
case NODE_BREAK:
case NODE_RETURN:
@@ -3480,8 +3852,10 @@ void_expr_error(parser_state *p, node *n)
break;
case NODE_AND:
case NODE_OR:
- void_expr_error(p, n->cdr->car);
- void_expr_error(p, n->cdr->cdr);
+ if (n->cdr) {
+ void_expr_error(p, n->cdr->car);
+ void_expr_error(p, n->cdr->cdr);
+ }
break;
case NODE_BEGIN:
if (n->cdr) {
@@ -3508,7 +3882,7 @@ nextc(parser_state *p)
if (p->pb) {
node *tmp;
- c = (int)(intptr_t)p->pb->car;
+ c = intn(p->pb->car);
tmp = p->pb;
p->pb = p->pb->cdr;
cons_free(tmp);
@@ -3532,14 +3906,6 @@ nextc(parser_state *p)
if (c >= 0) {
p->column++;
}
- if (c == '\r') {
- c = nextc(p);
- if (c != '\n') {
- pushback(p, c);
- return '\r';
- }
- return c;
- }
return c;
eof:
@@ -3557,7 +3923,7 @@ pushback(parser_state *p, int c)
if (c >= 0) {
p->column--;
}
- p->pb = cons((node*)(intptr_t)c, p->pb);
+ p->pb = cons(nint(c), p->pb);
}
static void
@@ -3582,7 +3948,7 @@ peekc_n(parser_state *p, int n)
c0 = nextc(p);
if (c0 == -1) return c0; /* do not skip partial EOF */
if (c0 >= 0) --p->column;
- list = push(list, (node*)(intptr_t)c0);
+ list = push(list, nint(c0));
} while(n--);
if (p->pb) {
p->pb = append((node*)list, p->pb);
@@ -4006,8 +4372,17 @@ parse_string(parser_state *p)
}
if (c < 0) {
char buf[256];
- snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term);
- yyerror(p, buf);
+ const char s1[] = "can't find heredoc delimiter \"";
+ const char s2[] = "\" anywhere before EOF";
+
+ if (sizeof(s1)+sizeof(s2)+strlen(hinf->term)+1 >= sizeof(buf)) {
+ yyerror(p, "can't find heredoc delimiter anywhere before EOF");
+ } else {
+ strcpy(buf, s1);
+ strcat(buf, hinf->term);
+ strcat(buf, s2);
+ yyerror(p, buf);
+ }
return 0;
}
pylval.nd = new_str(p, tok(p), toklen(p));
@@ -4019,11 +4394,11 @@ parse_string(parser_state *p)
}
else if (c == beg) {
nest_level++;
- p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
+ p->lex_strterm->cdr->car = nint(nest_level);
}
else if (c == end) {
nest_level--;
- p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
+ p->lex_strterm->cdr->car = nint(nest_level);
}
else if (c == '\\') {
c = nextc(p);
@@ -4157,9 +4532,14 @@ parse_string(parser_state *p)
pushback(p, re_opt);
if (toklen(p)) {
char msg[128];
+
+ strcpy(msg, "unknown regexp option");
tokfix(p);
- snprintf(msg, sizeof(msg), "unknown regexp option%s - %s",
- toklen(p) > 1 ? "s" : "", tok(p));
+ if (toklen(p) > 1) {
+ strcat(msg, "s");
+ }
+ strcat(msg, " - ");
+ strncat(msg, tok(p), sizeof(msg) - strlen(msg) - 1);
yyerror(p, msg);
}
if (f != 0) {
@@ -4190,6 +4570,45 @@ parse_string(parser_state *p)
return tSTRING;
}
+#ifdef MRB_SUFFIX_SUPPORT
+static int
+number_literal_suffix(parser_state *p, int mask)
+{
+ int c, result = 0;
+ node *list = 0;
+ int column = p->column;
+
+ while ((c = nextc(p)) != -1) {
+ list = push(list, (node*)(intptr_t)c);
+
+ if ((mask & NUM_SUFFIX_I) && c == 'i') {
+ result |= (mask & NUM_SUFFIX_I);
+ mask &= ~NUM_SUFFIX_I;
+ /* r after i, rational of complex is disallowed */
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if ((mask & NUM_SUFFIX_R) && c == 'r') {
+ result |= (mask & NUM_SUFFIX_R);
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if (!ISASCII(c) || ISALPHA(c) || c == '_') {
+ p->column = column;
+ if (p->pb) {
+ p->pb = append((node*)list, p->pb);
+ }
+ else {
+ p->pb = list;
+ }
+ return 0;
+ }
+ pushback(p, c);
+ break;
+ }
+ return result;
+}
+#endif
static int
heredoc_identifier(parser_state *p)
@@ -4310,52 +4729,68 @@ parser_yylex(parser_state *p)
/* fall through */
case -2: /* end of a file */
case '\n':
- maybe_heredoc:
+ maybe_heredoc:
heredoc_treat_nextline(p);
- switch (p->lstate) {
- case EXPR_BEG:
- case EXPR_FNAME:
- case EXPR_DOT:
- case EXPR_CLASS:
- case EXPR_VALUE:
- p->lineno++;
- p->column = 0;
- if (p->parsing_heredoc != NULL) {
- if (p->lex_strterm) {
- return parse_string(p);
+ switch (p->lstate) {
+ case EXPR_BEG:
+ case EXPR_FNAME:
+ case EXPR_DOT:
+ case EXPR_CLASS:
+ case EXPR_VALUE:
+ p->lineno++;
+ p->column = 0;
+ if (p->parsing_heredoc != NULL) {
+ if (p->lex_strterm) {
+ return parse_string(p);
+ }
}
- }
- goto retry;
- default:
- break;
- }
- if (p->parsing_heredoc != NULL) {
- return '\n';
- }
- while ((c = nextc(p))) {
- switch (c) {
- case ' ': case '\t': case '\f': case '\r':
- case '\13': /* '\v' */
- space_seen = 1;
+ goto retry;
+ default:
break;
- case '.':
- if ((c = nextc(p)) != '.') {
- pushback(p, c);
- pushback(p, '.');
+ }
+ if (p->parsing_heredoc != NULL) {
+ return '\n';
+ }
+ while ((c = nextc(p))) {
+ switch (c) {
+ case ' ': case '\t': case '\f': case '\r':
+ case '\13': /* '\v' */
+ space_seen = 1;
+ break;
+ case '#': /* comment as a whitespace */
+ pushback(p, '#');
goto retry;
+ case '\n': /* consecutive newlines */
+ p->lineno++;
+ p->column = 0;
+ pushback(p, '\n');
+ goto retry;
+ case '.':
+ if (!peek(p, '.')) {
+ pushback(p, '.');
+ goto retry;
+ }
+ pushback(p, c);
+ goto normal_newline;
+ case '&':
+ if (peek(p, '.')) {
+ pushback(p, '&');
+ goto retry;
+ }
+ pushback(p, c);
+ goto normal_newline;
+ case -1: /* EOF */
+ case -2: /* end of a file */
+ goto normal_newline;
+ default:
+ pushback(p, c);
+ goto normal_newline;
}
- case -1: /* EOF */
- case -2: /* end of a file */
- goto normal_newline;
- default:
- pushback(p, c);
- goto normal_newline;
}
- }
normal_newline:
- p->cmd_start = TRUE;
- p->lstate = EXPR_BEG;
- return '\n';
+ p->cmd_start = TRUE;
+ p->lstate = EXPR_BEG;
+ return '\n';
case '*':
if ((c = nextc(p)) == '*') {
@@ -4365,7 +4800,16 @@ parser_yylex(parser_state *p)
return tOP_ASGN;
}
pushback(p, c);
- c = tPOW;
+ if (IS_SPCARG(c)) {
+ yywarning(p, "'**' interpreted as argument prefix");
+ c = tDSTAR;
+ }
+ else if (IS_BEG()) {
+ c = tDSTAR;
+ }
+ else {
+ c = tPOW; /* "**", "argument prefix" */
+ }
}
else {
if (c == '=') {
@@ -4578,7 +5022,10 @@ parser_yylex(parser_state *p)
}
if (c2) {
char buf[256];
- snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2);
+ char cc = (char)c2;
+
+ strcpy(buf, "invalid character syntax; use ?\\");
+ strncat(buf, &cc, 1);
yyerror(p, buf);
}
}
@@ -4589,10 +5036,10 @@ parser_yylex(parser_state *p)
}
newtok(p);
/* need support UTF-8 if configured */
- if ((isalnum(c) || c == '_')) {
+ if ((ISALNUM(c) || c == '_')) {
int c2 = nextc(p);
pushback(p, c2);
- if ((isalnum(c2) || c2 == '_')) {
+ if ((ISALNUM(c2) || c2 == '_')) {
goto ternary;
}
}
@@ -4752,6 +5199,7 @@ parser_yylex(parser_state *p)
case '5': case '6': case '7': case '8': case '9':
{
int is_float, seen_point, seen_e, nondigit;
+ int suffix = 0;
is_float = seen_point = seen_e = nondigit = 0;
p->lstate = EXPR_ENDARG;
@@ -4785,7 +5233,10 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 16);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 16, suffix);
return tINTEGER;
}
if (c == 'b' || c == 'B') {
@@ -4809,7 +5260,10 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 2);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 2, suffix);
return tINTEGER;
}
if (c == 'd' || c == 'D') {
@@ -4833,7 +5287,10 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 10);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 10, suffix);
return tINTEGER;
}
if (c == '_') {
@@ -4866,7 +5323,10 @@ parser_yylex(parser_state *p)
pushback(p, c);
tokfix(p);
if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 8);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 8, suffix);
return tINTEGER;
}
if (nondigit) {
@@ -4883,7 +5343,10 @@ parser_yylex(parser_state *p)
}
else {
pushback(p, c);
- pylval.nd = new_int(p, "0", 10);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, "0", 10, suffix);
return tINTEGER;
}
}
@@ -4951,13 +5414,13 @@ parser_yylex(parser_state *p)
pushback(p, c);
if (nondigit) {
trailing_uc:
- yyerror_i(p, "trailing '%c' in number", nondigit);
+ yyerror_c(p, "trailing non digit in number: ", (char)nondigit);
}
tokfix(p);
if (is_float) {
#ifdef MRB_WITHOUT_FLOAT
- yywarning_s(p, "floating point numbers are not supported", tok(p));
- pylval.nd = new_int(p, "0", 10);
+ yywarning(p, "floating point numbers are not supported");
+ pylval.nd = new_int(p, "0", 10, 0);
return tINTEGER;
#else
double d;
@@ -4966,17 +5429,23 @@ parser_yylex(parser_state *p)
errno = 0;
d = mrb_float_read(tok(p), &endp);
if (d == 0 && endp == tok(p)) {
- yywarning_s(p, "corrupted float value %s", tok(p));
+ yywarning_s(p, "corrupted float value", tok(p));
}
else if (errno == ERANGE) {
- yywarning_s(p, "float %s out of range", tok(p));
+ yywarning_s(p, "float out of range", tok(p));
errno = 0;
}
- pylval.nd = new_float(p, tok(p));
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_float(p, tok(p), suffix);
return tFLOAT;
#endif
}
- pylval.nd = new_int(p, tok(p), 10);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 10, suffix);
return tINTEGER;
}
@@ -5160,7 +5629,7 @@ parser_yylex(parser_state *p)
}
else {
term = nextc(p);
- if (isalnum(term)) {
+ if (ISALNUM(term)) {
yyerror(p, "unknown type of %string");
return 0;
}
@@ -5304,14 +5773,14 @@ parser_yylex(parser_state *p)
do {
tokadd(p, c);
c = nextc(p);
- } while (c >= 0 && isdigit(c));
+ } while (c >= 0 && ISDIGIT(c));
pushback(p, c);
if (last_state == EXPR_FNAME) goto gvar;
tokfix(p);
{
unsigned long n = strtoul(tok(p), NULL, 10);
if (n > INT_MAX) {
- yyerror_i(p, "capture group index must be <= %d", INT_MAX);
+ yyerror(p, "capture group index must be <= " MRB_STRINGIZE(INT_MAX));
return 0;
}
pylval.nd = new_nth_ref(p, (int)n);
@@ -5346,12 +5815,12 @@ parser_yylex(parser_state *p)
}
return 0;
}
- else if (isdigit(c)) {
+ else if (ISDIGIT(c)) {
if (p->tidx == 1) {
- yyerror_i(p, "'@%c' is not allowed as an instance variable name", c);
+ yyerror_c(p, "wrong instance variable name: @", c);
}
else {
- yyerror_i(p, "'@@%c' is not allowed as a class variable name", c);
+ yyerror_c(p, "wrong class variable name: @@", c);
}
return 0;
}
@@ -5367,7 +5836,15 @@ parser_yylex(parser_state *p)
default:
if (!identchar(c)) {
- yyerror_i(p, "Invalid char '\\x%02X' in expression", c);
+ char buf[36];
+ const char s[] = "Invalid char in expression: 0x";
+ const char hexdigits[] = "0123456789ABCDEF";
+
+ strcpy(buf, s);
+ buf[sizeof(s)-1] = hexdigits[(c & 0xf0) >> 4];
+ buf[sizeof(s)] = hexdigits[(c & 0x0f)];
+ buf[sizeof(s)+1] = 0;
+ yyerror(p, buf);
goto retry;
}
@@ -5503,7 +5980,7 @@ parser_yylex(parser_state *p)
mrb_sym ident = intern_cstr(tok(p));
pylval.id = ident;
- if (last_state != EXPR_DOT && islower(tok(p)[0]) && local_var_p(p, ident)) {
+ if (last_state != EXPR_DOT && ISLOWER(tok(p)[0]) && local_var_p(p, ident)) {
p->lstate = EXPR_END;
}
}
@@ -5534,6 +6011,7 @@ parser_init_cxt(parser_state *p, mrbc_context *cxt)
}
p->capture_errors = cxt->capture_errors;
p->no_optimize = cxt->no_optimize;
+ p->on_eval = cxt->on_eval;
if (cxt->partial_hook) {
p->cxt = cxt;
}
@@ -5546,7 +6024,7 @@ parser_update_cxt(parser_state *p, mrbc_context *cxt)
int i = 0;
if (!cxt) return;
- if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return;
+ if (intn(p->tree->car) != NODE_SCOPE) return;
n0 = n = p->tree->cdr->car;
while (n) {
i++;
@@ -5712,7 +6190,7 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
mrb_sym* new_table;
sym = mrb_intern_cstr(p->mrb, f);
- p->filename = mrb_sym2name_len(p->mrb, sym, NULL);
+ p->filename_sym = sym;
p->lineno = (p->filename_table_length > 0)? 0 : 1;
for (i = 0; i < p->filename_table_length; ++i) {
@@ -5722,7 +6200,11 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
}
}
- p->current_filename_index = (int)p->filename_table_length++;
+ if (p->filename_table_length == UINT16_MAX) {
+ yyerror(p, "too many files to compile");
+ return;
+ }
+ p->current_filename_index = p->filename_table_length++;
new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length);
if (p->filename_table) {
@@ -5732,11 +6214,11 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
p->filename_table[p->filename_table_length - 1] = sym;
}
-MRB_API char const*
+MRB_API mrb_sym
mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
- if (idx >= p->filename_table_length) { return NULL; }
+ if (idx >= p->filename_table_length) return 0;
else {
- return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL);
+ return p->filename_table[idx];
}
}
@@ -5791,11 +6273,12 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
if (c) c->parser_nerr = p->nerr;
if (p->capture_errors) {
char buf[256];
- int n;
- n = snprintf(buf, sizeof(buf), "line %d: %s\n",
- p->error_buffer[0].lineno, p->error_buffer[0].message);
- mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n));
+ strcpy(buf, "line ");
+ dump_int(p->error_buffer[0].lineno, buf+5);
+ strcat(buf, ": ");
+ strncat(buf, p->error_buffer[0].message, sizeof(buf) - strlen(buf) - 1);
+ mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, strlen(buf)));
mrb_parser_free(p);
return mrb_undef_value();
}
@@ -5896,6 +6379,48 @@ dump_recur(mrb_state *mrb, node *tree, int offset)
}
}
+static void
+dump_args(mrb_state *mrb, node *n, int offset)
+{
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("optional args:\n");
+ {
+ node *n2 = n->car;
+
+ while (n2) {
+ dump_prefix(n2, offset+2);
+ printf("%s=\n", mrb_sym2name(mrb, sym(n2->car->car)));
+ mrb_parser_dump(mrb, n2->car->cdr, offset+3);
+ n2 = n2->cdr;
+ }
+ }
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("post mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+
+ n = n->cdr;
+ if (n) {
+ mrb_assert(intn(n->car) == NODE_ARGS_TAIL);
+ mrb_parser_dump(mrb, n, offset);
+ }
+}
+
#endif
void
@@ -5907,7 +6432,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
if (!tree) return;
again:
dump_prefix(tree, offset);
- nodetype = (int)(intptr_t)tree->car;
+ nodetype = intn(tree->car);
tree = tree->cdr;
switch (nodetype) {
case NODE_BEGIN:
@@ -5967,7 +6492,8 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_LAMBDA:
- printf("NODE_BLOCK:\n");
+ printf("NODE_LAMBDA:\n");
+ dump_prefix(tree, offset);
goto block;
case NODE_BLOCK:
@@ -5975,43 +6501,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
printf("NODE_BLOCK:\n");
tree = tree->cdr;
if (tree->car) {
- node *n = tree->car;
-
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("optional args:\n");
- {
- node *n2 = n->car;
-
- while (n2) {
- dump_prefix(n2, offset+2);
- printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
- mrb_parser_dump(mrb, n2->car->cdr, 0);
- n2 = n2->cdr;
- }
- }
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("post mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- if (n->cdr) {
- dump_prefix(n, offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
- }
+ dump_args(mrb, tree->car, offset+1);
}
dump_prefix(tree, offset+1);
printf("body:\n");
@@ -6163,7 +6653,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
dump_prefix(tree, offset+1);
printf("method='%s' (%d)\n",
mrb_sym2name(mrb, sym(tree->cdr->car)),
- (int)(intptr_t)tree->cdr->car);
+ intn(tree->cdr->car));
tree = tree->cdr->cdr->car;
if (tree) {
dump_prefix(tree, offset+1);
@@ -6218,6 +6708,19 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
break;
+ case NODE_KW_HASH:
+ printf("NODE_KW_HASH:\n");
+ while (tree) {
+ dump_prefix(tree, offset+1);
+ printf("key:\n");
+ mrb_parser_dump(mrb, tree->car->car, offset+2);
+ dump_prefix(tree, offset+1);
+ printf("value:\n");
+ mrb_parser_dump(mrb, tree->car->cdr, offset+2);
+ tree = tree->cdr;
+ }
+ break;
+
case NODE_SPLAT:
printf("NODE_SPLAT:\n");
mrb_parser_dump(mrb, tree, offset+1);
@@ -6280,7 +6783,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
mrb_parser_dump(mrb, tree->car, offset+2);
tree = tree->cdr;
dump_prefix(tree, offset+1);
- printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car);
+ printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), intn(tree->car));
tree = tree->cdr;
mrb_parser_dump(mrb, tree->car, offset+1);
break;
@@ -6362,11 +6865,11 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_BACK_REF:
- printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree);
+ printf("NODE_BACK_REF: $%c\n", intn(tree));
break;
case NODE_NTH_REF:
- printf("NODE_NTH_REF: $%" MRB_PRId "\n", (mrb_int)(intptr_t)tree);
+ printf("NODE_NTH_REF: $%d\n", intn(tree));
break;
case NODE_ARG:
@@ -6379,7 +6882,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_INT:
- printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car);
+ printf("NODE_INT %s base %d\n", (char*)tree->car, intn(tree->cdr->car));
break;
case NODE_FLOAT:
@@ -6392,7 +6895,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_STR:
- printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
+ printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, intn(tree->cdr));
break;
case NODE_DSTR:
@@ -6401,7 +6904,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_XSTR:
- printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
+ printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, intn(tree->cdr));
break;
case NODE_DXSTR:
@@ -6430,7 +6933,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_SYM:
printf("NODE_SYM :%s (%d)\n", mrb_sym2name(mrb, sym(tree)),
- (int)(intptr_t)tree);
+ intn(tree));
break;
case NODE_SELF:
@@ -6546,43 +7049,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
tree = tree->cdr;
if (tree->car) {
- node *n = tree->car;
-
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("optional args:\n");
- {
- node *n2 = n->car;
-
- while (n2) {
- dump_prefix(n2, offset+2);
- printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
- mrb_parser_dump(mrb, n2->car->cdr, 0);
- n2 = n2->cdr;
- }
- }
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("post mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- if (n->cdr) {
- dump_prefix(n, offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
- }
+ dump_args(mrb, tree->car, offset);
}
mrb_parser_dump(mrb, tree->cdr->car, offset+1);
break;
@@ -6595,44 +7062,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
printf(":%s\n", mrb_sym2name(mrb, sym(tree->car)));
tree = tree->cdr->cdr;
if (tree->car) {
- node *n = tree->car;
-
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("optional args:\n");
- {
- node *n2 = n->car;
-
- while (n2) {
- dump_prefix(n2, offset+2);
- printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
- mrb_parser_dump(mrb, n2->car->cdr, 0);
- n2 = n2->cdr;
- }
- }
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(n, offset+1);
- printf("post mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n) {
- dump_prefix(n, offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
- }
+ dump_args(mrb, tree->car, offset+1);
}
tree = tree->cdr;
mrb_parser_dump(mrb, tree->car, offset+1);
@@ -6648,6 +7078,37 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
break;
+ case NODE_ARGS_TAIL:
+ printf("NODE_ARGS_TAIL:\n");
+ {
+ node *kws = tree->car;
+
+ while (kws) {
+ mrb_parser_dump(mrb, kws->car, offset+1);
+ kws = kws->cdr;
+ }
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ mrb_assert(intn(tree->car->car) == NODE_KW_REST_ARGS);
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ dump_prefix(tree, offset+1);
+ printf("block='%s'\n", mrb_sym2name(mrb, sym(tree->car)));
+ }
+ break;
+
+ case NODE_KW_ARG:
+ printf("NODE_KW_ARG %s\n", mrb_sym2name(mrb, sym(tree->car)));
+ mrb_parser_dump(mrb, tree->cdr->car, offset + 1);
+ break;
+
+ case NODE_KW_REST_ARGS:
+ printf("NODE_KW_REST_ARGS %s\n", mrb_sym2name(mrb, sym(tree)));
+ break;
+
default:
printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype);
break;
diff --git a/mrbgems/mruby-compiler/mrbgem.rake b/mrbgems/mruby-compiler/mrbgem.rake
index 3bf6d6ae3..fa191e69b 100644
--- a/mrbgems/mruby-compiler/mrbgem.rake
+++ b/mrbgems/mruby-compiler/mrbgem.rake
@@ -23,10 +23,10 @@ MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
cc.run t.name, t.prerequisites.first, [], ["#{current_dir}/core"]
end
end
- file objfile("#{current_build_dir}/core/y.tab") => lex_def
# Parser
- file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y"] do |t|
+ file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y", lex_def] do |t|
+ FileUtils.mkdir_p File.dirname t.name
yacc.run t.name, t.prerequisites.first
end
@@ -35,6 +35,6 @@ MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
gperf.run t.name, t.prerequisites.first
end
- file libfile("#{build.build_dir}/lib/libmruby_core") => core_objs
+ file build.libmruby_core_static => core_objs
build.libmruby << core_objs
end
diff --git a/mrbgems/mruby-complex/mrbgem.rake b/mrbgems/mruby-complex/mrbgem.rake
new file mode 100644
index 000000000..19612e74d
--- /dev/null
+++ b/mrbgems/mruby-complex/mrbgem.rake
@@ -0,0 +1,10 @@
+MRuby::Gem::Specification.new('mruby-complex') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Complex class'
+
+ spec.add_dependency 'mruby-metaprog', core: 'mruby-metaprog'
+ spec.add_dependency 'mruby-object-ext', core: 'mruby-object-ext'
+ spec.add_dependency 'mruby-numeric-ext', core: 'mruby-numeric-ext'
+ spec.add_dependency 'mruby-math', core: 'mruby-math'
+end
diff --git a/mrbgems/mruby-complex/mrblib/complex.rb b/mrbgems/mruby-complex/mrblib/complex.rb
new file mode 100644
index 000000000..0299e7675
--- /dev/null
+++ b/mrbgems/mruby-complex/mrblib/complex.rb
@@ -0,0 +1,123 @@
+class Complex < Numeric
+ def self.polar(abs, arg = 0)
+ Complex(abs * Math.cos(arg), abs * Math.sin(arg))
+ end
+
+ def inspect
+ "(#{to_s})"
+ end
+
+ def to_s
+ "#{real}#{'+' unless imaginary.negative?}#{imaginary}i"
+ end
+
+ def +@
+ Complex(real, imaginary)
+ end
+
+ def -@
+ Complex(-real, -imaginary)
+ end
+
+ def +(rhs)
+ if rhs.is_a? Complex
+ Complex(real + rhs.real, imaginary + rhs.imaginary)
+ elsif rhs.is_a? Numeric
+ Complex(real + rhs, imaginary)
+ end
+ end
+
+ def -(rhs)
+ if rhs.is_a? Complex
+ Complex(real - rhs.real, imaginary - rhs.imaginary)
+ elsif rhs.is_a? Numeric
+ Complex(real - rhs, imaginary)
+ end
+ end
+
+ def *(rhs)
+ if rhs.is_a? Complex
+ Complex(real * rhs.real - imaginary * rhs.imaginary, real * rhs.imaginary + rhs.real * imaginary)
+ elsif rhs.is_a? Numeric
+ Complex(real * rhs, imaginary * rhs)
+ end
+ end
+
+ def /(rhs)
+ if rhs.is_a? Complex
+ div = rhs.real * rhs.real + rhs.imaginary * rhs.imaginary
+ Complex((real * rhs.real + imaginary * rhs.imaginary) / div, (rhs.real * imaginary - real * rhs.imaginary) / div)
+ elsif rhs.is_a? Numeric
+ Complex(real / rhs, imaginary / rhs)
+ end
+ end
+ alias_method :quo, :/
+
+ def ==(rhs)
+ if rhs.is_a? Complex
+ real == rhs.real && imaginary == rhs.imaginary
+ elsif rhs.is_a? Numeric
+ imaginary.zero? && real == rhs
+ end
+ end
+
+ def abs
+ Math.sqrt(abs2)
+ end
+ alias_method :magnitude, :abs
+
+ def abs2
+ real * real + imaginary * imaginary
+ end
+
+ def arg
+ Math.atan2 imaginary, real
+ end
+ alias_method :angle, :arg
+ alias_method :phase, :arg
+
+ def conjugate
+ Complex(real, -imaginary)
+ end
+ alias_method :conj, :conjugate
+
+ def fdiv(numeric)
+ Complex(real.to_f / numeric, imaginary.to_f / numeric)
+ end
+
+ def polar
+ [abs, arg]
+ end
+
+ def real?
+ false
+ end
+
+ def rectangular
+ [real, imaginary]
+ end
+ alias_method :rect, :rectangular
+
+ def to_r
+ raise RangeError.new "can't convert #{to_s} into Rational" unless imaginary.zero?
+ Rational(real, 1)
+ end
+
+ alias_method :imag, :imaginary
+end
+
+[Fixnum, Float].each do |cls|
+ [:+, :-, :*, :/, :==].each do |op|
+ cls.instance_exec do
+ original_operator_name = "__original_operator_#{op}_complex"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Complex
+ Complex(self).send(op, rhs)
+ else
+ send(original_operator_name, rhs)
+ end
+ end
+ end
+ end
+end
diff --git a/mrbgems/mruby-complex/src/complex.c b/mrbgems/mruby-complex/src/complex.c
new file mode 100644
index 000000000..8a0569d68
--- /dev/null
+++ b/mrbgems/mruby-complex/src/complex.c
@@ -0,0 +1,150 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+
+struct mrb_complex {
+ mrb_float real;
+ mrb_float imaginary;
+};
+
+#if defined(MRB_64BIT) || defined(MRB_USE_FLOAT)
+
+#define COMPLEX_USE_ISTRUCT
+/* use TT_ISTRUCT */
+#include <mruby/istruct.h>
+
+#define complex_ptr(mrb, v) (struct mrb_complex*)mrb_istruct_ptr(v)
+
+static struct RBasic*
+complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p)
+{
+ struct RIStruct *s;
+
+ s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
+ *p = (struct mrb_complex*)s->inline_data;
+
+ return (struct RBasic*)s;
+}
+
+#else
+/* use TT_DATA */
+#include <mruby/data.h>
+
+static const struct mrb_data_type mrb_complex_type = {"Complex", mrb_free};
+
+static struct RBasic*
+complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p)
+{
+ struct RData *d;
+
+ Data_Make_Struct(mrb, c, struct mrb_complex, &mrb_complex_type, *p, d);
+
+ return (struct RBasic*)d;
+}
+
+static struct mrb_complex*
+complex_ptr(mrb_state *mrb, mrb_value v)
+{
+ struct mrb_complex *p;
+
+ p = DATA_GET_PTR(mrb, v, &mrb_complex_type, struct mrb_complex);
+ if (!p) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized complex");
+ }
+ return p;
+}
+#endif
+
+static mrb_value
+complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary)
+{
+ struct RClass *c = mrb_class_get(mrb, "Complex");
+ struct mrb_complex *p;
+ struct RBasic *comp = complex_alloc(mrb, c, &p);
+ p->real = real;
+ p->imaginary = imaginary;
+ MRB_SET_FROZEN_FLAG(comp);
+
+ return mrb_obj_value(comp);
+}
+
+static mrb_value
+complex_real(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+ return mrb_float_value(mrb, p->real);
+}
+
+static mrb_value
+complex_imaginary(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+ return mrb_float_value(mrb, p->imaginary);
+}
+
+static mrb_value
+complex_s_rect(mrb_state *mrb, mrb_value self)
+{
+ mrb_float real, imaginary = 0.0;
+
+ mrb_get_args(mrb, "f|f", &real, &imaginary);
+ return complex_new(mrb, real, imaginary);
+}
+
+#ifndef MRB_WITHOUT_FLOAT
+static mrb_value
+complex_to_f(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+
+ if (p->imaginary != 0) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %S into Float", self);
+ }
+
+ return mrb_float_value(mrb, p->real);
+}
+#endif
+
+static mrb_value
+complex_to_i(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+
+ if (p->imaginary != 0) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %S into Float", self);
+ }
+ return mrb_int_value(mrb, p->real);
+}
+
+static mrb_value
+complex_to_c(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+void mrb_mruby_complex_gem_init(mrb_state *mrb)
+{
+ struct RClass *comp;
+
+#ifdef COMPLEX_USE_ISTRUCT
+ mrb_assert(sizeof(struct mrb_complex) < ISTRUCT_DATA_SIZE);
+#endif
+ comp = mrb_define_class(mrb, "Complex", mrb_class_get(mrb, "Numeric"));
+ //MRB_SET_INSTANCE_TT(comp, MRB_TT_ISTRUCT);
+ mrb_undef_class_method(mrb, comp, "new");
+ mrb_define_class_method(mrb, comp, "rectangular", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, comp, "rect", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, mrb->kernel_module, "Complex", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, comp, "real", complex_real, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "imaginary", complex_imaginary, MRB_ARGS_NONE());
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_define_method(mrb, comp, "to_f", complex_to_f, MRB_ARGS_NONE());
+#endif
+ mrb_define_method(mrb, comp, "to_i", complex_to_i, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "to_c", complex_to_c, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_complex_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-complex/test/complex.rb b/mrbgems/mruby-complex/test/complex.rb
new file mode 100644
index 000000000..5b7c3bfa4
--- /dev/null
+++ b/mrbgems/mruby-complex/test/complex.rb
@@ -0,0 +1,136 @@
+def assert_complex(real, exp)
+ assert do
+ assert_float real.real, exp.real
+ assert_float real.imaginary, exp.imaginary
+ end
+end
+
+assert 'Complex' do
+ c = 123i
+ assert_equal Complex, c.class
+ assert_equal [c.real, c.imaginary], [0, 123]
+ c = 123 + -1.23i
+ assert_equal Complex, c.class
+ assert_equal [c.real, c.imaginary], [123, -1.23]
+end
+
+assert 'Complex::polar' do
+ assert_complex Complex.polar(3, 0), (3 + 0i)
+ assert_complex Complex.polar(3, Math::PI/2), (0 + 3i)
+ assert_complex Complex.polar(3, Math::PI), (-3 + 0i)
+ assert_complex Complex.polar(3, -Math::PI/2), (0 + -3i)
+end
+
+assert 'Complex::rectangular' do
+ assert_complex Complex.rectangular(1, 2), (1 + 2i)
+end
+
+assert 'Complex#*' do
+ assert_complex Complex(2, 3) * Complex(2, 3), (-5 + 12i)
+ assert_complex Complex(900) * Complex(1), (900 + 0i)
+ assert_complex Complex(-2, 9) * Complex(-9, 2), (0 - 85i)
+ assert_complex Complex(9, 8) * 4, (36 + 32i)
+ assert_complex Complex(20, 9) * 9.8, (196.0 + 88.2i)
+end
+
+assert 'Complex#+' do
+ assert_complex Complex(2, 3) + Complex(2, 3) , (4 + 6i)
+ assert_complex Complex(900) + Complex(1) , (901 + 0i)
+ assert_complex Complex(-2, 9) + Complex(-9, 2), (-11 + 11i)
+ assert_complex Complex(9, 8) + 4 , (13 + 8i)
+ assert_complex Complex(20, 9) + 9.8 , (29.8 + 9i)
+end
+
+assert 'Complex#-' do
+ assert_complex Complex(2, 3) - Complex(2, 3) , (0 + 0i)
+ assert_complex Complex(900) - Complex(1) , (899 + 0i)
+ assert_complex Complex(-2, 9) - Complex(-9, 2), (7 + 7i)
+ assert_complex Complex(9, 8) - 4 , (5 + 8i)
+ assert_complex Complex(20, 9) - 9.8 , (10.2 + 9i)
+end
+
+assert 'Complex#-@' do
+ assert_complex(-Complex(1, 2), (-1 - 2i))
+end
+
+assert 'Complex#/' do
+ assert_complex Complex(2, 3) / Complex(2, 3) , (1 + 0i)
+ assert_complex Complex(900) / Complex(1) , (900 + 0i)
+ assert_complex Complex(-2, 9) / Complex(-9, 2), ((36 / 85) - (77i / 85))
+ assert_complex Complex(9, 8) / 4 , ((9 / 4) + 2i)
+ assert_complex Complex(20, 9) / 9.8 , (2.0408163265306123 + 0.9183673469387754i)
+end
+
+assert 'Complex#==' do
+ assert_true Complex(2, 3) == Complex(2, 3)
+ assert_true Complex(5) == 5
+ assert_true Complex(0) == 0.0
+end
+
+assert 'Complex#abs' do
+ assert_float Complex(-1).abs, 1
+ assert_float Complex(3.0, -4.0).abs, 5.0
+end
+
+assert 'Complex#abs2' do
+ assert_float Complex(-1).abs2, 1
+ assert_float Complex(3.0, -4.0).abs2, 25.0
+end
+
+assert 'Complex#arg' do
+ assert_float Complex.polar(3, Math::PI/2).arg, 1.5707963267948966
+end
+
+assert 'Complex#conjugate' do
+ assert_complex Complex(1, 2).conjugate, (1 - 2i)
+end
+
+assert 'Complex#fdiv' do
+ assert_complex Complex(11, 22).fdiv(3), (3.6666666666666665 + 7.333333333333333i)
+end
+
+assert 'Complex#imaginary' do
+ assert_float Complex(7).imaginary , 0
+ assert_float Complex(9, -4).imaginary, -4
+end
+
+assert 'Complex#polar' do
+ assert_equal Complex(1, 2).polar, [2.23606797749979, 1.1071487177940904]
+end
+
+assert 'Complex#real' do
+ assert_float Complex(7).real, 7
+ assert_float Complex(9, -4).real, 9
+end
+
+assert 'Complex#real?' do
+ assert_false Complex(1).real?
+end
+
+assert 'Complex::rectangular' do
+ assert_equal Complex(1, 2).rectangular, [1, 2]
+end
+
+assert 'Complex::to_c' do
+ assert_equal Complex(1, 2).to_c, Complex(1, 2)
+end
+
+assert 'Complex::to_f' do
+ assert_float Complex(1, 0).to_f, 1.0
+ assert_raise(RangeError) do
+ Complex(1, 2).to_f
+ end
+end
+
+assert 'Complex::to_i' do
+ assert_equal Complex(1, 0).to_i, 1
+ assert_raise(RangeError) do
+ Complex(1, 2).to_i
+ end
+end
+
+assert 'Complex#frozen?' do
+ assert_predicate(1i, :frozen?)
+ assert_predicate(Complex(2,3), :frozen?)
+ assert_predicate(4+5i, :frozen?)
+end
diff --git a/mrbgems/mruby-enum-chain/mrbgem.rake b/mrbgems/mruby-enum-chain/mrbgem.rake
new file mode 100644
index 000000000..7294f2644
--- /dev/null
+++ b/mrbgems/mruby-enum-chain/mrbgem.rake
@@ -0,0 +1,6 @@
+MRuby::Gem::Specification.new('mruby-enum-chain') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Enumerator::Chain class'
+ spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator')
+end
diff --git a/mrbgems/mruby-enum-chain/mrblib/chain.rb b/mrbgems/mruby-enum-chain/mrblib/chain.rb
new file mode 100644
index 000000000..98515ea14
--- /dev/null
+++ b/mrbgems/mruby-enum-chain/mrblib/chain.rb
@@ -0,0 +1,60 @@
+##
+# chain.rb Enumerator::Chain class
+# See Copyright Notice in mruby.h
+
+module Enumerable
+ def chain(*args)
+ Enumerator::Chain.new(self, *args)
+ end
+
+ def +(other)
+ Enumerator::Chain.new(self, other)
+ end
+end
+
+class Enumerator
+ class Chain
+ include Enumerable
+
+ def initialize(*args)
+ @enums = args
+ end
+
+ def initialize_copy(orig)
+ @enums = orig.__copy_enums
+ end
+
+ def each(&block)
+ return to_enum unless block_given?
+
+ @enums.each { |e| e.each(&block) }
+
+ self
+ end
+
+ def size
+ @enums.reduce(0) do |a, e|
+ return nil unless e.respond_to?(:size)
+ a + e.size
+ end
+ end
+
+ def rewind
+ @enums.reverse_each do |e|
+ e.rewind if e.respond_to?(:rewind)
+ end
+
+ self
+ end
+
+ def inspect
+ "#<#{self.class}: #{@enums.inspect}>"
+ end
+
+ def __copy_enums
+ @enums.each_with_object([]) do |e, a|
+ a << e.clone
+ end
+ end
+ end
+end
diff --git a/mrbgems/mruby-enum-chain/test/enum_chain.rb b/mrbgems/mruby-enum-chain/test/enum_chain.rb
new file mode 100644
index 000000000..4dd59bd37
--- /dev/null
+++ b/mrbgems/mruby-enum-chain/test/enum_chain.rb
@@ -0,0 +1,76 @@
+##
+# Enumerator::Chain test
+
+assert("Enumerable#chain") do
+ a = []
+ b = {}
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, a.chain
+ assert_kind_of Enumerator::Chain, a.chain(b)
+ assert_kind_of Enumerator::Chain, a.chain(b, c)
+ assert_raise(NoMethodError) { c.chain }
+end
+
+assert("Enumerable#+") do
+ a = [].each
+ b = {}.reverse_each
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, a + b
+ assert_kind_of Enumerator::Chain, a + c
+ assert_kind_of Enumerator::Chain, b + a
+ assert_kind_of Enumerator::Chain, b + c
+ assert_raise(NoMethodError) { c + a }
+end
+
+assert("Enumerator.new") do
+ a = []
+ b = {}
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, a)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, b)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, c)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, a)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, b)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, c)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(c, a)
+end
+
+assert("Enumerator::Chain#each") do
+ a = [1, 2, 3]
+
+ aa = a.chain(a)
+ assert_kind_of Enumerator, aa.each
+ assert_equal [1, 2, 3, 1, 2, 3], aa.each.to_a
+
+ aa = a.chain(a.reverse_each)
+ assert_kind_of Enumerator, aa.each
+ assert_equal [1, 2, 3, 3, 2, 1], aa.each.to_a
+
+ aa = a.chain(a.reverse_each, a)
+ assert_kind_of Enumerator, aa.each
+ assert_equal [1, 2, 3, 3, 2, 1, 1, 2, 3], aa.each.to_a
+
+ aa = a.chain(Object.new)
+ assert_kind_of Enumerator, aa.each
+ assert_raise(NoMethodError) { aa.each.to_a }
+end
+
+assert("Enumerator::Chain#size") do
+ a = [1, 2, 3]
+
+ aa = a.chain(a)
+ assert_equal 6, aa.size
+
+ aa = a.chain(a.reverse_each)
+ assert_nil aa.size
+
+ aa = a.chain(a.reverse_each, a)
+ assert_nil aa.size
+
+ aa = a.chain(Object.new)
+ assert_nil aa.size
+end
diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb
index a840ade3b..99b9cddba 100644
--- a/mrbgems/mruby-enum-ext/mrblib/enum.rb
+++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb
@@ -13,10 +13,9 @@ module Enumerable
# a.drop(3) #=> [4, 5, 0]
def drop(n)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ n = n.__to_int
raise ArgumentError, "attempt to drop negative size" if n < 0
- n = n.to_int
ary = []
self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 }
ary
@@ -57,8 +56,8 @@ module Enumerable
# a.take(3) #=> [1, 2, 3]
def take(n)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
- i = n.to_int
+ n = n.__to_int
+ i = n.to_i
raise ArgumentError, "attempt to take negative size" if i < 0
ary = []
return ary if i == 0
@@ -113,12 +112,12 @@ module Enumerable
# [8, 9, 10]
def each_cons(n, &block)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ n = n.__to_int
raise ArgumentError, "invalid size" if n <= 0
return to_enum(:each_cons,n) unless block
ary = []
- n = n.to_int
+ n = n.to_i
self.each do |*val|
ary.shift if ary.size == n
ary << val.__svalue
@@ -141,12 +140,12 @@ module Enumerable
# [10]
def each_slice(n, &block)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ n = n.__to_int
raise ArgumentError, "invalid slice size" if n <= 0
return to_enum(:each_slice,n) unless block
ary = []
- n = n.to_int
+ n = n.to_i
self.each do |*val|
ary << val.__svalue
if ary.size == n
@@ -201,14 +200,11 @@ module Enumerable
ary.push([block.call(e), i])
}
if ary.size > 1
- ary.__sort_sub__(0, ary.size - 1) do |a,b|
- a <=> b
- end
+ ary.sort!
end
ary.collect{|e,i| orig[i]}
end
- NONE = Object.new
##
# call-seq:
# enum.first -> obj or nil
@@ -225,9 +221,7 @@ module Enumerable
end
return nil
when 1
- n = args[0]
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
- i = n.to_int
+ i = args[0].__to_int
raise ArgumentError, "attempt to take negative size" if i < 0
ary = []
return ary if i == 0
@@ -675,13 +669,7 @@ module Enumerable
if nv.nil?
n = -1
else
- unless nv.respond_to?(:to_int)
- raise TypeError, "no implicit conversion of #{nv.class} into Integer"
- end
- n = nv.to_int
- unless n.kind_of?(Integer)
- raise TypeError, "no implicit conversion of #{nv.class} into Integer"
- end
+ n = nv.__to_int
return nil if n <= 0
end
@@ -803,13 +791,22 @@ module Enumerable
# # => {:hello => 0, :world => 1}
#
- def to_h
+ def to_h(&blk)
h = {}
- self.each do |*v|
- v = v.__svalue
- raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array
- raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2
- h[v[0]] = v[1]
+ if blk
+ self.each do |v|
+ v = blk.call(v)
+ raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array
+ raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2
+ h[v[0]] = v[1]
+ end
+ else
+ self.each do |*v|
+ v = v.__svalue
+ raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array
+ raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2
+ h[v[0]] = v[1]
+ end
end
h
end
diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb
index 46ed5f0f9..64b1bbda9 100644
--- a/mrbgems/mruby-enum-ext/test/enum.rb
+++ b/mrbgems/mruby-enum-ext/test/enum.rb
@@ -128,14 +128,14 @@ assert("Enumerable#any? (enhancement)") do
end
assert("Enumerable#each_with_object") do
- assert_true [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], (1..10).each_with_object([]) { |i, a| a << i*2 }
+ assert_equal [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], (1..10).each_with_object([]) { |i, a| a << i*2 }
assert_raise(ArgumentError) { (1..10).each_with_object() { |i, a| a << i*2 } }
end
assert("Enumerable#reverse_each") do
r = (1..3)
a = []
- assert_equal (1..3), r.reverse_each { |v| a << v }
+ assert_same r, r.reverse_each { |v| a << v }
assert_equal [3, 2, 1], a
end
@@ -188,4 +188,6 @@ assert("Enumerable#to_h") do
assert_equal h0, h
# mruby-enum-ext also provides nil.to_h
assert_equal Hash.new, nil.to_h
+
+ assert_equal({1=>4,3=>8}, c.new.to_h{|k,v|[k,v*2]})
end
diff --git a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
index 9227abe8a..e4f116a93 100644
--- a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
+++ b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
@@ -44,7 +44,7 @@ class Enumerator
def to_enum(meth=:each, *args, &block)
unless self.respond_to?(meth)
- raise NoMethodError, "undefined method #{meth}"
+ raise ArgumentError, "undefined method #{meth}"
end
lz = Lazy.new(self, &block)
lz.obj = self
diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
index 7ca1d5eb6..89472ef01 100644
--- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb
+++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
@@ -109,27 +109,30 @@ class Enumerator
#
# p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
#
- def initialize(obj=nil, meth=:each, *args, &block)
+ # In the second, deprecated, form, a generated Enumerator iterates over the
+ # given object using the given method with the given arguments passed. This
+ # form is left only for internal use.
+ #
+ # Use of this form is discouraged. Use Kernel#enum_for or Kernel#to_enum
+ # instead.
+ def initialize(obj=NONE, meth=:each, *args, &block)
if block
obj = Generator.new(&block)
- else
- raise ArgumentError unless obj
- end
- if @obj and !self.respond_to?(meth)
- raise NoMethodError, "undefined method #{meth}"
+ elsif obj == NONE
+ raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
end
@obj = obj
@meth = meth
- @args = args.dup
+ @args = args
@fib = nil
@dst = nil
@lookahead = nil
@feedvalue = nil
@stop_exc = false
end
- attr_accessor :obj, :meth, :args, :fib
- private :obj, :meth, :args, :fib
+ attr_accessor :obj, :meth, :args
+ attr_reader :fib
def initialize_copy(obj)
raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
@@ -157,12 +160,10 @@ class Enumerator
def with_index(offset=0, &block)
return to_enum :with_index, offset unless block
- offset = if offset.nil?
- 0
- elsif offset.respond_to?(:to_int)
- offset.to_int
+ if offset.nil?
+ offset = 0
else
- raise TypeError, "no implicit conversion of #{offset.class} into Integer"
+ offset = offset.__to_int
end
n = offset - 1
@@ -223,13 +224,11 @@ class Enumerator
end
def inspect
- return "#<#{self.class}: uninitialized>" unless @obj
-
if @args && @args.size > 0
args = @args.join(", ")
- "#<#{self.class}: #{@obj}:#{@meth}(#{args})>"
+ "#<#{self.class}: #{@obj.inspect}:#{@meth}(#{args})>"
else
- "#<#{self.class}: #{@obj}:#{@meth}>"
+ "#<#{self.class}: #{@obj.inspect}:#{@meth}>"
end
end
@@ -245,9 +244,10 @@ class Enumerator
#
# === Examples
#
- # "Hello, world!".scan(/\w+/) #=> ["Hello", "world"]
- # "Hello, world!".to_enum(:scan, /\w+/).to_a #=> ["Hello", "world"]
- # "Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"]
+ # Array.new(3) #=> [nil, nil, nil]
+ # Array.new(3) { |i| i } #=> [0, 1, 2]
+ # Array.to_enum(:new, 3).to_a #=> [0, 1, 2]
+ # Array.to_enum(:new).each(3).to_a #=> [0, 1, 2]
#
# obj = Object.new
#
@@ -623,9 +623,7 @@ module Enumerable
# use Enumerator to use infinite sequence
def zip(*args, &block)
args = args.map do |a|
- if a.respond_to?(:to_ary)
- a.to_ary.to_enum(:each)
- elsif a.respond_to?(:each)
+ if a.respond_to?(:each)
a.to_enum(:each)
else
raise TypeError, "wrong argument type #{a.class} (must respond to :each)"
diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb
index 428ea0307..d609cadb5 100644
--- a/mrbgems/mruby-enumerator/test/enumerator.rb
+++ b/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -6,11 +6,11 @@ class << @obj
end
end
-assert 'Enumerator' do
+assert 'Enumerator.class' do
assert_equal Class, Enumerator.class
end
-assert 'Enumerator' do
+assert 'Enumerator.superclass' do
assert_equal Object, Enumerator.superclass
end
@@ -19,11 +19,8 @@ assert 'Enumerator.new' do
assert_equal [:x,:y,:z], [:x,:y,:z].each.map{|i| i}.sort
assert_equal [[:x,1],[:y,2]], {x:1, y:2}.each.map{|i| i}.sort
assert_equal [1,2,3], @obj.to_enum(:foo, 1,2,3).to_a
- assert_equal [1,2,3], Enumerator.new(@obj, :foo, 1,2,3).to_a
assert_equal [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3)
assert_raise(ArgumentError) { Enumerator.new }
- enum = @obj.to_enum
- assert_raise(NoMethodError) { enum.each {} }
# examples
fib = Enumerator.new do |y|
@@ -55,12 +52,6 @@ assert 'Enumerator#with_index' do
assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a
end
-assert 'Enumerator#with_index nonnum offset' do
- s = Object.new
- def s.to_int; 1 end
- assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a)
-end
-
assert 'Enumerator#with_index string offset' do
assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a }
end
@@ -99,11 +90,13 @@ end
assert 'Enumerator#inspect' do
e = (0..10).each
- assert_equal("#<Enumerator: 0..10:each>", e.inspect)
- e = Enumerator.new("FooObject", :foo, 1)
- assert_equal("#<Enumerator: FooObject:foo(1)>", e.inspect)
- e = Enumerator.new("FooObject", :foo, 1, 2, 3)
- assert_equal("#<Enumerator: FooObject:foo(1, 2, 3)>", e.inspect)
+ assert_equal('#<Enumerator: 0..10:each>', e.inspect)
+ e = 'FooObject'.enum_for(:foo, 1)
+ assert_equal('#<Enumerator: "FooObject":foo(1)>', e.inspect)
+ e = 'FooObject'.enum_for(:foo, 1, 2, 3)
+ assert_equal('#<Enumerator: "FooObject":foo(1, 2, 3)>', e.inspect)
+ e = nil.enum_for(:to_s)
+ assert_equal('#<Enumerator: nil:to_s>', e.inspect)
end
assert 'Enumerator#each' do
@@ -425,8 +418,10 @@ assert 'nested iteration' do
end
assert 'Kernel#to_enum' do
+ e = nil
assert_equal Enumerator, [].to_enum.class
- assert_raise(ArgumentError){ nil.to_enum }
+ assert_nothing_raised { e = [].to_enum(:_not_implemented_) }
+ assert_raise(NoMethodError) { e.first }
end
assert 'modifying existing methods' do
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c
index 14e89ac14..30534aaec 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -44,7 +44,7 @@ search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom)
return NULL;
}
-static inline mrb_code
+static uint16_t
search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
{
mrb_irep *virep;
@@ -57,7 +57,7 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
}
for (pos = 0; pos < virep->nlocals - 1; pos++) {
if (vsym == virep->lv[pos].name) {
- return (MKARG_B(pos + 1) | MKARG_C(level + bnest));
+ return (pos+1)<<8 | (level+bnest);
}
}
}
@@ -71,8 +71,8 @@ irep_argc(mrb_irep *irep)
mrb_code c;
c = irep->iseq[0];
- if (GET_OPCODE(c) == OP_ENTER) {
- mrb_aspec ax = GETARG_Ax(c);
+ if (c == OP_ENTER) {
+ mrb_aspec ax = PEEK_W(irep->iseq+1);
/* extra 1 means a slot for block */
return MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1;
}
@@ -88,95 +88,132 @@ potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
return TRUE;
}
+extern uint8_t mrb_insn_size[];
+extern uint8_t mrb_insn_size1[];
+extern uint8_t mrb_insn_size2[];
+extern uint8_t mrb_insn_size3[];
+
static void
patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
{
int i;
- mrb_code c;
+ uint32_t a;
+ uint16_t b;
+ uint8_t c;
+ mrb_code insn;
int argc = irep_argc(irep);
- for (i = 0; i < irep->ilen; i++) {
- c = irep->iseq[i];
- switch(GET_OPCODE(c)){
+ for (i = 0; i < irep->ilen; ) {
+ insn = irep->iseq[i];
+ switch(insn){
case OP_EPUSH:
- patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, top);
+ b = PEEK_S(irep->iseq+i+1);
+ patch_irep(mrb, irep->reps[b], bnest + 1, top);
break;
case OP_LAMBDA:
- {
- int arg_c = GETARG_c(c);
- if (arg_c & OP_L_CAPTURE) {
- patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1, top);
- }
- }
+ case OP_BLOCK:
+ a = PEEK_B(irep->iseq+i+1);
+ b = PEEK_B(irep->iseq+i+2);
+ patch_irep(mrb, irep->reps[b], bnest + 1, top);
break;
case OP_SEND:
- if (GETARG_C(c) != 0) {
+ b = PEEK_B(irep->iseq+i+2);
+ c = PEEK_B(irep->iseq+i+3);
+ if (c != 0) {
break;
}
else {
- mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest);
+ uint16_t arg = search_variable(mrb, irep->syms[b], bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ irep->iseq[i] = OP_GETUPVAR;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
break;
case OP_MOVE:
+ a = PEEK_B(irep->iseq+i+1);
+ b = PEEK_B(irep->iseq+i+2);
/* src part */
- if (potential_upvar_p(irep->lv, GETARG_B(c), argc, irep->nlocals)) {
- mrb_code arg = search_variable(mrb, irep->lv[GETARG_B(c) - 1].name, bnest);
+ if (potential_upvar_p(irep->lv, b, argc, irep->nlocals)) {
+ uint16_t arg = search_variable(mrb, irep->lv[b - 1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ irep->iseq[i] = insn = OP_GETUPVAR;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
/* dst part */
- if (potential_upvar_p(irep->lv, GETARG_A(c), argc, irep->nlocals)) {
- mrb_code arg = search_variable(mrb, irep->lv[GETARG_A(c) - 1].name, bnest);
+ if (potential_upvar_p(irep->lv, a, argc, irep->nlocals)) {
+ uint16_t arg = search_variable(mrb, irep->lv[a - 1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_B(c)) | arg;
+ irep->iseq[i] = insn = OP_SETUPVAR;
+ irep->iseq[i+1] = (mrb_code)b;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
break;
case OP_GETUPVAR:
+ a = PEEK_B(irep->iseq+i+1);
+ b = PEEK_B(irep->iseq+i+2);
+ c = PEEK_B(irep->iseq+i+3);
{
- int lev = GETARG_C(c)+1;
+ int lev = c+1;
mrb_irep *tmp = search_irep(top, bnest, lev, irep);
- if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
- mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
+ if (potential_upvar_p(tmp->lv, b, irep_argc(tmp), tmp->nlocals)) {
+ uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ irep->iseq[i] = OP_GETUPVAR;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
}
break;
case OP_SETUPVAR:
+ a = PEEK_B(irep->iseq+i+1);
+ b = PEEK_B(irep->iseq+i+2);
+ c = PEEK_B(irep->iseq+i+3);
{
- int lev = GETARG_C(c)+1;
+ int lev = c+1;
mrb_irep *tmp = search_irep(top, bnest, lev, irep);
- if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
- mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
+ if (potential_upvar_p(tmp->lv, b, irep_argc(tmp), tmp->nlocals)) {
+ uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ irep->iseq[i] = OP_SETUPVAR;
+ irep->iseq[i+1] = a;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
}
break;
- case OP_STOP:
- if (mrb->c->ci->acc >= 0) {
- irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL);
- }
- break;
+ case OP_EXT1:
+ insn = PEEK_B(irep->iseq+i+1);
+ i += mrb_insn_size1[insn]+1;
+ continue;
+ case OP_EXT2:
+ insn = PEEK_B(irep->iseq+i+1);
+ i += mrb_insn_size2[insn]+1;
+ continue;
+ case OP_EXT3:
+ insn = PEEK_B(irep->iseq+i+1);
+ i += mrb_insn_size3[insn]+1;
+ continue;
}
+ i+=mrb_insn_size[insn];
}
}
@@ -189,7 +226,7 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
struct mrb_parser_state *p;
struct RProc *proc;
struct REnv *e;
- mrb_callinfo *ci = &mrb->c->ci[-1]; /* callinfo of eval caller */
+ mrb_callinfo *ci; /* callinfo of eval caller */
struct RClass *target_class = NULL;
int bidx;
@@ -198,11 +235,12 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
}
cxt = mrbc_context_new(mrb);
- cxt->lineno = (short)line;
+ cxt->lineno = (uint16_t)line;
mrbc_filename(mrb, cxt, file ? file : "(eval)");
cxt->capture_errors = TRUE;
cxt->no_optimize = TRUE;
+ cxt->on_eval = TRUE;
p = mrb_parse_nstring(mrb, s, len, cxt);
@@ -238,8 +276,16 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
mrbc_context_free(mrb, cxt);
mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error");
}
- target_class = MRB_PROC_TARGET_CLASS(ci->proc);
- if (!MRB_PROC_CFUNC_P(ci->proc)) {
+ if (mrb->c->ci > mrb->c->cibase) {
+ ci = &mrb->c->ci[-1];
+ }
+ else {
+ ci = mrb->c->cibase;
+ }
+ if (ci->proc) {
+ target_class = MRB_PROC_TARGET_CLASS(ci->proc);
+ }
+ if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) {
if (ci->env) {
e = ci->env;
}
@@ -254,6 +300,7 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
if (ci->argc < 0) bidx = 2;
else bidx += 1;
MRB_ENV_SET_BIDX(e, bidx);
+ ci->env = e;
}
proc->e.env = e;
proc->flags |= MRB_PROC_ENVSET;
@@ -276,10 +323,12 @@ exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc)
/* no argument passed from eval() */
mrb->c->ci->argc = 0;
if (mrb->c->ci->acc < 0) {
+ ptrdiff_t cioff = mrb->c->ci - mrb->c->cibase;
mrb_value ret = mrb_top_run(mrb, proc, self, 0);
if (mrb->exc) {
mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
}
+ mrb->c->ci = mrb->c->cibase + cioff;
return ret;
}
/* clear block */
@@ -325,6 +374,7 @@ f_instance_eval(mrb_state *mrb, mrb_value self)
proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line);
MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(cv));
mrb_assert(!MRB_PROC_CFUNC_P(proc));
+ mrb->c->ci->target_class = mrb_class_ptr(cv);
return exec_irep(mrb, self, proc);
}
else {
@@ -337,7 +387,7 @@ void
mrb_mruby_eval_gem_init(mrb_state* mrb)
{
mrb_define_module_function(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_ARG(1, 3));
- mrb_define_method(mrb, mrb->kernel_module, "instance_eval", f_instance_eval, MRB_ARGS_ARG(1, 2));
+ mrb_define_method(mrb, mrb_class_get(mrb, "BasicObject"), "instance_eval", f_instance_eval, MRB_ARGS_ARG(1, 2));
}
void
diff --git a/mrbgems/mruby-eval/test/eval.rb b/mrbgems/mruby-eval/test/eval.rb
index 66ca1fcdb..4930259c1 100644
--- a/mrbgems/mruby-eval/test/eval.rb
+++ b/mrbgems/mruby-eval/test/eval.rb
@@ -34,7 +34,7 @@ assert('Kernel.eval', '15.3.1.2.3') do
}
assert_equal(2) {
a = 10
- Kernel.eval 'def f(a); b=a.send(:+, 1); end'
+ Kernel.eval 'def f(a); b=a+1; end'
f(1)
}
end
@@ -58,7 +58,7 @@ end
assert('String instance_eval') do
obj = Object.new
- obj.instance_variable_set :@test, 'test'
+ obj.instance_eval{ @test = 'test' }
assert_raise(ArgumentError) { obj.instance_eval(0) { } }
assert_raise(ArgumentError) { obj.instance_eval('0', 'test', 0, 'test') }
assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)}
@@ -80,7 +80,7 @@ assert('Kernel.#eval(string) context') do
assert_equal('class') { obj.const_string }
end
-assert('Object#instance_eval with begin-rescue-ensure execution order') do
+assert('BasicObject#instance_eval with begin-rescue-ensure execution order') do
class HellRaiser
def raise_hell
order = [:enter_raise_hell]
@@ -99,3 +99,34 @@ assert('Object#instance_eval with begin-rescue-ensure execution order') do
hell_raiser = HellRaiser.new
assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell)
end
+
+assert('BasicObject#instance_eval to define singleton methods Issue #3141') do
+ foo_class = Class.new do
+ def bar(x)
+ instance_eval "def baz; #{x}; end"
+ end
+ end
+
+ f1 = foo_class.new
+ f2 = foo_class.new
+ f1.bar 1
+ f2.bar 2
+ assert_equal(1){f1.baz}
+ assert_equal(2){f2.baz}
+end
+
+assert('Kernel.#eval(string) Issue #4021') do
+ assert_equal('FOO') { (eval <<'EOS').call }
+foo = "FOO"
+Proc.new { foo }
+EOS
+ assert_equal('FOO') {
+ def do_eval(code)
+ eval(code)
+ end
+ do_eval(<<'EOS').call
+foo = "FOO"
+Proc.new { foo }
+EOS
+ }
+end
diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c
index 83153a9df..17ce77c5d 100644
--- a/mrbgems/mruby-fiber/src/fiber.c
+++ b/mrbgems/mruby-fiber/src/fiber.c
@@ -127,7 +127,6 @@ fiber_init(mrb_state *mrb, mrb_value self)
ci->proc = p;
mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p);
ci->pc = p->body.irep->iseq;
- ci->nregs = p->body.irep->nregs;
ci[1] = ci[0];
c->ci++; /* push dummy callinfo */
@@ -175,6 +174,9 @@ fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c)
static void
fiber_switch_context(mrb_state *mrb, struct mrb_context *c)
{
+ if (mrb->c->fib) {
+ mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
+ }
c->status = MRB_FIBER_RUNNING;
mrb->c = c;
}
@@ -184,26 +186,30 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
{
struct mrb_context *c = fiber_check(mrb, self);
struct mrb_context *old_c = mrb->c;
+ enum mrb_fiber_state status;
mrb_value value;
fiber_check_cfunc(mrb, c);
- if (resume && c->status == MRB_FIBER_TRANSFERRED) {
+ status = c->status;
+ if (resume && status == MRB_FIBER_TRANSFERRED) {
mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
}
- if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) {
- mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");
+ if (status == MRB_FIBER_RUNNING || status == MRB_FIBER_RESUMED) {
+ mrb_raise(mrb, E_FIBER_ERROR, "double resume");
}
- if (c->status == MRB_FIBER_TERMINATED) {
+ if (status == MRB_FIBER_TERMINATED) {
mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
}
- mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
+ old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
- if (c->status == MRB_FIBER_CREATED) {
+ fiber_switch_context(mrb, c);
+ if (status == MRB_FIBER_CREATED) {
mrb_value *b, *e;
- if (len >= c->stend - c->stack) {
- mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber");
+ if (!c->ci->proc) {
+ mrb_raise(mrb, E_FIBER_ERROR, "double resume (current)");
}
+ mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */
b = c->stack+1;
e = b + len;
while (b<e) {
@@ -215,7 +221,6 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
else {
value = fiber_result(mrb, a, len);
}
- fiber_switch_context(mrb, c);
if (vmexec) {
c->vmexec = TRUE;
diff --git a/mrbgems/mruby-fiber/test/fiber.rb b/mrbgems/mruby-fiber/test/fiber.rb
index d063a0a62..b6ecb798c 100644
--- a/mrbgems/mruby-fiber/test/fiber.rb
+++ b/mrbgems/mruby-fiber/test/fiber.rb
@@ -76,7 +76,7 @@ assert('Fiber iteration') do
end
assert('Fiber with splat in the block argument list') {
- Fiber.new{|*x|x}.resume(1) == [1]
+ assert_equal([1], Fiber.new{|*x|x}.resume(1))
}
assert('Fiber raises on resume when dead') do
diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb
index 549bca0a8..547f3404a 100644
--- a/mrbgems/mruby-hash-ext/mrblib/hash.rb
+++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb
@@ -27,9 +27,9 @@ class Hash
length = object.length
if length == 1
o = object[0]
- if o.respond_to?(:to_hash)
+ if Hash === o
h = self.new
- object[0].to_hash.each { |k, v| h[k] = v }
+ o.each { |k, v| h[k] = v }
return h
elsif o.respond_to?(:to_a)
h = self.new
@@ -62,25 +62,6 @@ class Hash
##
# call-seq:
- # Hash.try_convert(obj) -> hash or nil
- #
- # Try to convert <i>obj</i> into a hash, using to_hash method.
- # Returns converted hash or nil if <i>obj</i> cannot be converted
- # for any reason.
- #
- # Hash.try_convert({1=>2}) # => {1=>2}
- # Hash.try_convert("1=>2") # => nil
- #
- def self.try_convert(obj)
- if obj.respond_to?(:to_hash)
- obj.to_hash
- else
- nil
- end
- end
-
- ##
- # call-seq:
# hsh.merge!(other_hash) -> hsh
# hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh
#
@@ -101,7 +82,7 @@ class Hash
#
def merge!(other, &block)
- raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash)
+ raise TypeError, "Hash required (#{other.class} given)" unless Hash === other
if block
other.each_key{|k|
self[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k]
@@ -116,6 +97,31 @@ class Hash
##
# call-seq:
+ # hsh.compact! -> hsh
+ #
+ # Removes all nil values from the hash. Returns the hash.
+ # Returns nil if the hash does not contain nil values.
+ #
+ # h = { a: 1, b: false, c: nil }
+ # h.compact! #=> { a: 1, b: false }
+ #
+
+ def compact!
+ keys = self.keys
+ nk = keys.select{|k|
+ self[k] != nil
+ }
+ return nil if (keys.size == nk.size)
+ h = {}
+ nk.each {|k|
+ h[k] = self[k]
+ }
+ h
+ self.replace(h)
+ end
+
+ ##
+ # call-seq:
# hsh.compact -> new_hsh
#
# Returns a new hash with the nil values/key pairs removed
@@ -125,9 +131,13 @@ class Hash
# h #=> { a: 1, b: false, c: nil }
#
def compact
- result = self.dup
- result.compact!
- result
+ h = {}
+ self.keys.select{|k|
+ self[k] != nil
+ }.each {|k|
+ h[k] = self[k]
+ }
+ h
end
##
@@ -300,11 +310,7 @@ class Hash
# h1 < h1 #=> false
#
def <(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
size < hash.size and all? {|key, val|
hash.key?(key) and hash[key] == val
}
@@ -324,11 +330,7 @@ class Hash
# h1 <= h1 #=> true
#
def <=(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
size <= hash.size and all? {|key, val|
hash.key?(key) and hash[key] == val
}
@@ -348,11 +350,7 @@ class Hash
# h1 > h1 #=> false
#
def >(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
size > hash.size and hash.all? {|key, val|
key?(key) and self[key] == val
}
@@ -372,11 +370,7 @@ class Hash
# h1 >= h1 #=> true
#
def >=(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
size >= hash.size and hash.all? {|key, val|
key?(key) and self[key] == val
}
@@ -432,9 +426,9 @@ class Hash
return to_enum :transform_keys! unless block
self.keys.each do |k|
value = self[k]
- new_key = block.call(k)
self.__delete(k)
- self[new_key] = value
+ k = block.call(k) if block
+ self[k] = value
end
self
end
@@ -457,6 +451,7 @@ class Hash
end
hash
end
+
##
# call-seq:
# hsh.transform_values! {|key| block } -> hsh
diff --git a/mrbgems/mruby-hash-ext/src/hash-ext.c b/mrbgems/mruby-hash-ext/src/hash-ext.c
index 6619f5268..e6112667b 100644
--- a/mrbgems/mruby-hash-ext/src/hash-ext.c
+++ b/mrbgems/mruby-hash-ext/src/hash-ext.c
@@ -37,42 +37,6 @@ hash_values_at(mrb_state *mrb, mrb_value hash)
}
/*
- * call-seq:
- * hsh.compact! -> hsh
- *
- * Removes all nil values from the hash. Returns the hash.
- *
- * h = { a: 1, b: false, c: nil }
- * h.compact! #=> { a: 1, b: false }
- */
-static mrb_value
-hash_compact_bang(mrb_state *mrb, mrb_value hash)
-{
- khiter_t k;
- khash_t(ht) *h = RHASH_TBL(hash);
- mrb_int n = -1;
-
- if (!h) return mrb_nil_value();
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- mrb_value val = kh_value(h, k).v;
- khiter_t k2;
-
- if (mrb_nil_p(val)) {
- kh_del(ht, mrb, h, k);
- n = kh_value(h, k).n;
- for (k2 = kh_begin(h); k2 != kh_end(h); k2++) {
- if (!kh_exist(h, k2)) continue;
- if (kh_value(h, k2).n > n) kh_value(h, k2).n--;
- }
- }
- }
- }
- if (n < 0) return mrb_nil_value();
- return hash;
-}
-
-/*
* call-seq:
* hsh.slice(*keys) -> a_hash
*
@@ -85,28 +49,22 @@ hash_compact_bang(mrb_state *mrb, mrb_value hash)
static mrb_value
hash_slice(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
mrb_value *argv, result;
mrb_int argc, i;
- khiter_t k;
- int ai;
mrb_get_args(mrb, "*", &argv, &argc);
- if (argc == 0 || h == NULL) {
+ if (argc == 0) {
return mrb_hash_new_capa(mrb, argc);
}
result = mrb_hash_new_capa(mrb, argc);
- ai = mrb_gc_arena_save(mrb);
for (i = 0; i < argc; i++) {
mrb_value key = argv[i];
+ mrb_value val;
- k = kh_get(ht, mrb, h, key);
- if (k != kh_end(h)) {
- mrb_value val = kh_value(h, k).v;
-
+ val = mrb_hash_fetch(mrb, hash, key, mrb_undef_value());
+ if (!mrb_undef_p(val)) {
mrb_hash_set(mrb, result, key, val);
}
- mrb_gc_arena_restore(mrb, ai);
}
return result;
}
@@ -118,7 +76,6 @@ mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
h = mrb->hash_class;
mrb_define_method(mrb, h, "values_at", hash_values_at, MRB_ARGS_ANY());
- mrb_define_method(mrb, h, "compact!", hash_compact_bang, MRB_ARGS_NONE());
mrb_define_method(mrb, h, "slice", hash_slice, MRB_ARGS_ANY());
}
diff --git a/mrbgems/mruby-hash-ext/test/hash.rb b/mrbgems/mruby-hash-ext/test/hash.rb
index 269da800d..b5d0aaaf8 100644
--- a/mrbgems/mruby-hash-ext/test/hash.rb
+++ b/mrbgems/mruby-hash-ext/test/hash.rb
@@ -45,12 +45,6 @@ assert('Hash.[] for sub class') do
assert_equal(sub_hash_class, sub_hash.class)
end
-assert('Hash.try_convert') do
- assert_nil Hash.try_convert(nil)
- assert_nil Hash.try_convert("{1=>2}")
- assert_equal({1=>2}, Hash.try_convert({1=>2}))
-end
-
assert('Hash#merge!') do
a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
b = { 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' }
diff --git a/mrbgems/mruby-inline-struct/test/inline.c b/mrbgems/mruby-inline-struct/test/inline.c
index 0baaab617..51804ae31 100644
--- a/mrbgems/mruby-inline-struct/test/inline.c
+++ b/mrbgems/mruby-inline-struct/test/inline.c
@@ -11,17 +11,19 @@ istruct_test_initialize(mrb_state *mrb, mrb_value self)
mrb_value object;
mrb_get_args(mrb, "o", &object);
- if (mrb_float_p(object))
- {
- snprintf(string, size, "float(%.3f)", mrb_float(object));
+ if (mrb_fixnum_p(object)) {
+ strncpy(string, "fixnum", size-1);
}
- else if (mrb_fixnum_p(object))
- {
- snprintf(string, size, "fixnum(%" MRB_PRId ")", mrb_fixnum(object));
+#ifndef MRB_WITHOUT_FLOAT
+ else if (mrb_float_p(object)) {
+ strncpy(string, "float", size-1);
}
- else if (mrb_string_p(object))
- {
- snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object));
+#endif
+ else if (mrb_string_p(object)) {
+ strncpy(string, "string", size-1);
+ }
+ else {
+ strncpy(string, "anything", size-1);
}
string[size - 1] = 0; // force NULL at the end
@@ -47,7 +49,7 @@ istruct_test_test_receive(mrb_state *mrb, mrb_value self)
mrb_get_args(mrb, "o", &object);
if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest"))
{
- mrb_raisef(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
+ mrb_raise(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
}
return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's');
}
diff --git a/mrbgems/mruby-inline-struct/test/inline.rb b/mrbgems/mruby-inline-struct/test/inline.rb
index 495859232..f959a17c4 100644
--- a/mrbgems/mruby-inline-struct/test/inline.rb
+++ b/mrbgems/mruby-inline-struct/test/inline.rb
@@ -17,14 +17,14 @@ end
assert('InlineStructTest#dup') do
obj = InlineStructTest.new(1)
- assert_equal obj.to_s, 'fixnum(1)'
- assert_equal obj.dup.to_s, 'fixnum(1)'
+ assert_equal obj.to_s, 'fixnum'
+ assert_equal obj.dup.to_s, 'fixnum'
end
assert('InlineStructTest#clone') do
obj = InlineStructTest.new(1)
- assert_equal obj.to_s, 'fixnum(1)'
- assert_equal obj.clone.to_s, 'fixnum(1)'
+ assert_equal obj.to_s, 'fixnum'
+ assert_equal obj.clone.to_s, 'fixnum'
end
assert('InlineStruct#object_id') do
@@ -38,22 +38,22 @@ end
assert('InlineStructTest#mutate (dup)') do
obj1 = InlineStructTest.new("foo")
- assert_equal obj1.to_s, "string(foo)"
+ assert_equal obj1.to_s, "string"
obj2 = obj1.dup
- assert_equal obj2.to_s, "string(foo)"
+ assert_equal obj2.to_s, "string"
obj1.mutate
- assert_equal obj1.to_s, "mutate(foo)"
- assert_equal obj2.to_s, "string(foo)"
+ assert_equal obj1.to_s, "mutate"
+ assert_equal obj2.to_s, "string"
end
assert('InlineStructTest#mutate (clone)') do
obj1 = InlineStructTest.new("foo")
- assert_equal obj1.to_s, "string(foo)"
+ assert_equal obj1.to_s, "string"
obj2 = obj1.clone
- assert_equal obj2.to_s, "string(foo)"
+ assert_equal obj2.to_s, "string"
obj1.mutate
- assert_equal obj1.to_s, "mutate(foo)"
- assert_equal obj2.to_s, "string(foo)"
+ assert_equal obj1.to_s, "mutate"
+ assert_equal obj2.to_s, "string"
end
assert('InlineStructTest#test_receive(string)') do
@@ -101,26 +101,6 @@ if InlineStructTest.length == 24
assert('InlineStructTest length [64 bit]') do
assert_equal InlineStructTest.length, 3 * 8
end
-
- assert('InlineStructTest w/float [64 bit]') do
- obj = InlineStructTest.new(1.25)
- assert_equal obj.to_s, "float(1.250)"
- end
-
- assert('InlineStructTest w/fixnum [64 bit]') do
- obj = InlineStructTest.new(42)
- assert_equal obj.to_s, "fixnum(42)"
- end
-
- assert('InlineStructTest w/string [64 bit]') do
- obj = InlineStructTest.new("hello")
- assert_equal obj.to_s, "string(hello)"
- end
-
- assert('InlineStructTest w/long string [64 bit]') do
- obj = InlineStructTest.new("this won't fit in 3 * 8 bytes available for the structure")
- assert_equal obj.to_s, "string(this won't fit i"
- end
end
# 32-bit mode
@@ -128,24 +108,11 @@ if InlineStructTest.length == 12
assert('InlineStructTest length [32 bit]') do
assert_equal InlineStructTest.length, 3 * 4
end
+end
- assert('InlineStructTest w/float [32 bit]') do
- obj = InlineStructTest.new(1.25)
- assert_equal obj.to_s, "float(1.250"
- end
-
- assert('InlineStructTest w/fixnum [32 bit]') do
- obj = InlineStructTest.new(42)
- assert_equal obj.to_s, "fixnum(42)"
- end
-
- assert('InlineStructTest w/string [32 bit]') do
- obj = InlineStructTest.new("hello")
- assert_equal obj.to_s, "string(hell"
- end
-
- assert('InlineStructTest w/long string [32 bit]') do
- obj = InlineStructTest.new("this won't fit in 3 * 4 bytes available for the structure")
- assert_equal obj.to_s, "string(this"
+# 16-bit mode
+if InlineStructTest.length == 6
+ assert('InlineStructTest length [16 bit]') do
+ assert_equal InlineStructTest.length, 3 * 2
end
end
diff --git a/mrbgems/mruby-io/mrbgem.rake b/mrbgems/mruby-io/mrbgem.rake
index 50fa49678..d79964590 100644
--- a/mrbgems/mruby-io/mrbgem.rake
+++ b/mrbgems/mruby-io/mrbgem.rake
@@ -4,7 +4,7 @@ MRuby::Gem::Specification.new('mruby-io') do |spec|
spec.summary = 'IO and File class'
spec.cc.include_paths << "#{build.root}/src"
-
+
case RUBY_PLATFORM
when /mingw|mswin/
spec.linker.libraries += ['Ws2_32']
@@ -14,4 +14,5 @@ MRuby::Gem::Specification.new('mruby-io') do |spec|
if build.kind_of?(MRuby::CrossBuild) && %w(x86_64-w64-mingw32 i686-w64-mingw32).include?(build.host_target)
spec.linker.libraries += ['ws2_32']
end
+ spec.add_test_dependency 'mruby-time', core: 'mruby-time'
end
diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c
index 3dcfe3a0b..243f634b4 100644
--- a/mrbgems/mruby-io/src/file.c
+++ b/mrbgems/mruby-io/src/file.c
@@ -114,11 +114,13 @@ mrb_file_s_unlink(mrb_state *mrb, mrb_value obj)
mrb_get_args(mrb, "*", &argv, &argc);
for (i = 0; i < argc; i++) {
- pathv = mrb_convert_type(mrb, argv[i], MRB_TT_STRING, "String", "to_str");
- path = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &pathv), -1);
+ const char *utf8_path;
+ pathv = mrb_ensure_string_type(mrb, argv[i]);
+ utf8_path = mrb_string_value_cstr(mrb, &pathv);
+ path = mrb_locale_from_utf8(utf8_path, -1);
if (UNLINK(path) < 0) {
mrb_locale_free(path);
- mrb_sys_fail(mrb, path);
+ mrb_sys_fail(mrb, utf8_path);
}
mrb_locale_free(path);
}
@@ -144,7 +146,7 @@ mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
#endif
mrb_locale_free(src);
mrb_locale_free(dst);
- mrb_sys_fail(mrb, mrb_str_to_cstr(mrb, mrb_format(mrb, "(%S, %S)", from, to)));
+ mrb_sys_fail(mrb, RSTRING_PTR(mrb_format(mrb, "(%S, %S)", from, to)));
}
mrb_locale_free(src);
mrb_locale_free(dst);
@@ -157,12 +159,12 @@ mrb_file_dirname(mrb_state *mrb, mrb_value klass)
#if defined(_WIN32) || defined(_WIN64)
char dname[_MAX_DIR], vname[_MAX_DRIVE];
char buffer[_MAX_DRIVE + _MAX_DIR];
+ const char *utf8_path;
char *path;
size_t ridx;
- mrb_value s;
- mrb_get_args(mrb, "S", &s);
- path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, s), -1);
- _splitpath((const char*)path, vname, dname, NULL, NULL);
+ mrb_get_args(mrb, "z", &utf8_path);
+ path = mrb_locale_from_utf8(utf8_path, -1);
+ _splitpath(path, vname, dname, NULL, NULL);
snprintf(buffer, _MAX_DRIVE + _MAX_DIR, "%s%s", vname, dname);
mrb_locale_free(path);
ridx = strlen(buffer);
@@ -246,7 +248,7 @@ mrb_file_realpath(mrb_state *mrb, mrb_value klass)
s = mrb_str_append(mrb, s, pathname);
pathname = s;
}
- cpath = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, pathname), -1);
+ cpath = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &pathname), -1);
result = mrb_str_buf_new(mrb, PATH_MAX);
if (realpath(cpath, RSTRING_PTR(result)) == NULL) {
mrb_locale_free(cpath);
@@ -298,7 +300,7 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home");
}
} else {
- const char *cuser = mrb_str_to_cstr(mrb, username);
+ const char *cuser = mrb_string_value_cstr(mrb, &username);
struct passwd *pwd = getpwnam(cuser);
if (pwd == NULL) {
return mrb_nil_value();
@@ -310,7 +312,7 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
}
home = mrb_locale_from_utf8(home, -1);
path = mrb_str_new_cstr(mrb, home);
- mrb_utf8_free(home);
+ mrb_locale_free(home);
return path;
#else
argc = mrb_get_argc(mrb);
@@ -327,7 +329,7 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
}
home = mrb_locale_from_utf8(home, -1);
path = mrb_str_new_cstr(mrb, home);
- mrb_utf8_free(home);
+ mrb_locale_free(home);
return path;
#endif
}
@@ -343,7 +345,7 @@ mrb_file_mtime(mrb_state *mrb, mrb_value self)
fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
if (fstat(fd, &st) == -1)
return mrb_false_value();
- return mrb_funcall(mrb, obj, "at", 1, mrb_float_value(mrb, st.st_mtime));
+ return mrb_funcall(mrb, obj, "at", 1, mrb_fixnum_value(st.st_mtime));
}
mrb_value
@@ -391,9 +393,8 @@ mrb_file_s_symlink(mrb_state *mrb, mrb_value klass)
int ai = mrb_gc_arena_save(mrb);
mrb_get_args(mrb, "SS", &from, &to);
- src = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, from), -1);
- dst = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, to), -1);
-
+ src = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &from), -1);
+ dst = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &to), -1);
if (symlink(src, dst) == -1) {
mrb_locale_free(src);
mrb_locale_free(dst);
@@ -415,15 +416,16 @@ mrb_file_s_chmod(mrb_state *mrb, mrb_value klass) {
mrb_get_args(mrb, "i*", &mode, &filenames, &argc);
for (i = 0; i < argc; i++) {
- char *path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, filenames[i]), -1);
+ const char *utf8_path = mrb_string_value_cstr(mrb, &filenames[i]);
+ char *path = mrb_locale_from_utf8(utf8_path, -1);
if (CHMOD(path, mode) == -1) {
mrb_locale_free(path);
- mrb_sys_fail(mrb, path);
+ mrb_sys_fail(mrb, utf8_path);
}
mrb_locale_free(path);
+ mrb_gc_arena_restore(mrb, ai);
}
- mrb_gc_arena_restore(mrb, ai);
return mrb_fixnum_value(argc);
}
diff --git a/mrbgems/mruby-io/src/file_test.c b/mrbgems/mruby-io/src/file_test.c
index e429b06b3..85a221ff9 100644
--- a/mrbgems/mruby-io/src/file_test.c
+++ b/mrbgems/mruby-io/src/file_test.c
@@ -1,5 +1,5 @@
/*
-** file.c - File class
+** file_test.c - FileTest class
*/
#include "mruby.h"
@@ -42,16 +42,9 @@ extern struct mrb_data_type mrb_io_type;
static int
mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
{
- mrb_value tmp;
- mrb_value io_klass, str_klass;
-
- io_klass = mrb_obj_value(mrb_class_get(mrb, "IO"));
- str_klass = mrb_obj_value(mrb_class_get(mrb, "String"));
-
- tmp = mrb_funcall(mrb, obj, "is_a?", 1, io_klass);
- if (mrb_test(tmp)) {
+ if (mrb_obj_is_kind_of(mrb, obj, mrb_class_get(mrb, "IO"))) {
struct mrb_io *fptr;
- fptr = (struct mrb_io *)mrb_get_datatype(mrb, obj, &mrb_io_type);
+ fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, obj, &mrb_io_type);
if (fptr && fptr->fd >= 0) {
return fstat(fptr->fd, st);
@@ -60,10 +53,8 @@ mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
mrb_raise(mrb, E_IO_ERROR, "closed stream");
return -1;
}
-
- tmp = mrb_funcall(mrb, obj, "is_a?", 1, str_klass);
- if (mrb_test(tmp)) {
- char *path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, obj), -1);
+ else {
+ char *path = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &obj), -1);
int ret;
if (do_lstat) {
ret = LSTAT(path, st);
@@ -73,8 +64,6 @@ mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
mrb_locale_free(path);
return ret;
}
-
- return -1;
}
static int
diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c
index 38b0b06c2..99441f16b 100644
--- a/mrbgems/mruby-io/src/io.c
+++ b/mrbgems/mruby-io/src/io.c
@@ -79,7 +79,7 @@ io_get_open_fptr(mrb_state *mrb, mrb_value self)
{
struct mrb_io *fptr;
- fptr = (struct mrb_io *)mrb_get_datatype(mrb, self, &mrb_io_type);
+ fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, self, &mrb_io_type);
if (fptr == NULL) {
mrb_raise(mrb, E_IO_ERROR, "uninitialized stream.");
}
@@ -209,7 +209,7 @@ mrb_fd_cloexec(mrb_state *mrb, int fd)
#endif
}
-#ifndef _WIN32
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
static int
mrb_cloexec_pipe(mrb_state *mrb, int fildes[2])
{
@@ -305,7 +305,112 @@ option_to_fd(mrb_state *mrb, mrb_value obj, const char *key)
return -1; /* never reached */
}
-#ifndef _WIN32
+#ifdef _WIN32
+mrb_value
+mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value cmd, io;
+ mrb_value mode = mrb_str_new_cstr(mrb, "r");
+ mrb_value opt = mrb_hash_new(mrb);
+
+ struct mrb_io *fptr;
+ const char *pname;
+ int pid = 0, flags;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ SECURITY_ATTRIBUTES saAttr;
+
+ HANDLE ifd[2];
+ HANDLE ofd[2];
+
+ int doexec;
+ int opt_in, opt_out, opt_err;
+
+ ifd[0] = INVALID_HANDLE_VALUE;
+ ifd[1] = INVALID_HANDLE_VALUE;
+ ofd[0] = INVALID_HANDLE_VALUE;
+ ofd[1] = INVALID_HANDLE_VALUE;
+
+ mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
+ io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
+
+ pname = mrb_string_value_cstr(mrb, &cmd);
+ flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+
+ doexec = (strcmp("-", pname) != 0);
+ opt_in = option_to_fd(mrb, opt, "in");
+ opt_out = option_to_fd(mrb, opt, "out");
+ opt_err = option_to_fd(mrb, opt, "err");
+
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ if (flags & FMODE_READABLE) {
+ if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
+ || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
+ mrb_sys_fail(mrb, "pipe");
+ }
+ }
+
+ if (flags & FMODE_WRITABLE) {
+ if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
+ || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
+ mrb_sys_fail(mrb, "pipe");
+ }
+ }
+
+ if (doexec) {
+ ZeroMemory(&pi, sizeof(pi));
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags |= STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_HIDE;
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ if (flags & FMODE_READABLE) {
+ si.hStdOutput = ofd[1];
+ si.hStdError = ofd[1];
+ }
+ if (flags & FMODE_WRITABLE) {
+ si.hStdInput = ifd[0];
+ }
+ if (!CreateProcess(
+ NULL, (char*)pname, NULL, NULL,
+ TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
+ CloseHandle(ifd[0]);
+ CloseHandle(ifd[1]);
+ CloseHandle(ofd[0]);
+ CloseHandle(ofd[1]);
+ mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
+ }
+ CloseHandle(pi.hThread);
+ CloseHandle(ifd[0]);
+ CloseHandle(ofd[1]);
+ pid = pi.dwProcessId;
+ }
+
+ mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+
+ fptr = mrb_io_alloc(mrb);
+ fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
+ fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
+ fptr->pid = pid;
+ fptr->readable = ((flags & FMODE_READABLE) != 0);
+ fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+ fptr->sync = 0;
+
+ DATA_TYPE(io) = &mrb_io_type;
+ DATA_PTR(io) = fptr;
+ return io;
+}
+#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+mrb_value
+mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
+{
+ mrb_raise(mrb, E_NOTIMP_ERROR, "IO#popen is not supported on the platform");
+ return mrb_false_value();
+}
+#else
mrb_value
mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
{
@@ -440,104 +545,6 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
}
return result;
}
-#else
-mrb_value
-mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
-{
- mrb_value cmd, io;
- mrb_value mode = mrb_str_new_cstr(mrb, "r");
- mrb_value opt = mrb_hash_new(mrb);
-
- struct mrb_io *fptr;
- const char *pname;
- int pid = 0, flags;
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- SECURITY_ATTRIBUTES saAttr;
-
- HANDLE ifd[2];
- HANDLE ofd[2];
-
- int doexec;
- int opt_in, opt_out, opt_err;
-
- ifd[0] = INVALID_HANDLE_VALUE;
- ifd[1] = INVALID_HANDLE_VALUE;
- ofd[0] = INVALID_HANDLE_VALUE;
- ofd[1] = INVALID_HANDLE_VALUE;
-
- mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
- io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
-
- pname = mrb_string_value_cstr(mrb, &cmd);
- flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
-
- doexec = (strcmp("-", pname) != 0);
- opt_in = option_to_fd(mrb, opt, "in");
- opt_out = option_to_fd(mrb, opt, "out");
- opt_err = option_to_fd(mrb, opt, "err");
-
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- saAttr.lpSecurityDescriptor = NULL;
-
- if (flags & FMODE_READABLE) {
- if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
- || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
- mrb_sys_fail(mrb, "pipe");
- }
- }
-
- if (flags & FMODE_WRITABLE) {
- if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
- || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
- mrb_sys_fail(mrb, "pipe");
- }
- }
-
- if (doexec) {
- ZeroMemory(&pi, sizeof(pi));
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
- si.dwFlags |= STARTF_USESHOWWINDOW;
- si.wShowWindow = SW_HIDE;
- si.dwFlags |= STARTF_USESTDHANDLES;
- if (flags & FMODE_READABLE) {
- si.hStdOutput = ofd[1];
- si.hStdError = ofd[1];
- }
- if (flags & FMODE_WRITABLE) {
- si.hStdInput = ifd[0];
- }
- if (!CreateProcess(
- NULL, (char*)pname, NULL, NULL,
- TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
- CloseHandle(ifd[0]);
- CloseHandle(ifd[1]);
- CloseHandle(ofd[0]);
- CloseHandle(ofd[1]);
- mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
- }
- CloseHandle(pi.hThread);
- CloseHandle(ifd[0]);
- CloseHandle(ofd[1]);
- pid = pi.dwProcessId;
- }
-
- mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
-
- fptr = mrb_io_alloc(mrb);
- fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
- fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
- fptr->pid = pid;
- fptr->readable = ((flags & FMODE_READABLE) != 0);
- fptr->writable = ((flags & FMODE_WRITABLE) != 0);
- fptr->sync = 0;
-
- DATA_TYPE(io) = &mrb_io_type;
- DATA_PTR(io) = fptr;
- return io;
-}
#endif
static int
@@ -780,7 +787,7 @@ reopen:
mrb_str_modify(mrb, mrb_str_ptr(emsg));
mrb_sys_fail(mrb, RSTRING_PTR(emsg));
}
- mrb_utf8_free(fname);
+ mrb_locale_free(fname);
if (fd <= 2) {
mrb_fd_cloexec(mrb, fd);
@@ -948,7 +955,7 @@ mrb_value
mrb_io_closed(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
- fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+ fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, io, &mrb_io_type);
if (fptr == NULL || fptr->fd >= 0) {
return mrb_false_value();
}
@@ -1004,7 +1011,7 @@ mrb_io_read_data_pending(mrb_state *mrb, mrb_value io)
return 0;
}
-#ifndef _WIN32
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
static mrb_value
mrb_io_s_pipe(mrb_state *mrb, mrb_value klass)
{
@@ -1306,7 +1313,7 @@ mrb_init_io(mrb_state *mrb)
mrb_define_class_method(mrb, io, "for_fd", mrb_io_s_for_fd, MRB_ARGS_ANY());
mrb_define_class_method(mrb, io, "select", mrb_io_s_select, MRB_ARGS_ANY());
mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ANY());
-#ifndef _WIN32
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
mrb_define_class_method(mrb, io, "_pipe", mrb_io_s_pipe, MRB_ARGS_NONE());
#endif
diff --git a/mrbgems/mruby-io/test/file.rb b/mrbgems/mruby-io/test/file.rb
index dc6fe369a..1535ebb44 100644
--- a/mrbgems/mruby-io/test/file.rb
+++ b/mrbgems/mruby-io/test/file.rb
@@ -1,16 +1,14 @@
##
-# IO Test
+# File Test
-assert('File', '15.2.21') do
- File.class == Class
-end
+MRubyIOTestUtil.io_test_setup
-assert('File', '15.2.21.2') do
- File.superclass == IO
+assert('File.class', '15.2.21') do
+ assert_equal Class, File.class
end
-assert('File TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
+assert('File.superclass', '15.2.21.2') do
+ assert_equal IO, File.superclass
end
assert('File#initialize', '15.2.21.4.1') do
@@ -27,7 +25,7 @@ assert('File#path', '15.2.21.4.2') do
assert_equal $mrbtest_io_rfname, io.path
io.close
assert_equal $mrbtest_io_rfname, io.path
- io.closed?
+ assert_true io.closed?
end
assert('File.basename') do
@@ -35,6 +33,7 @@ assert('File.basename') do
assert_equal 'a', File.basename('/a/')
assert_equal 'b', File.basename('/a/b')
assert_equal 'b', File.basename('../a/b')
+ assert_raise(ArgumentError) { File.basename("/a/b\0") }
end
assert('File.dirname') do
@@ -69,18 +68,15 @@ assert('File#flock') do
end
assert('File#mtime') do
- unless Object.const_defined?(:Time)
- skip "File#mtime require Time"
- end
begin
- now = Time.now.to_i
- mt = 0
- File.open('mtime-test', 'w') do |f|
- mt = f.mtime.to_i
+ File.open("#{$mrbtest_io_wfname}.mtime", 'w') do |f|
+ assert_equal Time, f.mtime.class
+ File.open("#{$mrbtest_io_wfname}.mtime", 'r') do |f2|
+ assert_equal true, f.mtime == f2.mtime
+ end
end
- assert_equal true, mt >= now
ensure
- File.delete('mtime-test')
+ File.delete("#{$mrbtest_io_wfname}.mtime")
end
end
@@ -111,6 +107,8 @@ assert('File.realpath') do
MRubyIOTestUtil.rmdir dir
end
end
+
+ assert_raise(ArgumentError) { File.realpath("TO\0DO") }
end
assert("File.readlink") do
@@ -177,7 +175,6 @@ assert('File.path') do
assert_equal "a/../b/./c", File.path("a/../b/./c")
assert_raise(TypeError) { File.path(nil) }
assert_raise(TypeError) { File.path(123) }
-
end
assert('File.symlink') do
@@ -200,14 +197,12 @@ assert('File.symlink') do
end
assert('File.chmod') do
- File.open('chmod-test', 'w') {}
+ File.open("#{$mrbtest_io_wfname}.chmod-test", 'w') {}
begin
- assert_equal 1, File.chmod(0400, 'chmod-test')
+ assert_equal 1, File.chmod(0400, "#{$mrbtest_io_wfname}.chmod-test")
ensure
- File.delete('chmod-test')
+ File.delete("#{$mrbtest_io_wfname}.chmod-test")
end
end
-assert('File TEST CLEANUP') do
- assert_nil MRubyIOTestUtil.io_test_cleanup
-end
+MRubyIOTestUtil.io_test_cleanup
diff --git a/mrbgems/mruby-io/test/file_test.rb b/mrbgems/mruby-io/test/file_test.rb
index 2c831f0d5..2134c6a75 100644
--- a/mrbgems/mruby-io/test/file_test.rb
+++ b/mrbgems/mruby-io/test/file_test.rb
@@ -1,9 +1,7 @@
##
# FileTest
-assert('FileTest TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
-end
+MRubyIOTestUtil.io_test_setup
assert("FileTest.directory?") do
dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
@@ -22,9 +20,8 @@ assert("FileTest.exist?") do
assert_equal true, FileTest.exist?(io), "io obj - exist"
io.close
assert_equal true, io.closed?
- assert_raise IOError do
- FileTest.exist?(io)
- end
+ assert_raise(IOError) { FileTest.exist?(io) }
+ assert_raise(TypeError) { File.exist?($mrbtest_io_rfname.to_sym) }
end
assert("FileTest.file?") do
@@ -67,11 +64,11 @@ assert("FileTest.size?") do
assert_raise IOError do
FileTest.size?(fp1)
end
+ assert_true fp1.closed?
assert_raise IOError do
FileTest.size?(fp2)
end
-
- fp1.closed? && fp2.closed?
+ assert_true fp2.closed?
end
assert("FileTest.socket?") do
@@ -105,13 +102,11 @@ assert("FileTest.zero?") do
assert_raise IOError do
FileTest.zero?(fp1)
end
+ assert_true fp1.closed?
assert_raise IOError do
FileTest.zero?(fp2)
end
-
- fp1.closed? && fp2.closed?
+ assert_true fp2.closed?
end
-assert('FileTest TEST CLEANUP') do
- assert_nil MRubyIOTestUtil.io_test_cleanup
-end
+MRubyIOTestUtil.io_test_cleanup
diff --git a/mrbgems/mruby-io/test/gc_filedes.sh b/mrbgems/mruby-io/test/gc_filedes.sh
deleted file mode 100644
index 6e5d1bbf1..000000000
--- a/mrbgems/mruby-io/test/gc_filedes.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-ulimit -n 20
-mruby -e '100.times { File.open "'$0'" }'
diff --git a/mrbgems/mruby-io/test/io.rb b/mrbgems/mruby-io/test/io.rb
index e06b14996..1491a4cfe 100644
--- a/mrbgems/mruby-io/test/io.rb
+++ b/mrbgems/mruby-io/test/io.rb
@@ -1,57 +1,46 @@
##
# IO Test
-unless Object.respond_to? :assert_nothing_raised
- def assert_nothing_raised(*exp)
- ret = true
- if $mrbtest_assert
- $mrbtest_assert_idx += 1
- msg = exp.last.class == String ? exp.pop : ""
- begin
- yield
- rescue Exception => e
- msg = "#{msg} exception raised."
- diff = " Class: <#{e.class}>\n" +
- " Message: #{e.message}"
- $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
- ret = false
+MRubyIOTestUtil.io_test_setup
+$cr, $crlf, $cmd = MRubyIOTestUtil.win? ? [1, "\r\n", "cmd /c "] : [0, "\n", ""]
+
+def assert_io_open(meth)
+ assert do
+ fd = IO.sysopen($mrbtest_io_rfname)
+ assert_equal Fixnum, fd.class
+ io1 = IO.__send__(meth, fd)
+ begin
+ assert_equal IO, io1.class
+ assert_equal $mrbtest_io_msg, io1.read
+ ensure
+ io1.close
+ end
+
+ io2 = IO.__send__(meth, IO.sysopen($mrbtest_io_rfname))do |io|
+ if meth == :open
+ assert_equal $mrbtest_io_msg, io.read
+ else
+ flunk "IO.#{meth} does not take block"
end
end
- ret
+ io2.close unless meth == :open
end
end
-assert('IO TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
- $cr = MRubyIOTestUtil.win? ? 1 : 0 # "\n" include CR or not
-end
-
-assert('IO', '15.2.20') do
+assert('IO.class', '15.2.20') do
assert_equal(Class, IO.class)
end
-assert('IO', '15.2.20.2') do
+assert('IO.superclass', '15.2.20.2') do
assert_equal(Object, IO.superclass)
end
-assert('IO', '15.2.20.3') do
- assert_include(IO.included_modules, Enumerable)
+assert('IO.ancestors', '15.2.20.3') do
+ assert_include(IO.ancestors, Enumerable)
end
assert('IO.open', '15.2.20.4.1') do
- fd = IO.sysopen $mrbtest_io_rfname
- assert_equal Fixnum, fd.class
- io = IO.open fd
- assert_equal IO, io.class
- assert_equal $mrbtest_io_msg, io.read
- io.close
-
- fd = IO.sysopen $mrbtest_io_rfname
- IO.open(fd) do |io|
- assert_equal $mrbtest_io_msg, io.read
- end
-
- true
+ assert_io_open(:open)
end
assert('IO#close', '15.2.20.5.1') do
@@ -92,8 +81,6 @@ assert('IO#eof?', '15.2.20.5.6') do
io.read
assert_true io.eof?
io.close
-
- true
end
assert('IO#flush', '15.2.20.5.7') do
@@ -108,12 +95,11 @@ end
assert('IO#getc', '15.2.20.5.8') do
io = IO.new(IO.sysopen($mrbtest_io_rfname))
- $mrbtest_io_msg.each_char { |ch|
+ $mrbtest_io_msg.split("").each { |ch|
assert_equal ch, io.getc
}
assert_equal nil, io.getc
io.close
- true
end
#assert('IO#gets', '15.2.20.5.9') do
@@ -152,7 +138,7 @@ end
assert('IO#readchar', '15.2.20.5.15') do
# almost same as IO#getc
IO.open(IO.sysopen($mrbtest_io_rfname)) do |io|
- $mrbtest_io_msg.each_char { |ch|
+ $mrbtest_io_msg.split("").each { |ch|
assert_equal ch, io.readchar
}
assert_raise(EOFError) do
@@ -199,8 +185,6 @@ assert('IO#write', '15.2.20.5.20') do
io.rewind
assert_equal "ab123fg", io.read
io.close
-
- true
end
assert('IO#<<') do
@@ -208,7 +192,6 @@ assert('IO#<<') do
io << "" << ""
assert_equal 0, io.pos
io.close
- true
end
assert('IO#dup for readable') do
@@ -228,7 +211,6 @@ assert('IO#dup for readable') do
dup.close
assert_false io.closed?
io.close
- true
end
assert('IO#dup for writable') do
@@ -241,25 +223,18 @@ assert('IO#dup for writable') do
assert_equal "mruby", dup.sysread(5)
dup.close
io.close
- true
end
assert('IO.for_fd') do
- fd = IO.sysopen($mrbtest_io_rfname)
- io = IO.for_fd(fd)
- assert_equal $mrbtest_io_msg, io.read
- io.close
- true
+ assert_io_open(:for_fd)
end
assert('IO.new') do
- io = IO.new(0)
- io.close
- true
+ assert_io_open(:new)
end
assert('IO gc check') do
- 100.times { IO.new(0) }
+ assert_nothing_raised { 100.times { IO.new(0) } }
end
assert('IO.sysopen("./nonexistent")') do
@@ -300,7 +275,6 @@ assert('IO.sysopen, IO#sysread') do
io = IO.new fd, "w"
assert_raise(IOError) { io.sysread(1) }
io.close
- true
end
assert('IO.sysopen, IO#syswrite') do
@@ -314,8 +288,6 @@ assert('IO.sysopen, IO#syswrite') do
io = IO.new(IO.sysopen($mrbtest_io_rfname), "r")
assert_raise(IOError) { io.syswrite("a") }
io.close
-
- true
end
assert('IO#_read_buf') do
@@ -339,20 +311,25 @@ assert('IO#_read_buf') do
assert_equal true, io.eof
assert_equal true, io.eof?
io.close
- io.closed?
end
assert('IO#isatty') do
skip "isatty is not supported on this platform" if MRubyIOTestUtil.win?
- f1 = File.open("/dev/tty")
- f2 = File.open($mrbtest_io_rfname)
-
- assert_true f1.isatty
- assert_false f2.isatty
-
- f1.close
- f2.close
- true
+ begin
+ f = File.open("/dev/tty")
+ rescue RuntimeError => e
+ skip e.message
+ else
+ assert_true f.isatty
+ ensure
+ f&.close
+ end
+ begin
+ f = File.open($mrbtest_io_rfname)
+ assert_false f.isatty
+ ensure
+ f&.close
+ end
end
assert('IO#pos=, IO#seek') do
@@ -366,7 +343,6 @@ assert('IO#pos=, IO#seek') do
assert_equal 0, io.seek(0)
assert_equal 0, io.pos
io.close
- io.closed?
end
assert('IO#rewind') do
@@ -377,7 +353,6 @@ assert('IO#rewind') do
assert_equal 0, io.rewind
assert_equal 0, io.pos
io.close
- io.closed?
end
assert('IO#gets') do
@@ -426,7 +401,6 @@ assert('IO#gets') do
assert_equal nil, io.gets, "gets third line; returns nil"
io.close
- io.closed?
end
assert('IO#gets - paragraph mode') do
@@ -437,7 +411,6 @@ assert('IO#gets - paragraph mode') do
io.write "2" * 10 + "\n"
assert_equal 34 + $cr * 4, io.pos
io.close
- assert_equal true, io.closed?
fd = IO.sysopen $mrbtest_io_wfname
io = IO.new fd
@@ -448,13 +421,12 @@ assert('IO#gets - paragraph mode') do
text2 = io.gets("")
assert_equal para2, text2
io.close
- io.closed?
end
assert('IO.popen') do
begin
$? = nil
- io = IO.popen("echo mruby-io")
+ io = IO.popen("#{$cmd}echo mruby-io")
assert_true io.close_on_exec?
assert_equal Fixnum, io.pid.class
@@ -542,7 +514,6 @@ assert('IO#fileno') do
assert_equal io.fileno, fd
assert_equal io.to_i, fd
io.close
- io.closed?
end
assert('IO#close_on_exec') do
@@ -564,7 +535,6 @@ assert('IO#close_on_exec') do
assert_equal(false, io.close_on_exec?)
io.close
- io.closed?
begin
r, w = IO.pipe
@@ -635,12 +605,10 @@ end
assert('`cmd`') do
begin
- assert_equal `echo foo`, "foo\n"
+ assert_equal `#{$cmd}echo foo`, "foo#{$crlf}"
rescue NotImplementedError => e
skip e.message
end
end
-assert('IO TEST CLEANUP') do
- assert_nil MRubyIOTestUtil.io_test_cleanup
-end
+MRubyIOTestUtil.io_test_cleanup
diff --git a/mrbgems/mruby-io/test/mruby_io_test.c b/mrbgems/mruby-io/test/mruby_io_test.c
index 71239a827..3312d6c7e 100644
--- a/mrbgems/mruby-io/test/mruby_io_test.c
+++ b/mrbgems/mruby-io/test/mruby_io_test.c
@@ -215,11 +215,9 @@ mrb_io_test_mkdtemp(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_io_test_rmdir(mrb_state *mrb, mrb_value klass)
{
- mrb_value str;
- char *cp;
+ const char *cp;
- mrb_get_args(mrb, "S", &str);
- cp = mrb_str_to_cstr(mrb, str);
+ mrb_get_args(mrb, "z", &cp);
if (rmdir(cp) == -1) {
mrb_sys_fail(mrb, "rmdir");
}
diff --git a/mrbgems/mruby-kernel-ext/mrbgem.rake b/mrbgems/mruby-kernel-ext/mrbgem.rake
index fcb3a83b0..e52c63a55 100644
--- a/mrbgems/mruby-kernel-ext/mrbgem.rake
+++ b/mrbgems/mruby-kernel-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-kernel-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'Kernel module extension'
+ spec.summary = 'extensional function-like methods'
end
diff --git a/mrbgems/mruby-kernel-ext/mrblib/kernel.rb b/mrbgems/mruby-kernel-ext/mrblib/kernel.rb
deleted file mode 100644
index bf739ed1a..000000000
--- a/mrbgems/mruby-kernel-ext/mrblib/kernel.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Kernel
- # call-seq:
- # obj.yield_self {|_obj|...} -> an_object
- # obj.then {|_obj|...} -> an_object
- #
- # Yields <i>obj</i> and returns the result.
- #
- # 'my string'.yield_self {|s|s.upcase} #=> "MY STRING"
- #
- def yield_self(&block)
- return to_enum :yield_self unless block
- block.call(self)
- end
- alias then yield_self
-end
diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c
index 32d86376a..8e7e03c56 100644
--- a/mrbgems/mruby-kernel-ext/src/kernel.c
+++ b/mrbgems/mruby-kernel-ext/src/kernel.c
@@ -22,7 +22,7 @@ mrb_f_caller(mrb_state *mrb, mrb_value self)
case 1:
if (mrb_type(v) == MRB_TT_RANGE) {
mrb_int beg, len;
- if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) {
+ if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == MRB_RANGE_OK) {
lev = beg;
n = len;
}
@@ -93,9 +93,8 @@ mrb_f_method(mrb_state *mrb, mrb_value self)
* (<code>0</code>, <code>0b</code>, and <code>0x</code>) are honored.
* In any case, strings should be strictly conformed to numeric
* representation. This behavior is different from that of
- * <code>String#to_i</code>. Non string values will be converted using
- * <code>to_int</code>, and <code>to_i</code>. Passing <code>nil</code>
- * raises a TypeError.
+ * <code>String#to_i</code>. Non string values will be treated as integers.
+ * Passing <code>nil</code> raises a TypeError.
*
* Integer(123.999) #=> 123
* Integer("0x1a") #=> 26
@@ -142,8 +141,7 @@ mrb_f_float(mrb_state *mrb, mrb_value self)
* String(arg) -> string
*
* Returns <i>arg</i> as an <code>String</code>.
- *
- * First tries to call its <code>to_str</code> method, then its to_s method.
+ * converted using <code>to_s</code> method.
*
* String(self) #=> "main"
* String(self.class) #=> "Object"
@@ -155,10 +153,7 @@ mrb_f_string(mrb_state *mrb, mrb_value self)
mrb_value arg, tmp;
mrb_get_args(mrb, "o", &arg);
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_str");
- if (mrb_nil_p(tmp)) {
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s");
- }
+ tmp = mrb_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s");
return tmp;
}
@@ -166,9 +161,7 @@ mrb_f_string(mrb_state *mrb, mrb_value self)
* call-seq:
* Array(arg) -> array
*
- * Returns +arg+ as an Array.
- *
- * First tries to call Array#to_ary on +arg+, then Array#to_a.
+ * Returns +arg+ as an Array using to_a method.
*
* Array(1..5) #=> [1, 2, 3, 4, 5]
*
@@ -179,10 +172,7 @@ mrb_f_array(mrb_state *mrb, mrb_value self)
mrb_value arg, tmp;
mrb_get_args(mrb, "o", &arg);
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_ary");
- if (mrb_nil_p(tmp)) {
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_a");
- }
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_a");
if (mrb_nil_p(tmp)) {
return mrb_ary_new_from_values(mrb, 1, &arg);
}
@@ -194,9 +184,9 @@ mrb_f_array(mrb_state *mrb, mrb_value self)
* call-seq:
* Hash(arg) -> hash
*
- * Converts <i>arg</i> to a <code>Hash</code> by calling
- * <i>arg</i><code>.to_hash</code>. Returns an empty <code>Hash</code> when
- * <i>arg</i> is <tt>nil</tt> or <tt>[]</tt>.
+ * Returns a <code>Hash</code> if <i>arg</i> is a <code>Hash</code>.
+ * Returns an empty <code>Hash</code> when <i>arg</i> is <tt>nil</tt>
+ * or <tt>[]</tt>.
*
* Hash([]) #=> {}
* Hash(nil) #=> {}
@@ -207,37 +197,13 @@ mrb_f_array(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_f_hash(mrb_state *mrb, mrb_value self)
{
- mrb_value arg, tmp;
+ mrb_value arg;
mrb_get_args(mrb, "o", &arg);
- if (mrb_nil_p(arg)) {
+ if (mrb_nil_p(arg) || (mrb_array_p(arg) && RARRAY_LEN(arg) == 0)) {
return mrb_hash_new(mrb);
}
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_HASH, "Hash", "to_hash");
- if (mrb_nil_p(tmp)) {
- if (mrb_array_p(arg) && RARRAY_LEN(arg) == 0) {
- return mrb_hash_new(mrb);
- }
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into Hash",
- mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, arg)));
- }
- return tmp;
-}
-
-/*
- * call-seq:
- * obj.itself -> an_object
- *
- * Returns <i>obj</i>.
- *
- * string = 'my string' #=> "my string"
- * string.itself.object_id == string.object_id #=> true
- *
- */
-static mrb_value
-mrb_f_itself(mrb_state *mrb, mrb_value self)
-{
- return self;
+ return mrb_ensure_hash_type(mrb, arg);
}
void
@@ -255,7 +221,6 @@ mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
mrb_define_module_function(mrb, krn, "String", mrb_f_string, MRB_ARGS_REQ(1));
mrb_define_module_function(mrb, krn, "Array", mrb_f_array, MRB_ARGS_REQ(1));
mrb_define_module_function(mrb, krn, "Hash", mrb_f_hash, MRB_ARGS_REQ(1));
- mrb_define_module_function(mrb, krn, "itself", mrb_f_itself, MRB_ARGS_NONE());
}
void
diff --git a/mrbgems/mruby-kernel-ext/test/kernel.rb b/mrbgems/mruby-kernel-ext/test/kernel.rb
index 206b7ac74..ad9177165 100644
--- a/mrbgems/mruby-kernel-ext/test/kernel.rb
+++ b/mrbgems/mruby-kernel-ext/test/kernel.rb
@@ -49,21 +49,25 @@ assert('Kernel#__method__') do
end
assert('Kernel#Integer') do
- assert_equal(123, Integer(123.999)) if class_defined?("Float")
assert_equal(26, Integer("0x1a"))
assert_equal(930, Integer("0930", 10))
assert_equal(7, Integer("111", 2))
assert_equal(0, Integer("0"))
assert_equal(0, Integer("00000"))
assert_raise(TypeError) { Integer(nil) }
+ skip unless Object.const_defined?(:Float)
+ assert_equal(123, Integer(123.999))
end
assert('Kernel#Float') do
+ skip unless Object.const_defined?(:Float)
assert_equal(1.0, Float(1))
assert_equal(123.456, Float(123.456))
assert_equal(123.456, Float("123.456"))
assert_raise(TypeError) { Float(nil) }
-end if class_defined?("Float")
+ assert_raise(ArgumentError) { Float("1.5a") }
+ assert_raise(ArgumentError) { Float("1.5\0") }
+end
assert('Kernel#String') do
assert_equal("main", String(self))
diff --git a/mrbgems/mruby-math/src/math.c b/mrbgems/mruby-math/src/math.c
index c182debea..35fcd0fa6 100644
--- a/mrbgems/mruby-math/src/math.c
+++ b/mrbgems/mruby-math/src/math.c
@@ -4,6 +4,10 @@
** See Copyright Notice in mruby.h
*/
+#ifdef MRB_WITHOUT_FLOAT
+# error Conflict 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <mruby.h>
#include <mruby/array.h>
@@ -23,8 +27,6 @@ domain_error(mrb_state *mrb, const char *func)
#include <float.h>
-#define MATH_TOLERANCE 1E-12
-
double
asinh(double x)
{
@@ -122,7 +124,8 @@ erf(double x)
term *= xsqr/j;
sum += term/(2*j+1);
++j;
- } while (fabs(term/sum) > MATH_TOLERANCE);
+ if (sum == 0) break;
+ } while (fabs(term/sum) > DBL_EPSILON);
return two_sqrtpi*sum;
}
@@ -155,12 +158,16 @@ erfc(double x)
n += 0.5;
q1 = q2;
q2 = b/d;
- } while (fabs(q1-q2)/q2 > MATH_TOLERANCE);
+ } while (fabs(q1-q2)/q2 > DBL_EPSILON);
return one_sqrtpi*exp(-x*x)*q2;
}
#endif
+#if defined __FreeBSD__ && !defined __FreeBSD_version
+#include <osreldate.h> /* for __FreeBSD_version */
+#endif
+
#if (defined _MSC_VER && _MSC_VER < 1800) || defined __ANDROID__ || (defined __FreeBSD__ && __FreeBSD_version < 803000)
double
@@ -738,12 +745,6 @@ mrb_mruby_math_gem_init(mrb_state* mrb)
mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, exp(1.0)));
#endif
-#ifdef MRB_USE_FLOAT
- mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-5));
-#else
- mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-12));
-#endif
-
mrb_define_module_function(mrb, mrb_math, "sin", math_sin, MRB_ARGS_REQ(1));
mrb_define_module_function(mrb, mrb_math, "cos", math_cos, MRB_ARGS_REQ(1));
mrb_define_module_function(mrb, mrb_math, "tan", math_tan, MRB_ARGS_REQ(1));
diff --git a/mrbgems/mruby-math/test/math.rb b/mrbgems/mruby-math/test/math.rb
index e9ea07cc1..86a4fc12c 100644
--- a/mrbgems/mruby-math/test/math.rb
+++ b/mrbgems/mruby-math/test/math.rb
@@ -1,46 +1,31 @@
##
# Math Test
-##
-# Performs fuzzy check for equality on methods returning floats
-# on the basis of the Math::TOLERANCE constant.
-def check_float(a, b)
- tolerance = Math::TOLERANCE
- a = a.to_f
- b = b.to_f
- if a.finite? and b.finite?
- (a-b).abs < tolerance
- else
- true
- end
-end
-
assert('Math.sin 0') do
- check_float(Math.sin(0), 0)
+ assert_float(0, Math.sin(0))
end
assert('Math.sin PI/2') do
- check_float(Math.sin(Math::PI / 2), 1)
+ assert_float(1, Math.sin(Math::PI / 2))
end
assert('Math.cos 0') do
- check_float(Math.cos(0), 1)
+ assert_float(1, Math.cos(0))
end
assert('Math.cos PI/2') do
- check_float(Math.cos(Math::PI / 2), 0)
+ assert_float(0, Math.cos(Math::PI / 2))
end
assert('Math.tan 0') do
- check_float(Math.tan(0), 0)
+ assert_float(0, Math.tan(0))
end
assert('Math.tan PI/4') do
- check_float(Math.tan(Math::PI / 4), 1)
+ assert_float(1, Math.tan(Math::PI / 4))
end
assert('Fundamental trig identities') do
- result = true
N = 13
N.times do |i|
a = Math::PI / N * i
@@ -48,105 +33,100 @@ assert('Fundamental trig identities') do
s = Math.sin(a)
c = Math.cos(a)
t = Math.tan(a)
- result &= check_float(s, Math.cos(ca))
- result &= check_float(t, 1 / Math.tan(ca))
- result &= check_float(s ** 2 + c ** 2, 1)
- result &= check_float(t ** 2 + 1, (1/c) ** 2)
- result &= check_float((1/t) ** 2 + 1, (1/s) ** 2)
+ assert_float(Math.cos(ca), s)
+ assert_float(1 / Math.tan(ca), t)
+ assert_float(1, s ** 2 + c ** 2)
+ assert_float((1/c) ** 2, t ** 2 + 1)
+ assert_float((1/s) ** 2, (1/t) ** 2 + 1)
end
- result
end
assert('Math.erf 0') do
- check_float(Math.erf(0), 0)
+ assert_float(0, Math.erf(0))
end
assert('Math.exp 0') do
- check_float(Math.exp(0), 1.0)
+ assert_float(1.0, Math.exp(0))
end
assert('Math.exp 1') do
- check_float(Math.exp(1), 2.718281828459045)
+ assert_float(2.718281828459045, Math.exp(1))
end
assert('Math.exp 1.5') do
- check_float(Math.exp(1.5), 4.4816890703380645)
+ assert_float(4.4816890703380645, Math.exp(1.5))
end
assert('Math.log 1') do
- check_float(Math.log(1), 0)
+ assert_float(0, Math.log(1))
end
assert('Math.log E') do
- check_float(Math.log(Math::E), 1.0)
+ assert_float(1.0, Math.log(Math::E))
end
assert('Math.log E**3') do
- check_float(Math.log(Math::E**3), 3.0)
+ assert_float(3.0, Math.log(Math::E**3))
end
assert('Math.log2 1') do
- check_float(Math.log2(1), 0.0)
+ assert_float(0.0, Math.log2(1))
end
assert('Math.log2 2') do
- check_float(Math.log2(2), 1.0)
+ assert_float(1.0, Math.log2(2))
end
assert('Math.log10 1') do
- check_float(Math.log10(1), 0.0)
+ assert_float(0.0, Math.log10(1))
end
assert('Math.log10 10') do
- check_float(Math.log10(10), 1.0)
+ assert_float(1.0, Math.log10(10))
end
assert('Math.log10 10**100') do
- check_float(Math.log10(10**100), 100.0)
+ assert_float(100.0, Math.log10(10**100))
end
assert('Math.sqrt') do
num = [0.0, 1.0, 2.0, 3.0, 4.0]
sqr = [0, 1, 4, 9, 16]
- result = true
sqr.each_with_index do |v,i|
- result &= check_float(Math.sqrt(v), num[i])
+ assert_float(num[i], Math.sqrt(v))
end
- result
end
assert('Math.cbrt') do
num = [-2.0, -1.0, 0.0, 1.0, 2.0]
cub = [-8, -1, 0, 1, 8]
- result = true
cub.each_with_index do |v,i|
- result &= check_float(Math.cbrt(v), num[i])
+ assert_float(num[i], Math.cbrt(v))
end
- result
end
assert('Math.hypot') do
- check_float(Math.hypot(3, 4), 5.0)
+ assert_float(5.0, Math.hypot(3, 4))
end
assert('Math.frexp 1234') do
n = 1234
fraction, exponent = Math.frexp(n)
- check_float(Math.ldexp(fraction, exponent), n)
+ assert_float(n, Math.ldexp(fraction, exponent))
end
assert('Math.erf 1') do
- check_float(Math.erf(1), 0.842700792949715)
+ assert_float(0.842700792949715, Math.erf(1))
end
assert('Math.erfc 1') do
- check_float(Math.erfc(1), 0.157299207050285)
+ assert_float(0.157299207050285, Math.erfc(1))
end
assert('Math.erf -1') do
- check_float(Math.erf(-1), -0.8427007929497148)
+ assert_float(-0.8427007929497148, Math.erf(-1))
end
assert('Math.erfc -1') do
- check_float(Math.erfc(-1), 1.8427007929497148)
+ assert_float(1.8427007929497148, Math.erfc(-1))
end
diff --git a/mrbgems/mruby-metaprog/mrbgem.rake b/mrbgems/mruby-metaprog/mrbgem.rake
new file mode 100644
index 000000000..1b81d6345
--- /dev/null
+++ b/mrbgems/mruby-metaprog/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-metaprog') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Meta-programming features for mruby'
+end
diff --git a/mrbgems/mruby-metaprog/src/metaprog.c b/mrbgems/mruby-metaprog/src/metaprog.c
new file mode 100644
index 000000000..62daa5227
--- /dev/null
+++ b/mrbgems/mruby-metaprog/src/metaprog.c
@@ -0,0 +1,724 @@
+#include "mruby.h"
+#include "mruby/array.h"
+#include "mruby/hash.h"
+#include "mruby/variable.h"
+#include "mruby/proc.h"
+#include "mruby/class.h"
+#include "mruby/string.h"
+
+typedef enum {
+ NOEX_PUBLIC = 0x00,
+ NOEX_NOSUPER = 0x01,
+ NOEX_PRIVATE = 0x02,
+ NOEX_PROTECTED = 0x04,
+ NOEX_MASK = 0x06,
+ NOEX_BASIC = 0x08,
+ NOEX_UNDEF = NOEX_NOSUPER,
+ NOEX_MODFUNC = 0x12,
+ NOEX_SUPER = 0x20,
+ NOEX_VCALL = 0x40,
+ NOEX_RESPONDS = 0x80
+} mrb_method_flag_t;
+
+static mrb_value
+mrb_f_nil(mrb_state *mrb, mrb_value cv)
+{
+ return mrb_nil_value();
+}
+
+/* 15.3.1.3.20 */
+/*
+ * call-seq:
+ * obj.instance_variable_defined?(symbol) -> true or false
+ *
+ * Returns <code>true</code> if the given instance variable is
+ * defined in <i>obj</i>.
+ *
+ * class Fred
+ * def initialize(p1, p2)
+ * @a, @b = p1, p2
+ * end
+ * end
+ * fred = Fred.new('cat', 99)
+ * fred.instance_variable_defined?(:@a) #=> true
+ * fred.instance_variable_defined?("@b") #=> true
+ * fred.instance_variable_defined?("@c") #=> false
+ */
+static mrb_value
+mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym sym;
+
+ mrb_get_args(mrb, "n", &sym);
+ mrb_iv_name_sym_check(mrb, sym);
+ return mrb_bool_value(mrb_iv_defined(mrb, self, sym));
+}
+
+/* 15.3.1.3.21 */
+/*
+ * call-seq:
+ * obj.instance_variable_get(symbol) -> obj
+ *
+ * Returns the value of the given instance variable, or nil if the
+ * instance variable is not set. The <code>@</code> part of the
+ * variable name should be included for regular instance
+ * variables. Throws a <code>NameError</code> exception if the
+ * supplied symbol is not valid as an instance variable name.
+ *
+ * class Fred
+ * def initialize(p1, p2)
+ * @a, @b = p1, p2
+ * end
+ * end
+ * fred = Fred.new('cat', 99)
+ * fred.instance_variable_get(:@a) #=> "cat"
+ * fred.instance_variable_get("@b") #=> 99
+ */
+static mrb_value
+mrb_obj_ivar_get(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym iv_name;
+
+ mrb_get_args(mrb, "n", &iv_name);
+ mrb_iv_name_sym_check(mrb, iv_name);
+ return mrb_iv_get(mrb, self, iv_name);
+}
+
+/* 15.3.1.3.22 */
+/*
+ * call-seq:
+ * obj.instance_variable_set(symbol, obj) -> obj
+ *
+ * Sets the instance variable names by <i>symbol</i> to
+ * <i>object</i>, thereby frustrating the efforts of the class's
+ * author to attempt to provide proper encapsulation. The variable
+ * did not have to exist prior to this call.
+ *
+ * class Fred
+ * def initialize(p1, p2)
+ * @a, @b = p1, p2
+ * end
+ * end
+ * fred = Fred.new('cat', 99)
+ * fred.instance_variable_set(:@a, 'dog') #=> "dog"
+ * fred.instance_variable_set(:@c, 'cat') #=> "cat"
+ * fred.inspect #=> "#<Fred:0x401b3da8 @a=\"dog\", @b=99, @c=\"cat\">"
+ */
+static mrb_value
+mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym iv_name;
+ mrb_value val;
+
+ mrb_get_args(mrb, "no", &iv_name, &val);
+ mrb_iv_name_sym_check(mrb, iv_name);
+ mrb_iv_set(mrb, self, iv_name, val);
+ return val;
+}
+
+/* 15.3.1.2.7 */
+/* 15.3.1.3.28 */
+/*
+ * call-seq:
+ * local_variables -> array
+ *
+ * Returns the names of local variables in the current scope.
+ *
+ * [mruby limitation]
+ * If variable symbol information was stripped out from
+ * compiled binary files using `mruby-strip -l`, this
+ * method always returns an empty array.
+ */
+static mrb_value
+mrb_local_variables(mrb_state *mrb, mrb_value self)
+{
+ struct RProc *proc;
+ mrb_irep *irep;
+ mrb_value vars;
+ size_t i;
+
+ proc = mrb->c->ci[-1].proc;
+
+ if (MRB_PROC_CFUNC_P(proc)) {
+ return mrb_ary_new(mrb);
+ }
+ vars = mrb_hash_new(mrb);
+ while (proc) {
+ if (MRB_PROC_CFUNC_P(proc)) break;
+ irep = proc->body.irep;
+ if (!irep->lv) break;
+ for (i = 0; i + 1 < irep->nlocals; ++i) {
+ if (irep->lv[i].name) {
+ mrb_sym sym = irep->lv[i].name;
+ const char *name = mrb_sym2name(mrb, sym);
+ switch (name[0]) {
+ case '*': case '&':
+ break;
+ default:
+ mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value());
+ break;
+ }
+ }
+ }
+ if (!MRB_PROC_ENV_P(proc)) break;
+ proc = proc->upper;
+ //if (MRB_PROC_SCOPE_P(proc)) break;
+ if (!proc->c) break;
+ }
+
+ return mrb_hash_keys(mrb, vars);
+}
+
+KHASH_DECLARE(st, mrb_sym, char, FALSE)
+
+static void
+method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set)
+{
+ khint_t i;
+
+ khash_t(mt) *h = klass->mt;
+ if (!h || kh_size(h) == 0) return;
+ for (i=0;i<kh_end(h);i++) {
+ if (kh_exist(h, i)) {
+ mrb_method_t m = kh_value(h, i);
+ if (MRB_METHOD_UNDEF_P(m)) continue;
+ kh_put(st, mrb, set, kh_key(h, i));
+ }
+ }
+}
+
+static mrb_value
+mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* klass, int obj)
+{
+ khint_t i;
+ mrb_value ary;
+ mrb_bool prepended = FALSE;
+ struct RClass* oldklass;
+ khash_t(st)* set = kh_init(st, mrb);
+
+ if (!recur && (klass->flags & MRB_FL_CLASS_IS_PREPENDED)) {
+ MRB_CLASS_ORIGIN(klass);
+ prepended = TRUE;
+ }
+
+ oldklass = 0;
+ while (klass && (klass != oldklass)) {
+ method_entry_loop(mrb, klass, set);
+ if ((klass->tt == MRB_TT_ICLASS && !prepended) ||
+ (klass->tt == MRB_TT_SCLASS)) {
+ }
+ else {
+ if (!recur) break;
+ }
+ oldklass = klass;
+ klass = klass->super;
+ }
+
+ ary = mrb_ary_new_capa(mrb, kh_size(set));
+ for (i=0;i<kh_end(set);i++) {
+ if (kh_exist(set, i)) {
+ mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
+ }
+ }
+ kh_destroy(st, mrb, set);
+
+ return ary;
+}
+
+static mrb_value
+mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag)
+{
+ return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
+}
+/* 15.3.1.3.31 */
+/*
+ * call-seq:
+ * obj.methods -> array
+ *
+ * Returns a list of the names of methods publicly accessible in
+ * <i>obj</i>. This will include all the methods accessible in
+ * <i>obj</i>'s ancestors.
+ *
+ * class Klass
+ * def kMethod()
+ * end
+ * end
+ * k = Klass.new
+ * k.methods[0..9] #=> [:kMethod, :respond_to?, :nil?, :is_a?,
+ * # :class, :instance_variable_set,
+ * # :methods, :extend, :__send__, :instance_eval]
+ * k.methods.length #=> 42
+ */
+static mrb_value
+mrb_obj_methods_m(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_methods(mrb, recur, self, (mrb_method_flag_t)0); /* everything but private */
+}
+
+/* 15.3.1.3.36 */
+/*
+ * call-seq:
+ * obj.private_methods(all=true) -> array
+ *
+ * Returns the list of private methods accessible to <i>obj</i>. If
+ * the <i>all</i> parameter is set to <code>false</code>, only those methods
+ * in the receiver will be listed.
+ */
+static mrb_value
+mrb_obj_private_methods(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_methods(mrb, recur, self, NOEX_PRIVATE); /* private attribute not define */
+}
+
+/* 15.3.1.3.37 */
+/*
+ * call-seq:
+ * obj.protected_methods(all=true) -> array
+ *
+ * Returns the list of protected methods accessible to <i>obj</i>. If
+ * the <i>all</i> parameter is set to <code>false</code>, only those methods
+ * in the receiver will be listed.
+ */
+static mrb_value
+mrb_obj_protected_methods(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_methods(mrb, recur, self, NOEX_PROTECTED); /* protected attribute not define */
+}
+
+/* 15.3.1.3.38 */
+/*
+ * call-seq:
+ * obj.public_methods(all=true) -> array
+ *
+ * Returns the list of public methods accessible to <i>obj</i>. If
+ * the <i>all</i> parameter is set to <code>false</code>, only those methods
+ * in the receiver will be listed.
+ */
+static mrb_value
+mrb_obj_public_methods(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_methods(mrb, recur, self, NOEX_PUBLIC); /* public attribute not define */
+}
+
+static mrb_value
+mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj)
+{
+ khint_t i;
+ mrb_value ary;
+ struct RClass* klass;
+ khash_t(st)* set = kh_init(st, mrb);
+
+ klass = mrb_class(mrb, obj);
+
+ if (klass && (klass->tt == MRB_TT_SCLASS)) {
+ method_entry_loop(mrb, klass, set);
+ klass = klass->super;
+ }
+ if (recur) {
+ while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) {
+ method_entry_loop(mrb, klass, set);
+ klass = klass->super;
+ }
+ }
+
+ ary = mrb_ary_new(mrb);
+ for (i=0;i<kh_end(set);i++) {
+ if (kh_exist(set, i)) {
+ mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
+ }
+ }
+ kh_destroy(st, mrb, set);
+
+ return ary;
+}
+
+/* 15.3.1.3.45 */
+/*
+ * call-seq:
+ * obj.singleton_methods(all=true) -> array
+ *
+ * Returns an array of the names of singleton methods for <i>obj</i>.
+ * If the optional <i>all</i> parameter is true, the list will include
+ * methods in modules included in <i>obj</i>.
+ * Only public and protected singleton methods are returned.
+ *
+ * module Other
+ * def three() end
+ * end
+ *
+ * class Single
+ * def Single.four() end
+ * end
+ *
+ * a = Single.new
+ *
+ * def a.one()
+ * end
+ *
+ * class << a
+ * include Other
+ * def two()
+ * end
+ * end
+ *
+ * Single.singleton_methods #=> [:four]
+ * a.singleton_methods(false) #=> [:two, :one]
+ * a.singleton_methods #=> [:two, :one, :three]
+ */
+static mrb_value
+mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_singleton_methods(mrb, recur, self);
+}
+
+static mrb_value
+mod_define_singleton_method(mrb_state *mrb, mrb_value self)
+{
+ struct RProc *p;
+ mrb_method_t m;
+ mrb_sym mid;
+ mrb_value blk = mrb_nil_value();
+
+ mrb_get_args(mrb, "n&", &mid, &blk);
+ if (mrb_nil_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+ }
+ p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
+ mrb_proc_copy(p, mrb_proc_ptr(blk));
+ p->flags |= MRB_PROC_STRICT;
+ MRB_METHOD_FROM_PROC(m, p);
+ mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, m);
+ return mrb_symbol_value(mid);
+}
+
+static mrb_bool
+cv_name_p(mrb_state *mrb, const char *name, mrb_int len)
+{
+ return len > 2 && name[0] == '@' && name[1] == '@' &&
+ !ISDIGIT(name[2]) && mrb_ident_p(name+2, len-2);
+}
+
+static void
+check_cv_name_sym(mrb_state *mrb, mrb_sym id)
+{
+ mrb_int len;
+ const char *name = mrb_sym2name_len(mrb, id, &len);
+ if (!cv_name_p(mrb, name, len)) {
+ mrb_name_error(mrb, id, "'%S' is not allowed as a class variable name", mrb_sym2str(mrb, id));
+ }
+}
+
+/* 15.2.2.4.39 */
+/*
+ * call-seq:
+ * remove_class_variable(sym) -> obj
+ *
+ * Removes the definition of the <i>sym</i>, returning that
+ * constant's value.
+ *
+ * class Dummy
+ * @@var = 99
+ * puts @@var
+ * p class_variables
+ * remove_class_variable(:@@var)
+ * p class_variables
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * 99
+ * [:@@var]
+ * []
+ */
+
+static mrb_value
+mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod)
+{
+ mrb_value val;
+ mrb_sym id;
+
+ mrb_get_args(mrb, "n", &id);
+ check_cv_name_sym(mrb, id);
+
+ val = mrb_iv_remove(mrb, mod, id);
+ if (!mrb_undef_p(val)) return val;
+
+ if (mrb_cv_defined(mrb, mod, id)) {
+ mrb_name_error(mrb, id, "cannot remove %S for %S",
+ mrb_sym2str(mrb, id), mod);
+ }
+
+ mrb_name_error(mrb, id, "class variable %S not defined for %S",
+ mrb_sym2str(mrb, id), mod);
+
+ /* not reached */
+ return mrb_nil_value();
+}
+
+/* 15.2.2.4.16 */
+/*
+ * call-seq:
+ * obj.class_variable_defined?(symbol) -> true or false
+ *
+ * Returns <code>true</code> if the given class variable is defined
+ * in <i>obj</i>.
+ *
+ * class Fred
+ * @@foo = 99
+ * end
+ * Fred.class_variable_defined?(:@@foo) #=> true
+ * Fred.class_variable_defined?(:@@bar) #=> false
+ */
+
+static mrb_value
+mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod)
+{
+ mrb_sym id;
+
+ mrb_get_args(mrb, "n", &id);
+ check_cv_name_sym(mrb, id);
+ return mrb_bool_value(mrb_cv_defined(mrb, mod, id));
+}
+
+/* 15.2.2.4.17 */
+/*
+ * call-seq:
+ * mod.class_variable_get(symbol) -> obj
+ *
+ * Returns the value of the given class variable (or throws a
+ * <code>NameError</code> exception). The <code>@@</code> part of the
+ * variable name should be included for regular class variables
+ *
+ * class Fred
+ * @@foo = 99
+ * end
+ * Fred.class_variable_get(:@@foo) #=> 99
+ */
+
+static mrb_value
+mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod)
+{
+ mrb_sym id;
+
+ mrb_get_args(mrb, "n", &id);
+ check_cv_name_sym(mrb, id);
+ return mrb_cv_get(mrb, mod, id);
+}
+
+/* 15.2.2.4.18 */
+/*
+ * call-seq:
+ * obj.class_variable_set(symbol, obj) -> obj
+ *
+ * Sets the class variable names by <i>symbol</i> to
+ * <i>object</i>.
+ *
+ * class Fred
+ * @@foo = 99
+ * def foo
+ * @@foo
+ * end
+ * end
+ * Fred.class_variable_set(:@@foo, 101) #=> 101
+ * Fred.new.foo #=> 101
+ */
+
+static mrb_value
+mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod)
+{
+ mrb_value value;
+ mrb_sym id;
+
+ mrb_get_args(mrb, "no", &id, &value);
+ check_cv_name_sym(mrb, id);
+ mrb_cv_set(mrb, mod, id, value);
+ return value;
+}
+
+static mrb_value
+mrb_mod_included_modules(mrb_state *mrb, mrb_value self)
+{
+ mrb_value result;
+ struct RClass *c = mrb_class_ptr(self);
+ struct RClass *origin = c;
+
+ MRB_CLASS_ORIGIN(origin);
+ result = mrb_ary_new(mrb);
+ while (c) {
+ if (c != origin && c->tt == MRB_TT_ICLASS) {
+ if (c->c->tt == MRB_TT_MODULE) {
+ mrb_ary_push(mrb, result, mrb_obj_value(c->c));
+ }
+ }
+ c = c->super;
+ }
+
+ return result;
+}
+
+/* 15.2.2.4.33 */
+/*
+ * call-seq:
+ * mod.instance_methods(include_super=true) -> array
+ *
+ * Returns an array containing the names of the public and protected instance
+ * methods in the receiver. For a module, these are the public and protected methods;
+ * for a class, they are the instance (not singleton) methods. With no
+ * argument, or with an argument that is <code>false</code>, the
+ * instance methods in <i>mod</i> are returned, otherwise the methods
+ * in <i>mod</i> and <i>mod</i>'s superclasses are returned.
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * def method2() end
+ * end
+ * class C < B
+ * def method3() end
+ * end
+ *
+ * A.instance_methods #=> [:method1]
+ * B.instance_methods(false) #=> [:method2]
+ * C.instance_methods(false) #=> [:method3]
+ * C.instance_methods(true).length #=> 43
+ */
+
+static mrb_value
+mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod)
+{
+ struct RClass *c = mrb_class_ptr(mod);
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_class_instance_method_list(mrb, recur, c, 0);
+}
+
+static void
+remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid)
+{
+ struct RClass *c = mrb_class_ptr(mod);
+ khash_t(mt) *h;
+ khiter_t k;
+
+ MRB_CLASS_ORIGIN(c);
+ h = c->mt;
+
+ if (h) {
+ k = kh_get(mt, mrb, h, mid);
+ if (k != kh_end(h)) {
+ kh_del(mt, mrb, h, k);
+ mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid));
+ return;
+ }
+ }
+
+ mrb_name_error(mrb, mid, "method '%S' not defined in %S",
+ mrb_sym2str(mrb, mid), mod);
+}
+
+/* 15.2.2.4.41 */
+/*
+ * call-seq:
+ * remove_method(symbol) -> self
+ *
+ * Removes the method identified by _symbol_ from the current
+ * class. For an example, see <code>Module.undef_method</code>.
+ */
+
+static mrb_value
+mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
+{
+ mrb_int argc;
+ mrb_value *argv;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+ mrb_check_frozen(mrb, mrb_obj_ptr(mod));
+ while (argc--) {
+ remove_method(mrb, mod, mrb_obj_to_sym(mrb, *argv));
+ argv++;
+ }
+ return mod;
+}
+
+static mrb_value
+mrb_mod_s_constants(mrb_state *mrb, mrb_value mod)
+{
+ mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented");
+ return mrb_nil_value(); /* not reached */
+}
+
+static mrb_value
+mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod)
+{
+ struct RProc *proc;
+ mrb_value ary;
+ struct RClass *c = NULL;
+
+ mrb_get_args(mrb, "");
+ ary = mrb_ary_new(mrb);
+ proc = mrb->c->ci[-1].proc; /* callee proc */
+ mrb_assert(!MRB_PROC_CFUNC_P(proc));
+ while (proc) {
+ if (MRB_PROC_SCOPE_P(proc)) {
+ struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc);
+
+ if (c2 != c) {
+ c = c2;
+ mrb_ary_push(mrb, ary, mrb_obj_value(c));
+ }
+ }
+ proc = proc->upper;
+ }
+ return ary;
+}
+
+void
+mrb_mruby_metaprog_gem_init(mrb_state* mrb)
+{
+ struct RClass *krn = mrb->kernel_module;
+ struct RClass *mod = mrb->module_class;
+
+ mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 (15.3.1.2.4) */
+ mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 (15.3.1.2.7) */
+
+ mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE());
+ mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined, MRB_ARGS_REQ(1)); /* 15.3.1.3.20 */
+ mrb_define_method(mrb, krn, "instance_variable_get", mrb_obj_ivar_get, MRB_ARGS_REQ(1)); /* 15.3.1.3.21 */
+ mrb_define_method(mrb, krn, "instance_variable_set", mrb_obj_ivar_set, MRB_ARGS_REQ(2)); /* 15.3.1.3.22 */
+ mrb_define_method(mrb, krn, "instance_variables", mrb_obj_instance_variables, MRB_ARGS_NONE()); /* 15.3.1.3.23 */
+ mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */
+ mrb_define_method(mrb, krn, "private_methods", mrb_obj_private_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.36 */
+ mrb_define_method(mrb, krn, "protected_methods", mrb_obj_protected_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.37 */
+ mrb_define_method(mrb, krn, "public_methods", mrb_obj_public_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.38 */
+ mrb_define_method(mrb, krn, "singleton_methods", mrb_obj_singleton_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.45 */
+ mrb_define_method(mrb, krn, "define_singleton_method", mod_define_singleton_method, MRB_ARGS_ANY());
+ mrb_define_method(mrb, krn, "send", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.44 */
+
+ mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */
+ mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */
+ mrb_define_method(mrb, mod, "class_variable_defined?", mrb_mod_cvar_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */
+ mrb_define_method(mrb, mod, "class_variable_get", mrb_mod_cvar_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */
+ mrb_define_method(mrb, mod, "class_variable_set", mrb_mod_cvar_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */
+ mrb_define_method(mrb, mod, "included_modules", mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */
+ mrb_define_method(mrb, mod, "instance_methods", mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */
+ mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */
+ mrb_define_method(mrb, mod, "method_removed", mrb_f_nil, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */
+ mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */
+ mrb_define_class_method(mrb, mod, "nesting", mrb_mod_s_nesting, MRB_ARGS_REQ(0)); /* 15.2.2.3.2 */
+}
+
+void
+mrb_mruby_metaprog_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-metaprog/test/metaprog.rb b/mrbgems/mruby-metaprog/test/metaprog.rb
new file mode 100644
index 000000000..30b75bd60
--- /dev/null
+++ b/mrbgems/mruby-metaprog/test/metaprog.rb
@@ -0,0 +1,400 @@
+assert('Kernel#send', '15.3.1.3.44') do
+ # test with block
+ l = send(:lambda) do
+ true
+ end
+
+ assert_true l.call
+ assert_equal l.class, Proc
+ # test with argument
+ assert_true send(:respond_to?, :nil?)
+ # test without argument and without block
+ assert_equal send(:to_s).class, String
+end
+
+assert('Kernel#instance_variable_defined?', '15.3.1.3.20') do
+ o = Object.new
+ o.instance_variable_set(:@a, 1)
+
+ assert_true o.instance_variable_defined?("@a")
+ assert_false o.instance_variable_defined?("@b")
+ assert_true o.instance_variable_defined?("@a"[0,2])
+ assert_true o.instance_variable_defined?("@abc"[0,2])
+ assert_raise(NameError) { o.instance_variable_defined?("@0") }
+end
+
+assert('Kernel#instance_variable_get', '15.3.1.3.21') do
+ o = Class.new { attr_accessor :foo, :bar }.new
+ o.foo = "one"
+ o.bar = 2
+ assert_equal("one", o.instance_variable_get(:@foo))
+ assert_equal(2, o.instance_variable_get("@bar"))
+ assert_equal(nil, o.instance_variable_get(:@baz))
+ %w[foo @1].each do |n|
+ assert_raise(NameError) { o.instance_variable_get(n) }
+ end
+end
+
+assert('Kernel#instance_variable_set', '15.3.1.3.22') do
+ o = Class.new { attr_reader :foo, :_bar }.new
+ assert_equal("one", o.instance_variable_set(:@foo, "one"))
+ assert_equal("one", o.foo)
+ assert_equal(2, o.instance_variable_set("@_bar", 2))
+ assert_equal(2, o._bar)
+ %w[@6 @% @@a @ a].each do |n|
+ assert_raise(NameError) { o.instance_variable_set(n, 1) }
+ end
+ assert_raise(FrozenError) { o.freeze.instance_variable_set(:@a, 2) }
+ assert_raise(FrozenError, ArgumentError) { nil.instance_variable_set(:@a, 2) }
+end
+
+assert('Kernel#instance_variables', '15.3.1.3.23') do
+ o = Object.new
+ o.instance_eval do
+ @a = 11
+ @b = 12
+ end
+ ivars = o.instance_variables
+
+ assert_equal Array, ivars.class,
+ assert_equal(2, ivars.size)
+ assert_true ivars.include?(:@a)
+ assert_true ivars.include?(:@b)
+end
+
+assert('Kernel#methods', '15.3.1.3.31') do
+ assert_equal Array, methods.class
+end
+
+assert('Kernel#private_methods', '15.3.1.3.36') do
+ assert_equal Array, private_methods.class
+end
+
+assert('Kernel#protected_methods', '15.3.1.3.37') do
+ assert_equal Array, protected_methods.class
+end
+
+assert('Kernel#public_methods', '15.3.1.3.38') do
+ assert_equal Array, public_methods.class
+ class Foo
+ def foo
+ end
+ end
+ assert_equal [:foo], Foo.new.public_methods(false)
+end
+
+assert('Kernel#singleton_methods', '15.3.1.3.45') do
+ assert_equal singleton_methods.class, Array
+end
+
+assert('Kernel.global_variables', '15.3.1.2.4') do
+ assert_equal Array, Kernel.global_variables.class
+end
+
+assert('Kernel#global_variables', '15.3.1.3.14') do
+ variables1 = global_variables
+ assert_equal Array, variables1.class
+ assert_not_include(variables1, :$kernel_global_variables_test)
+
+ $kernel_global_variables_test = nil
+ variables2 = global_variables
+ assert_include(variables2, :$kernel_global_variables_test)
+ assert_equal(1, variables2.size - variables1.size)
+end
+
+assert('Kernel.local_variables', '15.3.1.2.7') do
+ a, b = 0, 1
+ a += b
+
+ vars = Kernel.local_variables.sort
+ assert_equal [:a, :b, :vars], vars
+
+ assert_equal [:a, :b, :c, :vars], Proc.new { |a, b|
+ c = 2
+ # Kernel#local_variables: 15.3.1.3.28
+ local_variables.sort
+ }.call(-1, -2)
+end
+
+assert('Kernel#define_singleton_method') do
+ o = Object.new
+ ret = o.define_singleton_method(:test_method) do
+ :singleton_method_ok
+ end
+ assert_equal :test_method, ret
+ assert_equal :singleton_method_ok, o.test_method
+ assert_raise(TypeError) { 2.define_singleton_method(:f){} }
+ assert_raise(FrozenError) { [].freeze.define_singleton_method(:f){} }
+end
+
+assert('Kernel#singleton_class') do
+ o1 = Object.new
+ assert_same(o1.singleton_class, class << o1; self end)
+
+ o2 = Object.new
+ sc2 = class << o2; self end
+ assert_same(o2.singleton_class, sc2)
+
+ o3 = Object.new
+ sc3 = o3.singleton_class
+ o3.freeze
+ assert_predicate(sc3, :frozen?)
+
+ assert_predicate(Object.new.freeze.singleton_class, :frozen?)
+end
+
+def labeled_module(name, &block)
+ Module.new do
+ (class <<self; self end).class_eval do
+ define_method(:to_s) { name }
+ alias_method :inspect, :to_s
+ end
+ class_eval(&block) if block
+ end
+end
+
+def labeled_class(name, supklass = Object, &block)
+ Class.new(supklass) do
+ (class <<self; self end).class_eval do
+ define_method(:to_s) { name }
+ alias_method :inspect, :to_s
+ end
+ class_eval(&block) if block
+ end
+end
+
+assert('Module#class_variable_defined?', '15.2.2.4.16') do
+ class Test4ClassVariableDefined
+ @@cv = 99
+ end
+
+ assert_true Test4ClassVariableDefined.class_variable_defined?(:@@cv)
+ assert_false Test4ClassVariableDefined.class_variable_defined?(:@@noexisting)
+ assert_raise(NameError) { Test4ClassVariableDefined.class_variable_defined?("@@2") }
+end
+
+assert('Module#class_variable_get', '15.2.2.4.17') do
+ class Test4ClassVariableGet
+ @@cv = 99
+ end
+
+ assert_equal 99, Test4ClassVariableGet.class_variable_get(:@@cv)
+ assert_raise(NameError) { Test4ClassVariableGet.class_variable_get(:@@a) }
+ %w[@@a? @@! @a a].each do |n|
+ assert_raise(NameError) { Test4ClassVariableGet.class_variable_get(n) }
+ end
+end
+
+assert('Module#class_variable_set', '15.2.2.4.18') do
+ class Test4ClassVariableSet
+ @@foo = 100
+ def foo
+ @@foo
+ end
+ end
+ assert_equal 99, Test4ClassVariableSet.class_variable_set(:@@cv, 99)
+ assert_equal 101, Test4ClassVariableSet.class_variable_set(:@@foo, 101)
+ assert_true Test4ClassVariableSet.class_variables.include? :@@cv
+ assert_equal 99, Test4ClassVariableSet.class_variable_get(:@@cv)
+ assert_equal 101, Test4ClassVariableSet.new.foo
+ %w[@@ @@1 @@x= @x @ x 1].each do |n|
+ assert_raise(NameError) { Test4ClassVariableSet.class_variable_set(n, 1) }
+ end
+
+ m = Module.new.freeze
+ assert_raise(FrozenError) { m.class_variable_set(:@@cv, 1) }
+
+ parent = Class.new{ class_variable_set(:@@a, nil) }.freeze
+ child = Class.new(parent)
+ assert_raise(FrozenError) { child.class_variable_set(:@@a, 1) }
+end
+
+assert('Module#class_variables', '15.2.2.4.19') do
+ class Test4ClassVariables1
+ @@var1 = 1
+ end
+ class Test4ClassVariables2 < Test4ClassVariables1
+ @@var2 = 2
+ end
+
+ assert_equal [:@@var1], Test4ClassVariables1.class_variables
+ assert_equal [:@@var2, :@@var1], Test4ClassVariables2.class_variables
+end
+
+assert('Module#constants', '15.2.2.4.24') do
+ $n = []
+ module TestA
+ C = 1
+ end
+ class TestB
+ include TestA
+ C2 = 1
+ $n = constants.sort
+ end
+
+ assert_equal [ :C ], TestA.constants
+ assert_equal [ :C, :C2 ], $n
+end
+
+assert('Module#included_modules', '15.2.2.4.30') do
+ module Test4includedModules
+ end
+ module Test4includedModules2
+ include Test4includedModules
+ end
+ r = Test4includedModules2.included_modules
+
+ assert_equal Array, r.class
+ assert_true r.include?(Test4includedModules)
+end
+
+assert('Module#instance_methods', '15.2.2.4.33') do
+ module Test4InstanceMethodsA
+ def method1() end
+ end
+ class Test4InstanceMethodsB
+ def method2() end
+ end
+ class Test4InstanceMethodsC < Test4InstanceMethodsB
+ def method3() end
+ end
+
+ r = Test4InstanceMethodsC.instance_methods(true)
+
+ assert_equal [:method1], Test4InstanceMethodsA.instance_methods
+ assert_equal [:method2], Test4InstanceMethodsB.instance_methods(false)
+ assert_equal [:method3], Test4InstanceMethodsC.instance_methods(false)
+ assert_equal Array, r.class
+ assert_true r.include?(:method3)
+ assert_true r.include?(:method2)
+end
+
+assert 'Module#prepend #instance_methods(false)' do
+ bug6660 = '[ruby-dev:45863]'
+ assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
+ assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
+end
+
+assert('Module#remove_class_variable', '15.2.2.4.39') do
+ class Test4RemoveClassVariable
+ @@cv = 99
+ end
+
+ assert_equal 99, Test4RemoveClassVariable.remove_class_variable(:@@cv)
+ assert_false Test4RemoveClassVariable.class_variables.include? :@@cv
+ assert_raise(NameError) do
+ Test4RemoveClassVariable.remove_class_variable(:@@cv)
+ end
+ assert_raise(NameError) do
+ Test4RemoveClassVariable.remove_class_variable(:@v)
+ end
+ assert_raise(FrozenError) do
+ Test4RemoveClassVariable.freeze.remove_class_variable(:@@cv)
+ end
+end
+
+assert('Module#remove_method', '15.2.2.4.41') do
+ module Test4RemoveMethod
+ class Parent
+ def hello
+ end
+ end
+
+ class Child < Parent
+ def hello
+ end
+ end
+ end
+
+ klass = Test4RemoveMethod::Child
+ assert_same klass, klass.class_eval{ remove_method :hello }
+ assert_true klass.instance_methods.include? :hello
+ assert_false klass.instance_methods(false).include? :hello
+ assert_raise(FrozenError) { klass.freeze.remove_method :m }
+end
+
+assert('Module.nesting', '15.2.2.2.2') do
+ module Test4ModuleNesting
+ module Test4ModuleNesting2
+ assert_equal [Test4ModuleNesting2, Test4ModuleNesting],
+ Module.nesting
+ end
+ end
+ module Test4ModuleNesting::Test4ModuleNesting2
+ assert_equal [Test4ModuleNesting::Test4ModuleNesting2], Module.nesting
+ end
+end
+
+assert('Moduler#prepend + #instance_methods') do
+ bug6655 = '[ruby-core:45915]'
+ assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655)
+end
+
+assert 'Module#prepend + #singleton_methods' do
+ o = Object.new
+ o.singleton_class.class_eval {prepend Module.new}
+ assert_equal([], o.singleton_methods)
+end
+
+assert 'Module#prepend + #remove_method' do
+ c = Class.new do
+ prepend Module.new { def foo; end }
+ end
+ assert_raise(NameError) do
+ c.class_eval do
+ remove_method(:foo)
+ end
+ end
+ c.class_eval do
+ def foo; end
+ end
+ removed = nil
+ c.singleton_class.class_eval do
+ define_method(:method_removed) {|id| removed = id}
+ end
+ assert_nothing_raised('[Bug #7843]') do
+ c.class_eval do
+ remove_method(:foo)
+ end
+ end
+ assert_equal(:foo, removed)
+end
+
+assert 'Module#prepend + #included_modules' do
+ bug8025 = '[ruby-core:53158] [Bug #8025]'
+ mixin = labeled_module("mixin")
+ c = labeled_module("c") {prepend mixin}
+ im = c.included_modules
+ assert_not_include(im, c, bug8025)
+ assert_include(im, mixin, bug8025)
+ c1 = labeled_class("c1") {prepend mixin}
+ c2 = labeled_class("c2", c1)
+ im = c2.included_modules
+ assert_not_include(im, c1, bug8025)
+ assert_not_include(im, c2, bug8025)
+ assert_include(im, mixin, bug8025)
+end
+
+assert("remove_method doesn't segfault if the passed in argument isn't a symbol") do
+ klass = Class.new
+ assert_raise(TypeError) { klass.remove_method nil }
+ assert_raise(TypeError) { klass.remove_method 123 }
+ assert_raise(TypeError) { klass.remove_method 1.23 }
+ assert_raise(NameError) { klass.remove_method "hello" }
+ assert_raise(TypeError) { klass.remove_method Class.new }
+end
+
+assert('alias_method and remove_method') do
+ begin
+ Fixnum.alias_method :to_s_, :to_s
+ Fixnum.remove_method :to_s
+
+ assert_nothing_raised do
+ # segfaults if mrb_cptr is used
+ 1.to_s
+ end
+ ensure
+ Fixnum.alias_method :to_s, :to_s_
+ Fixnum.remove_method :to_s_
+ end
+end
diff --git a/mrbgems/mruby-method/mrblib/kernel.rb b/mrbgems/mruby-method/mrblib/kernel.rb
index b2ebd45ea..eb17df5a6 100644
--- a/mrbgems/mruby-method/mrblib/kernel.rb
+++ b/mrbgems/mruby-method/mrblib/kernel.rb
@@ -1,8 +1,9 @@
module Kernel
def singleton_method(name)
m = method(name)
- if m.owner != singleton_class
- raise NameError, "undefined method `#{name}' for class `#{singleton_class}'"
+ sc = (class <<self; self; end)
+ if m.owner != sc
+ raise NameError, "undefined method '#{name}' for class '#{sc}'"
end
m
end
diff --git a/mrbgems/mruby-method/mrblib/method.rb b/mrbgems/mruby-method/mrblib/method.rb
index 5de0afdf7..f7cefa2e5 100644
--- a/mrbgems/mruby-method/mrblib/method.rb
+++ b/mrbgems/mruby-method/mrblib/method.rb
@@ -17,4 +17,12 @@ class Method
def name
@name
end
+
+ def <<(other)
+ ->(*args, &block) { call(other.call(*args, &block)) }
+ end
+
+ def >>(other)
+ ->(*args, &block) { other.call(call(*args, &block)) }
+ end
end
diff --git a/mrbgems/mruby-method/src/method.c b/mrbgems/mruby-method/src/method.c
index e445b34c8..d94db1cb2 100644
--- a/mrbgems/mruby-method/src/method.c
+++ b/mrbgems/mruby-method/src/method.c
@@ -212,19 +212,8 @@ static mrb_value
method_arity(mrb_state *mrb, mrb_value self)
{
mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
- struct RProc *rproc;
- struct RClass *orig;
- mrb_value ret;
-
- if (mrb_nil_p(proc))
- return mrb_fixnum_value(-1);
-
- rproc = mrb_proc_ptr(proc);
- orig = rproc->c;
- rproc->c = mrb->proc_class;
- ret = mrb_funcall(mrb, proc, "arity", 0);
- rproc->c = orig;
- return ret;
+ mrb_int arity = mrb_nil_p(proc) ? -1 : mrb_proc_arity(mrb_proc_ptr(proc));
+ return mrb_fixnum_value(arity);
}
static mrb_value
@@ -281,16 +270,16 @@ method_to_s(mrb_state *mrb, mrb_value self)
mrb_str_cat_lit(mrb, str, ": ");
rklass = mrb_class_ptr(klass);
if (mrb_class_ptr(owner) == rklass) {
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, owner));
mrb_str_cat_lit(mrb, str, "#");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, name));
}
else {
mrb_str_cat_cstr(mrb, str, mrb_class_name(mrb, rklass));
mrb_str_cat_lit(mrb, str, "(");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, owner));
mrb_str_cat_lit(mrb, str, ")#");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, name));
}
mrb_str_cat_lit(mrb, str, ">");
return str;
@@ -327,7 +316,7 @@ name_error:
s = mrb_class_name(mrb, c);
mrb_raisef(
mrb, E_NAME_ERROR,
- "undefined method `%S' for class `%S'",
+ "undefined method '%S' for class '%S'",
mrb_sym2str(mrb, name),
mrb_str_new_static(mrb, s, strlen(s))
);
@@ -387,7 +376,7 @@ mrb_mruby_method_gem_init(mrb_state* mrb)
mrb_define_method(mrb, unbound_method, "bind", unbound_method_bind, MRB_ARGS_REQ(1));
mrb_define_method(mrb, unbound_method, "super_method", method_super_method, MRB_ARGS_NONE());
mrb_define_method(mrb, unbound_method, "==", method_eql, MRB_ARGS_REQ(1));
- mrb_alias_method(mrb, unbound_method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
+ mrb_define_alias(mrb, unbound_method, "eql?", "==");
mrb_define_method(mrb, unbound_method, "to_s", method_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, unbound_method, "inspect", method_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, unbound_method, "arity", method_arity, MRB_ARGS_NONE());
@@ -396,11 +385,11 @@ mrb_mruby_method_gem_init(mrb_state* mrb)
mrb_undef_class_method(mrb, method, "new");
mrb_define_method(mrb, method, "==", method_eql, MRB_ARGS_REQ(1));
- mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
+ mrb_define_alias(mrb, method, "eql?", "==");
mrb_define_method(mrb, method, "to_s", method_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, method, "inspect", method_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, method, "call", method_call, MRB_ARGS_ANY());
- mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "[]"), mrb_intern_lit(mrb, "call"));
+ mrb_define_alias(mrb, method, "[]", "call");
mrb_define_method(mrb, method, "unbind", method_unbind, MRB_ARGS_NONE());
mrb_define_method(mrb, method, "super_method", method_super_method, MRB_ARGS_NONE());
mrb_define_method(mrb, method, "arity", method_arity, MRB_ARGS_NONE());
diff --git a/mrbgems/mruby-method/test/method.rb b/mrbgems/mruby-method/test/method.rb
index b229a4560..0b67d3e61 100644
--- a/mrbgems/mruby-method/test/method.rb
+++ b/mrbgems/mruby-method/test/method.rb
@@ -21,7 +21,7 @@ class Interpreter
}
def interpret(string)
@ret = ""
- string.each_char {|b| Dispatcher[b].bind(self).call }
+ string.split("").each {|b| Dispatcher[b].bind(self).call }
end
end
@@ -102,10 +102,7 @@ end
assert 'Method#call for regression' do
obj = BasicObject.new
- def obj.foo
- :ok
- end
- assert_equal :ok, Kernel.instance_method(:send).bind(obj).call(:foo), "https://github.com/ksss/mruby-method/issues/4"
+ assert_equal String, Kernel.instance_method(:inspect).bind(obj).call().class, "https://github.com/ksss/mruby-method/issues/4"
end
assert 'Method#call with undefined method' do
@@ -149,7 +146,7 @@ assert 'Method#source_location' do
assert_equal [filename, lineno], klass.new.method(:find_me_if_you_can).source_location
lineno = __LINE__ + 1
- klass.define_singleton_method(:s_find_me_if_you_can) {}
+ class <<klass; define_method(:s_find_me_if_you_can) {}; end
assert_equal [filename, lineno], klass.method(:s_find_me_if_you_can).source_location
klass = Class.new { def respond_to_missing?(m, b); m == :nothing; end }
@@ -243,7 +240,7 @@ assert 'owner' do
assert_equal(c, c.new.method(:foo).owner)
assert_equal(c, c2.new.method(:foo).owner)
- assert_equal(c.singleton_class, c2.method(:bar).owner)
+ assert_equal((class <<c; self; end), c2.method(:bar).owner)
end
assert 'owner missing' do
@@ -374,6 +371,25 @@ assert "Method#initialize_copy" do
assert_equal(m1, m2)
end
+assert "Method#<< and Method#>>" do
+ obj = Object.new
+ class << obj
+ def mul2(n); n * 2; end
+ def add3(n); n + 3; end
+ end
+
+ f = obj.method(:mul2)
+ g = obj.method(:add3)
+
+ m1 = f << g
+ assert_kind_of Proc, m1
+ assert_equal 16, m1.call(5)
+
+ m2 = f >> g
+ assert_kind_of Proc, m2
+ assert_equal 13, m2.call(5)
+end
+
assert 'UnboundMethod#arity' do
c = Class.new {
def foo(a, b)
@@ -413,12 +429,14 @@ assert 'UnboundMethod#bind' do
assert_equal(:meth, m.bind(1).call)
assert_equal(:meth, m.bind(:sym).call)
assert_equal(:meth, m.bind(Object.new).call)
- sc = Class.new {
- class << self
+ sc = nil
+ Class.new {
+ sc = class << self
def foo
end
+ self
end
- }.singleton_class
+ }
assert_raise(TypeError) { sc.instance_method(:foo).bind([]) }
assert_raise(TypeError) { Array.instance_method(:each).bind(1) }
assert_kind_of Method, Object.instance_method(:object_id).bind(Object.new)
diff --git a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
index f250538fe..576605cb1 100644
--- a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
+++ b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
@@ -1,8 +1,4 @@
-module Integral
- def div(other)
- self.divmod(other)[0]
- end
-
+class Numeric
def zero?
self == 0
end
diff --git a/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
index 1d6a07769..cd8bbf187 100644
--- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c
+++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
@@ -2,13 +2,10 @@
#include <mruby.h>
static inline mrb_int
-to_int(mrb_value x)
+to_int(mrb_state *mrb, mrb_value x)
{
- double f;
-
- if (mrb_fixnum_p(x)) return mrb_fixnum(x);
- f = mrb_float(x);
- return (mrb_int)f;
+ x = mrb_to_int(mrb, x);
+ return mrb_fixnum(x);
}
/*
@@ -28,7 +25,7 @@ mrb_int_chr(mrb_state *mrb, mrb_value x)
mrb_int chr;
char c;
- chr = to_int(x);
+ chr = to_int(mrb, x);
if (chr >= (1 << CHAR_BIT)) {
mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x);
}
@@ -48,8 +45,8 @@ mrb_int_allbits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
- n = to_int(self);
mrb_get_args(mrb, "i", &m);
+ n = to_int(mrb, self);
return mrb_bool_value((n & m) == m);
}
@@ -64,8 +61,8 @@ mrb_int_anybits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
- n = to_int(self);
mrb_get_args(mrb, "i", &m);
+ n = to_int(mrb, self);
return mrb_bool_value((n & m) != 0);
}
@@ -80,8 +77,8 @@ mrb_int_nobits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
- n = to_int(self);
mrb_get_args(mrb, "i", &m);
+ n = to_int(mrb, self);
return mrb_bool_value((n & m) == 0);
}
diff --git a/mrbgems/mruby-numeric-ext/test/numeric.rb b/mrbgems/mruby-numeric-ext/test/numeric.rb
index 6ea0c14e7..c85cb61f2 100644
--- a/mrbgems/mruby-numeric-ext/test/numeric.rb
+++ b/mrbgems/mruby-numeric-ext/test/numeric.rb
@@ -14,8 +14,9 @@ assert('Integer#div') do
end
assert('Float#div') do
+ skip unless Object.const_defined?(:Float)
assert_float 52, 365.2425.div(7)
-end if class_defined?("Float")
+end
assert('Integer#zero?') do
assert_equal true, 0.zero?
diff --git a/mrbgems/mruby-object-ext/mrbgem.rake b/mrbgems/mruby-object-ext/mrbgem.rake
index 6d14b4a51..1aea2c8cd 100644
--- a/mrbgems/mruby-object-ext/mrbgem.rake
+++ b/mrbgems/mruby-object-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-object-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'Object class extension'
+ spec.summary = 'extensional methods shared by all objects'
end
diff --git a/mrbgems/mruby-object-ext/mrblib/object.rb b/mrbgems/mruby-object-ext/mrblib/object.rb
index 581156cb0..f014df469 100644
--- a/mrbgems/mruby-object-ext/mrblib/object.rb
+++ b/mrbgems/mruby-object-ext/mrblib/object.rb
@@ -1,4 +1,18 @@
-class Object
+module Kernel
+ # call-seq:
+ # obj.yield_self {|_obj|...} -> an_object
+ # obj.then {|_obj|...} -> an_object
+ #
+ # Yields <i>obj</i> and returns the result.
+ #
+ # 'my string'.yield_self {|s|s.upcase} #=> "MY STRING"
+ #
+ def yield_self(&block)
+ return to_enum :yield_self unless block
+ block.call(self)
+ end
+ alias then yield_self
+
##
# call-seq:
# obj.tap{|x|...} -> obj
diff --git a/mrbgems/mruby-object-ext/src/object.c b/mrbgems/mruby-object-ext/src/object.c
index b076b3ec0..85db75b28 100644
--- a/mrbgems/mruby-object-ext/src/object.c
+++ b/mrbgems/mruby-object-ext/src/object.c
@@ -46,6 +46,22 @@ nil_to_i(mrb_state *mrb, mrb_value obj)
/*
* call-seq:
+ * obj.itself -> an_object
+ *
+ * Returns <i>obj</i>.
+ *
+ * string = 'my string' #=> "my string"
+ * string.itself.object_id == string.object_id #=> true
+ *
+ */
+static mrb_value
+mrb_f_itself(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+/*
+ * call-seq:
* obj.instance_exec(arg...) {|var...| block } -> obj
*
* Executes the given block within the context of the receiver
@@ -103,7 +119,9 @@ mrb_mruby_object_ext_gem_init(mrb_state* mrb)
#endif
mrb_define_method(mrb, n, "to_i", nil_to_i, MRB_ARGS_NONE());
- mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
+ mrb_define_method(mrb, mrb->kernel_module, "itself", mrb_f_itself, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, mrb_class_get(mrb, "BasicObject"), "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
}
void
diff --git a/mrbgems/mruby-object-ext/test/nil.rb b/mrbgems/mruby-object-ext/test/nil.rb
index 7f773637a..fbff20629 100644
--- a/mrbgems/mruby-object-ext/test/nil.rb
+++ b/mrbgems/mruby-object-ext/test/nil.rb
@@ -3,8 +3,9 @@ assert('NilClass#to_a') do
end
assert('NilClass#to_f') do
+ skip unless Object.const_defined?(:Float)
assert_equal 0.0, nil.to_f
-end if class_defined?("Float")
+end
assert('NilClass#to_i') do
assert_equal 0, nil.to_i
diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
index 3887091d3..b31dee04c 100644
--- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c
+++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
@@ -57,7 +57,7 @@ os_count_objects(mrb_state *mrb, mrb_value self)
hash = mrb_hash_new(mrb);
}
- if (!mrb_test(mrb_hash_empty_p(mrb, hash))) {
+ if (!mrb_hash_empty_p(mrb, hash)) {
mrb_hash_clear(mrb, hash);
}
diff --git a/mrbgems/mruby-objectspace/test/objectspace.rb b/mrbgems/mruby-objectspace/test/objectspace.rb
index 0553b97e2..9c44c2157 100644
--- a/mrbgems/mruby-objectspace/test/objectspace.rb
+++ b/mrbgems/mruby-objectspace/test/objectspace.rb
@@ -1,6 +1,6 @@
assert('ObjectSpace.count_objects') do
h = {}
- f = Fiber.new {} if Object.const_defined? :Fiber
+ f = Fiber.new {} if Object.const_defined?(:Fiber)
ObjectSpace.count_objects(h)
assert_kind_of(Hash, h)
assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
@@ -56,5 +56,5 @@ assert('ObjectSpace.each_object') do
end
assert 'Check class pointer of ObjectSpace.each_object.' do
- ObjectSpace.each_object { |obj| !obj }
+ assert_nothing_raised { ObjectSpace.each_object { |obj| !obj } }
end
diff --git a/mrbgems/mruby-pack/src/pack.c b/mrbgems/mruby-pack/src/pack.c
index d96ef1002..9f091194b 100644
--- a/mrbgems/mruby-pack/src/pack.c
+++ b/mrbgems/mruby-pack/src/pack.c
@@ -3,7 +3,7 @@
*/
#include "mruby.h"
-#include "error.h"
+#include "mruby/error.h"
#include "mruby/array.h"
#include "mruby/class.h"
#include "mruby/numeric.h"
@@ -60,19 +60,39 @@ enum {
#define PACK_BASE64_IGNORE 0xff
#define PACK_BASE64_PADDING 0xfe
-static int littleendian = 0;
-
const static unsigned char base64chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static signed char base64_dec_tab[128];
+static unsigned char base64_dec_tab[128];
+#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER__)
+# define BYTE_ORDER __BYTE_ORDER__
+#endif
+#if !defined(BIG_ENDIAN) && defined(__ORDER_BIG_ENDIAN__)
+# define BIG_ENDIAN __ORDER_BIG_ENDIAN__
+#endif
+#if !defined(LITTLE_ENDIAN) && defined(__ORDER_LITTLE_ENDIAN__)
+# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+#endif
-static int
+#ifdef BYTE_ORDER
+# if BYTE_ORDER == BIG_ENDIAN
+# define littleendian 0
+# define check_little_endian() (void)0
+# elif BYTE_ORDER == LITTLE_ENDIAN
+# define littleendian 1
+# define check_little_endian() (void)0
+# endif
+#endif
+#ifndef littleendian
+/* can't distinguish endian in compile time */
+static int littleendian = 0;
+static void
check_little_endian(void)
{
unsigned int n = 1;
- return (*(unsigned char *)&n == 1);
+ littleendian = (*(unsigned char *)&n == 1);
}
+#endif
static unsigned int
hex2int(unsigned char ch)
@@ -98,9 +118,9 @@ make_base64_dec_tab(void)
base64_dec_tab['a' + i] = i + 26;
for (i = 0; i < 10; i++)
base64_dec_tab['0' + i] = i + 52;
- base64_dec_tab['+'] = 62;
- base64_dec_tab['/'] = 63;
- base64_dec_tab['='] = PACK_BASE64_PADDING;
+ base64_dec_tab['+'+0] = 62;
+ base64_dec_tab['/'+0] = 63;
+ base64_dec_tab['='+0] = PACK_BASE64_PADDING;
}
static mrb_value
@@ -313,21 +333,23 @@ pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned i
d = mrb_float(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
- for (i = 0; i < 8; ++i) {
- RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+ if (littleendian) {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
+ }
+ else {
+ for (i = 0; i < 8; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+ }
}
-#else
- memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
-#endif
} else {
-#ifdef MRB_ENDIAN_BIG
- memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
-#else
- for (i = 0; i < 8; ++i) {
- RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+ if (littleendian) {
+ for (i = 0; i < 8; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+ }
+ }
+ else {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
}
-#endif
}
return 8;
@@ -341,21 +363,23 @@ unpack_double(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value a
uint8_t *buffer = (uint8_t *)&d;
if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
- for (i = 0; i < 8; ++i) {
- buffer[8 - i - 1] = src[i];
+ if (littleendian) {
+ memcpy(buffer, src, 8);
+ }
+ else {
+ for (i = 0; i < 8; ++i) {
+ buffer[8 - i - 1] = src[i];
+ }
}
-#else
- memcpy(buffer, src, 8);
-#endif
} else {
-#ifdef MRB_ENDIAN_BIG
- memcpy(buffer, src, 8);
-#else
- for (i = 0; i < 8; ++i) {
- buffer[8 - i - 1] = src[i];
+ if (littleendian) {
+ for (i = 0; i < 8; ++i) {
+ buffer[8 - i - 1] = src[i];
+ }
+ }
+ else {
+ memcpy(buffer, src, 8);
}
-#endif
}
mrb_ary_push(mrb, ary, mrb_float_value(mrb, d));
@@ -372,21 +396,23 @@ pack_float(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned in
f = (float)mrb_float(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
- for (i = 0; i < 4; ++i) {
- RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+ if (littleendian) {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
+ }
+ else {
+ for (i = 0; i < 4; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+ }
}
-#else
- memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
-#endif
} else {
-#ifdef MRB_ENDIAN_BIG
- memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
-#else
- for (i = 0; i < 4; ++i) {
- RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+ if (littleendian) {
+ for (i = 0; i < 4; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+ }
+ }
+ else {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
}
-#endif
}
return 4;
@@ -400,21 +426,23 @@ unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ar
uint8_t *buffer = (uint8_t *)&f;
if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
- for (i = 0; i < 4; ++i) {
- buffer[4 - i - 1] = src[i];
+ if (littleendian) {
+ memcpy(buffer, src, 4);
+ }
+ else {
+ for (i = 0; i < 4; ++i) {
+ buffer[4 - i - 1] = src[i];
+ }
}
-#else
- memcpy(buffer, src, 4);
-#endif
} else {
-#ifdef MRB_ENDIAN_BIG
- memcpy(buffer, src, 4);
-#else
- for (i = 0; i < 4; ++i) {
- buffer[4 - i - 1] = src[i];
+ if (littleendian) {
+ for (i = 0; i < 4; ++i) {
+ buffer[4 - i - 1] = src[i];
+ }
+ }
+ else {
+ memcpy(buffer, src, 4);
}
-#endif
}
mrb_ary_push(mrb, ary, mrb_float_value(mrb, f));
@@ -429,11 +457,6 @@ pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count,
int len = 0;
uint32_t c = 0;
-#ifndef MRB_WITHOUT_FLOAT
- if (mrb_float_p(o)) {
- goto range_error;
- }
-#endif
c = (uint32_t)mrb_fixnum(o);
/* Unicode character */
@@ -461,9 +484,6 @@ pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count,
len = 4;
}
else {
-#ifndef MRB_WITHOUT_FLOAT
-range_error:
-#endif
mrb_raise(mrb, E_RANGE_ERROR, "pack(U): value out of range");
}
@@ -510,7 +530,7 @@ utf8_to_uv(mrb_state *mrb, const char *p, long *lenp)
}
if (n > *lenp) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %S bytes, given %S bytes)",
- mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
+ mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
}
*lenp = n--;
if (n != 0) {
@@ -598,19 +618,21 @@ unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, u
}
copylen = slen;
- if (flags & PACK_FLAG_Z) { /* "Z" */
+ if (slen >= 0 && flags & PACK_FLAG_Z) { /* "Z" */
if ((cp = (const char *)memchr(sptr, '\0', slen)) != NULL) {
copylen = (int)(cp - sptr);
if (count == -1) {
slen = copylen + 1;
}
}
- } else if (!(flags & PACK_FLAG_a)) { /* "A" */
- while (copylen > 0 && (sptr[copylen - 1] == '\0' || isspace(sptr[copylen - 1]))) {
+ }
+ else if (!(flags & PACK_FLAG_a)) { /* "A" */
+ while (copylen > 0 && (sptr[copylen - 1] == '\0' || ISSPACE(sptr[copylen - 1]))) {
copylen--;
}
}
+ if (copylen < 0) copylen = 0;
dst = mrb_str_new(mrb, sptr, (mrb_int)copylen);
mrb_ary_push(mrb, ary, dst);
return slen;
@@ -980,7 +1002,7 @@ alias:
case 'm':
dir = PACK_DIR_BASE64;
type = PACK_TYPE_STRING;
- flags |= PACK_FLAG_WIDTH;
+ flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2;
break;
case 'N': /* = "L>" */
dir = PACK_DIR_LONG;
@@ -1050,13 +1072,14 @@ alias:
/* read suffix [0-9*_!<>] */
while (tmpl->idx < tlen) {
ch = tptr[tmpl->idx++];
- if (isdigit(ch)) {
+ if (ISDIGIT(ch)) {
count = ch - '0';
- while (tmpl->idx < tlen && isdigit(tptr[tmpl->idx])) {
- count = count * 10 + (tptr[tmpl->idx++] - '0');
- if (count < 0) {
- mrb_raisef(mrb, E_RUNTIME_ERROR, "too big template length");
+ while (tmpl->idx < tlen && ISDIGIT(tptr[tmpl->idx])) {
+ int ch = tptr[tmpl->idx++] - '0';
+ if (count+ch > INT_MAX/10) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "too big template length");
}
+ count = count * 10 + ch;
}
continue; /* special case */
} else if (ch == '*') {
@@ -1122,13 +1145,16 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
o = mrb_ary_ref(mrb, ary, aidx);
if (type == PACK_TYPE_INTEGER) {
o = mrb_to_int(mrb, o);
+ }
#ifndef MRB_WITHOUT_FLOAT
- } else if (type == PACK_TYPE_FLOAT) {
+ else if (type == PACK_TYPE_FLOAT) {
if (!mrb_float_p(o)) {
- o = mrb_funcall(mrb, o, "to_f", 0);
+ mrb_float f = mrb_to_flo(mrb, o);
+ o = mrb_float_value(mrb, f);
}
+ }
#endif
- } else if (type == PACK_TYPE_STRING) {
+ else if (type == PACK_TYPE_STRING) {
if (!mrb_string_p(o)) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o)));
}
@@ -1170,7 +1196,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
default:
break;
}
- if (dir == PACK_DIR_STR) { /* always consumes 1 entry */
+ if (dir == PACK_DIR_STR || dir == PACK_DIR_BASE64) { /* always consumes 1 entry */
aidx++;
break;
}
@@ -1188,7 +1214,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
}
static mrb_value
-mrb_pack_unpack(mrb_state *mrb, mrb_value str)
+pack_unpack(mrb_state *mrb, mrb_value str, int single)
{
mrb_value result;
struct tmpl tmpl;
@@ -1223,6 +1249,9 @@ mrb_pack_unpack(mrb_state *mrb, mrb_value str)
case PACK_DIR_STR:
srcidx += unpack_a(mrb, sptr, srclen - srcidx, result, count, flags);
break;
+ case PACK_DIR_BASE64:
+ srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
+ break;
}
continue;
}
@@ -1249,9 +1278,6 @@ mrb_pack_unpack(mrb_state *mrb, mrb_value str)
case PACK_DIR_QUAD:
srcidx += unpack_q(mrb, sptr, srclen - srcidx, result, flags);
break;
- case PACK_DIR_BASE64:
- srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
- break;
#ifndef MRB_WITHOUT_FLOAT
case PACK_DIR_FLOAT:
srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags);
@@ -1270,19 +1296,39 @@ mrb_pack_unpack(mrb_state *mrb, mrb_value str)
count--;
}
}
+ if (single) break;
}
+ if (single) {
+ if (RARRAY_LEN(result) > 0) {
+ return RARRAY_PTR(result)[0];
+ }
+ return mrb_nil_value();
+ }
return result;
}
+static mrb_value
+mrb_pack_unpack(mrb_state *mrb, mrb_value str)
+{
+ return pack_unpack(mrb, str, 0);
+}
+
+static mrb_value
+mrb_pack_unpack1(mrb_state *mrb, mrb_value str)
+{
+ return pack_unpack(mrb, str, 1);
+}
+
void
mrb_mruby_pack_gem_init(mrb_state *mrb)
{
- littleendian = check_little_endian();
+ check_little_endian();
make_base64_dec_tab();
mrb_define_method(mrb, mrb->array_class, "pack", mrb_pack_pack, MRB_ARGS_REQ(1));
mrb_define_method(mrb, mrb->string_class, "unpack", mrb_pack_unpack, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mrb->string_class, "unpack1", mrb_pack_unpack1, MRB_ARGS_REQ(1));
}
void
diff --git a/mrbgems/mruby-pack/test/pack.rb b/mrbgems/mruby-pack/test/pack.rb
index 1788c2b72..68ef4165f 100644
--- a/mrbgems/mruby-pack/test/pack.rb
+++ b/mrbgems/mruby-pack/test/pack.rb
@@ -1,101 +1,95 @@
+PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01
+
+def assert_pack tmpl, packed, unpacked
+ t = tmpl.inspect
+ assert do
+ assert_equal packed, unpacked.pack(tmpl), "#{unpacked.inspect}.pack(#{t})"
+ assert_equal unpacked, packed.unpack(tmpl), "#{packed.inspect}.unpack(#{t})"
+ end
+end
+
# pack & unpack 'm' (base64)
assert('[""].pack("m")') do
- ary = ""
- str = ""
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
+ assert_pack "m", "", [""]
end
assert('["\0"].pack("m")') do
- ary = "\0"
- str = "AA==\n"
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
+ assert_pack "m", "AA==\n", ["\0"]
end
assert('["\0\0"].pack("m")') do
- ary = "\0\0"
- str = "AAA=\n"
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
+ assert_pack "m", "AAA=\n", ["\0\0"]
end
assert('["\0\0\0"].pack("m")') do
- ary = "\0\0\0"
- str = "AAAA\n"
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
+ assert_pack "m", "AAAA\n", ["\0\0\0"]
end
assert('["abc..xyzABC..XYZ"].pack("m")') do
- ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"].pack("m") == "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n"
+ assert_pack "m", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
end
assert('"YWJ...".unpack("m") should "abc..xyzABC..XYZ"') do
- str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m") == [str] and
- "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m") == [str]
+ ary = ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
+ assert_equal ary, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m")
+ assert_equal ary, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m")
+end
+
+assert('["A", "B"].pack') do
+ assert_equal "QQ==\n", ["A", "B"].pack("m50")
+ assert_equal ["A"], "QQ==\n".unpack("m50")
+ assert_equal "QQ==Qg==", ["A", "B"].pack("m0 m0")
+ assert_equal ["A", "B"], "QQ==Qg==".unpack("m10 m10")
+end
+
+assert('["abc..xyzABC..XYZ"].pack("m0")') do
+ assert_pack "m0", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
end
# pack & unpack 'H'
assert('["3031"].pack("H*")') do
- ary = "3031"
- str = "01"
- [ary].pack("H*") == str and
- str.unpack("H*") == [ary]
+ assert_pack "H*", "01", ["3031"]
end
assert('["10"].pack("H*")') do
- ary = "10"
- str = "\020"
- [ary].pack("H*") == str and
- str.unpack("H*") == [ary]
+ assert_pack "H*", "\020", ["10"]
end
assert('[0,1,127,128,255].pack("C*")') do
- ary = [ 0, 1, 127, 128, 255 ]
- str = "\x00\x01\x7F\x80\xFF"
- ary.pack("C*") == str and str.unpack("C*") == ary
+ assert_pack "C*", "\x00\x01\x7F\x80\xFF", [0, 1, 127, 128, 255]
end
# pack "a"
assert('["abc"].pack("a")') do
- ["abc"].pack("a") == "a" and
- ["abc"].pack("a*") == "abc" and
- ["abc"].pack("a4") == "abc\0"
+ assert_equal "a", ["abc"].pack("a")
+ assert_equal "abc", ["abc"].pack("a*")
+ assert_equal "abc\0", ["abc"].pack("a4")
end
# upack "a"
assert('["abc"].pack("a")') do
- "abc\0".unpack("a4") == ["abc\0"] and
- "abc ".unpack("a4") == ["abc "]
+ assert_equal ["abc\0"], "abc\0".unpack("a4")
+ assert_equal ["abc "], "abc ".unpack("a4")
end
# pack "A"
assert('["abc"].pack("A")') do
- ["abc"].pack("A") == "a" and
- ["abc"].pack("A*") == "abc" and
- ["abc"].pack("A4") == "abc "
+ assert_equal "a", ["abc"].pack("A")
+ assert_equal "abc", ["abc"].pack("A*")
+ assert_equal "abc ", ["abc"].pack("A4")
end
# upack "A"
assert('["abc"].pack("A")') do
- "abc\0".unpack("A4") == ["abc"] and
- "abc ".unpack("A4") == ["abc"]
+ assert_equal ["abc"], "abc\0".unpack("A4")
+ assert_equal ["abc"], "abc ".unpack("A4")
end
# regression tests
assert('issue #1') do
- [1, 2].pack("nn") == "\000\001\000\002"
+ assert_equal "\000\001\000\002", [1, 2].pack("nn")
end
-def assert_pack tmpl, packed, unpacked
- assert_equal packed, unpacked.pack(tmpl)
- assert_equal unpacked, packed.unpack(tmpl)
-end
-
-PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01
-
assert 'pack float' do
assert_pack 'e', "\x00\x00@@", [3.0]
assert_pack 'g', "@@\x00\x00", [3.0]
diff --git a/mrbgems/mruby-print/mrblib/print.rb b/mrbgems/mruby-print/mrblib/print.rb
index 38a10661b..27567d858 100644
--- a/mrbgems/mruby-print/mrblib/print.rb
+++ b/mrbgems/mruby-print/mrblib/print.rb
@@ -45,16 +45,13 @@ module Kernel
__printstr__ "\n"
i += 1
end
- args[0]
+ args.__svalue
end
unless Kernel.respond_to?(:sprintf)
def printf(*args)
raise NotImplementedError.new('printf not available')
end
- def sprintf(*args)
- raise NotImplementedError.new('sprintf not available')
- end
else
def printf(*args)
__printstr__(sprintf(*args))
diff --git a/mrbgems/mruby-print/src/print.c b/mrbgems/mruby-print/src/print.c
index e181b06e0..f7f99fc77 100644
--- a/mrbgems/mruby-print/src/print.c
+++ b/mrbgems/mruby-print/src/print.c
@@ -23,7 +23,6 @@ printstr(mrb_state *mrb, mrb_value obj)
char* utf8 = RSTRING_PTR(obj);
int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0);
wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t));
- if (utf16 == NULL) return;
if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) {
utf16[wlen] = 0;
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
diff --git a/mrbgems/mruby-proc-ext/mrblib/proc.rb b/mrbgems/mruby-proc-ext/mrblib/proc.rb
index b71663938..abe9c7944 100644
--- a/mrbgems/mruby-proc-ext/mrblib/proc.rb
+++ b/mrbgems/mruby-proc-ext/mrblib/proc.rb
@@ -27,7 +27,7 @@ class Proc
pproc = self
make_curry = proc do |given_args=[]|
- send(type) do |*args|
+ __send__(type) do |*args|
new_args = given_args + args
if new_args.size >= arity
pproc[*new_args]
@@ -39,4 +39,12 @@ class Proc
make_curry.call
end
+ def <<(other)
+ ->(*args, &block) { call(other.call(*args, &block)) }
+ end
+
+ def >>(other)
+ ->(*args, &block) { other.call(call(*args, &block)) }
+ end
+
end
diff --git a/mrbgems/mruby-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c
index b13606f5f..c9041ec75 100644
--- a/mrbgems/mruby-proc-ext/src/proc.c
+++ b/mrbgems/mruby-proc-ext/src/proc.c
@@ -25,8 +25,8 @@ mrb_proc_source_location(mrb_state *mrb, mrb_value self)
int32_t line;
const char *filename;
- filename = mrb_debug_get_filename(irep, 0);
- line = mrb_debug_get_line(irep, 0);
+ filename = mrb_debug_get_filename(mrb, irep, 0);
+ line = mrb_debug_get_line(mrb, irep, 0);
return (!filename && line == -1)? mrb_nil_value()
: mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, filename), mrb_fixnum_value(line));
@@ -38,7 +38,7 @@ mrb_proc_inspect(mrb_state *mrb, mrb_value self)
{
struct RProc *p = mrb_proc_ptr(self);
mrb_value str = mrb_str_new_lit(mrb, "#<Proc:");
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(self)));
+ mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(self)));
if (!MRB_PROC_CFUNC_P(p)) {
mrb_irep *irep = p->body.irep;
@@ -46,13 +46,13 @@ mrb_proc_inspect(mrb_state *mrb, mrb_value self)
int32_t line;
mrb_str_cat_lit(mrb, str, "@");
- filename = mrb_debug_get_filename(irep, 0);
+ filename = mrb_debug_get_filename(mrb, irep, 0);
mrb_str_cat_cstr(mrb, str, filename ? filename : "-");
mrb_str_cat_lit(mrb, str, ":");
- line = mrb_debug_get_line(irep, 0);
+ line = mrb_debug_get_line(mrb, irep, 0);
if (line != -1) {
- str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line));
+ mrb_str_concat(mrb, str, mrb_fixnum_value(line));
}
else {
mrb_str_cat_lit(mrb, str, "-");
@@ -94,21 +94,23 @@ static mrb_value
mrb_proc_parameters(mrb_state *mrb, mrb_value self)
{
struct parameters_type {
- int size;
+ size_t len;
const char *name;
+ int size;
} *p, parameters_list [] = {
- {0, "req"},
- {0, "opt"},
- {0, "rest"},
- {0, "req"},
- {0, "block"},
- {0, NULL}
+ {sizeof("req") - 1, "req", 0},
+ {sizeof("opt") - 1, "opt", 0},
+ {sizeof("rest") - 1, "rest", 0},
+ {sizeof("req") - 1, "req", 0},
+ {sizeof("block") - 1, "block", 0},
+ {0, NULL, 0}
};
const struct RProc *proc = mrb_proc_ptr(self);
const struct mrb_irep *irep = proc->body.irep;
mrb_aspec aspec;
- mrb_value sname, parameters;
+ mrb_value parameters;
int i, j;
+ int max = -1;
if (MRB_PROC_CFUNC_P(proc)) {
// TODO cfunc aspec is not implemented yet
@@ -120,16 +122,18 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
if (!irep->lv) {
return mrb_ary_new(mrb);
}
- if (GET_OPCODE(*irep->iseq) != OP_ENTER) {
+ if (*irep->iseq != OP_ENTER) {
return mrb_ary_new(mrb);
}
if (!MRB_PROC_STRICT_P(proc)) {
+ parameters_list[0].len = sizeof("opt") - 1;
parameters_list[0].name = "opt";
+ parameters_list[3].len = sizeof("opt") - 1;
parameters_list[3].name = "opt";
}
- aspec = GETARG_Ax(*irep->iseq);
+ aspec = PEEK_W(irep->iseq+1);
parameters_list[0].size = MRB_ASPEC_REQ(aspec);
parameters_list[1].size = MRB_ASPEC_OPT(aspec);
parameters_list[2].size = MRB_ASPEC_REST(aspec);
@@ -138,14 +142,25 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
parameters = mrb_ary_new_capa(mrb, irep->nlocals-1);
+ max = irep->nlocals-1;
for (i = 0, p = parameters_list; p->name; p++) {
- if (p->size <= 0) continue;
- sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name));
+ mrb_value sname = mrb_symbol_value(mrb_intern_static(mrb, p->name, p->len));
+
for (j = 0; j < p->size; i++, j++) {
- mrb_value a = mrb_ary_new(mrb);
+ mrb_value a;
+
+ a = mrb_ary_new(mrb);
mrb_ary_push(mrb, a, sname);
- if (irep->lv[i].name) {
- mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i].name));
+ if (i < max && irep->lv[i].name) {
+ mrb_sym sym = irep->lv[i].name;
+ const char *name = mrb_sym2name(mrb, sym);
+ switch (name[0]) {
+ case '*': case '&':
+ break;
+ default:
+ mrb_ary_push(mrb, a, mrb_symbol_value(sym));
+ break;
+ }
}
mrb_ary_push(mrb, parameters, a);
}
diff --git a/mrbgems/mruby-proc-ext/test/proc.rb b/mrbgems/mruby-proc-ext/test/proc.rb
index 037d8d124..a6321d371 100644
--- a/mrbgems/mruby-proc-ext/test/proc.rb
+++ b/mrbgems/mruby-proc-ext/test/proc.rb
@@ -1,16 +1,37 @@
##
# Proc(Ext) Test
+def enable_debug_info?
+ return @enable_debug_info unless @enable_debug_info == nil
+ begin
+ raise
+ rescue => e
+ @enable_debug_info = !e.backtrace.empty?
+ end
+end
+
assert('Proc#source_location') do
- loc = Proc.new {}.source_location
- next true if loc.nil?
- assert_equal loc[0][-7, 7], 'proc.rb'
- assert_equal loc[1], 5
+ skip unless enable_debug_info?
+ file, line = Proc.new{}.source_location
+ assert_equal __FILE__, file
+ assert_equal __LINE__ - 2, line
end
assert('Proc#inspect') do
ins = Proc.new{}.inspect
- assert_kind_of String, ins
+ if enable_debug_info?
+ metas = %w(\\ * ? [ ] { })
+ file = __FILE__.split("").map{|c| metas.include?(c) ? "\\#{c}" : c}.join
+ line = __LINE__ - 4
+ else
+ file = line = "-"
+ end
+ assert_match "#<Proc:0x*@#{file}:#{line}>", ins
+end
+
+assert('Proc#parameters') do
+ parameters = Proc.new{|x,y=42,*other|}.parameters
+ assert_equal [[:opt, :x], [:opt, :y], [:rest, :other]], parameters
end
assert('Proc#lambda?') do
@@ -72,6 +93,19 @@ assert('Kernel#proc') do
end
end
+assert "Proc#<< and Proc#>>" do
+ add3 = ->(n) { n + 3 }
+ mul2 = ->(n) { n * 2 }
+
+ f1 = mul2 << add3
+ assert_kind_of Proc, f1
+ assert_equal 16, f1.call(5)
+
+ f2 = mul2 >> add3
+ assert_kind_of Proc, f2
+ assert_equal 13, f2.call(5)
+end
+
assert('mrb_proc_new_cfunc_with_env') do
ProcExtTest.mrb_proc_new_cfunc_with_env(:test)
ProcExtTest.mrb_proc_new_cfunc_with_env(:mruby)
diff --git a/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c
index b865244cc..99f2b02e4 100644
--- a/mrbgems/mruby-random/src/random.c
+++ b/mrbgems/mruby-random/src/random.c
@@ -4,6 +4,10 @@
** See Copyright Notice in mruby.h
*/
+#ifdef MRB_WITHOUT_FLOAT
+# error Conflict 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <mruby.h>
#include <mruby/variable.h>
#include <mruby/class.h>
@@ -79,12 +83,12 @@ get_opt(mrb_state* mrb)
mrb_get_args(mrb, "|o", &arg);
if (!mrb_nil_p(arg)) {
- arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int");
- if (mrb_nil_p(arg)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type");
- }
- if (mrb_fixnum(arg) < 0) {
- arg = mrb_fixnum_value(0 - mrb_fixnum(arg));
+ mrb_int i;
+
+ arg = mrb_to_int(mrb, arg);
+ i = mrb_fixnum(arg);
+ if (i < 0) {
+ arg = mrb_fixnum_value(0 - i);
}
}
return arg;
@@ -219,7 +223,7 @@ mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
mrb_int j;
mrb_value *ptr = RARRAY_PTR(ary);
mrb_value tmp;
-
+
j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary))));
diff --git a/mrbgems/mruby-random/test/random.rb b/mrbgems/mruby-random/test/random.rb
index 1c59be3a6..cf4a55141 100644
--- a/mrbgems/mruby-random/test/random.rb
+++ b/mrbgems/mruby-random/test/random.rb
@@ -1,48 +1,58 @@
##
# Random Test
-assert("Random#srand") do
+assert("Random.new") do
r1 = Random.new(123)
r2 = Random.new(123)
- r1.rand == r2.rand
+ r3 = Random.new(124)
+ assert_equal(r1.rand, r2.rand)
+ assert_not_equal(r1.rand, r3.rand)
end
-assert("Kernel::srand") do
+assert("Kernel.srand") do
srand(234)
r1 = rand
srand(234)
r2 = rand
- r1 == r2
+ srand(235)
+ r3 = rand
+ assert_equal(r1, r2)
+ assert_not_equal(r1, r3)
end
-assert("Random::srand") do
+assert("Random.srand") do
Random.srand(345)
r1 = rand
srand(345)
r2 = Random.rand
- r1 == r2
+ Random.srand(346)
+ r3 = rand
+ assert_equal(r1, r2)
+ assert_not_equal(r1, r3)
end
-assert("fixnum") do
- rand(3).class == Fixnum
-end
-
-assert("float") do
- rand.class == Float
+assert("return class of Kernel.rand") do
+ assert_kind_of(Fixnum, rand(3))
+ assert_kind_of(Fixnum, rand(1.5))
+ assert_kind_of(Float, rand)
+ assert_kind_of(Float, rand(0.5))
end
assert("Array#shuffle") do
- ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ orig = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary = orig.dup
shuffled = ary.shuffle
-
- ary == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and shuffled != ary and 10.times { |x| ary.include? x }
+ assert_equal(orig, ary)
+ assert_not_equal(ary, shuffled)
+ assert_equal(orig, shuffled.sort)
end
assert('Array#shuffle!') do
- ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- ary.shuffle!
-
- ary != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary.include? x }
+ orig = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary = orig.dup
+ assert_same(ary, ary.shuffle!)
+ assert_not_equal(orig, ary)
+ assert_equal(orig, ary.sort)
end
assert("Array#shuffle(random)") do
@@ -52,12 +62,12 @@ assert("Array#shuffle(random)") do
end
# verify that the same seed causes the same results
- ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- shuffle1 = ary1.shuffle Random.new 345
- ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- shuffle2 = ary2.shuffle Random.new 345
-
- ary1 != shuffle1 and 10.times { |x| shuffle1.include? x } and shuffle1 == shuffle2
+ ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ shuffled1 = ary.shuffle Random.new 345
+ shuffled2 = ary.shuffle Random.new 345
+ shuffled3 = ary.shuffle Random.new 346
+ assert_equal(shuffled1, shuffled2)
+ assert_not_equal(shuffled1, shuffled3)
end
assert('Array#shuffle!(random)') do
@@ -71,18 +81,42 @@ assert('Array#shuffle!(random)') do
ary1.shuffle! Random.new 345
ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
ary2.shuffle! Random.new 345
-
- ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2
+ ary3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary3.shuffle! Random.new 346
+ assert_equal(ary1, ary2)
+ assert_not_equal(ary1, ary3)
end
-assert('Array#sample checks input length after reading arguments') do
- $ary = [1, 2, 3]
- class ArrayChange
- def to_i
- $ary << 4
- 4
+assert('Array#sample') do
+ 100.times do
+ assert_include([0, 1, 2], [2, 1, 0].sample)
+ [2, 1, 0].sample(2).each { |sample| assert_include([0, 1, 2], sample) }
+ h = {}
+ (1..10).to_a.sample(7).each do |sample|
+ assert_not_include(h, sample)
+ h[sample] = true
end
end
- assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort
+ assert_nil([].sample)
+ assert_equal([], [].sample(1))
+ assert_equal([], [2, 1].sample(0))
+ assert_raise(TypeError) { [2, 1].sample(true) }
+ assert_raise(ArgumentError) { [2, 1].sample(-1) }
+end
+
+assert('Array#sample(random)') do
+ assert_raise(TypeError) do
+ # this will cause an exception due to the wrong argument
+ [1, 2].sample(2, "Not a Random instance")
+ end
+
+ # verify that the same seed causes the same results
+ ary = (1..10).to_a
+ srand(15)
+ samples1 = ary.sample(4)
+ samples2 = ary.sample(4, Random.new(15))
+ samples3 = ary.sample(4, Random.new(16))
+ assert_equal(samples1, samples2)
+ assert_not_equal(samples1, samples3)
end
diff --git a/mrbgems/mruby-range-ext/mrblib/range.rb b/mrbgems/mruby-range-ext/mrblib/range.rb
index e5d1fb079..a149a57dc 100644
--- a/mrbgems/mruby-range-ext/mrblib/range.rb
+++ b/mrbgems/mruby-range-ext/mrblib/range.rb
@@ -15,10 +15,7 @@ class Range
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1
nv = args[0]
- raise TypeError, "no implicit conversion from nil to integer" if nv.nil?
- raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int)
- n = nv.to_int
- raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer)
+ n = nv.__to_int
raise ArgumentError, "negative array size (or size too big)" unless 0 <= n
ary = []
each do |i|
@@ -28,4 +25,43 @@ class Range
end
ary
end
+
+ def max(&block)
+ val = self.first
+ last = self.last
+ return super if block
+
+ # fast path for numerics
+ if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
+ raise TypeError if exclude_end? && !last.kind_of?(Fixnum)
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ max = last
+ max -= 1 if exclude_end?
+ return max
+ end
+
+ # delegate to Enumerable
+ super
+ end
+
+ def min(&block)
+ val = self.first
+ last = self.last
+ return super if block
+
+ # fast path for numerics
+ if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
+ raise TypeError if exclude_end? && !last.kind_of?(Fixnum)
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ min = val
+ return min
+ end
+
+ # delegate to Enumerable
+ super
+ end
end
diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c
index aca71cc01..fb76fe0d8 100644
--- a/mrbgems/mruby-range-ext/src/range.c
+++ b/mrbgems/mruby-range-ext/src/range.c
@@ -1,3 +1,7 @@
+#ifdef MRB_WITHOUT_FLOAT
+# error Conflict 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <mruby.h>
#include <mruby/range.h>
#include <math.h>
@@ -40,7 +44,7 @@ r_lt(mrb_state *mrb, mrb_value a, mrb_value b)
* ("a".."z").cover?("cc") #=> true
*/
static mrb_value
-mrb_range_cover(mrb_state *mrb, mrb_value range)
+range_cover(mrb_state *mrb, mrb_value range)
{
mrb_value val;
struct RRange *r = mrb_range_ptr(mrb, range);
@@ -48,11 +52,11 @@ mrb_range_cover(mrb_state *mrb, mrb_value range)
mrb_get_args(mrb, "o", &val);
- beg = r->edges->beg;
- end = r->edges->end;
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
if (r_le(mrb, beg, val)) {
- if (r->excl) {
+ if (RANGE_EXCL(r)) {
if (r_lt(mrb, val, end))
return mrb_true_value();
}
@@ -82,14 +86,13 @@ mrb_range_cover(mrb_state *mrb, mrb_value range)
* (10...20).last(3) #=> [17, 18, 19]
*/
static mrb_value
-mrb_range_last(mrb_state *mrb, mrb_value range)
+range_last(mrb_state *mrb, mrb_value range)
{
mrb_value num;
mrb_value array;
- struct RRange *r = mrb_range_ptr(mrb, range);
if (mrb_get_args(mrb, "|o", &num) == 0) {
- return r->edges->end;
+ return mrb_range_end(mrb, range);
}
array = mrb_funcall(mrb, range, "to_a", 0);
@@ -108,7 +111,7 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
*/
static mrb_value
-mrb_range_size(mrb_state *mrb, mrb_value range)
+range_size(mrb_state *mrb, mrb_value range)
{
struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
@@ -116,9 +119,9 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
mrb_bool num_p = TRUE;
mrb_bool excl;
- beg = r->edges->beg;
- end = r->edges->end;
- excl = r->excl;
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
+ excl = RANGE_EXCL(r);
if (mrb_fixnum_p(beg)) {
beg_f = (mrb_float)mrb_fixnum(beg);
}
@@ -165,9 +168,9 @@ mrb_mruby_range_ext_gem_init(mrb_state* mrb)
{
struct RClass * s = mrb_class_get(mrb, "Range");
- mrb_define_method(mrb, s, "cover?", mrb_range_cover, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, s, "last", mrb_range_last, MRB_ARGS_OPT(1));
- mrb_define_method(mrb, s, "size", mrb_range_size, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "cover?", range_cover, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "last", range_last, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, s, "size", range_size, MRB_ARGS_NONE());
}
void
diff --git a/mrbgems/mruby-range-ext/test/range.rb b/mrbgems/mruby-range-ext/test/range.rb
index efcbdabe4..b56d6b58e 100644
--- a/mrbgems/mruby-range-ext/test/range.rb
+++ b/mrbgems/mruby-range-ext/test/range.rb
@@ -30,3 +30,105 @@ assert('Range#size') do
assert_equal Float::INFINITY, (0..Float::INFINITY).size
assert_nil ('a'..'z').size
end
+
+assert('Range#max') do
+ # returns the maximum value in the range when called with no arguments
+ assert_equal 10, (1..10).max
+ assert_equal 9, (1...10).max
+ assert_equal 4294967295, (0...2**32).max
+
+ # returns the maximum value in the Float range when called with no arguments
+ assert_equal 908.1111, (303.20..908.1111).max
+
+ # raises TypeError when called on an exclusive range and a non Integer value
+ assert_raise(TypeError) { (303.20...908.1111).max }
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, (100..10).max
+
+ # returns nil when the endpoint equals the start point and the range is exclusive
+ assert_equal nil, (5...5).max
+
+ # returns the endpoint when the endpoint equals the start point and the range is inclusive
+ assert_equal 5, (5..5).max
+
+ # returns nil when the endpoint is less than the start point in a Float range
+ assert_equal nil, (3003.20..908.1111).max
+end
+
+assert('Range#max given a block') do
+ # passes each pair of values in the range to the block
+ acc = []
+ (1..10).max do |a, b|
+ acc << a
+ acc << b
+ a
+ end
+ (1..10).each do |value|
+ assert_true acc.include?(value)
+ end
+
+ # passes each pair of elements to the block in reversed order
+ acc = []
+ (1..5).max do |a, b|
+ acc << [a, b]
+ a
+ end
+ assert_equal [[2, 1], [3, 2], [4, 3], [5, 4]], acc
+
+ # returns the element the block determines to be the maximum
+ assert_equal 1, ((1..3).max { |_a, _b| -3 })
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, ((100..10).max { |x, y| x <=> y })
+ assert_equal nil, ((5...5).max { |x, y| x <=> y })
+end
+
+assert('Range#min') do
+ # returns the minimum value in the range when called with no arguments
+ assert_equal 1, (1..10).min
+ assert_equal 1, (1...10).min
+
+ # returns the minimum value in the Float range when called with no arguments
+ assert_equal 303.20, (303.20..908.1111).min
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, (100..10).min
+
+ # returns nil when the endpoint equals the start point and the range is exclusive
+ assert_equal nil, (5...5).max
+
+ # returns the endpoint when the endpoint equals the start point and the range is inclusive
+ assert_equal 5, (5..5).max
+
+ # returns nil when the start point is greater than the endpoint in a Float range
+ assert_equal nil, (3003.20..908.1111).max
+end
+
+assert('Range#min given a block') do
+ # passes each pair of values in the range to the block
+ acc = []
+ (1..10).min do |a, b|
+ acc << a
+ acc << b
+ a
+ end
+ (1..10).each do |value|
+ assert_true acc.include?(value)
+ end
+
+ # passes each pair of elements to the block in reversed order
+ acc = []
+ (1..5).min do |a, b|
+ acc << [a, b]
+ a
+ end
+ assert_equal [[2, 1], [3, 1], [4, 1], [5, 1]], acc
+
+ # returns the element the block determines to be the minimum
+ assert_equal 3, ((1..3).min { |_a, _b| -3 })
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, ((100..10).min { |x, y| x <=> y })
+ assert_equal nil, ((5...5).min { |x, y| x <=> y })
+end
diff --git a/mrbgems/mruby-rational/mrbgem.rake b/mrbgems/mruby-rational/mrbgem.rake
new file mode 100644
index 000000000..4b540dec4
--- /dev/null
+++ b/mrbgems/mruby-rational/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-rational') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Rational class'
+end
diff --git a/mrbgems/mruby-rational/mrblib/rational.rb b/mrbgems/mruby-rational/mrblib/rational.rb
new file mode 100644
index 000000000..93af72b96
--- /dev/null
+++ b/mrbgems/mruby-rational/mrblib/rational.rb
@@ -0,0 +1,116 @@
+class Rational < Numeric
+ def inspect
+ "(#{to_s})"
+ end
+
+ def to_s
+ "#{numerator}/#{denominator}"
+ end
+
+ def *(rhs)
+ if rhs.is_a? Rational
+ Rational(numerator * rhs.numerator, denominator * rhs.denominator)
+ elsif rhs.is_a? Integer
+ Rational(numerator * rhs, denominator)
+ elsif rhs.is_a? Numeric
+ numerator * rhs / denominator
+ end
+ end
+
+ def +(rhs)
+ if rhs.is_a? Rational
+ Rational(numerator * rhs.denominator + rhs.numerator * denominator, denominator * rhs.denominator)
+ elsif rhs.is_a? Integer
+ Rational(numerator + rhs * denominator, denominator)
+ elsif rhs.is_a? Numeric
+ (numerator + rhs * denominator) / denominator
+ end
+ end
+
+ def -(rhs)
+ if rhs.is_a? Rational
+ Rational(numerator * rhs.denominator - rhs.numerator * denominator, denominator * rhs.denominator)
+ elsif rhs.is_a? Integer
+ Rational(numerator - rhs * denominator, denominator)
+ elsif rhs.is_a? Numeric
+ (numerator - rhs * denominator) / denominator
+ end
+ end
+
+ def /(rhs)
+ if rhs.is_a? Rational
+ Rational(numerator * rhs.denominator, denominator * rhs.numerator)
+ elsif rhs.is_a? Integer
+ Rational(numerator, denominator * rhs)
+ elsif rhs.is_a? Numeric
+ numerator / rhs / denominator
+ end
+ end
+
+ def <=>(rhs)
+ if rhs.is_a?(Integral)
+ return numerator <=> rhs if denominator == 1
+ rhs = Rational(rhs)
+ end
+
+ case rhs
+ when Rational
+ (numerator * rhs.denominator - denominator * rhs.numerator) <=> 0
+ when Numeric
+ (rhs <=> self)&.-@
+ else
+ nil
+ end
+ end
+
+ def ==(rhs)
+ return true if self.equal?(rhs)
+ if rhs.is_a?(Integral) && denominator == 1
+ return numerator == rhs
+ end
+ if rhs.is_a?(Rational)
+ numerator * rhs.denominator == denominator * rhs.numerator
+ else
+ rhs == self
+ end
+ end
+end
+
+class Numeric
+ def to_r
+ Rational(self, 1)
+ end
+end
+
+module Kernel
+ def Rational(numerator, denominator = 1)
+ a = numerator
+ b = denominator
+ a, b = b, a % b until b == 0
+ Rational._new(numerator.div(a), denominator.div(a))
+ end
+end
+
+[:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op|
+ Fixnum.instance_eval do
+ original_operator_name = "__original_operator_#{op}_rational"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Rational
+ Rational(self).__send__(op, rhs)
+ else
+ __send__(original_operator_name, rhs)
+ end
+ end
+ end
+ Float.instance_eval do
+ original_operator_name = "__original_operator_#{op}_rational"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Rational
+ rhs = rhs.to_f
+ end
+ __send__(original_operator_name, rhs)
+ end
+ end if Object.const_defined?(:Float)
+end
diff --git a/mrbgems/mruby-rational/src/rational.c b/mrbgems/mruby-rational/src/rational.c
new file mode 100644
index 000000000..31471e934
--- /dev/null
+++ b/mrbgems/mruby-rational/src/rational.c
@@ -0,0 +1,206 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/numeric.h>
+
+struct mrb_rational {
+ mrb_int numerator;
+ mrb_int denominator;
+};
+
+#if MRB_INT_MAX <= INTPTR_MAX
+
+#define RATIONAL_USE_ISTRUCT
+/* use TT_ISTRUCT */
+#include <mruby/istruct.h>
+
+#define rational_ptr(mrb, v) (struct mrb_rational*)mrb_istruct_ptr(v)
+
+static struct RBasic*
+rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
+{
+ struct RIStruct *s;
+
+ s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
+ *p = (struct mrb_rational*)s->inline_data;
+
+ return (struct RBasic*)s;
+}
+
+#else
+/* use TT_DATA */
+#include <mruby/data.h>
+
+static const struct mrb_data_type mrb_rational_type = {"Rational", mrb_free};
+
+static struct RBasic*
+rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
+{
+ struct RData *d;
+
+ Data_Make_Struct(mrb, c, struct mrb_rational, &mrb_rational_type, *p, d);
+
+ return (struct RBasic*)d;
+}
+
+static struct mrb_rational*
+rational_ptr(mrb_state *mrb, mrb_value v)
+{
+ struct mrb_rational *p;
+
+ p = DATA_GET_PTR(mrb, v, &mrb_rational_type, struct mrb_rational);
+ if (!p) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized rational");
+ }
+ return p;
+}
+#endif
+
+static mrb_value
+rational_numerator(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ return mrb_fixnum_value(p->numerator);
+}
+
+static mrb_value
+rational_denominator(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ return mrb_fixnum_value(p->denominator);
+}
+
+static mrb_value
+rational_new(mrb_state *mrb, mrb_int numerator, mrb_int denominator)
+{
+ struct RClass *c = mrb_class_get(mrb, "Rational");
+ struct mrb_rational *p;
+ struct RBasic *rat = rational_alloc(mrb, c, &p);
+ p->numerator = numerator;
+ p->denominator = denominator;
+ MRB_SET_FROZEN_FLAG(rat);
+ return mrb_obj_value(rat);
+}
+
+static mrb_value
+rational_s_new(mrb_state *mrb, mrb_value self)
+{
+ mrb_int numerator, denominator;
+
+#ifdef MRB_WITHOUT_FLOAT
+ mrb_get_args(mrb, "ii", &numerator, &denominator);
+#else
+
+#define DROP_PRECISION(cond, num, denom) \
+ do { \
+ while (cond) { \
+ num /= 2; \
+ denom /= 2; \
+ } \
+ } while (0)
+
+ mrb_value numv, denomv;
+
+ mrb_get_args(mrb, "oo", &numv, &denomv);
+ if (mrb_fixnum_p(numv)) {
+ numerator = mrb_fixnum(numv);
+
+ if (mrb_fixnum_p(denomv)) {
+ denominator = mrb_fixnum(denomv);
+ }
+ else {
+ mrb_float denomf = mrb_to_flo(mrb, denomv);
+
+ DROP_PRECISION(denomf < MRB_INT_MIN || denomf > MRB_INT_MAX, numerator, denomf);
+ denominator = denomf;
+ }
+ }
+ else {
+ mrb_float numf = mrb_to_flo(mrb, numv);
+
+ if (mrb_fixnum_p(denomv)) {
+ denominator = mrb_fixnum(denomv);
+ }
+ else {
+ mrb_float denomf = mrb_to_flo(mrb, denomv);
+
+ DROP_PRECISION(denomf < MRB_INT_MIN || denomf > MRB_INT_MAX, numf, denomf);
+ denominator = denomf;
+ }
+
+ DROP_PRECISION(numf < MRB_INT_MIN || numf > MRB_INT_MAX, numf, denominator);
+ numerator = numf;
+ }
+#endif
+
+ return rational_new(mrb, numerator, denominator);
+}
+
+#ifndef MRB_WITHOUT_FLOAT
+static mrb_value
+rational_to_f(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ mrb_float f = (mrb_float)p->numerator / (mrb_float)p->denominator;
+
+ return mrb_float_value(mrb, f);
+}
+#endif
+
+static mrb_value
+rational_to_i(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ if (p->denominator == 0) {
+ mrb_raise(mrb, mrb_exc_get(mrb, "StandardError"), "divided by 0");
+ }
+ return mrb_fixnum_value(p->numerator / p->denominator);
+}
+
+static mrb_value
+rational_to_r(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+static mrb_value
+rational_negative_p(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ if (p->numerator < 0) {
+ return mrb_true_value();
+ }
+ return mrb_false_value();
+}
+
+static mrb_value
+fix_to_r(mrb_state *mrb, mrb_value self)
+{
+ return rational_new(mrb, mrb_fixnum(self), 1);
+}
+
+void mrb_mruby_rational_gem_init(mrb_state *mrb)
+{
+ struct RClass *rat;
+
+#ifdef COMPLEX_USE_RATIONAL
+ mrb_assert(sizeof(struct mrb_rational) < ISTRUCT_DATA_SIZE);
+#endif
+ rat = mrb_define_class(mrb, "Rational", mrb_class_get(mrb, "Numeric"));
+ mrb_undef_class_method(mrb, rat, "new");
+ mrb_define_class_method(mrb, rat, "_new", rational_s_new, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, rat, "numerator", rational_numerator, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "denominator", rational_denominator, MRB_ARGS_NONE());
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_define_method(mrb, rat, "to_f", rational_to_f, MRB_ARGS_NONE());
+#endif
+ mrb_define_method(mrb, rat, "to_i", rational_to_i, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "to_r", rational_to_r, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "negative?", rational_negative_p, MRB_ARGS_NONE());
+ mrb_define_method(mrb, mrb->fixnum_class, "to_r", fix_to_r, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_rational_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-rational/test/rational.rb b/mrbgems/mruby-rational/test/rational.rb
new file mode 100644
index 000000000..11737034b
--- /dev/null
+++ b/mrbgems/mruby-rational/test/rational.rb
@@ -0,0 +1,308 @@
+class UserDefinedNumeric < Numeric
+ def initialize(n)
+ @n = n
+ end
+
+ def <=>(rhs)
+ return nil unless rhs.respond_to?(:to_i)
+ rhs = rhs.to_i
+ rhs < 0 ? nil : @n <=> rhs
+ end
+
+ def inspect
+ "#{self.class}(#{@n})"
+ end
+end
+
+class ComplexLikeNumeric < UserDefinedNumeric
+ def ==(rhs)
+ @n == 0 && rhs == 0
+ end
+
+ undef <=>
+end
+
+def assert_rational(exp, real)
+ assert do
+ assert_float exp.numerator, real.numerator
+ assert_float exp.denominator, real.denominator
+ end
+end
+
+def assert_equal_rational(exp, o1, o2)
+ assert do
+ if exp
+ assert_operator(o1, :==, o2)
+ assert_not_operator(o1, :!=, o2)
+ else
+ assert_not_operator(o1, :==, o2)
+ assert_operator(o1, :!=, o2)
+ end
+ end
+end
+
+def assert_cmp(exp, o1, o2)
+ if exp == (o1 <=> o2)
+ pass
+ else
+ flunk "", " Expected #{o1.inspect} <=> #{o2.inspect} to be #{exp}."
+ end
+end
+
+assert 'Rational' do
+ r = 5r
+ assert_equal(Rational, r.class)
+ assert_equal([5, 1], [r.numerator, r.denominator])
+end
+
+assert 'Kernel#Rational' do
+ r = Rational(4,10)
+ assert_equal(2, r.numerator)
+ assert_equal(5, r.denominator)
+
+ r = Rational(3)
+ assert_equal(3, r.numerator)
+ assert_equal(1, r.denominator)
+
+ assert_raise(ArgumentError) { Rational() }
+ assert_raise(ArgumentError) { Rational(1,2,3) }
+end
+
+assert 'Rational#to_f' do
+ assert_float(2.0, Rational(2).to_f)
+ assert_float(2.25, Rational(9, 4).to_f)
+ assert_float(-0.75, Rational(-3, 4).to_f)
+ assert_float(6.666666666666667, Rational(20, 3).to_f)
+end
+
+assert 'Rational#to_i' do
+ assert_equal(0, Rational(2, 3).to_i)
+ assert_equal(3, Rational(3).to_i)
+ assert_equal(300, Rational(300.6).to_i)
+ assert_equal(1, Rational(98, 71).to_i)
+ assert_equal(-15, Rational(-30, 2).to_i)
+end
+
+assert 'Rational#*' do
+ assert_rational(Rational(4, 9), Rational(2, 3) * Rational(2, 3))
+ assert_rational(Rational(900, 1), Rational(900) * Rational(1))
+ assert_rational(Rational(1, 1), Rational(-2, 9) * Rational(-9, 2))
+ assert_rational(Rational(9, 2), Rational(9, 8) * 4)
+ assert_float( 21.77777777777778, Rational(20, 9) * 9.8)
+end
+
+assert 'Rational#+' do
+ assert_rational(Rational(4, 3), Rational(2, 3) + Rational(2, 3))
+ assert_rational(Rational(901, 1), Rational(900) + Rational(1))
+ assert_rational(Rational(-85, 18), Rational(-2, 9) + Rational(-9, 2))
+ assert_rational(Rational(41, 8), Rational(9, 8) + 4)
+ assert_float( 12.022222222222222, Rational(20, 9) + 9.8)
+end
+
+assert 'Rational#-' do
+ assert_rational(Rational(0, 1), Rational(2, 3) - Rational(2, 3))
+ assert_rational(Rational(899, 1), Rational(900) - Rational(1))
+ assert_rational(Rational(77, 18), Rational(-2, 9) - Rational(-9, 2))
+ assert_rational(Rational(-23, 8), Rational(9, 8) - 4)
+ assert_float( -7.577777777777778, Rational(20, 9) - 9.8)
+end
+
+assert 'Rational#/' do
+ assert_rational(Rational(1, 1), Rational(2, 3) / Rational(2, 3))
+ assert_rational(Rational(900, 1), Rational(900) / Rational(1))
+ assert_rational(Rational(4, 81), Rational(-2, 9) / Rational(-9, 2))
+ assert_rational(Rational(9, 32), Rational(9, 8) / 4)
+ assert_float( 0.22675736961451246, Rational(20, 9) / 9.8)
+end
+
+assert 'Rational#==, Rational#!=' do
+ assert_equal_rational(true, Rational(1,1), Rational(1))
+ assert_equal_rational(true, Rational(-1,1), -1r)
+ assert_equal_rational(true, Rational(13,4), 3.25)
+ assert_equal_rational(true, Rational(13,3.25), Rational(4,1))
+ assert_equal_rational(true, Rational(-3,-4), Rational(3,4))
+ assert_equal_rational(true, Rational(-4,5), Rational(4,-5))
+ assert_equal_rational(true, Rational(4,2), 2)
+ assert_equal_rational(true, Rational(-4,2), -2)
+ assert_equal_rational(true, Rational(4,-2), -2)
+ assert_equal_rational(true, Rational(4,2), 2.0)
+ assert_equal_rational(true, Rational(-4,2), -2.0)
+ assert_equal_rational(true, Rational(4,-2), -2.0)
+ assert_equal_rational(true, Rational(8,6), Rational(4,3))
+ assert_equal_rational(false, Rational(13,4), 3)
+ assert_equal_rational(false, Rational(13,4), 3.3)
+ assert_equal_rational(false, Rational(2,1), 1r)
+ assert_equal_rational(false, Rational(1), nil)
+ assert_equal_rational(false, Rational(1), '')
+ assert_equal_rational(true, 0r, UserDefinedNumeric.new(0))
+ assert_equal_rational(true, 1r, UserDefinedNumeric.new(1))
+ assert_equal_rational(false, 1r, UserDefinedNumeric.new(2))
+ assert_equal_rational(false, -1r, UserDefinedNumeric.new(-1))
+ assert_equal_rational(true, 0r, ComplexLikeNumeric.new(0))
+ assert_equal_rational(false, 1r, ComplexLikeNumeric.new(1))
+ assert_equal_rational(false, 1r, ComplexLikeNumeric.new(2))
+end
+
+assert 'Fixnum#==(Rational), Fixnum#!=(Rational)' do
+ assert_equal_rational(true, 2, Rational(4,2))
+ assert_equal_rational(true, -2, Rational(-4,2))
+ assert_equal_rational(true, -2, Rational(4,-2))
+ assert_equal_rational(false, 3, Rational(13,4))
+end
+
+assert 'Float#==(Rational), Float#!=(Rational)' do
+ assert_equal_rational(true, 2.0, Rational(4,2))
+ assert_equal_rational(true, -2.0, Rational(-4,2))
+ assert_equal_rational(true, -2.0, Rational(4,-2))
+ assert_equal_rational(false, 3.3, Rational(13,4))
+end
+
+assert 'Rational#<=>' do
+ assert_cmp(-1, Rational(-1), Rational(0))
+ assert_cmp(0, Rational(0), Rational(0))
+ assert_cmp(1, Rational(1), Rational(0))
+ assert_cmp(-1, Rational(-1), 0)
+ assert_cmp(0, Rational(0), 0)
+ assert_cmp(1, Rational(1), 0)
+ assert_cmp(-1, Rational(-1), 0.0)
+ assert_cmp(0, Rational(0), 0.0)
+ assert_cmp(1, Rational(1), 0.0)
+ assert_cmp(-1, Rational(1,2), Rational(2,3))
+ assert_cmp(0, Rational(2,3), Rational(2,3))
+ assert_cmp(1, Rational(2,3), Rational(1,2))
+ assert_cmp(1, Rational(2,3), Rational(1,2))
+ assert_cmp(1, Rational(0), Rational(-1))
+ assert_cmp(-1, Rational(0), Rational(1))
+ assert_cmp(1, Rational(2,3), Rational(1,2))
+ assert_cmp(0, Rational(2,3), Rational(2,3))
+ assert_cmp(-1, Rational(1,2), Rational(2,3))
+ assert_cmp(-1, Rational(1,2), Rational(2,3))
+ assert_cmp(nil, 3r, "3")
+ assert_cmp(1, 3r, UserDefinedNumeric.new(2))
+ assert_cmp(0, 3r, UserDefinedNumeric.new(3))
+ assert_cmp(-1, 3r, UserDefinedNumeric.new(4))
+ assert_cmp(nil, Rational(-3), UserDefinedNumeric.new(5))
+ assert_raise(NoMethodError) { 0r <=> ComplexLikeNumeric.new(0) }
+ assert_raise(NoMethodError) { 1r <=> ComplexLikeNumeric.new(2) }
+end
+
+assert 'Fixnum#<=>(Rational)' do
+ assert_cmp(-1, -2, Rational(-9,5))
+ assert_cmp(0, 5, 5r)
+ assert_cmp(1, 3, Rational(8,3))
+end
+
+assert 'Float#<=>(Rational)' do
+ assert_cmp(-1, -2.1, Rational(-9,5))
+ assert_cmp(0, 5.0, 5r)
+ assert_cmp(1, 2.7, Rational(8,3))
+end
+
+assert 'Rational#<' do
+ assert_operator(Rational(1,2), :<, Rational(2,3))
+ assert_not_operator(Rational(2,3), :<, Rational(2,3))
+ assert_operator(Rational(2,3), :<, 1)
+ assert_not_operator(2r, :<, 2)
+ assert_not_operator(Rational(2,3), :<, -3)
+ assert_operator(Rational(-4,3), :<, -0.3)
+ assert_not_operator(Rational(13,4), :<, 3.25)
+ assert_not_operator(Rational(2,3), :<, 0.6)
+ assert_raise(ArgumentError) { 1r < "2" }
+end
+
+assert 'Fixnum#<(Rational)' do
+ assert_not_operator(1, :<, Rational(2,3))
+ assert_not_operator(2, :<, 2r)
+ assert_operator(-3, :<, Rational(2,3))
+end
+
+assert 'Float#<(Rational)' do
+ assert_not_operator(-0.3, :<, Rational(-4,3))
+ assert_not_operator(3.25, :<, Rational(13,4))
+ assert_operator(0.6, :<, Rational(2,3))
+end
+
+assert 'Rational#<=' do
+ assert_operator(Rational(1,2), :<=, Rational(2,3))
+ assert_operator(Rational(2,3), :<=, Rational(2,3))
+ assert_operator(Rational(2,3), :<=, 1)
+ assert_operator(2r, :<=, 2)
+ assert_not_operator(Rational(2,3), :<=, -3)
+ assert_operator(Rational(-4,3), :<=, -0.3)
+ assert_operator(Rational(13,4), :<=, 3.25)
+ assert_not_operator(Rational(2,3), :<=, 0.6)
+ assert_raise(ArgumentError) { 1r <= "2" }
+end
+
+assert 'Fixnum#<=(Rational)' do
+ assert_not_operator(1, :<=, Rational(2,3))
+ assert_operator(2, :<=, 2r)
+ assert_operator(-3, :<=, Rational(2,3))
+end
+
+assert 'Float#<=(Rational)' do
+ assert_not_operator(-0.3, :<=, Rational(-4,3))
+ assert_operator(3.25, :<=, Rational(13,4))
+ assert_operator(0.6, :<=, Rational(2,3))
+end
+
+assert 'Rational#>' do
+ assert_not_operator(Rational(1,2), :>, Rational(2,3))
+ assert_not_operator(Rational(2,3), :>, Rational(2,3))
+ assert_not_operator(Rational(2,3), :>, 1)
+ assert_not_operator(2r, :>, 2)
+ assert_operator(Rational(2,3), :>, -3)
+ assert_not_operator(Rational(-4,3), :>, -0.3)
+ assert_not_operator(Rational(13,4), :>, 3.25)
+ assert_operator(Rational(2,3), :>, 0.6)
+ assert_raise(ArgumentError) { 1r > "2" }
+end
+
+assert 'Fixnum#>(Rational)' do
+ assert_operator(1, :>, Rational(2,3))
+ assert_not_operator(2, :>, 2r)
+ assert_not_operator(-3, :>, Rational(2,3))
+end
+
+assert 'Float#>(Rational)' do
+ assert_operator(-0.3, :>, Rational(-4,3))
+ assert_not_operator(3.25, :>, Rational(13,4))
+ assert_not_operator(0.6, :>, Rational(2,3))
+end
+
+assert 'Rational#>=' do
+ assert_not_operator(Rational(1,2), :>=, Rational(2,3))
+ assert_operator(Rational(2,3), :>=, Rational(2,3))
+ assert_not_operator(Rational(2,3), :>=, 1)
+ assert_operator(2r, :>=, 2)
+ assert_operator(Rational(2,3), :>=, -3)
+ assert_not_operator(Rational(-4,3), :>=, -0.3)
+ assert_operator(Rational(13,4), :>=, 3.25)
+ assert_operator(Rational(2,3), :>=, 0.6)
+ assert_raise(ArgumentError) { 1r >= "2" }
+end
+
+assert 'Fixnum#>=(Rational)' do
+ assert_operator(1, :>=, Rational(2,3))
+ assert_operator(2, :>=, 2r)
+ assert_not_operator(-3, :>=, Rational(2,3))
+end
+
+assert 'Float#>=(Rational)' do
+ assert_operator(-0.3, :>=, Rational(-4,3))
+ assert_operator(3.25, :>=, Rational(13,4))
+ assert_not_operator(0.6, :>=, Rational(2,3))
+end
+
+assert 'Rational#negative?' do
+ assert_predicate(Rational(-2,3), :negative?)
+ assert_predicate(Rational(2,-3), :negative?)
+ assert_not_predicate(Rational(2,3), :negative?)
+ assert_not_predicate(Rational(0), :negative?)
+end
+
+assert 'Rational#frozen?' do
+ assert_predicate(1r, :frozen?)
+ assert_predicate(Rational(2,3), :frozen?)
+ assert_predicate(4/5r, :frozen?)
+end
diff --git a/mrbgems/mruby-sleep/.gitignore b/mrbgems/mruby-sleep/.gitignore
new file mode 100644
index 000000000..b9f9e71df
--- /dev/null
+++ b/mrbgems/mruby-sleep/.gitignore
@@ -0,0 +1,4 @@
+/mruby
+
+*.so
+*.a \ No newline at end of file
diff --git a/mrbgems/mruby-sleep/.travis.yml b/mrbgems/mruby-sleep/.travis.yml
new file mode 100644
index 000000000..ad6b007b6
--- /dev/null
+++ b/mrbgems/mruby-sleep/.travis.yml
@@ -0,0 +1,29 @@
+dist: trusty
+
+language: c
+compiler:
+ - gcc
+ - clang
+env:
+ - MRUBY_VERSION=1.2.0
+ - MRUBY_VERSION=master
+matrix:
+ allow_failures:
+ - env: MRUBY_VERSION=master
+branches:
+ only:
+ - master
+addons:
+ apt:
+ packages:
+ - rake
+ - bison
+ - git
+ - gperf
+ # - aclocal
+ # - automake
+ # - autoconf
+ # - autotools-dev
+
+script:
+ - rake test \ No newline at end of file
diff --git a/mrbgems/mruby-sleep/.travis_build_config.rb b/mrbgems/mruby-sleep/.travis_build_config.rb
new file mode 100644
index 000000000..b32e38a9d
--- /dev/null
+++ b/mrbgems/mruby-sleep/.travis_build_config.rb
@@ -0,0 +1,6 @@
+MRuby::Build.new do |conf|
+ toolchain :gcc
+ conf.gembox 'default'
+ conf.gem '../mruby-sleep'
+ conf.enable_test
+end
diff --git a/mrbgems/mruby-sleep/README.md b/mrbgems/mruby-sleep/README.md
new file mode 100644
index 000000000..7707cd040
--- /dev/null
+++ b/mrbgems/mruby-sleep/README.md
@@ -0,0 +1,27 @@
+# Sleep Module for mruby
+mruby sleep module
+
+## install by mrbgems
+ - add conf.gem line to `build_config.rb`
+```ruby
+MRuby::Build.new do |conf|
+
+ # ... (snip) ...
+
+ conf.gem :core => 'mruby-sleep'
+end
+```
+
+## example
+
+```ruby
+sleep(10)
+usleep(10000)
+```
+
+# License
+under the MIT License:
+
+* http://www.opensource.org/licenses/mit-license.php
+
+
diff --git a/mrbgems/mruby-sleep/Rakefile b/mrbgems/mruby-sleep/Rakefile
new file mode 100644
index 000000000..5e3c46173
--- /dev/null
+++ b/mrbgems/mruby-sleep/Rakefile
@@ -0,0 +1,29 @@
+MRUBY_CONFIG=File.expand_path(ENV["MRUBY_CONFIG"] || ".travis_build_config.rb")
+MRUBY_VERSION=ENV["MRUBY_VERSION"] || "1.2.0"
+
+file :mruby do
+ cmd = "git clone --depth=1 git://github.com/mruby/mruby.git"
+ if MRUBY_VERSION != 'master'
+ cmd << " && cd mruby"
+ cmd << " && git fetch --tags && git checkout $(git rev-parse #{MRUBY_VERSION})"
+ end
+ sh cmd
+end
+
+desc "compile binary"
+task :compile => :mruby do
+ sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all"
+end
+
+desc "test"
+task :test => :mruby do
+ sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all test"
+end
+
+desc "cleanup"
+task :clean do
+ exit 0 unless File.directory?('mruby')
+ sh "cd mruby && rake deep_clean"
+end
+
+task :default => :compile
diff --git a/mrbgems/mruby-sleep/example/sleep.rb b/mrbgems/mruby-sleep/example/sleep.rb
new file mode 100644
index 000000000..e5acea3b2
--- /dev/null
+++ b/mrbgems/mruby-sleep/example/sleep.rb
@@ -0,0 +1,3 @@
+sleep(10)
+usleep(10000)
+
diff --git a/mrbgems/mruby-sleep/mrbgem.rake b/mrbgems/mruby-sleep/mrbgem.rake
new file mode 100644
index 000000000..8827b3580
--- /dev/null
+++ b/mrbgems/mruby-sleep/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-sleep') do |spec|
+ spec.license = 'MIT'
+ spec.authors = 'MATSUMOTO Ryosuke'
+ spec.version = '0.0.1'
+end
diff --git a/mrbgems/mruby-sleep/src/mrb_sleep.c b/mrbgems/mruby-sleep/src/mrb_sleep.c
new file mode 100644
index 000000000..3f8ef90cf
--- /dev/null
+++ b/mrbgems/mruby-sleep/src/mrb_sleep.c
@@ -0,0 +1,135 @@
+/*
+** mrb_sleep - sleep methods for mruby
+**
+** Copyright (c) mod_mruby developers 2012-
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
+*/
+
+#include <time.h>
+#ifdef _WIN32
+ #include <windows.h>
+ #define sleep(x) Sleep(x * 1000)
+ #define usleep(x) Sleep((DWORD)((x)<1000) ? 1 : ((x)/1000))
+#else
+ #include <unistd.h>
+ #include <sys/time.h>
+#endif
+
+#include "mruby.h"
+
+/* not implemented forever sleep (called without an argument)*/
+static mrb_value
+mrb_f_sleep(mrb_state *mrb, mrb_value self)
+{
+ time_t beg = time(0);
+ time_t end;
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_float sec;
+
+ mrb_get_args(mrb, "f", &sec);
+ if (sec >= 0) {
+ usleep(sec * 1000000);
+ }
+ else {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative");
+ }
+#else
+ mrb_int sec;
+
+ mrb_get_args(mrb, "i", &sec);
+ if (sec >= 0) {
+ sleep(sec);
+ } else {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative");
+ }
+#endif
+ end = time(0) - beg;
+
+ return mrb_fixnum_value(end);
+}
+
+/* mruby special; needed for mruby without float numbers */
+static mrb_value
+mrb_f_usleep(mrb_state *mrb, mrb_value self)
+{
+ mrb_int usec;
+#ifdef _WIN32
+ FILETIME st_ft,ed_ft;
+ unsigned __int64 st_time = 0;
+ unsigned __int64 ed_time = 0;
+#else
+ struct timeval st_tm,ed_tm;
+#endif
+ time_t slp_tm;
+
+#ifdef _WIN32
+ GetSystemTimeAsFileTime(&st_ft);
+#else
+ gettimeofday(&st_tm, NULL);
+#endif
+
+ /* not implemented forever sleep (called without an argument)*/
+ mrb_get_args(mrb, "i", &usec);
+
+ if (usec >= 0) {
+ usleep(usec);
+ } else {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative integer");
+ }
+
+#ifdef _WIN32
+ GetSystemTimeAsFileTime(&ed_ft);
+
+ st_time |= st_ft.dwHighDateTime;
+ st_time <<=32;
+ st_time |= st_ft.dwLowDateTime;
+ ed_time |= ed_ft.dwHighDateTime;
+ ed_time <<=32;
+ ed_time |= ed_ft.dwLowDateTime;
+
+ slp_tm = (ed_time - st_time) / 10;
+#else
+ gettimeofday(&ed_tm, NULL);
+
+ if (st_tm.tv_usec > ed_tm.tv_usec) {
+ slp_tm = 1000000 + ed_tm.tv_usec - st_tm.tv_usec;
+ }
+ else {
+ slp_tm = ed_tm.tv_usec - st_tm.tv_usec;
+ }
+#endif
+
+ return mrb_fixnum_value(slp_tm);
+}
+
+void
+mrb_mruby_sleep_gem_init(mrb_state *mrb)
+{
+ mrb_define_method(mrb, mrb->kernel_module, "sleep", mrb_f_sleep, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mrb->kernel_module, "usleep", mrb_f_usleep, MRB_ARGS_REQ(1));
+}
+
+void
+mrb_mruby_sleep_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-sleep/test/sleep_test.rb b/mrbgems/mruby-sleep/test/sleep_test.rb
new file mode 100644
index 000000000..f05b7a30b
--- /dev/null
+++ b/mrbgems/mruby-sleep/test/sleep_test.rb
@@ -0,0 +1,29 @@
+assert("sleep works") do
+ assert_nothing_raised { sleep(1) }
+ assert_nothing_raised { sleep(0) }
+end
+
+assert("sleep would accept non-negative float value") do
+ skip unless Object.const_defined?(:Float)
+ assert_nothing_raised { sleep(0.01) }
+ assert_nothing_raised { sleep(0.0) }
+ assert_nothing_raised { sleep(-0.0) }
+end
+
+assert("sleep would not accept negative integer value") do
+ assert_raise(ArgumentError) { sleep(-1) }
+end
+
+assert("sleep would not accept negative float value") do
+ skip unless Object.const_defined?(:Float)
+ assert_raise(ArgumentError) { sleep(-0.1) }
+end
+
+assert("usleep works") do
+ assert_nothing_raised { usleep(100) }
+ assert_nothing_raised { usleep(0) }
+end
+
+assert("usleep would not accept negative value") do
+ assert_raise(ArgumentError) { usleep(-100) }
+end
diff --git a/mrbgems/mruby-socket/mrbgem.rake b/mrbgems/mruby-socket/mrbgem.rake
index 8096815eb..b0894e095 100644
--- a/mrbgems/mruby-socket/mrbgem.rake
+++ b/mrbgems/mruby-socket/mrbgem.rake
@@ -4,6 +4,7 @@ MRuby::Gem::Specification.new('mruby-socket') do |spec|
spec.summary = 'standard socket class'
spec.cc.include_paths << "#{build.root}/src"
+ #spec.cc.defines << "HAVE_SA_LEN=0"
# If Windows, use winsock
if ( /mswin|mingw|win32/ =~ RUBY_PLATFORM ) then
diff --git a/mrbgems/mruby-socket/src/socket.c b/mrbgems/mruby-socket/src/socket.c
index 4d56d5374..9b06274dc 100644
--- a/mrbgems/mruby-socket/src/socket.c
+++ b/mrbgems/mruby-socket/src/socket.c
@@ -10,6 +10,7 @@
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
+ #include <winerror.h>
#define SHUT_RDWR SD_BOTH
#ifndef _SSIZE_T_DEFINED
@@ -19,6 +20,7 @@
#else
#include <sys/types.h>
#include <sys/socket.h>
+ #include <sys/param.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@@ -36,12 +38,21 @@
#include "mruby/array.h"
#include "mruby/class.h"
#include "mruby/data.h"
+#include "mruby/numeric.h"
#include "mruby/string.h"
#include "mruby/variable.h"
-#include "error.h"
+#include "mruby/error.h"
#include "mruby/ext/io.h"
+#if !defined(HAVE_SA_LEN)
+#if (defined(BSD) && (BSD >= 199006))
+#define HAVE_SA_LEN 1
+#else
+#define HAVE_SA_LEN 0
+#endif
+#endif
+
#define E_SOCKET_ERROR (mrb_class_get(mrb, "SocketError"))
#if !defined(mrb_cptr)
@@ -51,7 +62,7 @@
#endif
#ifdef _WIN32
-const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
+static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
{
if (af == AF_INET)
{
@@ -76,7 +87,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
return NULL;
}
-int inet_pton(int af, const char *src, void *dst)
+static int inet_pton(int af, const char *src, void *dst)
{
struct addrinfo hints, *res, *ressave;
@@ -120,7 +131,7 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "oo|oooi", &nodename, &service, &family, &socktype, &protocol, &flags);
if (mrb_string_p(nodename)) {
- hostname = mrb_str_to_cstr(mrb, nodename);
+ hostname = mrb_string_value_cstr(mrb, &nodename);
} else if (mrb_nil_p(nodename)) {
hostname = NULL;
} else {
@@ -128,9 +139,9 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
}
if (mrb_string_p(service)) {
- servname = mrb_str_to_cstr(mrb, service);
+ servname = mrb_string_value_cstr(mrb, &service);
} else if (mrb_fixnum_p(service)) {
- servname = mrb_str_to_cstr(mrb, mrb_funcall(mrb, service, "to_s", 0));
+ servname = RSTRING_PTR(mrb_fixnum_to_str(mrb, service, 10));
} else if (mrb_nil_p(service)) {
servname = NULL;
} else {
@@ -194,8 +205,8 @@ mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self)
mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr");
}
error = getnameinfo((struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr), RSTRING_PTR(host), NI_MAXHOST, RSTRING_PTR(serv), NI_MAXSERV, (int)flags);
- if (error != 0) {
- mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %s", gai_strerror(error));
+ if (error) {
+ mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %S", mrb_str_new_cstr(mrb, gai_strerror(error)));
}
ary = mrb_ary_new_capa(mrb, 2);
mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host)));
@@ -214,7 +225,11 @@ mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self)
sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr"));
if (((struct sockaddr *)RSTRING_PTR(sastr))->sa_family != AF_UNIX)
mrb_raise(mrb, E_SOCKET_ERROR, "need AF_UNIX address");
- return mrb_str_new_cstr(mrb, ((struct sockaddr_un *)RSTRING_PTR(sastr))->sun_path);
+ if (RSTRING_LEN(sastr) < (mrb_int)offsetof(struct sockaddr_un, sun_path) + 1) {
+ return mrb_str_new(mrb, "", 0);
+ } else {
+ return mrb_str_new_cstr(mrb, ((struct sockaddr_un *)RSTRING_PTR(sastr))->sun_path);
+ }
}
#endif
@@ -287,7 +302,7 @@ mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self)
mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)egid));
return ary;
#else
- mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not avaialble on this system");
+ mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not available on this system");
return mrb_nil_value();
#endif
}
@@ -456,12 +471,12 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
}
} else if (argc == 1) {
if (strcmp(mrb_obj_classname(mrb, so), "Socket::Option") != 0)
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option");
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option");
level = mrb_fixnum(mrb_funcall(mrb, so, "level", 0));
optname = mrb_fixnum(mrb_funcall(mrb, so, "optname", 0));
optval = mrb_funcall(mrb, so, "data", 0);
} else {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%d for 3)", argc);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 3)", mrb_fixnum_value(argc));
}
s = socket_fd(mrb, self);
@@ -664,19 +679,15 @@ mrb_socket_listen(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_socket_sockaddr_family(mrb_state *mrb, mrb_value klass)
{
- mrb_value sa;
+ const struct sockaddr *sa;
+ mrb_value str;
- mrb_get_args(mrb, "S", &sa);
-#ifdef __linux__
- if ((size_t)RSTRING_LEN(sa) < offsetof(struct sockaddr, sa_family) + sizeof(sa_family_t)) {
- mrb_raisef(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
- }
-#else
- if ((size_t)RSTRING_LEN(sa) < sizeof(struct sockaddr)) {
- mrb_raisef(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
+ mrb_get_args(mrb, "S", &str);
+ if ((size_t)RSTRING_LEN(str) < offsetof(struct sockaddr, sa_family) + sizeof(sa->sa_family)) {
+ mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
}
-#endif
- return mrb_fixnum_value(((struct sockaddr *)RSTRING_PTR(sa))->sa_family);
+ sa = (const struct sockaddr *)RSTRING_PTR(str);
+ return mrb_fixnum_value(sa->sa_family);
}
static mrb_value
@@ -691,10 +702,13 @@ mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "S", &path);
if ((size_t)RSTRING_LEN(path) > sizeof(sunp->sun_path) - 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %ubytes)", (unsigned int)sizeof(sunp->sun_path) - 1);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %S bytes)", mrb_fixnum_value(sizeof(sunp->sun_path) - 1));
}
s = mrb_str_buf_new(mrb, sizeof(struct sockaddr_un));
sunp = (struct sockaddr_un *)RSTRING_PTR(s);
+#if HAVE_SA_LEN
+ sunp->sun_len = sizeof(struct sockaddr_un);
+#endif
sunp->sun_family = AF_UNIX;
memcpy(sunp->sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
sunp->sun_path[RSTRING_LEN(path)] = '\0';
diff --git a/mrbgems/mruby-socket/test/addrinfo.rb b/mrbgems/mruby-socket/test/addrinfo.rb
index 3ae46cd7b..491656179 100644
--- a/mrbgems/mruby-socket/test/addrinfo.rb
+++ b/mrbgems/mruby-socket/test/addrinfo.rb
@@ -7,7 +7,7 @@ assert('super class of Addrinfo') do
end
assert('Addrinfo.getaddrinfo') do
- ary = Addrinfo.getaddrinfo("localhost", "domain", Socket::AF_INET, Socket::SOCK_STREAM)
+ ary = Addrinfo.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_STREAM)
assert_true(ary.size >= 1)
ai = ary[0]
assert_equal(ai.afamily, Socket::AF_INET)
@@ -19,9 +19,9 @@ end
assert('Addrinfo.foreach') do
# assume Addrinfo.getaddrinfo works well
- a = Addrinfo.getaddrinfo("localhost", "domain")
+ a = Addrinfo.getaddrinfo("localhost", 80)
b = []
- Addrinfo.foreach("localhost", "domain") { |ai| b << ai }
+ Addrinfo.foreach("localhost", 80) { |ai| b << ai }
assert_equal(a.size, b.size)
end
@@ -35,7 +35,7 @@ assert('Addrinfo.ip') do
end
assert('Addrinfo.tcp') do
- ai = Addrinfo.tcp('127.0.0.1', 'smtp')
+ ai = Addrinfo.tcp('127.0.0.1', 25)
assert_equal('127.0.0.1', ai.ip_address)
assert_equal(Socket::AF_INET, ai.afamily)
assert_equal(25, ai.ip_port)
@@ -44,7 +44,7 @@ assert('Addrinfo.tcp') do
end
assert('Addrinfo.udp') do
- ai = Addrinfo.udp('127.0.0.1', 'domain')
+ ai = Addrinfo.udp('127.0.0.1', 53)
assert_equal('127.0.0.1', ai.ip_address)
assert_equal(Socket::AF_INET, ai.afamily)
assert_equal(53, ai.ip_port)
diff --git a/mrbgems/mruby-socket/test/socket.rb b/mrbgems/mruby-socket/test/socket.rb
index aa893588f..b64a67919 100644
--- a/mrbgems/mruby-socket/test/socket.rb
+++ b/mrbgems/mruby-socket/test/socket.rb
@@ -5,7 +5,7 @@ assert('Socket.gethostname') do
end
assert('Socket::getaddrinfo') do
- ret = Socket.getaddrinfo("localhost", "domain", Socket::AF_INET, Socket::SOCK_DGRAM)
+ ret = Socket.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_DGRAM)
assert_true ret.size >= 1
a = ret[0]
assert_equal "AF_INET", a[0]
@@ -28,7 +28,7 @@ assert('Socket#recvfrom') do
rstr, ai = s.recvfrom sstr.size
assert_equal sstr, rstr
- assert_true "127.0.0.1", ai.ip_address
+ assert_equal "127.0.0.1", ai.ip_address
ensure
s.close rescue nil
c.close rescue nil
diff --git a/mrbgems/mruby-socket/test/sockettest.c b/mrbgems/mruby-socket/test/sockettest.c
index 086bc4892..3017c7cc1 100644
--- a/mrbgems/mruby-socket/test/sockettest.c
+++ b/mrbgems/mruby-socket/test/sockettest.c
@@ -12,6 +12,7 @@
#include <fcntl.h>
#include <sys/stat.h>
+#define open _open
#define close _close
#define unlink _unlink
diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c
index 7eea1a1f3..985ffe276 100644
--- a/mrbgems/mruby-sprintf/src/sprintf.c
+++ b/mrbgems/mruby-sprintf/src/sprintf.c
@@ -119,13 +119,11 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
#define FPREC0 128
#define CHECK(l) do {\
-/* int cr = ENC_CODERANGE(result);*/\
while ((l) >= bsiz - blen) {\
+ if (bsiz > MRB_INT_MAX/2) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
bsiz*=2;\
- if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
}\
mrb_str_resize(mrb, result, bsiz);\
-/* ENC_CODERANGE_SET(result, cr);*/\
buf = RSTRING_PTR(result);\
} while (0)
@@ -202,11 +200,10 @@ check_name_arg(mrb_state *mrb, int posarg, const char *name, mrb_int len)
#define GETNUM(n, val) \
for (; p < end && ISDIGIT(*p); p++) {\
- mrb_int next_n = 10 * n + (*p - '0'); \
- if (next_n / 10 != n) {\
+ if (n > (MRB_INT_MAX - (*p - '0'))/10) {\
mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \
} \
- n = next_n; \
+ n = 10 * n + (*p - '0'); \
} \
if (p >= end) { \
mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed format string - %*[0-9]"); \
@@ -236,7 +233,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, mrb_int argc, const mrb_value *argv)
if (argc != 2) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
}
- tmp = mrb_check_convert_type(mrb, argv[1], MRB_TT_HASH, "Hash", "to_hash");
+ tmp = mrb_check_hash_type(mrb, argv[1]);
if (mrb_nil_p(tmp)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
}
@@ -555,7 +552,7 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm
++argc;
--argv;
- fmt = mrb_str_to_str(mrb, fmt);
+ mrb_to_str(mrb, fmt);
p = RSTRING_PTR(fmt);
end = p + RSTRING_LEN(fmt);
blen = 0;
@@ -1059,18 +1056,22 @@ retry:
if (i > 0)
need = BIT_DIGITS(i);
}
+ if (need > MRB_INT_MAX - ((flags&FPREC) ? prec : 6)) {
+ too_big_width:
+ mrb_raise(mrb, E_ARGUMENT_ERROR,
+ (width > prec ? "width too big" : "prec too big"));
+ }
need += (flags&FPREC) ? prec : 6;
if ((flags&FWIDTH) && need < width)
need = width;
- need += 20;
- if (need <= 0) {
- mrb_raise(mrb, E_ARGUMENT_ERROR,
- (width > prec ? "width too big" : "prec too big"));
+ if (need > MRB_INT_MAX - 20) {
+ goto too_big_width;
}
+ need += 20;
CHECK(need);
n = snprintf(&buf[blen], need, fbuf, fval);
- if (n < 0) {
+ if (n < 0 || n >= need) {
mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error");
}
blen += n;
diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb
index a5fd4e638..137812ae7 100644
--- a/mrbgems/mruby-sprintf/test/sprintf.rb
+++ b/mrbgems/mruby-sprintf/test/sprintf.rb
@@ -4,12 +4,14 @@
assert('String#%') do
assert_equal "one=1", "one=%d" % 1
assert_equal "1 one", "%d %s" % [ 1, "one" ]
- assert_equal "1.0", "%3.1f" % 1.01 if class_defined?("Float")
assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" }
assert_equal 15, ("%b" % (1<<14)).size
+ skip unless Object.const_defined?(:Float)
+ assert_equal "1.0", "%3.1f" % 1.01
end
assert('String#% with inf') do
+ skip unless Object.const_defined?(:Float)
inf = Float::INFINITY
assert_equal "Inf", "%f" % inf
@@ -35,9 +37,10 @@ assert('String#% with inf') do
assert_equal " Inf", "% 3f" % inf
assert_equal " Inf", "% 4f" % inf
assert_equal " Inf", "% 5f" % inf
-end if class_defined?("Float")
+end
assert('String#% with nan') do
+ skip unless Object.const_defined?(:Float)
nan = Float::NAN
assert_equal "NaN", "%f" % nan
@@ -63,7 +66,7 @@ assert('String#% with nan') do
assert_equal " NaN", "% 3f" % nan
assert_equal " NaN", "% 4f" % nan
assert_equal " NaN", "% 5f" % nan
-end if class_defined?("Float")
+end
assert("String#% with invalid chr") do
begin
diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb
index 27ca30610..fdaf2f960 100644
--- a/mrbgems/mruby-string-ext/mrblib/string.rb
+++ b/mrbgems/mruby-string-ext/mrblib/string.rb
@@ -1,25 +1,6 @@
class String
##
- # call-seq:
- # String.try_convert(obj) -> string or nil
- #
- # Try to convert <i>obj</i> into a String, using to_str method.
- # Returns converted string or nil if <i>obj</i> cannot be converted
- # for any reason.
- #
- # String.try_convert("str") #=> "str"
- # String.try_convert(/re/) #=> nil
- #
- def self.try_convert(obj)
- if obj.respond_to?(:to_str)
- obj.to_str
- else
- nil
- end
- end
-
- ##
# call-seq:
# string.clear -> string
#
@@ -112,7 +93,7 @@ class String
# "hello".rstrip! #=> nil
#
def rstrip!
- raise RuntimeError, "can't modify frozen String" if frozen?
+ raise FrozenError, "can't modify frozen String" if frozen?
s = self.rstrip
(s == self) ? nil : self.replace(s)
end
@@ -142,7 +123,7 @@ class String
# "abcdef".casecmp("ABCDEF") #=> 0
#
def casecmp(str)
- self.downcase <=> str.to_str.downcase
+ self.downcase <=> str.__to_str.downcase
rescue NoMethodError
nil
end
@@ -329,11 +310,15 @@ class String
end
end
+ ##
+ # Call the given block for each character of
+ # +self+.
def each_char(&block)
return to_enum :each_char unless block
-
- split('').each do |i|
- block.call(i)
+ pos = 0
+ while pos < self.size
+ block.call(self[pos])
+ pos += 1
end
self
end
diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c
index 4cab49094..50a4e5582 100644
--- a/mrbgems/mruby-string-ext/src/string.c
+++ b/mrbgems/mruby-string-ext/src/string.c
@@ -29,7 +29,7 @@ mrb_str_setbyte(mrb_state *mrb, mrb_value str)
len = RSTRING_LEN(str);
if (pos < -len || len <= pos)
- mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos));
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of string", mrb_fixnum_value(pos));
if (pos < 0)
pos += len;
@@ -42,44 +42,32 @@ mrb_str_setbyte(mrb_state *mrb, mrb_value str)
static mrb_value
mrb_str_byteslice(mrb_state *mrb, mrb_value str)
{
- mrb_value a1;
- mrb_int len;
+ mrb_value a1, a2;
+ mrb_int str_len = RSTRING_LEN(str), beg, len;
+ mrb_bool empty = TRUE;
- if (mrb_get_argc(mrb) == 2) {
- mrb_int pos;
- mrb_get_args(mrb, "ii", &pos, &len);
- return mrb_str_substr(mrb, str, pos, len);
+ if (mrb_get_args(mrb, "o|o", &a1, &a2) == 2) {
+ beg = mrb_fixnum(mrb_to_int(mrb, a1));
+ len = mrb_fixnum(mrb_to_int(mrb, a2));
+ goto subseq;
}
- mrb_get_args(mrb, "o|i", &a1, &len);
- switch (mrb_type(a1)) {
- case MRB_TT_RANGE:
- {
- mrb_int beg;
-
- len = RSTRING_LEN(str);
- switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) {
- case 0: /* not range */
- break;
- case 1: /* range */
- return mrb_str_substr(mrb, str, beg, len);
- case 2: /* out of range */
- mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1);
- break;
- }
- return mrb_nil_value();
+ if (mrb_type(a1) == MRB_TT_RANGE) {
+ if (mrb_range_beg_len(mrb, a1, &beg, &len, str_len, TRUE) == MRB_RANGE_OK) {
+ goto subseq;
}
-#ifndef MRB_WITHOUT_FLOAT
- case MRB_TT_FLOAT:
- a1 = mrb_fixnum_value((mrb_int)mrb_float(a1));
- /* fall through */
-#endif
- case MRB_TT_FIXNUM:
- return mrb_str_substr(mrb, str, mrb_fixnum(a1), 1);
- default:
- mrb_raise(mrb, E_TYPE_ERROR, "wrong type of argument");
+ return mrb_nil_value();
+ }
+
+ beg = mrb_fixnum(mrb_to_int(mrb, a1));
+ len = 1;
+ empty = FALSE;
+subseq:
+ if (mrb_str_beg_len(str_len, &beg, &len) && (empty || len != 0)) {
+ return mrb_str_byte_subseq(mrb, str, beg, len);
+ }
+ else {
+ return mrb_nil_value();
}
- /* not reached */
- return mrb_nil_value();
}
/*
@@ -163,7 +151,7 @@ mrb_str_concat_m(mrb_state *mrb, mrb_value self)
if (mrb_fixnum_p(str))
str = mrb_fixnum_chr(mrb, str);
else
- str = mrb_string_type(mrb, str);
+ str = mrb_ensure_string_type(mrb, str);
mrb_str_concat(mrb, self, str);
return self;
}
@@ -191,7 +179,7 @@ mrb_str_start_with(mrb_state *mrb, mrb_value self)
for (i = 0; i < argc; i++) {
size_t len_l, len_r;
int ai = mrb_gc_arena_save(mrb);
- sub = mrb_string_type(mrb, argv[i]);
+ sub = mrb_ensure_string_type(mrb, argv[i]);
mrb_gc_arena_restore(mrb, ai);
len_l = RSTRING_LEN(self);
len_r = RSTRING_LEN(sub);
@@ -220,7 +208,7 @@ mrb_str_end_with(mrb_state *mrb, mrb_value self)
for (i = 0; i < argc; i++) {
size_t len_l, len_r;
int ai = mrb_gc_arena_save(mrb);
- sub = mrb_string_type(mrb, argv[i]);
+ sub = mrb_ensure_string_type(mrb, argv[i]);
mrb_gc_arena_restore(mrb, ai);
len_l = RSTRING_LEN(self);
len_r = RSTRING_LEN(sub);
@@ -235,6 +223,592 @@ mrb_str_end_with(mrb_state *mrb, mrb_value self)
return mrb_false_value();
}
+enum tr_pattern_type {
+ TR_UNINITIALIZED = 0,
+ TR_IN_ORDER = 1,
+ TR_RANGE = 2,
+};
+
+/*
+ #tr Pattern syntax
+
+ <syntax> ::= (<pattern>)* | '^' (<pattern>)*
+ <pattern> ::= <in order> | <range>
+ <in order> ::= (<ch>)+
+ <range> ::= <ch> '-' <ch>
+*/
+struct tr_pattern {
+ uint8_t type; // 1:in-order, 2:range
+ mrb_bool flag_reverse : 1;
+ mrb_bool flag_on_heap : 1;
+ uint16_t n;
+ union {
+ uint16_t start_pos;
+ char ch[2];
+ } val;
+ struct tr_pattern *next;
+};
+
+#define STATIC_TR_PATTERN { 0 }
+
+static inline void
+tr_free_pattern(mrb_state *mrb, struct tr_pattern *pat)
+{
+ while (pat) {
+ struct tr_pattern *p = pat->next;
+ if (pat->flag_on_heap) {
+ mrb_free(mrb, pat);
+ }
+ pat = p;
+ }
+}
+
+static struct tr_pattern*
+tr_parse_pattern(mrb_state *mrb, struct tr_pattern *ret, const mrb_value v_pattern, mrb_bool flag_reverse_enable)
+{
+ const char *pattern = RSTRING_PTR(v_pattern);
+ mrb_int pattern_length = RSTRING_LEN(v_pattern);
+ mrb_bool flag_reverse = FALSE;
+ struct tr_pattern *pat1;
+ mrb_int i = 0;
+
+ if(flag_reverse_enable && pattern_length >= 2 && pattern[0] == '^') {
+ flag_reverse = TRUE;
+ i++;
+ }
+
+ while (i < pattern_length) {
+ /* is range pattern ? */
+ mrb_bool const ret_uninit = (ret->type == TR_UNINITIALIZED);
+ pat1 = ret_uninit
+ ? ret
+ : (struct tr_pattern*)mrb_malloc_simple(mrb, sizeof(struct tr_pattern));
+ if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-') {
+ if (pat1 == NULL && ret) {
+ nomem:
+ tr_free_pattern(mrb, ret);
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
+ return NULL; /* not reached */
+ }
+ pat1->type = TR_RANGE;
+ pat1->flag_reverse = flag_reverse;
+ pat1->flag_on_heap = !ret_uninit;
+ pat1->n = pattern[i+2] - pattern[i] + 1;
+ pat1->next = NULL;
+ pat1->val.ch[0] = pattern[i];
+ pat1->val.ch[1] = pattern[i+2];
+ i += 3;
+ }
+ else {
+ /* in order pattern. */
+ mrb_int start_pos = i++;
+ mrb_int len;
+
+ while (i < pattern_length) {
+ if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-')
+ break;
+ i++;
+ }
+
+ len = i - start_pos;
+ if (len > UINT16_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "tr pattern too long (max 65536)");
+ }
+ if (pat1 == NULL && ret) {
+ goto nomem;
+ }
+ pat1->type = TR_IN_ORDER;
+ pat1->flag_reverse = flag_reverse;
+ pat1->flag_on_heap = !ret_uninit;
+ pat1->n = len;
+ pat1->next = NULL;
+ pat1->val.start_pos = start_pos;
+ }
+
+ if (ret == NULL || ret_uninit) {
+ ret = pat1;
+ }
+ else {
+ struct tr_pattern *p = ret;
+ while (p->next != NULL) {
+ p = p->next;
+ }
+ p->next = pat1;
+ }
+ }
+
+ return ret;
+}
+
+static inline mrb_int
+tr_find_character(const struct tr_pattern *pat, const char *pat_str, int ch)
+{
+ mrb_int ret = -1;
+ mrb_int n_sum = 0;
+ mrb_int flag_reverse = pat ? pat->flag_reverse : 0;
+
+ while (pat != NULL) {
+ if (pat->type == TR_IN_ORDER) {
+ int i;
+ for (i = 0; i < pat->n; i++) {
+ if (pat_str[pat->val.start_pos + i] == ch) ret = n_sum + i;
+ }
+ }
+ else if (pat->type == TR_RANGE) {
+ if (pat->val.ch[0] <= ch && ch <= pat->val.ch[1])
+ ret = n_sum + ch - pat->val.ch[0];
+ }
+ else {
+ mrb_assert(pat->type == TR_UNINITIALIZED);
+ }
+ n_sum += pat->n;
+ pat = pat->next;
+ }
+
+ if (flag_reverse) {
+ return (ret < 0) ? MRB_INT_MAX : -1;
+ }
+ return ret;
+}
+
+static inline mrb_int
+tr_get_character(const struct tr_pattern *pat, const char *pat_str, mrb_int n_th)
+{
+ mrb_int n_sum = 0;
+
+ while (pat != NULL) {
+ if (n_th < (n_sum + pat->n)) {
+ mrb_int i = (n_th - n_sum);
+
+ switch (pat->type) {
+ case TR_IN_ORDER:
+ return pat_str[pat->val.start_pos + i];
+ case TR_RANGE:
+ return pat->val.ch[0]+i;
+ case TR_UNINITIALIZED:
+ return -1;
+ }
+ }
+ if (pat->next == NULL) {
+ switch (pat->type) {
+ case TR_IN_ORDER:
+ return pat_str[pat->val.start_pos + pat->n - 1];
+ case TR_RANGE:
+ return pat->val.ch[1];
+ case TR_UNINITIALIZED:
+ return -1;
+ }
+ }
+ n_sum += pat->n;
+ pat = pat->next;
+ }
+
+ return -1;
+}
+
+static inline void
+tr_bitmap_set(uint8_t bitmap[32], uint8_t ch)
+{
+ uint8_t idx1 = ch / 8;
+ uint8_t idx2 = ch % 8;
+ bitmap[idx1] |= (1<<idx2);
+}
+
+static inline mrb_bool
+tr_bitmap_detect(uint8_t bitmap[32], uint8_t ch)
+{
+ uint8_t idx1 = ch / 8;
+ uint8_t idx2 = ch % 8;
+ if (bitmap[idx1] & (1<<idx2))
+ return TRUE;
+ return FALSE;
+}
+
+/* compile patter to bitmap */
+static void
+tr_compile_pattern(const struct tr_pattern *pat, mrb_value pstr, uint8_t bitmap[32])
+{
+ const char *pattern = RSTRING_PTR(pstr);
+ mrb_int flag_reverse = pat ? pat->flag_reverse : 0;
+ int i;
+
+ for (i=0; i<32; i++) {
+ bitmap[i] = 0;
+ }
+ while (pat != NULL) {
+ if (pat->type == TR_IN_ORDER) {
+ for (i = 0; i < pat->n; i++) {
+ tr_bitmap_set(bitmap, pattern[pat->val.start_pos + i]);
+ }
+ }
+ else if (pat->type == TR_RANGE) {
+ for (i = pat->val.ch[0]; i < pat->val.ch[1]; i++) {
+ tr_bitmap_set(bitmap, i);
+ }
+ }
+ else {
+ mrb_assert(pat->type == TR_UNINITIALIZED);
+ }
+ pat = pat->next;
+ }
+
+ if (flag_reverse) {
+ for (i=0; i<32; i++) {
+ bitmap[i] ^= 0xff;
+ }
+ }
+}
+
+static mrb_bool
+str_tr(mrb_state *mrb, mrb_value str, mrb_value p1, mrb_value p2, mrb_bool squeeze)
+{
+ struct tr_pattern pat = STATIC_TR_PATTERN;
+ struct tr_pattern rep_storage = STATIC_TR_PATTERN;
+ char *s;
+ mrb_int len;
+ mrb_int i;
+ mrb_int j;
+ mrb_bool flag_changed = FALSE;
+ mrb_int lastch = -1;
+ struct tr_pattern *rep;
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ tr_parse_pattern(mrb, &pat, p1, TRUE);
+ rep = tr_parse_pattern(mrb, &rep_storage, p2, FALSE);
+ s = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+
+ for (i=j=0; i<len; i++,j++) {
+ mrb_int n = tr_find_character(&pat, RSTRING_PTR(p1), s[i]);
+
+ if (i>j) s[j] = s[i];
+ if (n >= 0) {
+ flag_changed = TRUE;
+ if (rep == NULL) {
+ j--;
+ }
+ else {
+ mrb_int c = tr_get_character(rep, RSTRING_PTR(p2), n);
+
+ if (c < 0 || (squeeze && c == lastch)) {
+ j--;
+ continue;
+ }
+ if (c > 0x80) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "character (%S) out of range",
+ mrb_fixnum_value((mrb_int)c));
+ }
+ lastch = c;
+ s[i] = (char)c;
+ }
+ }
+ }
+
+ tr_free_pattern(mrb, &pat);
+ tr_free_pattern(mrb, rep);
+
+ if (flag_changed) {
+ RSTR_SET_LEN(RSTRING(str), j);
+ RSTRING_PTR(str)[j] = 0;
+ }
+ return flag_changed;
+}
+
+/*
+ * call-seq:
+ * str.tr(from_str, to_str) => new_str
+ *
+ * Returns a copy of str with the characters in from_str replaced by the
+ * corresponding characters in to_str. If to_str is shorter than from_str,
+ * it is padded with its last character in order to maintain the
+ * correspondence.
+ *
+ * "hello".tr('el', 'ip') #=> "hippo"
+ * "hello".tr('aeiou', '*') #=> "h*ll*"
+ * "hello".tr('aeiou', 'AA*') #=> "hAll*"
+ *
+ * Both strings may use the c1-c2 notation to denote ranges of characters,
+ * and from_str may start with a ^, which denotes all characters except
+ * those listed.
+ *
+ * "hello".tr('a-y', 'b-z') #=> "ifmmp"
+ * "hello".tr('^aeiou', '*') #=> "*e**o"
+ *
+ * The backslash character \ can be used to escape ^ or - and is otherwise
+ * ignored unless it appears at the end of a range or the end of the
+ * from_str or to_str:
+ *
+ *
+ * "hello^world".tr("\\^aeiou", "*") #=> "h*ll**w*rld"
+ * "hello-world".tr("a\\-eo", "*") #=> "h*ll**w*rld"
+ *
+ * "hello\r\nworld".tr("\r", "") #=> "hello\nworld"
+ * "hello\r\nworld".tr("\\r", "") #=> "hello\r\nwold"
+ * "hello\r\nworld".tr("\\\r", "") #=> "hello\nworld"
+ *
+ * "X['\\b']".tr("X\\", "") #=> "['b']"
+ * "X['\\b']".tr("X-\\]", "") #=> "'b'"
+ *
+ * Note: conversion is effective only in ASCII region.
+ */
+static mrb_value
+mrb_str_tr(mrb_state *mrb, mrb_value str)
+{
+ mrb_value dup;
+ mrb_value p1, p2;
+
+ mrb_get_args(mrb, "SS", &p1, &p2);
+ dup = mrb_str_dup(mrb, str);
+ str_tr(mrb, dup, p1, p2, FALSE);
+ return dup;
+}
+
+/*
+ * call-seq:
+ * str.tr!(from_str, to_str) -> str or nil
+ *
+ * Translates str in place, using the same rules as String#tr.
+ * Returns str, or nil if no changes were made.
+ */
+static mrb_value
+mrb_str_tr_bang(mrb_state *mrb, mrb_value str)
+{
+ mrb_value p1, p2;
+
+ mrb_get_args(mrb, "SS", &p1, &p2);
+ if (str_tr(mrb, str, p1, p2, FALSE)) {
+ return str;
+ }
+ return mrb_nil_value();
+}
+
+/*
+ * call-seq:
+ * str.tr_s(from_str, to_str) -> new_str
+ *
+ * Processes a copy of str as described under String#tr, then removes
+ * duplicate characters in regions that were affected by the translation.
+ *
+ * "hello".tr_s('l', 'r') #=> "hero"
+ * "hello".tr_s('el', '*') #=> "h*o"
+ * "hello".tr_s('el', 'hx') #=> "hhxo"
+ */
+static mrb_value
+mrb_str_tr_s(mrb_state *mrb, mrb_value str)
+{
+ mrb_value dup;
+ mrb_value p1, p2;
+
+ mrb_get_args(mrb, "SS", &p1, &p2);
+ dup = mrb_str_dup(mrb, str);
+ str_tr(mrb, dup, p1, p2, TRUE);
+ return dup;
+}
+
+/*
+ * call-seq:
+ * str.tr_s!(from_str, to_str) -> str or nil
+ *
+ * Performs String#tr_s processing on str in place, returning
+ * str, or nil if no changes were made.
+ */
+static mrb_value
+mrb_str_tr_s_bang(mrb_state *mrb, mrb_value str)
+{
+ mrb_value p1, p2;
+
+ mrb_get_args(mrb, "SS", &p1, &p2);
+ if (str_tr(mrb, str, p1, p2, TRUE)) {
+ return str;
+ }
+ return mrb_nil_value();
+}
+
+static mrb_bool
+str_squeeze(mrb_state *mrb, mrb_value str, mrb_value v_pat)
+{
+ struct tr_pattern pat_storage = STATIC_TR_PATTERN;
+ struct tr_pattern *pat = NULL;
+ mrb_int i, j;
+ char *s;
+ mrb_int len;
+ mrb_bool flag_changed = FALSE;
+ mrb_int lastch = -1;
+ uint8_t bitmap[32];
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ if (!mrb_nil_p(v_pat)) {
+ pat = tr_parse_pattern(mrb, &pat_storage, v_pat, TRUE);
+ tr_compile_pattern(pat, v_pat, bitmap);
+ tr_free_pattern(mrb, pat);
+ }
+ s = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+
+ if (pat) {
+ for (i=j=0; i<len; i++,j++) {
+ if (i>j) s[j] = s[i];
+ if (tr_bitmap_detect(bitmap, s[i]) && s[i] == lastch) {
+ flag_changed = TRUE;
+ j--;
+ }
+ lastch = s[i];
+ }
+ }
+ else {
+ for (i=j=0; i<len; i++,j++) {
+ if (i>j) s[j] = s[i];
+ if (s[i] >= 0 && s[i] == lastch) {
+ flag_changed = TRUE;
+ j--;
+ }
+ lastch = s[i];
+ }
+ }
+
+ if (flag_changed) {
+ RSTR_SET_LEN(RSTRING(str), j);
+ RSTRING_PTR(str)[j] = 0;
+ }
+ return flag_changed;
+}
+
+/*
+ * call-seq:
+ * str.squeeze([other_str]) -> new_str
+ *
+ * Builds a set of characters from the other_str
+ * parameter(s) using the procedure described for String#count. Returns a
+ * new string where runs of the same character that occur in this set are
+ * replaced by a single character. If no arguments are given, all runs of
+ * identical characters are replaced by a single character.
+ *
+ * "yellow moon".squeeze #=> "yelow mon"
+ * " now is the".squeeze(" ") #=> " now is the"
+ * "putters shoot balls".squeeze("m-z") #=> "puters shot balls"
+ */
+static mrb_value
+mrb_str_squeeze(mrb_state *mrb, mrb_value str)
+{
+ mrb_value pat = mrb_nil_value();
+ mrb_value dup;
+
+ mrb_get_args(mrb, "|S", &pat);
+ dup = mrb_str_dup(mrb, str);
+ str_squeeze(mrb, dup, pat);
+ return dup;
+}
+
+/*
+ * call-seq:
+ * str.squeeze!([other_str]) -> str or nil
+ *
+ * Squeezes str in place, returning either str, or nil if no
+ * changes were made.
+ */
+static mrb_value
+mrb_str_squeeze_bang(mrb_state *mrb, mrb_value str)
+{
+ mrb_value pat = mrb_nil_value();
+
+ mrb_get_args(mrb, "|S", &pat);
+ if (str_squeeze(mrb, str, pat)) {
+ return str;
+ }
+ return mrb_nil_value();
+}
+
+static mrb_bool
+str_delete(mrb_state *mrb, mrb_value str, mrb_value v_pat)
+{
+ struct tr_pattern pat = STATIC_TR_PATTERN;
+ mrb_int i, j;
+ char *s;
+ mrb_int len;
+ mrb_bool flag_changed = FALSE;
+ uint8_t bitmap[32];
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ tr_parse_pattern(mrb, &pat, v_pat, TRUE);
+ tr_compile_pattern(&pat, v_pat, bitmap);
+ tr_free_pattern(mrb, &pat);
+
+ s = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+
+ for (i=j=0; i<len; i++,j++) {
+ if (i>j) s[j] = s[i];
+ if (tr_bitmap_detect(bitmap, s[i])) {
+ flag_changed = TRUE;
+ j--;
+ }
+ }
+ if (flag_changed) {
+ RSTR_SET_LEN(RSTRING(str), j);
+ RSTRING_PTR(str)[j] = 0;
+ }
+ return flag_changed;
+}
+
+static mrb_value
+mrb_str_delete(mrb_state *mrb, mrb_value str)
+{
+ mrb_value pat;
+ mrb_value dup;
+
+ mrb_get_args(mrb, "S", &pat);
+ dup = mrb_str_dup(mrb, str);
+ str_delete(mrb, dup, pat);
+ return dup;
+}
+
+static mrb_value
+mrb_str_delete_bang(mrb_state *mrb, mrb_value str)
+{
+ mrb_value pat;
+
+ mrb_get_args(mrb, "S", &pat);
+ if (str_delete(mrb, str, pat)) {
+ return str;
+ }
+ return mrb_nil_value();
+}
+
+/*
+ * call_seq:
+ * str.count([other_str]) -> integer
+ *
+ * Each other_str parameter defines a set of characters to count. The
+ * intersection of these sets defines the characters to count in str. Any
+ * other_str that starts with a caret ^ is negated. The sequence c1-c2
+ * means all characters between c1 and c2. The backslash character \ can
+ * be used to escape ^ or - and is otherwise ignored unless it appears at
+ * the end of a sequence or the end of a other_str.
+ */
+static mrb_value
+mrb_str_count(mrb_state *mrb, mrb_value str)
+{
+ mrb_value v_pat = mrb_nil_value();
+ mrb_int i;
+ char *s;
+ mrb_int len;
+ mrb_int count = 0;
+ struct tr_pattern pat = STATIC_TR_PATTERN;
+ uint8_t bitmap[32];
+
+ mrb_get_args(mrb, "S", &v_pat);
+ tr_parse_pattern(mrb, &pat, v_pat, TRUE);
+ tr_compile_pattern(&pat, v_pat, bitmap);
+ tr_free_pattern(mrb, &pat);
+
+ s = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+ for (i = 0; i < len; i++) {
+ if (tr_bitmap_detect(bitmap, s[i])) count++;
+ }
+ return mrb_fixnum_value(count);
+}
+
static mrb_value
mrb_str_hex(mrb_state *mrb, mrb_value self)
{
@@ -620,6 +1194,15 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "swapcase", mrb_str_swapcase, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "concat", mrb_str_concat_m, MRB_ARGS_REQ(1));
mrb_define_method(mrb, s, "<<", mrb_str_concat_m, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "count", mrb_str_count, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "tr", mrb_str_tr, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "tr!", mrb_str_tr_bang, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "tr_s", mrb_str_tr_s, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "tr_s!", mrb_str_tr_s_bang, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "squeeze", mrb_str_squeeze, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, s, "squeeze!", mrb_str_squeeze_bang, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, s, "delete", mrb_str_delete, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "delete!", mrb_str_delete_bang, MRB_ARGS_REQ(1));
mrb_define_method(mrb, s, "start_with?", mrb_str_start_with, MRB_ARGS_REST());
mrb_define_method(mrb, s, "end_with?", mrb_str_end_with, MRB_ARGS_REST());
mrb_define_method(mrb, s, "hex", mrb_str_hex, MRB_ARGS_NONE());
@@ -627,8 +1210,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE());
- mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ"));
- mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!"));
+ mrb_define_alias(mrb, s, "next", "succ");
+ mrb_define_alias(mrb, s, "next!", "succ!");
mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "delete_prefix!", mrb_str_del_prefix_bang, MRB_ARGS_REQ(1));
mrb_define_method(mrb, s, "delete_prefix", mrb_str_del_prefix, MRB_ARGS_REQ(1));
diff --git a/mrbgems/mruby-string-ext/test/range.rb b/mrbgems/mruby-string-ext/test/range.rb
new file mode 100644
index 000000000..80c286850
--- /dev/null
+++ b/mrbgems/mruby-string-ext/test/range.rb
@@ -0,0 +1,26 @@
+assert('Range#max') do
+ # returns the maximum value in the range when called with no arguments
+ assert_equal 'l', ('f'..'l').max
+ assert_equal 'e', ('a'...'f').max
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, ('z'..'l').max
+end
+
+assert('Range#max given a block') do
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, (('z'..'l').max { |x, y| x <=> y })
+end
+
+assert('Range#min') do
+ # returns the minimum value in the range when called with no arguments
+ assert_equal 'f', ('f'..'l').min
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, ('z'..'l').min
+end
+
+assert('Range#min given a block') do
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, (('z'..'l').min { |x, y| x <=> y })
+end
diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb
index b6146fb90..9a324c46d 100644
--- a/mrbgems/mruby-string-ext/test/string.rb
+++ b/mrbgems/mruby-string-ext/test/string.rb
@@ -2,14 +2,7 @@
##
# String(Ext) Test
-UTF8STRING = ("\343\201\202".size == 1)
-
-assert('String.try_convert') do
- assert_nil String.try_convert(nil)
- assert_nil String.try_convert(:foo)
- assert_equal "", String.try_convert("")
- assert_equal "1,2,3", String.try_convert("1,2,3")
-end
+UTF8STRING = __ENCODING__ == "UTF-8"
assert('String#getbyte') do
str1 = "hello"
@@ -31,83 +24,132 @@ assert('String#setbyte') do
assert_equal("Hello", str1)
end
-assert("String#setbyte raises IndexError if arg conversion resizes String") do
- $s = "01234\n"
- class Tmp
- def to_i
- $s.chomp! ''
- 95
- end
- end
- tmp = Tmp.new
- assert_raise(IndexError) { $s.setbyte(5, tmp) }
-end
-
assert('String#byteslice') do
str1 = "hello"
+ str2 = "\u3042ab" # "\xE3\x81\x82ab"
+
+ assert_equal("h", str1.byteslice(0))
assert_equal("e", str1.byteslice(1))
+ assert_equal(nil, str1.byteslice(5))
assert_equal("o", str1.byteslice(-1))
+ assert_equal(nil, str1.byteslice(-6))
+ assert_equal("\xE3", str2.byteslice(0))
+ assert_equal("\x81", str2.byteslice(1))
+ assert_equal(nil, str2.byteslice(5))
+ assert_equal("b", str2.byteslice(-1))
+ assert_equal(nil, str2.byteslice(-6))
+
+ assert_equal("", str1.byteslice(0, 0))
+ assert_equal(str1, str1.byteslice(0, 6))
+ assert_equal("el", str1.byteslice(1, 2))
+ assert_equal("", str1.byteslice(5, 1))
+ assert_equal("o", str1.byteslice(-1, 6))
+ assert_equal(nil, str1.byteslice(-6, 1))
+ assert_equal(nil, str1.byteslice(0, -1))
+ assert_equal("", str2.byteslice(0, 0))
+ assert_equal(str2, str2.byteslice(0, 6))
+ assert_equal("\x81\x82", str2.byteslice(1, 2))
+ assert_equal("", str2.byteslice(5, 1))
+ assert_equal("b", str2.byteslice(-1, 6))
+ assert_equal(nil, str2.byteslice(-6, 1))
+ assert_equal(nil, str2.byteslice(0, -1))
+
assert_equal("ell", str1.byteslice(1..3))
assert_equal("el", str1.byteslice(1...3))
+ assert_equal("h", str1.byteslice(0..0))
+ assert_equal("", str1.byteslice(5..0))
+ assert_equal("o", str1.byteslice(4..5))
+ assert_equal(nil, str1.byteslice(6..0))
+ assert_equal("", str1.byteslice(-1..0))
+ assert_equal("llo", str1.byteslice(-3..5))
+ assert_equal("\x81\x82a", str2.byteslice(1..3))
+ assert_equal("\x81\x82", str2.byteslice(1...3))
+ assert_equal("\xE3", str2.byteslice(0..0))
+ assert_equal("", str2.byteslice(5..0))
+ assert_equal("b", str2.byteslice(4..5))
+ assert_equal(nil, str2.byteslice(6..0))
+ assert_equal("", str2.byteslice(-1..0))
+ assert_equal("\x82ab", str2.byteslice(-3..5))
+
+ assert_raise(ArgumentError) { str1.byteslice }
+ assert_raise(ArgumentError) { str1.byteslice(1, 2, 3) }
+ assert_raise(TypeError) { str1.byteslice("1") }
+ assert_raise(TypeError) { str1.byteslice("1", 2) }
+ assert_raise(TypeError) { str1.byteslice(1, "2") }
+ assert_raise(TypeError) { str1.byteslice(1..2, 3) }
+
+ skip unless Object.const_defined?(:Float)
+ assert_equal("o", str1.byteslice(4.0))
+ assert_equal("\x82ab", str2.byteslice(2.0, 3.0))
end
assert('String#dump') do
- ("\1" * 100).dump # should not raise an exception - regress #1210
- "\0".inspect == "\"\\000\"" and
- "foo".dump == "\"foo\""
+ assert_equal("\"\\x00\"", "\0".dump)
+ assert_equal("\"foo\"", "foo".dump)
+ assert_nothing_raised { ("\1" * 100).dump } # regress #1210
end
assert('String#strip') do
s = " abc "
- "".strip == "" and " \t\r\n\f\v".strip == "" and
- "\0a\0".strip == "\0a" and
- "abc".strip == "abc" and
- " abc".strip == "abc" and
- "abc ".strip == "abc" and
- " abc ".strip == "abc" and
- s == " abc "
+ assert_equal("abc", s.strip)
+ assert_equal(" abc ", s)
+ assert_equal("", "".strip)
+ assert_equal("", " \t\r\n\f\v".strip)
+ assert_equal("\0a", "\0a\0".strip)
+ assert_equal("abc", "abc".strip)
+ assert_equal("abc", " abc".strip)
+ assert_equal("abc", "abc ".strip)
end
assert('String#lstrip') do
s = " abc "
- s.lstrip
- "".lstrip == "" and " \t\r\n\f\v".lstrip == "" and
- "\0a\0".lstrip == "\0a\0" and
- "abc".lstrip == "abc" and
- " abc".lstrip == "abc" and
- "abc ".lstrip == "abc " and
- " abc ".lstrip == "abc " and
- s == " abc "
+ assert_equal("abc ", s.lstrip)
+ assert_equal(" abc ", s)
+ assert_equal("", "".lstrip)
+ assert_equal("", " \t\r\n\f\v".lstrip)
+ assert_equal("\0a\0", "\0a\0".lstrip)
+ assert_equal("abc", "abc".lstrip)
+ assert_equal("abc", " abc".lstrip)
+ assert_equal("abc ", "abc ".lstrip)
end
assert('String#rstrip') do
s = " abc "
- s.rstrip
- "".rstrip == "" and " \t\r\n\f\v".rstrip == "" and
- "\0a\0".rstrip == "\0a" and
- "abc".rstrip == "abc" and
- " abc".rstrip == " abc" and
- "abc ".rstrip == "abc" and
- " abc ".rstrip == " abc" and
- s == " abc "
+ assert_equal(" abc", s.rstrip)
+ assert_equal(" abc ", s)
+ assert_equal("", "".rstrip)
+ assert_equal("", " \t\r\n\f\v".rstrip)
+ assert_equal("\0a", "\0a\0".rstrip)
+ assert_equal("abc", "abc".rstrip)
+ assert_equal(" abc", " abc".rstrip)
+ assert_equal("abc", "abc ".rstrip)
end
assert('String#strip!') do
s = " abc "
t = "abc"
- s.strip! == "abc" and s == "abc" and t.strip! == nil
+ assert_equal("abc", s.strip!)
+ assert_equal("abc", s)
+ assert_nil(t.strip!)
+ assert_equal("abc", t)
end
assert('String#lstrip!') do
s = " abc "
t = "abc "
- s.lstrip! == "abc " and s == "abc " and t.lstrip! == nil
+ assert_equal("abc ", s.lstrip!)
+ assert_equal("abc ", s)
+ assert_nil(t.lstrip!)
+ assert_equal("abc ", t)
end
assert('String#rstrip!') do
s = " abc "
t = " abc"
- s.rstrip! == " abc" and s == " abc" and t.rstrip! == nil
+ assert_equal(" abc", s.rstrip!)
+ assert_equal(" abc", s)
+ assert_nil(t.rstrip!)
+ assert_equal(" abc", t)
end
assert('String#swapcase') do
@@ -126,12 +168,6 @@ assert('String#concat') do
assert_equal "Hello World!", "Hello " << "World" << 33
assert_equal "Hello World!", "Hello ".concat("World").concat(33)
- o = Object.new
- def o.to_str
- "to_str"
- end
- assert_equal "hi to_str", "hi " << o
-
assert_raise(TypeError) { "".concat(Object.new) }
end
@@ -140,11 +176,69 @@ assert('String#casecmp') do
assert_equal 0, "aBcDeF".casecmp("abcdef")
assert_equal(-1, "abcdef".casecmp("abcdefg"))
assert_equal 0, "abcdef".casecmp("ABCDEF")
- o = Object.new
- def o.to_str
- "ABCDEF"
- end
- assert_equal 0, "abcdef".casecmp(o)
+end
+
+assert('String#count') do
+ s = "abccdeff123"
+ assert_equal 0, s.count("")
+ assert_equal 1, s.count("a")
+ assert_equal 2, s.count("ab")
+ assert_equal 9, s.count("^c")
+ assert_equal 8, s.count("a-z")
+ assert_equal 4, s.count("a0-9")
+end
+
+assert('String#tr') do
+ assert_equal "ABC", "abc".tr('a-z', 'A-Z')
+ assert_equal "hippo", "hello".tr('el', 'ip')
+ assert_equal "Ruby", "Lisp".tr("Lisp", "Ruby")
+ assert_equal "*e**o", "hello".tr('^aeiou', '*')
+ assert_equal "heo", "hello".tr('l', '')
+end
+
+assert('String#tr!') do
+ s = "abcdefghijklmnopqR"
+ assert_equal "ab12222hijklmnopqR", s.tr!("cdefg", "12")
+ assert_equal "ab12222hijklmnopqR", s
+end
+
+assert('String#tr_s') do
+ assert_equal "hero", "hello".tr_s('l', 'r')
+ assert_equal "h*o", "hello".tr_s('el', '*')
+ assert_equal "hhxo", "hello".tr_s('el', 'hx')
+end
+
+assert('String#tr_s!') do
+ s = "hello"
+ assert_equal "hero", s.tr_s!('l', 'r')
+ assert_equal "hero", s
+ assert_nil s.tr_s!('l', 'r')
+end
+
+assert('String#squeeze') do
+ assert_equal "yelow mon", "yellow moon".squeeze
+ assert_equal " now is the", " now is the".squeeze(" ")
+ assert_equal "puters shot balls", "putters shoot balls".squeeze("m-z")
+end
+
+assert('String#squeeze!') do
+ s = " now is the"
+ assert_equal " now is the", s.squeeze!(" ")
+ assert_equal " now is the", s
+end
+
+assert('String#delete') do
+ assert_equal "he", "hello".delete("lo")
+ assert_equal "hll", "hello".delete("aeiou")
+ assert_equal "ll", "hello".delete("^l")
+ assert_equal "ho", "hello".delete("ej-m")
+end
+
+assert('String#delete!') do
+ s = "hello"
+ assert_equal "he", s.delete!("lo")
+ assert_equal "he", s
+ assert_nil s.delete!("lz")
end
assert('String#start_with?') do
@@ -614,19 +708,19 @@ assert('String#chars(UTF-8)') do
end if UTF8STRING
assert('String#each_char') do
- s = ""
+ chars = []
"hello!".each_char do |x|
- s += x
+ chars << x
end
- assert_equal "hello!", s
+ assert_equal ["h", "e", "l", "l", "o", "!"], chars
end
assert('String#each_char(UTF-8)') do
- s = ""
+ chars = []
"こんにちは世界!".each_char do |x|
- s += x
+ chars << x
end
- assert_equal "こんにちは世界!", s
+ assert_equal ["こ", "ん", "に", "ち", "は", "世", "界", "!"], chars
end if UTF8STRING
assert('String#codepoints') do
diff --git a/mrbgems/mruby-struct/mrblib/struct.rb b/mrbgems/mruby-struct/mrblib/struct.rb
index 86c635df7..7682ac033 100644
--- a/mrbgems/mruby-struct/mrblib/struct.rb
+++ b/mrbgems/mruby-struct/mrblib/struct.rb
@@ -79,23 +79,22 @@ if Object.const_defined?(:Struct)
# 15.2.18.4.11(x)
#
alias to_s inspect
- end
- ##
- # call-seq:
- # hsh.dig(key,...) -> object
- #
- # Extracts the nested value specified by the sequence of <i>key</i>
- # objects by calling +dig+ at each step, returning +nil+ if any
- # intermediate step is +nil+.
- #
- def dig(idx,*args)
- n = self[idx]
- if args.size > 0
- n&.dig(*args)
- else
- n
+ ##
+ # call-seq:
+ # hsh.dig(key,...) -> object
+ #
+ # Extracts the nested value specified by the sequence of <i>key</i>
+ # objects by calling +dig+ at each step, returning +nil+ if any
+ # intermediate step is +nil+.
+ #
+ def dig(idx,*args)
+ n = self[idx]
+ if args.size > 0
+ n&.dig(*args)
+ else
+ n
+ end
end
end
end
-
diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c
index adeb09bc1..e04fe13ad 100644
--- a/mrbgems/mruby-struct/src/struct.c
+++ b/mrbgems/mruby-struct/src/struct.c
@@ -24,19 +24,18 @@ struct_class(mrb_state *mrb)
}
static inline mrb_value
-struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
+struct_ivar_get(mrb_state *mrb, mrb_value cls, mrb_sym id)
{
- struct RClass* kclass;
+ struct RClass* c = mrb_class_ptr(cls);
struct RClass* sclass = struct_class(mrb);
mrb_value ans;
for (;;) {
- ans = mrb_iv_get(mrb, c, id);
+ ans = mrb_iv_get(mrb, mrb_obj_value(c), id);
if (!mrb_nil_p(ans)) return ans;
- kclass = RCLASS_SUPER(c);
- if (kclass == 0 || kclass == sclass)
+ c = c->super;
+ if (c == sclass || c == 0)
return mrb_nil_value();
- c = mrb_obj_value(kclass);
}
}
@@ -88,10 +87,7 @@ mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
static void
mrb_struct_modify(mrb_state *mrb, mrb_value strct)
{
- if (MRB_FROZEN_P(mrb_basic_ptr(strct))) {
- mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen struct");
- }
-
+ mrb_check_frozen(mrb, mrb_basic_ptr(strct));
mrb_write_barrier(mrb, mrb_basic_ptr(strct));
}
@@ -127,19 +123,29 @@ mrb_struct_ref(mrb_state *mrb, mrb_value obj)
static mrb_sym
mrb_id_attrset(mrb_state *mrb, mrb_sym id)
{
+#define ONSTACK_ALLOC_MAX 32
+#define ONSTACK_STRLEN_MAX (ONSTACK_ALLOC_MAX - 1) /* '=' character */
+
const char *name;
char *buf;
mrb_int len;
mrb_sym mid;
+ char onstack[ONSTACK_ALLOC_MAX];
name = mrb_sym2name_len(mrb, id, &len);
- buf = (char *)mrb_malloc(mrb, (size_t)len+2);
+ if (len > ONSTACK_STRLEN_MAX) {
+ buf = (char *)mrb_malloc(mrb, (size_t)len+1);
+ }
+ else {
+ buf = onstack;
+ }
memcpy(buf, name, (size_t)len);
buf[len] = '=';
- buf[len+1] = '\0';
mid = mrb_intern(mrb, buf, len+1);
- mrb_free(mrb, buf);
+ if (buf != onstack) {
+ mrb_free(mrb, buf);
+ }
return mid;
}
@@ -162,20 +168,6 @@ mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
return val;
}
-static mrb_bool
-is_local_id(mrb_state *mrb, const char *name)
-{
- if (!name) return FALSE;
- return !ISUPPER(name[0]);
-}
-
-static mrb_bool
-is_const_id(mrb_state *mrb, const char *name)
-{
- if (!name) return FALSE;
- return ISUPPER(name[0]);
-}
-
static void
make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
{
@@ -186,19 +178,15 @@ make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c
for (i=0; i<len; i++) {
mrb_sym id = mrb_symbol(ptr_members[i]);
- const char *name = mrb_sym2name_len(mrb, id, NULL);
-
- if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
- mrb_method_t m;
- mrb_value at = mrb_fixnum_value(i);
- struct RProc *aref = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_ref, 1, &at);
- struct RProc *aset = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_set_m, 1, &at);
- MRB_METHOD_FROM_PROC(m, aref);
- mrb_define_method_raw(mrb, c, id, m);
- MRB_METHOD_FROM_PROC(m, aset);
- mrb_define_method_raw(mrb, c, mrb_id_attrset(mrb, id), m);
- mrb_gc_arena_restore(mrb, ai);
- }
+ mrb_method_t m;
+ mrb_value at = mrb_fixnum_value(i);
+ struct RProc *aref = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_ref, 1, &at);
+ struct RProc *aset = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_set_m, 1, &at);
+ MRB_METHOD_FROM_PROC(m, aref);
+ mrb_define_method_raw(mrb, c, id, m);
+ MRB_METHOD_FROM_PROC(m, aset);
+ mrb_define_method_raw(mrb, c, mrb_id_attrset(mrb, id), m);
+ mrb_gc_arena_restore(mrb, ai);
}
}
@@ -214,9 +202,9 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass *kl
}
else {
/* old style: should we warn? */
- name = mrb_str_to_str(mrb, name);
+ mrb_to_str(mrb, name);
id = mrb_obj_to_sym(mrb, name);
- if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
+ if (!mrb_const_name_p(mrb, RSTRING_PTR(name), RSTRING_LEN(name))) {
mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
}
if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
@@ -303,17 +291,19 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
}
}
rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
- for (i=0; i<RARRAY_LEN(rest); i++) {
+ for (i=0; i<argcnt; i++) {
id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
}
- }
- st = make_struct(mrb, name, rest, mrb_class_ptr(klass));
- if (!mrb_nil_p(b)) {
- mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
- }
+ st = make_struct(mrb, name, rest, mrb_class_ptr(klass));
+ if (!mrb_nil_p(b)) {
+ mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
+ }
- return st;
+ return st;
+ }
+ /* not reached */
+ return mrb_nil_value();
}
static mrb_int
@@ -398,23 +388,24 @@ struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
return ptr[i];
}
}
- mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id));
+ mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
return mrb_nil_value(); /* not reached */
}
static mrb_value
struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
{
- if (i < 0) i = RSTRUCT_LEN(s) + i;
- if (i < 0)
- mrb_raisef(mrb, E_INDEX_ERROR,
- "offset %S too small for struct(size:%S)",
- mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
- if (RSTRUCT_LEN(s) <= i)
+ mrb_int idx = i < 0 ? RSTRUCT_LEN(s) + i : i;
+
+ if (idx < 0)
+ mrb_raisef(mrb, E_INDEX_ERROR,
+ "offset %S too small for struct(size:%S)",
+ mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
+ if (RSTRUCT_LEN(s) <= idx)
mrb_raisef(mrb, E_INDEX_ERROR,
"offset %S too large for struct(size:%S)",
mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
- return RSTRUCT_PTR(s)[i];
+ return RSTRUCT_PTR(s)[idx];
}
/* 15.2.18.4.2 */
diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb
index 2b5751086..93930730b 100644
--- a/mrbgems/mruby-struct/test/struct.rb
+++ b/mrbgems/mruby-struct/test/struct.rb
@@ -153,14 +153,14 @@ assert("Struct#dig") do
assert_equal 1, a.dig(1, 0)
end
-assert("Struct.new removes existing constant") do
- skip "redefining Struct with same name cause warnings"
- begin
- assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b)
- ensure
- Struct.remove_const :Test
- end
-end
+# TODO: suppress redefining Struct warning during test
+# assert("Struct.new removes existing constant") do
+# begin
+# assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b)
+# ensure
+# Struct.remove_const :Test
+# end
+# end
assert("Struct#initialize_copy requires struct to be the same type") do
begin
@@ -182,6 +182,10 @@ assert("Struct.new does not allow array") do
end
end
+assert("Struct.new does not allow invalid class name") do
+ assert_raise(NameError) { Struct.new("Test-", :a) }
+end
+
assert("Struct.new generates subclass of Struct") do
begin
original_struct = Struct
@@ -200,7 +204,7 @@ assert 'Struct#freeze' do
assert_equal :test, o.m
o.freeze
- assert_raise(RuntimeError) { o.m = :modify }
- assert_raise(RuntimeError) { o[:m] = :modify }
+ assert_raise(FrozenError) { o.m = :modify }
+ assert_raise(FrozenError) { o[:m] = :modify }
assert_equal :test, o.m
end
diff --git a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
index 28cce3156..99fa275d5 100644
--- a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
+++ b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
@@ -3,12 +3,6 @@ class Symbol
alias intern to_sym
- def to_proc
- ->(obj,*args,&block) do
- obj.__send__(self, *args, &block)
- end
- end
-
##
# call-seq:
# sym.capitalize -> symbol
diff --git a/mrbgems/mruby-symbol-ext/src/symbol.c b/mrbgems/mruby-symbol-ext/src/symbol.c
index a992dbfce..ccb2971dc 100644
--- a/mrbgems/mruby-symbol-ext/src/symbol.c
+++ b/mrbgems/mruby-symbol-ext/src/symbol.c
@@ -1,11 +1,7 @@
#include <mruby.h>
#include <mruby/khash.h>
#include <mruby/array.h>
-
-typedef struct symbol_name {
- size_t len;
- const char *name;
-} symbol_name;
+#include <mruby/string.h>
/*
* call-seq:
@@ -22,6 +18,7 @@ typedef struct symbol_name {
* :Tms, :getwd, :$=, :ThreadGroup,
* :wait2, :$>]
*/
+#ifdef MRB_ENABLE_ALL_SYMBOLS
static mrb_value
mrb_sym_all_symbols(mrb_state *mrb, mrb_value self)
{
@@ -29,11 +26,13 @@ mrb_sym_all_symbols(mrb_state *mrb, mrb_value self)
mrb_value ary = mrb_ary_new_capa(mrb, mrb->symidx);
for (i=1, lim=mrb->symidx+1; i<lim; i++) {
- mrb_ary_push(mrb, ary, mrb_symbol_value(i));
+ mrb_sym sym = i<<1;
+ mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
}
return ary;
}
+#endif
/*
* call-seq:
@@ -45,7 +44,13 @@ static mrb_value
mrb_sym_length(mrb_state *mrb, mrb_value self)
{
mrb_int len;
+#ifdef MRB_UTF8_STRING
+ mrb_int byte_len;
+ const char *name = mrb_sym2name_len(mrb, mrb_symbol(self), &byte_len);
+ len = mrb_utf8_len(name, byte_len);
+#else
mrb_sym2name_len(mrb, mrb_symbol(self), &len);
+#endif
return mrb_fixnum_value(len);
}
@@ -53,7 +58,9 @@ void
mrb_mruby_symbol_ext_gem_init(mrb_state* mrb)
{
struct RClass *s = mrb->symbol_class;
+#ifdef MRB_ENABLE_ALL_SYMBOLS
mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE());
+#endif
mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE());
}
diff --git a/mrbgems/mruby-symbol-ext/test/symbol.rb b/mrbgems/mruby-symbol-ext/test/symbol.rb
index 6070d1418..db686e5f4 100644
--- a/mrbgems/mruby-symbol-ext/test/symbol.rb
+++ b/mrbgems/mruby-symbol-ext/test/symbol.rb
@@ -1,19 +1,27 @@
+# coding: utf-8
##
# Symbol(Ext) Test
-assert('Symbol#to_proc') do
- assert_equal 5, :abs.to_proc[-5]
-end
-
-assert('Symbol.all_symbols') do
- foo = [:__symbol_test_1, :__symbol_test_2, :__symbol_test_3].sort
- symbols = Symbol.all_symbols.select{|sym|sym.to_s.include? '__symbol_test'}.sort
- assert_equal foo, symbols
-end
-
-assert("Symbol#length") do
- assert_equal 5, :hello.size
- assert_equal 5, :mruby.length
+if Symbol.respond_to?(:all_symbols)
+ assert('Symbol.all_symbols') do
+ foo = [:__symbol_test_1, :__symbol_test_2, :__symbol_test_3].sort
+ symbols = Symbol.all_symbols.select{|sym|sym.to_s.include? '__symbol_test'}.sort
+ assert_equal foo, symbols
+ end
+end
+
+%w[size length].each do |n|
+ assert("Symbol##{n}") do
+ assert_equal 5, :hello.__send__(n)
+ assert_equal 4, :"aA\0b".__send__(n)
+ if __ENCODING__ == "UTF-8"
+ assert_equal 8, :"こんにちは世界!".__send__(n)
+ assert_equal 4, :"aあ\0b".__send__(n)
+ else
+ assert_equal 22, :"こんにちは世界!".__send__(n)
+ assert_equal 6, :"aあ\0b".__send__(n)
+ end
+ end
end
assert("Symbol#capitalize") do
diff --git a/mrbgems/mruby-test/driver.c b/mrbgems/mruby-test/driver.c
index 434d1fee5..602b76c79 100644
--- a/mrbgems/mruby-test/driver.c
+++ b/mrbgems/mruby-test/driver.c
@@ -18,8 +18,9 @@
#include <mruby/variable.h>
#include <mruby/array.h>
-void
-mrb_init_mrbtest(mrb_state *);
+extern const uint8_t mrbtest_assert_irep[];
+
+void mrbgemtest_init(mrb_state* mrb);
/* Print a short remark for the user */
static void
@@ -29,56 +30,182 @@ print_hint(void)
}
static int
-check_error(mrb_state *mrb)
-{
- /* Error check */
- /* $ko_test and $kill_test should be 0 */
- mrb_value ko_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$ko_test"));
- mrb_value kill_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$kill_test"));
-
- return mrb_fixnum_p(ko_test) && mrb_fixnum(ko_test) == 0 && mrb_fixnum_p(kill_test) && mrb_fixnum(kill_test) == 0;
-}
-
-static int
eval_test(mrb_state *mrb)
{
/* evaluate the test */
- mrb_funcall(mrb, mrb_top_self(mrb), "report", 0);
+ mrb_value result = mrb_funcall(mrb, mrb_top_self(mrb), "report", 0);
/* did an exception occur? */
if (mrb->exc) {
mrb_print_error(mrb);
mrb->exc = 0;
return EXIT_FAILURE;
}
- else if (!check_error(mrb)) {
- return EXIT_FAILURE;
+ else {
+ return mrb_bool(result) ? EXIT_SUCCESS : EXIT_FAILURE;
}
- return EXIT_SUCCESS;
}
-static void
-t_printstr(mrb_state *mrb, mrb_value obj)
+/* Implementation of print due to the reason that there might be no print */
+static mrb_value
+t_print(mrb_state *mrb, mrb_value self)
{
- char *s;
- mrb_int len;
-
- if (mrb_string_p(obj)) {
- s = RSTRING_PTR(obj);
- len = RSTRING_LEN(obj);
- fwrite(s, len, 1, stdout);
- fflush(stdout);
+ mrb_value *argv;
+ mrb_int argc;
+ mrb_int i;
+
+ mrb_get_args(mrb, "*!", &argv, &argc);
+ for (i = 0; i < argc; ++i) {
+ mrb_value s = mrb_obj_as_string(mrb, argv[i]);
+ fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stdout);
}
+ fflush(stdout);
+
+ return mrb_nil_value();
}
-mrb_value
-mrb_t_printstr(mrb_state *mrb, mrb_value self)
+#define UNESCAPE(p, endp) ((p) != (endp) && *(p) == '\\' ? (p)+1 : (p))
+#define CHAR_CMP(c1, c2) ((unsigned char)(c1) - (unsigned char)(c2))
+
+static const char *
+str_match_bracket(const char *p, const char *pat_end,
+ const char *s, const char *str_end)
{
- mrb_value argv;
+ mrb_bool ok = FALSE, negated = FALSE;
+
+ if (p == pat_end) return NULL;
+ if (*p == '!' || *p == '^') {
+ negated = TRUE;
+ ++p;
+ }
+
+ while (*p != ']') {
+ const char *t1 = p;
+ if ((t1 = UNESCAPE(t1, pat_end)) == pat_end) return NULL;
+ if ((p = t1 + 1) == pat_end) return NULL;
+ if (p[0] == '-' && p[1] != ']') {
+ const char *t2 = p + 1;
+ if ((t2 = UNESCAPE(t2, pat_end)) == pat_end) return NULL;
+ p = t2 + 1;
+ if (!ok && CHAR_CMP(*t1, *s) <= 0 && CHAR_CMP(*s, *t2) <= 0) ok = TRUE;
+ }
+ else {
+ if (!ok && CHAR_CMP(*t1, *s) == 0) ok = TRUE;
+ }
+ }
- mrb_get_args(mrb, "o", &argv);
- t_printstr(mrb, argv);
+ return ok == negated ? NULL : p + 1;
+}
+
+static mrb_bool
+str_match_no_brace_p(const char *pat, mrb_int pat_len,
+ const char *str, mrb_int str_len)
+{
+ const char *p = pat, *s = str;
+ const char *pat_end = pat + pat_len, *str_end = str + str_len;
+ const char *p_tmp = NULL, *s_tmp = NULL;
+
+ for (;;) {
+ if (p == pat_end) return s == str_end;
+ switch (*p) {
+ case '*':
+ do { ++p; } while (p != pat_end && *p == '*');
+ if (UNESCAPE(p, pat_end) == pat_end) return TRUE;
+ if (s == str_end) return FALSE;
+ p_tmp = p;
+ s_tmp = s;
+ continue;
+ case '?':
+ if (s == str_end) return FALSE;
+ ++p;
+ ++s;
+ continue;
+ case '[': {
+ const char *t;
+ if (s == str_end) return FALSE;
+ if ((t = str_match_bracket(p+1, pat_end, s, str_end))) {
+ p = t;
+ ++s;
+ continue;
+ }
+ goto L_failed;
+ }
+ }
+
+ /* ordinary */
+ p = UNESCAPE(p, pat_end);
+ if (s == str_end) return p == pat_end;
+ if (p == pat_end) goto L_failed;
+ if (*p++ != *s++) goto L_failed;
+ continue;
+
+ L_failed:
+ if (p_tmp && s_tmp) {
+ /* try next '*' position */
+ p = p_tmp;
+ s = ++s_tmp;
+ continue;
+ }
+
+ return FALSE;
+ }
+}
+
+#define COPY_AND_INC(dst, src, len) \
+ do { memcpy(dst, src, len); dst += len; } while (0)
+
+static mrb_bool
+str_match_p(mrb_state *mrb,
+ const char *pat, mrb_int pat_len,
+ const char *str, mrb_int str_len)
+{
+ const char *p = pat, *pat_end = pat + pat_len;
+ const char *lbrace = NULL, *rbrace = NULL;
+ int nest = 0;
+ mrb_bool ret = FALSE;
+
+ for (; p != pat_end; ++p) {
+ if (*p == '{' && nest++ == 0) lbrace = p;
+ else if (*p == '}' && lbrace && --nest == 0) { rbrace = p; break; }
+ else if (*p == '\\' && ++p == pat_end) break;
+ }
+
+ if (lbrace && rbrace) {
+ /* expand brace */
+ char *ex_pat = (char *)mrb_malloc(mrb, pat_len-2); /* expanded pattern */
+ char *ex_p = ex_pat;
+
+ COPY_AND_INC(ex_p, pat, lbrace-pat);
+ p = lbrace;
+ while (p < rbrace) {
+ char *orig_ex_p = ex_p;
+ const char *t = ++p;
+ for (nest = 0; p < rbrace && !(*p == ',' && nest == 0); ++p) {
+ if (*p == '{') ++nest;
+ else if (*p == '}') --nest;
+ else if (*p == '\\' && ++p == rbrace) break;
+ }
+ COPY_AND_INC(ex_p, t, p-t);
+ COPY_AND_INC(ex_p, rbrace+1, pat_end-rbrace-1);
+ if ((ret = str_match_p(mrb, ex_pat, ex_p-ex_pat, str, str_len))) break;
+ ex_p = orig_ex_p;
+ }
+ mrb_free(mrb, ex_pat);
+ }
+ else if (!lbrace && !rbrace) {
+ ret = str_match_no_brace_p(pat, pat_len, str, str_len);
+ }
+
+ return ret;
+}
+
+static mrb_value
+m_str_match_p(mrb_state *mrb, mrb_value self)
+{
+ const char *pat, *str;
+ mrb_int pat_len, str_len;
- return argv;
+ mrb_get_args(mrb, "ss", &pat, &pat_len, &str, &str_len);
+ return mrb_bool_value(str_match_p(mrb, pat, pat_len, str, str_len));
}
void
@@ -87,7 +214,8 @@ mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose)
struct RClass *krn, *mrbtest;
krn = mrb->kernel_module;
- mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, krn, "t_print", t_print, MRB_ARGS_ANY());
+ mrb_define_method(mrb, krn, "_str_match?", m_str_match_p, MRB_ARGS_REQ(2));
mrbtest = mrb_define_module(mrb, "Mrbtest");
@@ -130,6 +258,7 @@ mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src)
TEST_COUNT_PASS(ok_test);
TEST_COUNT_PASS(ko_test);
TEST_COUNT_PASS(kill_test);
+ TEST_COUNT_PASS(skip_test);
#undef TEST_COUNT_PASS
@@ -167,7 +296,8 @@ main(int argc, char **argv)
}
mrb_init_test_driver(mrb, verbose);
- mrb_init_mrbtest(mrb);
+ mrb_load_irep(mrb, mrbtest_assert_irep);
+ mrbgemtest_init(mrb);
ret = eval_test(mrb);
mrb_close(mrb);
diff --git a/mrbgems/mruby-test/init_mrbtest.c b/mrbgems/mruby-test/init_mrbtest.c
deleted file mode 100644
index 7678a5200..000000000
--- a/mrbgems/mruby-test/init_mrbtest.c
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <stdlib.h>
-#include <mruby.h>
-#include <mruby/irep.h>
-#include <mruby/variable.h>
-
-extern const uint8_t mrbtest_assert_irep[];
-
-void mrbgemtest_init(mrb_state* mrb);
-void mrb_init_test_driver(mrb_state* mrb, mrb_bool verbose);
-void mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src);
-
-void
-mrb_init_mrbtest(mrb_state *mrb)
-{
- mrb_state *core_test;
-
- mrb_load_irep(mrb, mrbtest_assert_irep);
-
- core_test = mrb_open_core(mrb_default_allocf, NULL);
- if (core_test == NULL) {
- fprintf(stderr, "Invalid mrb_state, exiting %s", __FUNCTION__);
- exit(EXIT_FAILURE);
- }
- mrb_init_test_driver(core_test, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));
- mrb_load_irep(core_test, mrbtest_assert_irep);
- mrb_t_pass_result(mrb, core_test);
-
-#ifndef DISABLE_GEMS
- mrbgemtest_init(mrb);
-#endif
-
- if (mrb->exc) {
- mrb_print_error(mrb);
- mrb_close(mrb);
- exit(EXIT_FAILURE);
- }
- mrb_close(core_test);
-}
-
diff --git a/mrbgems/mruby-test/mrbgem.rake b/mrbgems/mruby-test/mrbgem.rake
index 7da0b5e50..dcb7bb719 100644
--- a/mrbgems/mruby-test/mrbgem.rake
+++ b/mrbgems/mruby-test/mrbgem.rake
@@ -7,23 +7,16 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb")
- if build.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT")
- spec.test_rbfiles.delete("#{MRUBY_ROOT}/test/t/float.rb")
- end
-
clib = "#{build_dir}/mrbtest.c"
mlib = clib.ext(exts.object)
exec = exefile("#{build.build_dir}/bin/mrbtest")
- libmruby = libfile("#{build.build_dir}/lib/libmruby")
- libmruby_core = libfile("#{build.build_dir}/lib/libmruby_core")
-
mrbtest_lib = libfile("#{build_dir}/mrbtest")
mrbtest_objs = []
driver_obj = objfile("#{build_dir}/driver")
- driver = "#{spec.dir}/driver.c"
+ # driver = "#{spec.dir}/driver.c"
assert_c = "#{build_dir}/assert.c"
assert_rb = "#{MRUBY_ROOT}/test/assert.rb"
@@ -31,7 +24,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
mrbtest_objs << assert_lib
file assert_lib => assert_c
- file assert_c => assert_rb do |t|
+ file assert_c => [assert_rb, build.mrbcfile] do |t|
open(t.name, 'w') do |f|
mrbc.run f, assert_rb, 'mrbtest_assert_irep'
end
@@ -45,7 +38,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
dep_list = build.gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions)
file test_rbobj => g.test_rbireps
- file g.test_rbireps => [g.test_rbfiles].flatten do |t|
+ file g.test_rbireps => [g.test_rbfiles, build.mrbcfile].flatten do |t|
FileUtils.mkdir_p File.dirname(t.name)
open(t.name, 'w') do |f|
g.print_gem_test_header(f)
@@ -140,32 +133,36 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
end
unless build.build_mrbtest_lib_only?
- file exec => [driver_obj, mlib, mrbtest_lib, libmruby_core, libmruby] do |t|
+ file exec => [driver_obj, mlib, mrbtest_lib, build.libmruby_static] do |t|
gem_flags = build.gems.map { |g| g.linker.flags }
gem_flags_before_libraries = build.gems.map { |g| g.linker.flags_before_libraries }
gem_flags_after_libraries = build.gems.map { |g| g.linker.flags_after_libraries }
gem_libraries = build.gems.map { |g| g.linker.libraries }
gem_library_paths = build.gems.map { |g| g.linker.library_paths }
- build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries
+ build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags,
+ gem_flags_before_libraries, gem_flags_after_libraries
end
end
- init = "#{spec.dir}/init_mrbtest.c"
-
# store the last gem selection and make the re-build
# of the test gem depending on a change to the gem
# selection
- active_gems = "#{build_dir}/active_gems.lst"
- FileUtils.mkdir_p File.dirname(active_gems)
- open(active_gems, 'w+') do |f|
- build.gems.each do |g|
- f.puts g.name
- end
+ active_gems_path = "#{build_dir}/active_gems_path.lst"
+ active_gem_list = if File.exist? active_gems_path
+ File.read active_gems_path
+ else
+ FileUtils.mkdir_p File.dirname(active_gems_path)
+ nil
+ end
+ current_gem_list = build.gems.map(&:name).join("\n")
+ task active_gems_path do |_t|
+ FileUtils.mkdir_p File.dirname(active_gems_path)
+ File.write active_gems_path, current_gem_list
end
- file clib => active_gems
+ file clib => active_gems_path if active_gem_list != current_gem_list
file mlib => clib
- file clib => init do |t|
+ file clib => [build.mrbcfile, __FILE__] do |_t|
_pp "GEN", "*.rb", "#{clib.relative_path}"
FileUtils.mkdir_p File.dirname(clib)
open(clib, 'w') do |f|
@@ -178,7 +175,8 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
f.puts %Q[ * All manual changes will get lost.]
f.puts %Q[ */]
f.puts %Q[]
- f.puts IO.read(init)
+ f.puts %Q[struct mrb_state;]
+ f.puts %Q[typedef struct mrb_state mrb_state;]
build.gems.each do |g|
f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
end
diff --git a/mrbgems/mruby-time/include/mruby/time.h b/mrbgems/mruby-time/include/mruby/time.h
new file mode 100644
index 000000000..d71f4ccd3
--- /dev/null
+++ b/mrbgems/mruby-time/include/mruby/time.h
@@ -0,0 +1,25 @@
+/*
+** mruby/time.h - Time class
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_TIME_H
+#define MRUBY_TIME_H
+
+#include "mruby/common.h"
+
+MRB_BEGIN_DECL
+
+typedef enum mrb_timezone {
+ MRB_TIMEZONE_NONE = 0,
+ MRB_TIMEZONE_UTC = 1,
+ MRB_TIMEZONE_LOCAL = 2,
+ MRB_TIMEZONE_LAST = 3
+} mrb_timezone;
+
+MRB_API mrb_value mrb_time_at(mrb_state *mrb, double sec, double usec, mrb_timezone timezone);
+
+MRB_END_DECL
+
+#endif /* MRUBY_TIME_H */
diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c
index cfd51ac63..4f0afd6c6 100644
--- a/mrbgems/mruby-time/src/time.c
+++ b/mrbgems/mruby-time/src/time.c
@@ -4,11 +4,16 @@
** See Copyright Notice in mruby.h
*/
+#ifdef MRB_WITHOUT_FLOAT
+# error Conflict 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <math.h>
#include <time.h>
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/data.h>
+#include <mruby/time.h>
#ifndef MRB_DISABLE_STDIO
#include <stdio.h>
@@ -17,6 +22,7 @@
#endif
#define NDIV(x,y) (-(-((x)+1)/(y))-1)
+#define TO_S_FMT "%Y-%m-%d %H:%M:%S "
#if defined(_MSC_VER) && _MSC_VER < 1800
double round(double x) {
@@ -46,7 +52,7 @@ double round(double x) {
/* #define NO_GMTIME_R */
#ifdef _WIN32
-#if _MSC_VER
+#ifdef _MSC_VER
/* Win32 platform do not provide gmtime_r/localtime_r; emulate them using gmtime_s/localtime_s */
#define gmtime_r(tp, tm) ((gmtime_s((tm), (tp)) == 0) ? (tm) : NULL)
#define localtime_r(tp, tm) ((localtime_s((tm), (tp)) == 0) ? (tm) : NULL)
@@ -166,13 +172,6 @@ timegm(struct tm *tm)
* second level. Also, there are only 2 timezones, namely UTC and LOCAL.
*/
-enum mrb_timezone {
- MRB_TIMEZONE_NONE = 0,
- MRB_TIMEZONE_UTC = 1,
- MRB_TIMEZONE_LOCAL = 2,
- MRB_TIMEZONE_LAST = 3
-};
-
typedef struct mrb_timezone_name {
const char name[8];
size_t len;
@@ -204,20 +203,25 @@ struct mrb_time {
static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
/** Updates the datetime of a mrb_time based on it's timezone and
-seconds setting. Returns self on success, NULL of failure. */
+ seconds setting. Returns self on success, NULL of failure.
+ if `dealloc` is set `true`, it frees `self` on error. */
static struct mrb_time*
-time_update_datetime(mrb_state *mrb, struct mrb_time *self)
+time_update_datetime(mrb_state *mrb, struct mrb_time *self, int dealloc)
{
struct tm *aid;
+ time_t t = self->sec;
if (self->timezone == MRB_TIMEZONE_UTC) {
- aid = gmtime_r(&self->sec, &self->datetime);
+ aid = gmtime_r(&t, &self->datetime);
}
else {
- aid = localtime_r(&self->sec, &self->datetime);
+ aid = localtime_r(&t, &self->datetime);
}
if (!aid) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec));
+ mrb_float sec = (mrb_float)t;
+
+ if (dealloc) mrb_free(mrb, self);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
/* not reached */
return NULL;
}
@@ -269,17 +273,17 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
tm->sec = tsec;
tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec);
if (tm->usec < 0) {
- long sec2 = (long)NDIV(usec,1000000); /* negative div */
+ long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */
tm->usec -= sec2 * 1000000;
tm->sec += sec2;
}
else if (tm->usec >= 1000000) {
- long sec2 = (long)(usec / 1000000);
+ long sec2 = (long)(tm->usec / 1000000);
tm->usec -= sec2 * 1000000;
tm->sec += sec2;
}
tm->timezone = timezone;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, TRUE);
return tm;
}
@@ -293,24 +297,24 @@ mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mr
static struct mrb_time*
current_mrb_time(mrb_state *mrb)
{
+ struct mrb_time tmzero = {0};
struct mrb_time *tm;
+ time_t sec, usec;
- tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
-#if defined(TIME_UTC)
+#if defined(TIME_UTC) && !defined(__ANDROID__)
{
struct timespec ts;
if (timespec_get(&ts, TIME_UTC) == 0) {
- mrb_free(mrb, tm);
mrb_raise(mrb, E_RUNTIME_ERROR, "timespec_get() failed for unknown reasons");
}
- tm->sec = ts.tv_sec;
- tm->usec = ts.tv_nsec / 1000;
+ sec = ts.tv_sec;
+ usec = ts.tv_nsec / 1000;
}
#elif defined(NO_GETTIMEOFDAY)
{
static time_t last_sec = 0, last_usec = 0;
- tm->sec = time(NULL);
+ sec = time(NULL);
if (tm->sec != last_sec) {
last_sec = tm->sec;
last_usec = 0;
@@ -319,19 +323,22 @@ current_mrb_time(mrb_state *mrb)
/* add 1 usec to differentiate two times */
last_usec += 1;
}
- tm->usec = last_usec;
+ usec = last_usec;
}
#else
{
struct timeval tv;
gettimeofday(&tv, NULL);
- tm->sec = tv.tv_sec;
- tm->usec = tv.tv_usec;
+ sec = tv.tv_sec;
+ usec = tv.tv_usec;
}
#endif
+ tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
+ *tm = tmzero;
+ tm->sec = sec; tm->usec = usec;
tm->timezone = MRB_TIMEZONE_LOCAL;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, TRUE);
return tm;
}
@@ -343,10 +350,16 @@ mrb_time_now(mrb_state *mrb, mrb_value self)
return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb));
}
+MRB_API mrb_value
+mrb_time_at(mrb_state *mrb, double sec, double usec, enum mrb_timezone zone)
+{
+ return mrb_time_make(mrb, mrb_class_get(mrb, "Time"), sec, usec, zone);
+}
+
/* 15.2.19.6.1 */
/* Creates an instance of time at the given time in seconds, etc. */
static mrb_value
-mrb_time_at(mrb_state *mrb, mrb_value self)
+mrb_time_at_m(mrb_state *mrb, mrb_value self)
{
mrb_float f, f2 = 0;
@@ -572,10 +585,9 @@ mrb_time_asctime(mrb_state *mrb, mrb_value self)
#else
char buf[256];
- len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d",
+ len = snprintf(buf, sizeof(buf), "%s %s %2d %02d:%02d:%02d %.4d",
wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday,
d->tm_hour, d->tm_min, d->tm_sec,
- tm->timezone == MRB_TIMEZONE_UTC ? "UTC " : "",
d->tm_year + 1900);
#endif
return mrb_str_new(mrb, buf, len);
@@ -616,7 +628,7 @@ mrb_time_getutc(mrb_state *mrb, mrb_value self)
tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
*tm2 = *tm;
tm2->timezone = MRB_TIMEZONE_UTC;
- time_update_datetime(mrb, tm2);
+ time_update_datetime(mrb, tm2, TRUE);
return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
}
@@ -631,7 +643,7 @@ mrb_time_getlocal(mrb_state *mrb, mrb_value self)
tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
*tm2 = *tm;
tm2->timezone = MRB_TIMEZONE_LOCAL;
- time_update_datetime(mrb, tm2);
+ time_update_datetime(mrb, tm2, TRUE);
return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
}
@@ -709,7 +721,7 @@ mrb_time_localtime(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
tm->timezone = MRB_TIMEZONE_LOCAL;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, FALSE);
return self;
}
@@ -757,7 +769,6 @@ mrb_time_sec(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(tm->datetime.tm_sec);
}
-
/* 15.2.19.7.24 */
/* Returns a Float with the time since the epoch in seconds. */
static mrb_value
@@ -806,7 +817,7 @@ mrb_time_utc(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
tm->timezone = MRB_TIMEZONE_UTC;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, FALSE);
return self;
}
@@ -821,6 +832,15 @@ mrb_time_utc_p(mrb_state *mrb, mrb_value self)
return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC);
}
+static mrb_value
+mrb_time_to_s(mrb_state *mrb, mrb_value self)
+{
+ char buf[64];
+ struct mrb_time *tm = time_get_ptr(mrb, self);
+ const char *fmt = tm->timezone == MRB_TIMEZONE_UTC ? TO_S_FMT "UTC" : TO_S_FMT "%z";
+ size_t len = strftime(buf, sizeof(buf), fmt, &tm->datetime);
+ return mrb_str_new(mrb, buf, len);
+}
void
mrb_mruby_time_gem_init(mrb_state* mrb)
@@ -830,7 +850,7 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
tc = mrb_define_class(mrb, "Time", mrb->object_class);
MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA);
mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable"));
- mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ARG(1, 1)); /* 15.2.19.6.1 */
+ mrb_define_class_method(mrb, tc, "at", mrb_time_at_m, MRB_ARGS_ARG(1, 1)); /* 15.2.19.6.1 */
mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.2 */
mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */
mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */
@@ -841,8 +861,8 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
mrb_define_method(mrb, tc, "<=>" , mrb_time_cmp , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */
mrb_define_method(mrb, tc, "+" , mrb_time_plus , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */
mrb_define_method(mrb, tc, "-" , mrb_time_minus , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */
- mrb_define_method(mrb, tc, "to_s" , mrb_time_asctime, MRB_ARGS_NONE());
- mrb_define_method(mrb, tc, "inspect", mrb_time_asctime, MRB_ARGS_NONE());
+ mrb_define_method(mrb, tc, "to_s" , mrb_time_to_s , MRB_ARGS_NONE());
+ mrb_define_method(mrb, tc, "inspect", mrb_time_to_s , MRB_ARGS_NONE());
mrb_define_method(mrb, tc, "asctime", mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */
mrb_define_method(mrb, tc, "ctime" , mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */
mrb_define_method(mrb, tc, "day" , mrb_time_day , MRB_ARGS_NONE()); /* 15.2.19.7.6 */
diff --git a/mrbgems/mruby-time/test/time.rb b/mrbgems/mruby-time/test/time.rb
index 54c446ca3..f59e27bc1 100644
--- a/mrbgems/mruby-time/test/time.rb
+++ b/mrbgems/mruby-time/test/time.rb
@@ -2,11 +2,11 @@
# Time ISO Test
assert('Time.new', '15.2.3.3.3') do
- Time.new.class == Time
+ assert_equal(Time, Time.new.class)
end
assert('Time', '15.2.19') do
- Time.class == Class
+ assert_equal(Class, Time.class)
end
assert('Time.at', '15.2.19.6.1') do
@@ -21,30 +21,58 @@ assert('Time.at', '15.2.19.6.1') do
end
assert('Time.gm', '15.2.19.6.2') do
- Time.gm(2012, 12, 23)
+ t = Time.gm(2012, 9, 23)
+ assert_operator(2012, :eql?, t.year)
+ assert_operator( 9, :eql?, t.month)
+ assert_operator( 23, :eql?, t.day)
+ assert_operator( 0, :eql?, t.hour)
+ assert_operator( 0, :eql?, t.min)
+ assert_operator( 0, :eql?, t.sec)
+ assert_operator( 0, :eql?, t.usec)
end
assert('Time.local', '15.2.19.6.3') do
- Time.local(2012, 12, 23)
+ t = Time.local(2014, 12, 27, 18)
+ assert_operator(2014, :eql?, t.year)
+ assert_operator( 12, :eql?, t.month)
+ assert_operator( 27, :eql?, t.day)
+ assert_operator( 18, :eql?, t.hour)
+ assert_operator( 0, :eql?, t.min)
+ assert_operator( 0, :eql?, t.sec)
+ assert_operator( 0, :eql?, t.usec)
end
assert('Time.mktime', '15.2.19.6.4') do
- Time.mktime(2012, 12, 23)
+ t = Time.mktime(2013, 10, 4, 6, 15, 58, 3485)
+ assert_operator(2013, :eql?, t.year)
+ assert_operator( 10, :eql?, t.month)
+ assert_operator( 4, :eql?, t.day)
+ assert_operator( 6, :eql?, t.hour)
+ assert_operator( 15, :eql?, t.min)
+ assert_operator( 58, :eql?, t.sec)
+ assert_operator(3485, :eql?, t.usec)
end
assert('Time.now', '15.2.19.6.5') do
- Time.now.class == Time
+ assert_equal(Time, Time.now.class)
end
assert('Time.utc', '15.2.19.6.6') do
- Time.utc(2012, 12, 23)
+ t = Time.utc(2034)
+ assert_operator(2034, :eql?, t.year)
+ assert_operator( 1, :eql?, t.month)
+ assert_operator( 1, :eql?, t.day)
+ assert_operator( 0, :eql?, t.hour)
+ assert_operator( 0, :eql?, t.min)
+ assert_operator( 0, :eql?, t.sec)
+ assert_operator( 0, :eql?, t.usec)
end
assert('Time#+', '15.2.19.7.1') do
t1 = Time.at(1300000000.0)
t2 = t1.+(60)
- assert_equal(t2.utc.asctime, "Sun Mar 13 07:07:40 UTC 2011")
+ assert_equal("Sun Mar 13 07:07:40 2011", t2.utc.asctime)
assert_raise(FloatDomainError) { Time.at(0) + Float::NAN }
assert_raise(FloatDomainError) { Time.at(0) + Float::INFINITY }
@@ -55,7 +83,7 @@ assert('Time#-', '15.2.19.7.2') do
t1 = Time.at(1300000000.0)
t2 = t1.-(60)
- assert_equal(t2.utc.asctime, "Sun Mar 13 07:05:40 UTC 2011")
+ assert_equal("Sun Mar 13 07:05:40 2011", t2.utc.asctime)
assert_raise(FloatDomainError) { Time.at(0) - Float::NAN }
assert_raise(FloatDomainError) { Time.at(0) - Float::INFINITY }
@@ -67,30 +95,30 @@ assert('Time#<=>', '15.2.19.7.3') do
t2 = Time.at(1400000000.0)
t3 = Time.at(1500000000.0)
- t2.<=>(t1) == 1 and
- t2.<=>(t2) == 0 and
- t2.<=>(t3) == -1 and
- t2.<=>(nil) == nil
+ assert_equal(1, t2 <=> t1)
+ assert_equal(0, t2 <=> t2)
+ assert_equal(-1, t2 <=> t3)
+ assert_nil(t2 <=> nil)
end
assert('Time#asctime', '15.2.19.7.4') do
- Time.at(1300000000.0).utc.asctime == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("Thu Mar 4 05:06:07 1982", Time.gm(1982,3,4,5,6,7).asctime)
end
assert('Time#ctime', '15.2.19.7.5') do
- Time.at(1300000000.0).utc.ctime == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("Thu Oct 24 15:26:47 2013", Time.gm(2013,10,24,15,26,47).ctime)
end
assert('Time#day', '15.2.19.7.6') do
- Time.gm(2012, 12, 23).day == 23
+ assert_equal(23, Time.gm(2012, 12, 23).day)
end
assert('Time#dst?', '15.2.19.7.7') do
- not Time.gm(2012, 12, 23).utc.dst?
+ assert_not_predicate(Time.gm(2012, 12, 23).utc, :dst?)
end
assert('Time#getgm', '15.2.19.7.8') do
- Time.at(1300000000.0).getgm.asctime == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("Sun Mar 13 07:06:40 2011", Time.at(1300000000.0).getgm.asctime)
end
assert('Time#getlocal', '15.2.19.7.9') do
@@ -98,114 +126,120 @@ assert('Time#getlocal', '15.2.19.7.9') do
t2 = Time.at(1300000000.0)
t3 = t1.getlocal
- t1 == t3 and t3 == t2.getlocal
+ assert_equal(t1, t3)
+ assert_equal(t3, t2.getlocal)
end
assert('Time#getutc', '15.2.19.7.10') do
- Time.at(1300000000.0).getutc.asctime == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("Sun Mar 13 07:06:40 2011", Time.at(1300000000.0).getutc.asctime)
end
assert('Time#gmt?', '15.2.19.7.11') do
- Time.at(1300000000.0).utc.gmt?
+ assert_predicate(Time.at(1300000000.0).utc, :gmt?)
end
# ATM not implemented
# assert('Time#gmt_offset', '15.2.19.7.12') do
assert('Time#gmtime', '15.2.19.7.13') do
- Time.at(1300000000.0).gmtime
+ t = Time.now
+ assert_predicate(t.gmtime, :gmt?)
+ assert_predicate(t, :gmt?)
end
# ATM not implemented
# assert('Time#gmtoff', '15.2.19.7.14') do
assert('Time#hour', '15.2.19.7.15') do
- Time.gm(2012, 12, 23, 7, 6).hour == 7
+ assert_equal(7, Time.gm(2012, 12, 23, 7, 6).hour)
end
# ATM doesn't really work
# assert('Time#initialize', '15.2.19.7.16') do
assert('Time#initialize_copy', '15.2.19.7.17') do
- time_tmp_2 = Time.at(7.0e6)
- time_tmp_2.clone == time_tmp_2
+ t = Time.at(7.0e6)
+ assert_equal(t, t.clone)
end
assert('Time#localtime', '15.2.19.7.18') do
- t1 = Time.at(1300000000.0)
- t2 = Time.at(1300000000.0)
+ t1 = Time.utc(2014, 5 ,6)
+ t2 = Time.utc(2014, 5 ,6)
+ t3 = t2.getlocal
- t1.localtime
- t1 == t2.getlocal
+ assert_equal(t3, t1.localtime)
+ assert_equal(t3, t1)
end
assert('Time#mday', '15.2.19.7.19') do
- Time.gm(2012, 12, 23).mday == 23
+ assert_equal(23, Time.gm(2012, 12, 23).mday)
end
assert('Time#min', '15.2.19.7.20') do
- Time.gm(2012, 12, 23, 7, 6).min == 6
+ assert_equal(6, Time.gm(2012, 12, 23, 7, 6).min)
end
assert('Time#mon', '15.2.19.7.21') do
- Time.gm(2012, 12, 23).mon == 12
+ assert_equal(12, Time.gm(2012, 12, 23).mon)
end
assert('Time#month', '15.2.19.7.22') do
- Time.gm(2012, 12, 23).month == 12
+ assert_equal(12, Time.gm(2012, 12, 23).month)
end
assert('Times#sec', '15.2.19.7.23') do
- Time.gm(2012, 12, 23, 7, 6, 40).sec == 40
+ assert_equal(40, Time.gm(2012, 12, 23, 7, 6, 40).sec)
end
assert('Time#to_f', '15.2.19.7.24') do
- Time.at(1300000000.0).to_f == 1300000000.0
+ assert_operator(2.0, :eql?, Time.at(2).to_f)
end
assert('Time#to_i', '15.2.19.7.25') do
- Time.at(1300000000.0).to_i == 1300000000
+ assert_operator(2, :eql?, Time.at(2.0).to_i)
end
assert('Time#usec', '15.2.19.7.26') do
- Time.at(1300000000.0).usec == 0
+ assert_equal(0, Time.at(1300000000.0).usec)
end
assert('Time#utc', '15.2.19.7.27') do
- Time.at(1300000000.0).utc
+ t = Time.now
+ assert_predicate(t.utc, :gmt?)
+ assert_predicate(t, :gmt?)
end
assert('Time#utc?', '15.2.19.7.28') do
- Time.at(1300000000.0).utc.utc?
+ assert_predicate(Time.at(1300000000.0).utc, :utc?)
end
# ATM not implemented
# assert('Time#utc_offset', '15.2.19.7.29') do
assert('Time#wday', '15.2.19.7.30') do
- Time.gm(2012, 12, 23).wday == 0
+ assert_equal(0, Time.gm(2012, 12, 23).wday)
end
assert('Time#yday', '15.2.19.7.31') do
- Time.gm(2012, 12, 23).yday == 358
+ assert_equal(358, Time.gm(2012, 12, 23).yday)
end
assert('Time#year', '15.2.19.7.32') do
- Time.gm(2012, 12, 23).year == 2012
+ assert_equal(2012, Time.gm(2012, 12, 23).year)
end
assert('Time#zone', '15.2.19.7.33') do
- Time.at(1300000000.0).utc.zone == 'UTC'
+ assert_equal('UTC', Time.at(1300000000.0).utc.zone)
end
# Not ISO specified
assert('Time#to_s') do
- Time.at(1300000000.0).utc.to_s == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("2003-04-05 06:07:08 UTC", Time.gm(2003,4,5,6,7,8,9).to_s)
end
assert('Time#inspect') do
- Time.at(1300000000.0).utc.inspect == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_match("2013-10-28 16:27:48 [^U]*", Time.local(2013,10,28,16,27,48).inspect)
end
assert('day of week methods') do
@@ -224,7 +258,7 @@ assert('2000 times 500us make a second') do
2000.times do
t += 0.0005
end
- t.usec == 0
+ assert_equal(0, t.usec)
end
assert('Time.gm with Dec 31 23:59:59 1969 raise ArgumentError') do
diff --git a/mrbgems/mruby-toplevel-ext/test/toplevel.rb b/mrbgems/mruby-toplevel-ext/test/toplevel.rb
index aebdd8b4b..26aae9a7d 100644
--- a/mrbgems/mruby-toplevel-ext/test/toplevel.rb
+++ b/mrbgems/mruby-toplevel-ext/test/toplevel.rb
@@ -16,8 +16,8 @@ assert('Toplevel#include') do
self.include ToplevelTestModule2, ToplevelTestModule1
- assert_true self.class.included_modules.include?( ToplevelTestModule1 )
- assert_true self.class.included_modules.include?( ToplevelTestModule2 )
+ assert_true self.class.ancestors.include?( ToplevelTestModule1 )
+ assert_true self.class.ancestors.include?( ToplevelTestModule2 )
assert_equal :foo, method_foo
assert_equal :bar2, CONST_BAR
end