diff options
| author | YAMAMOTO Masaya <[email protected]> | 2017-11-04 01:23:12 +0900 |
|---|---|---|
| committer | YAMAMOTO Masaya <[email protected]> | 2017-11-04 01:23:12 +0900 |
| commit | 625f9f6fa314872968632c5adbee7fb3823268b8 (patch) | |
| tree | fdde1700b13048212606e4a995907f3757e18e2f /mrbgems | |
| parent | b70d69de09130ce2bc89289b4826b3deea8afaae (diff) | |
| parent | e7fe6ee2638dee438c1d79ab16a0403aebec0a60 (diff) | |
| download | mruby-625f9f6fa314872968632c5adbee7fb3823268b8.tar.gz mruby-625f9f6fa314872968632c5adbee7fb3823268b8.zip | |
Merge branch 'master' of github.com:mruby/mruby
Diffstat (limited to 'mrbgems')
26 files changed, 545 insertions, 95 deletions
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox index 64f05de10..65584681d 100644 --- a/mrbgems/default.gembox +++ b/mrbgems/default.gembox @@ -14,6 +14,9 @@ MRuby::GemBox.new do |conf| # Use standard Struct class conf.gem :core => "mruby-struct" + # Use Comparable module extension + conf.gem :core => "mruby-compar-ext" + # Use Enumerable module extension conf.gem :core => "mruby-enum-ext" diff --git a/mrbgems/mruby-array-ext/mrbgem.rake b/mrbgems/mruby-array-ext/mrbgem.rake index 882caf1ab..58d4428d4 100644 --- a/mrbgems/mruby-array-ext/mrbgem.rake +++ b/mrbgems/mruby-array-ext/mrbgem.rake @@ -2,4 +2,5 @@ MRuby::Gem::Specification.new('mruby-array-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Array class extension' + spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator' end diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb index e28e5238d..b525d3006 100644 --- a/mrbgems/mruby-array-ext/mrblib/array.rb +++ b/mrbgems/mruby-array-ext/mrblib/array.rb @@ -808,4 +808,100 @@ class Array n end end + + ## + # call-seq: + # ary.permutation { |p| block } -> ary + # ary.permutation -> Enumerator + # ary.permutation(n) { |p| block } -> ary + # ary.permutation(n) -> Enumerator + # + # When invoked with a block, yield all permutations of length +n+ of the + # elements of the array, then return the array itself. + # + # If +n+ is not specified, yield all permutations of all elements. + # + # The implementation makes no guarantees about the order in which the + # permutations are yielded. + # + # If no block is given, an Enumerator is returned instead. + # + # Examples: + # + # a = [1, 2, 3] + # a.permutation.to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] + # a.permutation(1).to_a #=> [[1],[2],[3]] + # a.permutation(2).to_a #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] + # a.permutation(3).to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] + # a.permutation(0).to_a #=> [[]] # one permutation of length 0 + # a.permutation(4).to_a #=> [] # no permutations of length 4 + def permutation(n=self.size, &block) + size = self.size + return to_enum(:permutation, n) unless block + return if n > size + if n == 0 + yield [] + else + i = 0 + while i<size + result = [self[i]] + if n-1 > 0 + ary = self[0...i] + self[i+1..-1] + ary.permutation(n-1) do |c| + yield result + c + end + else + yield result + end + i += 1 + end + end + end + + ## + # call-seq: + # ary.combination(n) { |c| block } -> ary + # ary.combination(n) -> Enumerator + # + # When invoked with a block, yields all combinations of length +n+ of elements + # from the array and then returns the array itself. + # + # The implementation makes no guarantees about the order in which the + # combinations are yielded. + # + # If no block is given, an Enumerator is returned instead. + # + # Examples: + # + # a = [1, 2, 3, 4] + # a.combination(1).to_a #=> [[1],[2],[3],[4]] + # a.combination(2).to_a #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]] + # a.combination(3).to_a #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]] + # a.combination(4).to_a #=> [[1,2,3,4]] + # a.combination(0).to_a #=> [[]] # one combination of length 0 + # a.combination(5).to_a #=> [] # no combinations of length 5 + + def combination(n, &block) + size = self.size + return to_enum(:combination, n) unless block + return if n > size + if n == 0 + yield [] + elsif n == 1 + i = 0 + while i<size + yield [self[i]] + i += 1 + end + else + i = 0 + while i<size + result = [self[i]] + self[i+1..-1].combination(n-1) do |c| + yield result + c + end + i += 1 + end + end + end end diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c index e99599b09..169f968f9 100644 --- a/mrbgems/mruby-array-ext/src/array.c +++ b/mrbgems/mruby-array-ext/src/array.c @@ -176,14 +176,16 @@ 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_value index; mrb_value val; mrb_value *ptr; mrb_value ary; mrb_ary_modify(mrb, a); - if (mrb_get_args(mrb, "o|i", &index, &len) == 1) { + if (mrb_get_argc(mrb) == 1) { + mrb_value index; + + 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) { @@ -201,7 +203,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self) } } - i = mrb_fixnum(index); + mrb_get_args(mrb, "ii", &i, &len); delete_pos_len: if (i < 0) i += alen; if (i < 0 || alen < i) return mrb_nil_value(); diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb index c0db1b1cc..bf96b885e 100644 --- a/mrbgems/mruby-array-ext/test/array.rb +++ b/mrbgems/mruby-array-ext/test/array.rb @@ -381,3 +381,31 @@ assert("Array#slice!") do assert_equal(i, [1, 2, 3]) assert_equal(j, nil) end + +assert("Array#permutation") do + a = [1, 2, 3] + assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], + a.permutation.to_a) + assert_equal([[1],[2],[3]], + a.permutation(1).to_a) + assert_equal([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]], + a.permutation(2).to_a) + assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], + a.permutation(3).to_a) + assert_equal([[]], a.permutation(0).to_a) + assert_equal([], a.permutation(4).to_a) +end + +assert("Array#combination") do + a = [1, 2, 3, 4] + assert_equal([[1],[2],[3],[4]], + a.combination(1).to_a) + assert_equal([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], + a.combination(2).to_a) + assert_equal([[1,2,3],[1,2,4],[1,3,4],[2,3,4]], + a.combination(3).to_a) + assert_equal([[1,2,3,4]], + a.combination(4).to_a) + assert_equal([[]], a.combination(0).to_a) + assert_equal([], a.combination(5).to_a) +end diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index fe311d830..7e5fd392f 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -544,6 +544,13 @@ done: if (args.verbose) { mrb_codedump_all(mrb, proc); } + /* adjust stack length of toplevel environment */ + if (mrb->c->cibase->env) { + struct REnv *e = mrb->c->cibase->env; + if (e && MRB_ENV_STACK_LEN(e) < proc->body.irep->nlocals) { + MRB_ENV_SET_STACK_LEN(e, proc->body.irep->nlocals); + } + } /* pass a proc for evaluation */ /* evaluate the bytecode */ result = mrb_vm_run(mrb, @@ -577,6 +584,8 @@ done: mrb_free(mrb, history_path); #endif + if (args.rfp) fclose(args.rfp); + mrb_free(mrb, args.argv); mrbc_context_free(mrb, cxt); mrb_close(mrb); diff --git a/mrbgems/mruby-compar-ext/mrbgem.rake b/mrbgems/mruby-compar-ext/mrbgem.rake new file mode 100644 index 000000000..dcf584339 --- /dev/null +++ b/mrbgems/mruby-compar-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-compar-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Enumerable module extension' +end diff --git a/mrbgems/mruby-compar-ext/mrblib/compar.rb b/mrbgems/mruby-compar-ext/mrblib/compar.rb new file mode 100644 index 000000000..d66f816ef --- /dev/null +++ b/mrbgems/mruby-compar-ext/mrblib/compar.rb @@ -0,0 +1,31 @@ +module Comparable + ## + # Returns <i>min</i> if <i>obj</i> <code><=></code> <i>min</i> is less + # than zero, <i>max</i> if <i>obj</i> <code><=></code> <i>max</i> is + # greater than zero and <i>obj</i> otherwise. + # + # 12.clamp(0, 100) #=> 12 + # 523.clamp(0, 100) #=> 100 + # -3.123.clamp(0, 100) #=> 0 + # + # 'd'.clamp('a', 'f') #=> 'd' + # 'z'.clamp('a', 'f') #=> 'f' + # + def clamp(min, max) + if (min <=> max) > 0 + raise ArgumentError, "min argument must be smaller than max argument" + end + c = self <=> min + if c == 0 + return self + elsif c < 0 + return min + end + c = self <=> max + if c > 0 + return max + else + return self + end + end +end diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index ba8ed2c71..fce5ac490 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -278,6 +278,19 @@ genop_peep(codegen_scope *s, mrb_code i, int val) } } 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)); @@ -335,6 +348,14 @@ genop_peep(codegen_scope *s, mrb_code i, int val) 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)]; @@ -826,10 +847,10 @@ gen_values(codegen_scope *s, node *t, int val, int extra) codegen(s, t->car, VAL); pop(); pop(); if (is_splat) { - genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); + genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL); } else { - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL); } } t = t->cdr; @@ -838,10 +859,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(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); + genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL); } else { - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL); } t = t->cdr; } @@ -2146,7 +2167,7 @@ codegen(codegen_scope *s, node *tree, int val) while (up) { idx = lv_idx(up, nsym(tree)); if (idx > 0) { - genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv)); + genop_peep(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv), VAL); break; } lv++; @@ -3031,6 +3052,9 @@ mrb_generate_code(mrb_state *mrb, parser_state *p) mrb_irep_decref(mrb, scope->irep); mrb_pool_close(scope->mpool); proc->c = NULL; + if (mrb->c->cibase && mrb->c->cibase->proc == proc->upper) { + proc->upper = NULL; + } mrb->jmp = prev_jmp; return proc; } diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index b4a70873d..01269d8da 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -2579,7 +2579,7 @@ do_block : keyword_do_block local_nest(p); } opt_block_param - compstmt + bodystmt keyword_end { $$ = new_block(p,$3,$4); @@ -2669,7 +2669,7 @@ brace_block : '{' $<num>$ = p->lineno; } opt_block_param - compstmt keyword_end + bodystmt keyword_end { $$ = new_block(p,$3,$4); SET_LINENO($$, $<num>2); @@ -5731,7 +5731,7 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f) new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length); if (p->filename_table) { - memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length); + memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->current_filename_index); } p->filename_table = new_table; p->filename_table[p->filename_table_length - 1] = sym; @@ -5833,7 +5833,7 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c) c->keep_lv = TRUE; } } - proc->target_class = target; + MRB_PROC_SET_TARGET_CLASS(proc, target); if (mrb->c->ci) { mrb->c->ci->target_class = target; } diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index 7741e515d..327b573d4 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -708,4 +708,20 @@ module Enumerable def nil.to_h {} end + + def uniq(&block) + hash = {} + if block + self.each do|*v| + v = v.__svalue + hash[block.call(v)] ||= v + end + else + self.each do|*v| + v = v.__svalue + hash[v] ||= v + end + end + hash.values + end end diff --git a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb index c98681edf..9227abe8a 100644 --- a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb +++ b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb @@ -158,6 +158,21 @@ class Enumerator } end + def uniq(&block) + hash = {} + Lazy.new(self){|yielder, val| + if block + v = block.call(val) + else + v = val + end + unless hash.include?(v) + yielder << val + hash[v] = val + end + } + end + alias force to_a end end diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb index 763cd36e2..37169ade9 100644 --- a/mrbgems/mruby-enumerator/test/enumerator.rb +++ b/mrbgems/mruby-enumerator/test/enumerator.rb @@ -33,7 +33,7 @@ assert 'Enumerator.new' do a, b = b, a + b end end - assert_equal fib.take(10), [1,1,2,3,5,8,13,21,34,55] + assert_equal [1,1,2,3,5,8,13,21,34,55], fib.take(10) end assert 'Enumerator#initialize_copy' do diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c index 7fd4f1437..997b69e25 100644 --- a/mrbgems/mruby-eval/src/eval.c +++ b/mrbgems/mruby-eval/src/eval.c @@ -12,29 +12,14 @@ mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self); static struct mrb_irep * get_closure_irep(mrb_state *mrb, int level) { - struct mrb_context *c = mrb->c; - struct REnv *e = c->ci[-1].proc->env; - struct RProc *proc; + struct RProc *proc = mrb->c->ci[-1].proc; - if (level == 0) { - proc = c->ci[-1].proc; - if (MRB_PROC_CFUNC_P(proc)) { - return NULL; - } - return proc->body.irep; + while (level--) { + if (!proc) return NULL; + proc = proc->upper; } - - while (--level) { - e = (struct REnv*)e->c; - if (!e) return NULL; - } - - if (!e) return NULL; - if (!MRB_ENV_STACK_SHARED_P(e)) return NULL; - c = e->cxt.c; - proc = c->cibase[e->cioff].proc; - - if (!proc || MRB_PROC_CFUNC_P(proc)) { + if (!proc) return NULL; + if (MRB_PROC_CFUNC_P(proc)) { return NULL; } return proc->body.irep; @@ -67,7 +52,7 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest) int pos; for (level = 0; (virep = get_closure_irep(mrb, level)); level++) { - if (!virep || virep->lv == NULL) { + if (virep->lv == NULL) { continue; } for (pos = 0; pos < virep->nlocals - 1; pos++) { @@ -130,7 +115,7 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top) if (GETARG_C(c) != 0) { break; } - { + else { mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest); if (arg != 0) { /* must replace */ @@ -204,7 +189,9 @@ 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; - struct mrb_context *c = mrb->c; + mrb_callinfo *ci = &mrb->c->ci[-1]; /* callinfo of eval caller */ + struct RClass *target_class = NULL; + int bidx; if (!mrb_nil_p(binding)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Binding of eval must be nil."); @@ -251,19 +238,29 @@ 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"); } - if (c->ci[-1].proc->target_class) { - proc->target_class = c->ci[-1].proc->target_class; + target_class = MRB_PROC_TARGET_CLASS(ci->proc); + if (!MRB_PROC_CFUNC_P(ci->proc)) { + if (ci->env) { + e = ci->env; + } + else { + e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, + (struct RClass*)target_class); + e->mid = ci->mid; + e->stack = ci[1].stackent; + e->cxt = mrb->c; + MRB_ENV_SET_STACK_LEN(e, ci->proc->body.irep->nlocals); + bidx = ci->argc; + if (ci->argc < 0) bidx = 2; + else bidx += 1; + MRB_ENV_SET_BIDX(e, bidx); + } + proc->e.env = e; + proc->flags |= MRB_PROC_ENVSET; + mrb_field_write_barrier(mrb, (struct RBasic*)proc, (struct RBasic*)e); } - e = c->ci[-1].proc->env; - if (!e) e = c->ci[-1].env; - e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)e); - e->cxt.c = c; - e->cioff = c->ci - c->cibase; - e->stack = c->ci->stackent; - MRB_SET_ENV_STACK_LEN(e, c->ci->proc->body.irep->nlocals); - c->ci->target_class = proc->target_class; - c->ci->env = 0; - proc->env = e; + proc->upper = ci->proc; + mrb->c->ci->target_class = target_class; patch_irep(mrb, proc->body.irep, 0, proc->body.irep); /* mrb_codedump_all(mrb, proc); */ @@ -322,8 +319,7 @@ f_instance_eval(mrb_state *mrb, mrb_value self) mrb_get_args(mrb, "s|zi", &s, &len, &file, &line); cv = mrb_singleton_class(mrb, self); proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line); - proc->target_class = mrb_class_ptr(cv); - mrb->c->ci->env = NULL; + MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(cv)); mrb_assert(!MRB_PROC_CFUNC_P(proc)); return exec_irep(mrb, self, proc); } diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c index 57fe9401c..be9033063 100644 --- a/mrbgems/mruby-fiber/src/fiber.c +++ b/mrbgems/mruby-fiber/src/fiber.c @@ -123,7 +123,7 @@ fiber_init(mrb_state *mrb, mrb_value self) /* adjust return callinfo */ ci = c->ci; - ci->target_class = p->target_class; + ci->target_class = MRB_PROC_TARGET_CLASS(p); ci->proc = p; mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p); ci->pc = p->body.irep->iseq; @@ -213,7 +213,7 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr *b++ = *a++; } c->cibase->argc = (int)len; - value = c->stack[0] = c->ci->proc->env->stack[0]; + value = c->stack[0] = MRB_PROC_ENV(c->ci->proc)->stack[0]; } else { value = fiber_result(mrb, a, len); diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb index 73d1fbe6d..d1a709325 100644 --- a/mrbgems/mruby-hash-ext/mrblib/hash.rb +++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb @@ -474,4 +474,29 @@ class Hash end self end + + def to_proc + ->x{self[x]} + end + + ## + # call-seq: + # hsh.fetch_values(key, ...) -> array + # hsh.fetch_values(key, ...) { |key| block } -> array + # + # Returns an array containing the values associated with the given keys + # but also raises <code>KeyError</code> when one of keys can't be found. + # Also see <code>Hash#values_at</code> and <code>Hash#fetch</code>. + # + # h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } + # + # h.fetch_values("cow", "cat") #=> ["bovine", "feline"] + # h.fetch_values("cow", "bird") # raises KeyError + # h.fetch_values("cow", "bird") { |k| k.upcase } #=> ["bovine", "BIRD"] + # + def fetch_values(*keys, &block) + keys.map do |k| + self.fetch(k, &block) + end + end end diff --git a/mrbgems/mruby-kernel-ext/mrblib/kernel.rb b/mrbgems/mruby-kernel-ext/mrblib/kernel.rb new file mode 100644 index 000000000..25a4d4ed4 --- /dev/null +++ b/mrbgems/mruby-kernel-ext/mrblib/kernel.rb @@ -0,0 +1,13 @@ +module Kernel + # call-seq: + # obj.yield_self {|_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 +end diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c index 33abe1531..32d86376a 100644 --- a/mrbgems/mruby-kernel-ext/src/kernel.c +++ b/mrbgems/mruby-kernel-ext/src/kernel.c @@ -224,6 +224,22 @@ mrb_f_hash(mrb_state *mrb, mrb_value self) 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; +} + void mrb_mruby_kernel_ext_gem_init(mrb_state *mrb) { @@ -239,6 +255,7 @@ 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-numeric-ext/mrblib/numeric_ext.rb b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb index 0bf3c6ab6..f250538fe 100644 --- a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb +++ b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb @@ -14,4 +14,12 @@ module Integral self end end + + def positive? + self > 0 + end + + def negative? + self < 0 + end end diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index c3a7eb380..cb36d9a48 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -144,7 +144,20 @@ class String def casecmp(str) self.downcase <=> str.to_str.downcase rescue NoMethodError - raise TypeError, "no implicit conversion of #{str.class} into String" + nil + end + + ## + # call-seq: + # str.casecmp?(other) -> true, false, or nil + # + # Returns true if str and other_str are equal after case folding, + # false if they are not equal, and nil if other_str is not a string. + + def casecmp?(str) + c = self.casecmp(str) + return nil if c.nil? + return c == 0 end def partition(sep) diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index ac3e82a45..ddceb03ff 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -44,12 +44,13 @@ mrb_str_byteslice(mrb_state *mrb, mrb_value str) { mrb_value a1; mrb_int len; - mrb_int argc; - argc = mrb_get_args(mrb, "o|i", &a1, &len); - if (argc == 2) { + if (mrb_get_argc(mrb) == 2) { + mrb_int pos; + mrb_get_args(mrb, "ii", &pos, &len); return mrb_str_substr(mrb, str, mrb_fixnum(a1), len); } + mrb_get_args(mrb, "o|i", &a1, &len); switch (mrb_type(a1)) { case MRB_TT_RANGE: { @@ -652,6 +653,118 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg) return beg; } +/* + * call-seq: + * str.delete_prefix!(prefix) -> self or nil + * + * Deletes leading <code>prefix</code> from <i>str</i>, returning + * <code>nil</code> if no change was made. + * + * "hello".delete_prefix!("hel") #=> "lo" + * "hello".delete_prefix!("llo") #=> nil + */ +static mrb_value +mrb_str_del_prefix_bang(mrb_state *mrb, mrb_value self) +{ + mrb_int plen, slen; + char *ptr, *s; + struct RString *str = RSTRING(self); + + mrb_get_args(mrb, "s", &ptr, &plen); + slen = RSTR_LEN(str); + if (plen > slen) return mrb_nil_value(); + s = RSTR_PTR(str); + if (memcmp(s, ptr, plen) != 0) return mrb_nil_value(); + if (!MRB_FROZEN_P(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) { + str->as.heap.ptr += plen; + } + else { + mrb_str_modify(mrb, str); + s = RSTR_PTR(str); + memmove(s, s+plen, slen-plen); + } + RSTR_SET_LEN(str, slen-plen); + return self; +} + +/* + * call-seq: + * str.delete_prefix(prefix) -> new_str + * + * Returns a copy of <i>str</i> with leading <code>prefix</code> deleted. + * + * "hello".delete_prefix("hel") #=> "lo" + * "hello".delete_prefix("llo") #=> "hello" + */ +static mrb_value +mrb_str_del_prefix(mrb_state *mrb, mrb_value self) +{ + mrb_int plen, slen; + char *ptr; + + mrb_get_args(mrb, "s", &ptr, &plen); + slen = RSTRING_LEN(self); + if (plen > slen) return mrb_str_dup(mrb, self); + if (memcmp(RSTRING_PTR(self), ptr, plen) != 0) + return mrb_str_dup(mrb, self); + return mrb_str_substr(mrb, self, plen, slen-plen); +} + +/* + * call-seq: + * str.delete_suffix!(suffix) -> self or nil + * + * Deletes trailing <code>suffix</code> from <i>str</i>, returning + * <code>nil</code> if no change was made. + * + * "hello".delete_suffix!("llo") #=> "he" + * "hello".delete_suffix!("hel") #=> nil + */ +static mrb_value +mrb_str_del_suffix_bang(mrb_state *mrb, mrb_value self) +{ + mrb_int plen, slen; + char *ptr, *s; + struct RString *str = RSTRING(self); + + mrb_get_args(mrb, "s", &ptr, &plen); + slen = RSTR_LEN(str); + if (plen > slen) return mrb_nil_value(); + s = RSTR_PTR(str); + if (memcmp(s+slen-plen, ptr, plen) != 0) return mrb_nil_value(); + if (!MRB_FROZEN_P(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) { + /* no need to modify string */ + } + else { + mrb_str_modify(mrb, str); + } + RSTR_SET_LEN(str, slen-plen); + return self; +} + +/* + * call-seq: + * str.delete_suffix(suffix) -> new_str + * + * Returns a copy of <i>str</i> with leading <code>suffix</code> deleted. + * + * "hello".delete_suffix("hel") #=> "lo" + * "hello".delete_suffix("llo") #=> "hello" + */ +static mrb_value +mrb_str_del_suffix(mrb_state *mrb, mrb_value self) +{ + mrb_int plen, slen; + char *ptr; + + mrb_get_args(mrb, "s", &ptr, &plen); + slen = RSTRING_LEN(self); + if (plen > slen) return mrb_str_dup(mrb, self); + if (memcmp(RSTRING_PTR(self)+slen-plen, ptr, plen) != 0) + return mrb_str_dup(mrb, self); + return mrb_str_substr(mrb, self, 0, slen-plen); +} + void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { @@ -675,8 +788,12 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) 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_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE()); - mrb_define_method(mrb, s, "upto", mrb_str_upto, MRB_ARGS_ANY()); + mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "upto", mrb_str_upto, MRB_ARGS_ANY()); + 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)); + mrb_define_method(mrb, s, "delete_suffix!", mrb_str_del_suffix_bang, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, s, "delete_suffix", mrb_str_del_suffix, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE()); } diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb index 2a568c7d6..b6146fb90 100644 --- a/mrbgems/mruby-string-ext/test/string.rb +++ b/mrbgems/mruby-string-ext/test/string.rb @@ -1,3 +1,4 @@ +# coding: utf-8 ## # String(Ext) Test @@ -665,3 +666,17 @@ assert('String#each_codepoint(UTF-8)') do end assert_equal expect, cp end if UTF8STRING + +assert('String#delete_prefix') do + assert_equal "llo", "hello".delete_prefix("he") + assert_equal "hello", "hello".delete_prefix("llo") + assert_equal "llo", "hello".delete_prefix!("he") + assert_nil "hello".delete_prefix!("llo") +end + +assert('String#delete_suffix') do + assert_equal "he", "hello".delete_suffix("llo") + assert_equal "hello", "hello".delete_suffix("he") + assert_equal "he", "hello".delete_suffix!("llo") + assert_nil "hello".delete_suffix!("he") +end diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index 67762a948..1d2e62583 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -12,6 +12,7 @@ #include <mruby/variable.h> #include <mruby/hash.h> #include <mruby/range.h> +#include <mruby/proc.h> #define RSTRUCT_LEN(st) RARRAY_LEN(st) #define RSTRUCT_PTR(st) RARRAY_PTR(st) @@ -113,12 +114,14 @@ mrb_struct_members(mrb_state *mrb, mrb_value obj) return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); } -static mrb_value struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id); - static mrb_value mrb_struct_ref(mrb_state *mrb, mrb_value obj) { - return struct_aref_sym(mrb, obj, mrb->c->ci->mid); + mrb_int i = mrb_fixnum(mrb_proc_cfunc_env_get(mrb, 0)); + mrb_value *ptr = RSTRUCT_PTR(obj); + + if (!ptr) return mrb_nil_value(); + return ptr[i]; } static mrb_sym @@ -140,24 +143,23 @@ mrb_id_attrset(mrb_state *mrb, mrb_sym id) return mid; } -static mrb_value mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val); - static mrb_value mrb_struct_set_m(mrb_state *mrb, mrb_value obj) { + mrb_int i = mrb_fixnum(mrb_proc_cfunc_env_get(mrb, 0)); + mrb_value *ptr; mrb_value val; - const char *name; - mrb_int slen; - mrb_sym mid; - mrb_get_args(mrb, "o", &val); - - /* get base id */ - name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen); - mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */ - - return mrb_struct_aset_sym(mrb, obj, mid, val); + mrb_struct_modify(mrb, obj); + ptr = RSTRUCT_PTR(obj); + if (ptr == NULL || i >= RSTRUCT_LEN(obj)) { + mrb_ary_set(mrb, obj, i, val); + } + else { + ptr[i] = val; + } + return val; } static mrb_bool @@ -187,15 +189,18 @@ make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c const char *name = mrb_sym2name_len(mrb, id, NULL); if (is_local_id(mrb, name) || is_const_id(mrb, name)) { - mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE()); - mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1)); + 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_define_method_raw(mrb, c, id, aref); + mrb_define_method_raw(mrb, c, mrb_id_attrset(mrb, id), aset); mrb_gc_arena_restore(mrb, ai); } } } static mrb_value -make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass) +make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass *klass) { mrb_value nstr; mrb_sym id; @@ -278,17 +283,21 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass) name = mrb_nil_value(); mrb_get_args(mrb, "*&", &argv, &argc, &b); if (argc == 0) { /* special case to avoid crash */ - rest = mrb_ary_new(mrb); + mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } else { - if (argc > 0) name = argv[0]; - pargv = &argv[1]; - argcnt = argc-1; - if (!mrb_nil_p(name) && mrb_symbol_p(name)) { - /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */ - name = mrb_nil_value(); - pargv = &argv[0]; - argcnt++; + pargv = argv; + argcnt = argc; + if (argc > 0) { + name = argv[0]; + if (mrb_symbol_p(name)) { + /* 1stArgument:symbol -> name=nil rest=argv[0..n] */ + name = mrb_nil_value(); + } + else { + pargv++; + argcnt--; + } } rest = mrb_ary_new_from_values(mrb, argcnt, pargv); for (i=0; i<RARRAY_LEN(rest); i++) { diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb index 421fe4b5d..982e344e2 100644 --- a/mrbgems/mruby-struct/test/struct.rb +++ b/mrbgems/mruby-struct/test/struct.rb @@ -11,13 +11,6 @@ assert('Struct.new', '15.2.18.3.1') do assert_equal [:m1, :m2], c.members end -# Check crash bug with Struc.new and no params. -assert('Struct.new', '15.2.18.3.1') do - c = Struct.new() - assert_equal Struct, c.superclass - assert_equal [], c.members -end - assert('Struct#==', '15.2.18.4.1') do c = Struct.new(:m1, :m2) cc1 = c.new(1,2) @@ -192,7 +185,7 @@ assert("Struct.new generates subclass of Struct") do begin original_struct = Struct Struct = String - assert_equal original_struct, original_struct.new.superclass + assert_equal original_struct, original_struct.new(:foo).superclass ensure Struct = original_struct end diff --git a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb index 1e3d24b80..28cce3156 100644 --- a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb +++ b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb @@ -48,10 +48,23 @@ class Symbol def casecmp(other) return nil unless other.kind_of?(Symbol) lhs = self.to_s; lhs.upcase! - rhs = other.to_s; rhs.upcase! + rhs = other.to_s.upcase lhs <=> rhs end + ## + # call-seq: + # sym.casecmp?(other) -> true, false, or nil + # + # Returns true if sym and other_sym are equal after case folding, + # false if they are not equal, and nil if other_sym is not a string. + + def casecmp?(sym) + c = self.casecmp(sym) + return nil if c.nil? + return c == 0 + end + # # call-seq: # sym.empty? -> true or false diff --git a/mrbgems/mruby-test/driver.c b/mrbgems/mruby-test/driver.c index bc8cce607..434d1fee5 100644 --- a/mrbgems/mruby-test/driver.c +++ b/mrbgems/mruby-test/driver.c @@ -66,6 +66,7 @@ t_printstr(mrb_state *mrb, mrb_value obj) s = RSTRING_PTR(obj); len = RSTRING_LEN(obj); fwrite(s, len, 1, stdout); + fflush(stdout); } } |
