diff options
| author | Yukihiro "Matz" Matsumoto <[email protected]> | 2019-07-17 10:35:41 +0900 |
|---|---|---|
| committer | GitHub <[email protected]> | 2019-07-17 10:35:41 +0900 |
| commit | d605b72c1d6fa4564a0a5e88535504b6850463b5 (patch) | |
| tree | 774fc0de56002abb3bb2b1c3387ff08f91876d17 /mrbgems | |
| parent | 2af92d0ebcbeca6d3d85a27c8193273080a63090 (diff) | |
| parent | 9af3b7c6258de327218dd04e69d76ae68caf17b1 (diff) | |
| download | mruby-d605b72c1d6fa4564a0a5e88535504b6850463b5.tar.gz mruby-d605b72c1d6fa4564a0a5e88535504b6850463b5.zip | |
Merge branch 'master' into i110/inspect-recursion
Diffstat (limited to 'mrbgems')
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 |
