diff options
| -rw-r--r-- | doc/opcode.md | 8 | ||||
| -rw-r--r-- | include/mruby/hash.h | 27 | ||||
| -rw-r--r-- | include/mruby/ops.h | 11 | ||||
| -rw-r--r-- | mrbgems/mruby-compiler/core/codegen.c | 149 | ||||
| -rw-r--r-- | mrbgems/mruby-compiler/core/node.h | 4 | ||||
| -rw-r--r-- | mrbgems/mruby-compiler/core/parse.y | 493 | ||||
| -rw-r--r-- | mrbgems/mruby-objectspace/src/mruby_objectspace.c | 2 | ||||
| -rw-r--r-- | mrbgems/mruby-proc-ext/src/proc.c | 10 | ||||
| -rw-r--r-- | src/codedump.c | 50 | ||||
| -rw-r--r-- | src/hash.c | 86 | ||||
| -rw-r--r-- | src/kernel.c | 10 | ||||
| -rw-r--r-- | src/vm.c | 146 | ||||
| -rw-r--r-- | test/t/syntax.rb | 183 |
13 files changed, 887 insertions, 292 deletions
diff --git a/doc/opcode.md b/doc/opcode.md index d904256e5..54d1f0553 100644 --- a/doc/opcode.md +++ b/doc/opcode.md @@ -77,7 +77,7 @@ with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed. |OP_SENDB" |BBB |R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1)) |OP_CALL' |B |R(a) = self.call(frame.argc, frame.argv) |OP_SUPER' |BB |R(a) = super(R(a+1),... ,R(a+b+1)) -|OP_ARGARY' |BS |R(a) = argument array (16=6:1:5:4) +|OP_ARGARY' |BS |R(a) = argument array (16=5:1:5:1:4) |OP_ENTER |W |arg setup according to flags (23=5:5:1:5:5:1:1) |OP_KARG" |BB |R(a) = kdict[Syms(Bx)] # todo |OP_KARG2" |BB |R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo @@ -85,7 +85,7 @@ with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed. |OP_RETURN' |B |return R(a) (normal) |OP_RETURN_BLK' |B |return R(a) (in-block return) |OP_BREAK' |B |break R(a) -|OP_BLKPUSH' |BS |R(a) = block (16=6:1:5:4) +|OP_BLKPUSH' |BS |R(a) = block (16=5:1:5:1:4) |OP_ADD" |BB |R(a) = R(a)+R(a+1) (Syms[b]=:+) |OP_ADDI" |BBB |R(a) = R(a)+mrb_int(c) (Syms[b]=:+) |OP_SUB" |BB |R(a) = R(a)-R(a+1) (Syms[b]=:-) @@ -207,7 +207,7 @@ with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed. |OP_SENDB" |BBB |R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1)) |OP_CALL' |B |R(a) = self.call(frame.argc, frame.argv) |OP_SUPER' |BB |R(a) = super(R(a+1),... ,R(a+b+1)) -|OP_ARGARY' |BS |R(a) = argument array (16=6:1:5:4) +|OP_ARGARY' |BS |R(a) = argument array (16=5:1:5:1:4) |OP_ENTER |W |arg setup according to flags (23=5:5:1:5:5:1:1) |OP_KARG" |BB |R(a) = kdict[Syms(Bx)] # todo |OP_KARG2" |BB |R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo @@ -215,7 +215,7 @@ with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed. |OP_RETURN' |B |return R(a) (normal) |OP_RETURN_BLK' |B |return R(a) (in-block return) |OP_BREAK' |B |break R(a) -|OP_BLKPUSH' |BS |R(a) = block (16=6:1:5:4) +|OP_BLKPUSH' |BS |R(a) = block (16=5:1:5:1:4) |OP_ADD" |BB |R(a) = R(a)+R(a+1) (Syms[b]=:+) |OP_ADDI" |BBB |R(a) = R(a)+mrb_int(c) (Syms[b]=:+) |OP_SUB" |BB |R(a) = R(a)-R(a+1) (Syms[b]=:-) diff --git a/include/mruby/hash.h b/include/mruby/hash.h index 1a870785a..9a3812850 100644 --- a/include/mruby/hash.h +++ b/include/mruby/hash.h @@ -25,6 +25,7 @@ struct RHash { #define mrb_hash_value(p) mrb_obj_value((void*)(p)) MRB_API mrb_value mrb_hash_new_capa(mrb_state*, mrb_int); +MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); /* * Initializes a new hash. @@ -110,7 +111,19 @@ MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value * @return An array with the keys of the hash. */ MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash); -MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); +/* + * Check if the hash has the key. + * + * Equivalent to: + * + * hash.key?(key) + * + * @param mrb The mruby state reference. + * @param hash The target hash. + * @param key The key to check existence. + * @return True if the hash has the key + */ +MRB_API mrb_bool mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key); /* * Check if the hash is empty @@ -123,7 +136,7 @@ MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); * @param self The target hash. * @return True if the hash is empty, false otherwise. */ -MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self); +MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self); /* * Gets an array of values. @@ -151,6 +164,16 @@ MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash); */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash); +/* + * Copies the hash. + * + * + * @param mrb The mruby state reference. + * @param hash The target hash. + * @return The copy of the hash + */ +MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value hash); + /* declaration of struct kh_ht */ /* be careful when you touch the internal */ typedef struct { diff --git a/include/mruby/ops.h b/include/mruby/ops.h index 882ad6f25..9675d6158 100644 --- a/include/mruby/ops.h +++ b/include/mruby/ops.h @@ -61,15 +61,16 @@ OPCODE(SEND, BBB) /* R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c)) */ OPCODE(SENDB, BBB) /* R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1)) */ OPCODE(CALL, Z) /* R(0) = self.call(frame.argc, frame.argv) */ OPCODE(SUPER, BB) /* R(a) = super(R(a+1),... ,R(a+b+1)) */ -OPCODE(ARGARY, BS) /* R(a) = argument array (16=6:1:5:4) */ -OPCODE(ENTER, W) /* arg setup according to flags (23=5:5:1:5:5:1:1) */ -OPCODE(KARG, BB) /* R(a) = kdict[Syms(Bx)] # todo */ -OPCODE(KARG2, BB) /* R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo */ +OPCODE(ARGARY, BS) /* R(a) = argument array (16=m5:r1:m5:d1:lv4) */ +OPCODE(ENTER, W) /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */ +OPCODE(KEY_P, BB) /* R(a) = kdict.key?(Syms(b)) # todo */ +OPCODE(KEYEND, Z) /* raise unless kdict.empty? # todo */ +OPCODE(KARG, BB) /* R(a) = kdict[Syms(b)]; kdict.delete(Syms(b)) # todo */ OPCODE(KDICT, B) /* R(a) = kdict # todo */ OPCODE(RETURN, B) /* return R(a) (normal) */ OPCODE(RETURN_BLK, B) /* return R(a) (in-block return) */ OPCODE(BREAK, B) /* break R(a) */ -OPCODE(BLKPUSH, BS) /* R(a) = block (16=6:1:5:4) */ +OPCODE(BLKPUSH, BS) /* R(a) = block (16=m5:r1:m5:d1:lv4) */ OPCODE(ADD, BB) /* R(a) = R(a)+R(a+1) (Syms[b]=:+) */ OPCODE(ADDI, BBB) /* R(a) = R(a)+mrb_int(c) (Syms[b]=:+) */ OPCODE(SUB, BB) /* R(a) = R(a)-R(a+1) (Syms[b]=:-) */ diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 3ba2324fb..070aaac51 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -420,6 +420,9 @@ 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 { @@ -454,6 +457,25 @@ gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep) } static void +gen_return(codegen_scope *s, uint8_t op, uint16_t src) +{ + 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 { + genop_1(s, op, src); + } + } +} + +static void gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst, uint16_t idx) { if (no_peephole(s)) { @@ -514,30 +536,28 @@ dispatch_linked(codegen_scope *s, uint16_t pos) #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 >= 0xffff) { + 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 >= 0xffff) { - 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 @@ -644,8 +664,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) { @@ -694,7 +718,7 @@ for_body(codegen_scope *s, node *tree) /* loop body */ codegen(s, tree->cdr->cdr->car, VAL); pop(); - genop_1(s, OP_RETURN, cursp()); + gen_return(s, OP_RETURN, cursp()); loop_pop(s, NOVAL); scope_finish(s); s = prev; @@ -726,32 +750,44 @@ lambda_body(codegen_scope *s, node *tree, int blk) int ma, oa, ra, pa, ka, kd, ba; int pos, i; node *n, *opt; + node *tail; + /* mandatory arguments */ ma = node_len(tree->car->car); n = tree->car->car; while (n) { n = n->cdr; } + 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; - + /* 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); + 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); @@ -776,11 +812,50 @@ lambda_body(codegen_scope *s, node *tree, int blk) if (oa > 0) { dispatch(s, pos+i*3+1); } + + 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, cursp(), new_sym(s, kwd_sym)); + jmpif_key_p = genjmp2(s, OP_JMPIF, cursp(), 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); + } + } } codegen(s, tree->cdr->car, VAL); pop(); if (s->pc > 0) { - genop_1(s, OP_RETURN, cursp()); + gen_return(s, OP_RETURN, cursp()); } if (blk) { loop_pop(s, NOVAL); @@ -798,7 +873,7 @@ scope_body(codegen_scope *s, node *tree, int val) } codegen(scope, tree->cdr, VAL); - genop_1(scope, OP_RETURN, scope->sp-1); + gen_return(scope, OP_RETURN, scope->sp-1); if (!s->iseq) { genop_0(scope, OP_STOP); } @@ -810,9 +885,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) { @@ -1703,11 +1775,18 @@ 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) { + if (nt == NODE_KW_HASH && + nint(tree->car->car->car) == NODE_KW_REST_ARGS) { + tree = tree->cdr; + continue; + } + codegen(s, tree->car->car, val); codegen(s, tree->car->cdr, val); len++; @@ -2055,10 +2134,10 @@ codegen(codegen_scope *s, node *tree, int val) genop_1(s, OP_LOADNIL, cursp()); } if (s->loop) { - genop_1(s, OP_RETURN_BLK, cursp()); + gen_return(s, OP_RETURN_BLK, cursp()); } else { - genop_1(s, OP_RETURN, cursp()); + gen_return(s, OP_RETURN, cursp()); } if (val) push(); break; @@ -2116,7 +2195,7 @@ codegen(codegen_scope *s, node *tree, int val) else { genop_1(s, OP_LOADNIL, cursp()); } - genop_1(s, OP_RETURN, cursp()); + gen_return(s, OP_RETURN, cursp()); } if (val) push(); break; @@ -2999,7 +3078,7 @@ loop_break(codegen_scope *s, node *tree) if (!tree) { genop_1(s, OP_LOADNIL, cursp()); } - genop_1(s, OP_BREAK, cursp()); + gen_return(s, OP_BREAK, cursp()); } } } diff --git a/mrbgems/mruby-compiler/core/node.h b/mrbgems/mruby-compiler/core/node.h index 9636dd759..af71332e7 100644 --- a/mrbgems/mruby-compiler/core/node.h +++ b/mrbgems/mruby-compiler/core/node.h @@ -46,6 +46,7 @@ enum node_type { NODE_ARRAY, NODE_ZARRAY, NODE_HASH, + NODE_KW_HASH, NODE_RETURN, NODE_YIELD, NODE_LVAR, @@ -73,6 +74,9 @@ enum node_type { NODE_DREGX_ONCE, NODE_LIST, NODE_ARG, + NODE_ARGS_TAIL, + NODE_KW_ARG, + NODE_KW_REST_ARGS, NODE_ARGSCAT, NODE_ARGSPUSH, NODE_SPLAT, diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index 61d94edc9..44cb28608 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -568,6 +568,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 +678,61 @@ new_arg(parser_state *p, mrb_sym sym) return cons((node*)NODE_ARG, nsym(sym)); } -/* (m o r m2 b) */ +/* (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)); + 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; + + /* allocate register for keywords hash */ + if (kws || kwrest) { + local_add_f(p, (kwrest && kwrest->cdr)? sym(kwrest->cdr) : mrb_intern_lit(p->mrb, "**")); + } + + /* allocate register for block */ + local_add_f(p, blk? blk : mrb_intern_lit(p->mrb, "&")); + + // 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) @@ -1134,6 +1179,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 arg_value 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 +1208,7 @@ heredoc_end(parser_state *p) %token tLBRACE /* { */ %token tLBRACE_ARG /* { */ %token tSTAR /* * */ +%token tDSTAR /* ** */ %token tAMPER /* & */ %token tLAMBDA /* -> */ %token tANDDOT /* &. */ @@ -1736,6 +1786,7 @@ op : '|' { $$ = intern_c('|'); } | '/' { $$ = intern_c('/'); } | '%' { $$ = intern_c('%'); } | tPOW { $$ = intern("**",2); } + | tDSTAR { $$ = intern("**",2); } | '!' { $$ = intern_c('!'); } | '~' { $$ = intern_c('~'); } | tUPLUS { $$ = intern("+@",2); } @@ -1944,11 +1995,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 +2035,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 +2058,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 @@ -2451,23 +2502,51 @@ f_margs : f_marg_list } ; -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); } @@ -2475,39 +2554,39 @@ block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg { $$ = new_args(p, $1, 0, 0, 0, 0); } - | 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); } @@ -3021,65 +3100,153 @@ f_arglist : '(' f_args rparen } ; -f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg +f_label : tIDENTIFIER tLABEL_TAG + ; + +arg_value : arg + ; + +f_kw : f_label arg_value + { + $$ = 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); } ; @@ -3189,7 +3356,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 +3367,6 @@ blkarg_mark : '&' f_block_arg : blkarg_mark tIDENTIFIER { - local_add_f(p, $2); $$ = $2; } ; @@ -3211,7 +3377,6 @@ opt_f_block_arg : ',' f_block_arg } | none { - local_add_f(p, 0); $$ = 0; } ; @@ -3285,6 +3450,10 @@ assoc : arg tASSOC arg $$ = cons(new_sym(p, new_strsym(p, $1)), $3); } } + | tDSTAR arg_value + { + $$ = cons(cons((node*)NODE_KW_REST_ARGS, 0), $2); + } ; operation : tIDENTIFIER @@ -3450,13 +3619,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_i(p, "can't set variable $%" MRB_PRId, intn(n->cdr)); } else if (c == NODE_BACK_REF) { - yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr); + yyerror_i(p, "can't set variable $%c", intn(n->cdr)); } else { mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c)); @@ -3469,7 +3638,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: @@ -3508,7 +3677,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); @@ -3557,7 +3726,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 +3751,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); @@ -4019,11 +4188,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); @@ -4365,7 +4534,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 == '=') { @@ -5547,7 +5725,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++; @@ -5897,6 +6075,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 @@ -5908,7 +6128,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: @@ -5968,7 +6188,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: @@ -5976,43 +6197,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"); @@ -6164,7 +6349,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); @@ -6281,7 +6466,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; @@ -6363,11 +6548,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: @@ -6380,7 +6565,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: @@ -6393,7 +6578,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: @@ -6402,7 +6587,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: @@ -6431,7 +6616,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: @@ -6547,43 +6732,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; @@ -6596,44 +6745,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); @@ -6649,18 +6761,35 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset) dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); break; - case NODE_LITERAL_DELIM: - printf("NODE_LITERAL_DELIM\n"); + 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_SYMBOLS: - printf("NODE_SYMBOLS:\n"); - dump_recur(mrb, tree, offset+1); + 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_WORDS: - printf("NODE_SYMBOLS:\n"); - dump_recur(mrb, tree, offset+1); + case NODE_KW_REST_ARGS: + printf("NODE_KW_REST_ARGS %s\n", mrb_sym2name(mrb, sym(tree))); break; default: 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-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c index 323529dcc..9ce6c1831 100644 --- a/mrbgems/mruby-proc-ext/src/proc.c +++ b/mrbgems/mruby-proc-ext/src/proc.c @@ -149,7 +149,15 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self) a = mrb_ary_new(mrb); mrb_ary_push(mrb, a, sname); if (i < max && irep->lv[i].name) { - mrb_ary_push(mrb, a, mrb_symbol_value(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/src/codedump.c b/src/codedump.c index 22cbc59eb..dc0e0e548 100644 --- a/src/codedump.c +++ b/src/codedump.c @@ -78,6 +78,16 @@ codedump(mrb_state *mrb, mrb_irep *irep) printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", (void*)irep, irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen); + if (irep->lv) { + int i; + + printf("local variable names:\n"); + for (i = 1; i < irep->nlocals; ++i) { + char const *n = mrb_sym2name(mrb, irep->lv[i - 1].name); + printf(" R%d:%s\n", irep->lv[i - 1].r, n? n : ""); + } + } + pc = irep->iseq; pcend = pc + irep->ilen; while (pc < pcend) { @@ -246,10 +256,11 @@ codedump(mrb_state *mrb, mrb_irep *irep) printf("OP_SUPER\tR%d\t%d\n", a, b); break; CASE(OP_ARGARY, BS): - printf("OP_ARGARY\tR%d\t%d:%d:%d:%d", a, - (b>>10)&0x3f, - (b>>9)&0x1, - (b>>4)&0x1f, + printf("OP_ARGARY\tR%d\t%d:%d:%d:%d (%d)", a, + (b>>11)&0x3f, + (b>>10)&0x1, + (b>>5)&0x1f, + (b>>4)&0x1, (b>>0)&0xf); print_lv_a(mrb, irep, a); break; @@ -263,32 +274,39 @@ codedump(mrb_state *mrb, mrb_irep *irep) (a>>1)&0x1, a & 0x1); break; - CASE(OP_KARG, BB): - printf("OP_KARG\tR(%d)\tK(%d)\n", a, b); + CASE(OP_KEY_P, BB): + printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b])); + print_lv_a(mrb, irep, a); break; - CASE(OP_KARG2, BB): - printf("OP_KARG2\tR(%d)\tK(%d)\n", a, b); + CASE(OP_KEYEND, Z): + printf("OP_KEYEND\n"); + break; + CASE(OP_KARG, BB): + printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b])); + print_lv_a(mrb, irep, a); break; CASE(OP_KDICT, B): - printf("OP_KDICt\tR(%d)\n", a); + printf("OP_KDICT\tR%d\t\t", a); + print_lv_a(mrb, irep, a); break; CASE(OP_RETURN, B): - printf("OP_RETURN\tR%d", a); + printf("OP_RETURN\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_RETURN_BLK, B): - printf("OP_RETURN_BLK\tR%d", a); + printf("OP_RETURN_BLK\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_BREAK, B): - printf("OP_BREAK\tR%d", a); + printf("OP_BREAK\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_BLKPUSH, BS): - printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", a, - (b>>10)&0x3f, - (b>>9)&0x1, - (b>>4)&0x1f, + printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d (%d)", a, + (b>>11)&0x3f, + (b>>10)&0x1, + (b>>5)&0x1f, + (b>>4)&0x1, (b>>0)&0xf); print_lv_a(mrb, irep, a); break; diff --git a/src/hash.c b/src/hash.c index db9d1d9c8..0dce81677 100644 --- a/src/hash.c +++ b/src/hash.c @@ -208,6 +208,54 @@ mrb_hash_init_copy(mrb_state *mrb, mrb_value self) return vret; } +void +mrb_hash_check_kdict(mrb_state *mrb, mrb_value self) +{ + khash_t(ht) *orig_h; + khiter_t k; + int nosym = FALSE; + + orig_h = RHASH_TBL(self); + if (!orig_h || kh_size(orig_h) == 0) return; + for (k = kh_begin(orig_h); k != kh_end(orig_h); k++) { + if (kh_exist(orig_h, k)) { + mrb_value key = kh_key(orig_h, k); + + if (!mrb_symbol_p(key)) nosym = TRUE; + } + } + if (nosym) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys"); + } +} + +MRB_API mrb_value +mrb_hash_dup(mrb_state *mrb, mrb_value self) +{ + struct RHash* copy; + khash_t(ht) *orig_h; + + orig_h = RHASH_TBL(self); + copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); + copy->ht = kh_init(ht, mrb); + + if (orig_h && kh_size(orig_h) > 0) { + int ai = mrb_gc_arena_save(mrb); + khash_t(ht) *copy_h = copy->ht; + khiter_t k, copy_k; + + for (k = kh_begin(orig_h); k != kh_end(orig_h); k++) { + if (kh_exist(orig_h, k)) { + copy_k = kh_put(ht, mrb, copy_h, KEY(kh_key(orig_h, k))); + mrb_gc_arena_restore(mrb, ai); + kh_val(copy_h, copy_k).v = kh_val(orig_h, k).v; + kh_val(copy_h, copy_k).n = kh_size(copy_h)-1; + } + } + } + return mrb_obj_value(copy); +} + MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) { @@ -716,13 +764,21 @@ mrb_hash_size_m(mrb_state *mrb, mrb_value self) * {}.empty? #=> true * */ -MRB_API mrb_value +MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self) { khash_t(ht) *h = RHASH_TBL(self); - if (h) return mrb_bool_value(kh_size(h) == 0); - return mrb_true_value(); + if (h) return kh_size(h) == 0; + return TRUE; +} + +static mrb_value +mrb_hash_empty_m(mrb_state *mrb, mrb_value self) +{ + if (mrb_hash_empty_p(mrb, self)) + return mrb_true_value(); + return mrb_false_value(); } /* 15.2.13.4.29 (x)*/ @@ -833,21 +889,29 @@ mrb_hash_values(mrb_state *mrb, mrb_value hash) * */ -static mrb_value -mrb_hash_has_key(mrb_state *mrb, mrb_value hash) +MRB_API mrb_bool +mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key) { - mrb_value key; khash_t(ht) *h; khiter_t k; - mrb_get_args(mrb, "o", &key); - h = RHASH_TBL(hash); if (h) { k = kh_get(ht, mrb, h, key); - return mrb_bool_value(k != kh_end(h)); + return k != kh_end(h); } - return mrb_false_value(); + return FALSE; +} + +static mrb_value +mrb_hash_has_key(mrb_state *mrb, mrb_value hash) +{ + mrb_value key; + mrb_bool key_p; + + mrb_get_args(mrb, "o", &key); + key_p = mrb_hash_key_p(mrb, hash, key); + return mrb_bool_value(key_p); } /* 15.2.13.4.14 */ @@ -904,7 +968,7 @@ mrb_init_hash(mrb_state *mrb) mrb_define_method(mrb, h, "default_proc", mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7 */ mrb_define_method(mrb, h, "default_proc=", mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7 */ mrb_define_method(mrb, h, "__delete", mrb_hash_delete, MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8 */ - mrb_define_method(mrb, h, "empty?", mrb_hash_empty_p, MRB_ARGS_NONE()); /* 15.2.13.4.12 */ + mrb_define_method(mrb, h, "empty?", mrb_hash_empty_m, MRB_ARGS_NONE()); /* 15.2.13.4.12 */ mrb_define_method(mrb, h, "has_key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */ mrb_define_method(mrb, h, "has_value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */ mrb_define_method(mrb, h, "include?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.15 */ diff --git a/src/kernel.c b/src/kernel.c index 155868eaa..42e9ca6a4 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1194,7 +1194,15 @@ mrb_local_variables(mrb_state *mrb, mrb_value self) if (!irep->lv) break; for (i = 0; i + 1 < irep->nlocals; ++i) { if (irep->lv[i].name) { - mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value()); + 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; @@ -969,6 +969,8 @@ check_target_class(mrb_state *mrb) return TRUE; } +void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self); + MRB_API mrb_value mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc) { @@ -1639,9 +1641,10 @@ RETRY_TRY_BLOCK: } CASE(OP_ARGARY, BS) { - int m1 = (b>>10)&0x3f; - int r = (b>>9)&0x1; - int m2 = (b>>4)&0x1f; + int m1 = (b>>11)&0x3f; + int r = (b>>10)&0x1; + int m2 = (b>>5)&0x1f; + int kd = (b>>4)&0x1; int lv = (b>>0)&0xf; mrb_value *stack; @@ -1657,12 +1660,12 @@ RETRY_TRY_BLOCK: else { struct REnv *e = uvenv(mrb, lv-1); if (!e) goto L_NOSUPER; - if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) + if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+kd+1) goto L_NOSUPER; stack = e->stack + 1; } if (r == 0) { - regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack); + regs[a] = mrb_ary_new_from_values(mrb, m1+m2+kd, stack); } else { mrb_value *pp = NULL; @@ -1675,7 +1678,7 @@ RETRY_TRY_BLOCK: pp = ARY_PTR(ary); len = (int)ARY_LEN(ary); } - regs[a] = mrb_ary_new_capa(mrb, m1+len+m2); + regs[a] = mrb_ary_new_capa(mrb, m1+len+m2+kd); rest = mrb_ary_ptr(regs[a]); if (m1 > 0) { stack_copy(ARY_PTR(rest), stack, m1); @@ -1686,7 +1689,10 @@ RETRY_TRY_BLOCK: if (m2 > 0) { stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2); } - ARY_SET_LEN(rest, m1+len+m2); + if (kd) { + stack_copy(ARY_PTR(rest)+m1+len+m2, stack+m1+m2+1, kd); + } + ARY_SET_LEN(rest, m1+len+m2+kd); } regs[a+1] = stack[m1+r+m2]; mrb_gc_arena_restore(mrb, ai); @@ -1698,74 +1704,114 @@ RETRY_TRY_BLOCK: int o = MRB_ASPEC_OPT(a); int r = MRB_ASPEC_REST(a); int m2 = MRB_ASPEC_POST(a); + int kd = (MRB_ASPEC_KEY(a) > 0 || MRB_ASPEC_KDICT(a))? 1 : 0; /* unused - int k = MRB_ASPEC_KEY(a); - int kd = MRB_ASPEC_KDICT(a); int b = MRB_ASPEC_BLOCK(a); */ int argc = mrb->c->ci->argc; mrb_value *argv = regs+1; - mrb_value *argv0 = argv; - int len = m1 + o + r + m2; + mrb_value * const argv0 = argv; + int const len = m1 + o + r + m2; + int const blk_pos = len + kd + 1; mrb_value *blk = &argv[argc < 0 ? 1 : argc]; + mrb_value kdict; + int kargs = kd; + /* arguments is passed with Array */ if (argc < 0) { struct RArray *ary = mrb_ary_ptr(regs[1]); argv = ARY_PTR(ary); argc = (int)ARY_LEN(ary); mrb_gc_protect(mrb, regs[1]); } + + /* strict argument check */ if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) { - if (argc >= 0) { - if (argc < m1 + m2 || (r == 0 && argc > len)) { + if (argc >= 0 && !(argc <= 1 && kd)) { + if (argc < m1 + m2 + kd || (r == 0 && argc > len + kd)) { argnum_error(mrb, m1+m2); goto L_RAISE; } } } + /* extract first argument array to arguments */ else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) { mrb_gc_protect(mrb, argv[0]); argc = (int)RARRAY_LEN(argv[0]); argv = RARRAY_PTR(argv[0]); } + + if (kd) { + /* check last arguments is hash if method takes keyword arguments */ + if (argc == m1+m2) { + kdict = mrb_hash_new(mrb); + kargs = 0; + } + else { + if (!mrb_hash_p(argv[argc - 1])) { + if (r) { + kdict = mrb_hash_new(mrb); + kargs = 0; + } + else { + mrb_value str = mrb_str_new_lit(mrb, "Excepcted `Hash` as last argument for keyword arguments"); + mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str)); + goto L_RAISE; + } + } + else { + kdict = argv[argc-1]; + } + mrb_hash_check_kdict(mrb, kdict); + if (MRB_ASPEC_KEY(a) > 0) { + kdict = mrb_hash_dup(mrb, kdict); + } + } + } + + /* no rest arguments */ if (argc < len) { int mlen = m2; if (argc < m1+m2) { - if (m1 < argc) - mlen = argc - m1; - else - mlen = 0; + mlen = m1 < argc ? argc - m1 : 0; } - regs[len+1] = *blk; /* move block */ + regs[blk_pos] = *blk; /* move block */ + if (kd) regs[len + 1] = kdict; + SET_NIL_VALUE(regs[argc+1]); + /* copy mandatory and optional arguments */ if (argv0 != argv) { value_move(®s[1], argv, argc-mlen); /* m1 + o */ } if (argc < m1) { stack_clear(®s[argc+1], m1-argc); } + /* copy post mandatory arguments */ if (mlen) { value_move(®s[len-m2+1], &argv[argc-mlen], mlen); } if (mlen < m2) { stack_clear(®s[len-m2+mlen+1], m2-mlen); } + /* initalize rest arguments with empty Array */ if (r) { regs[m1+o+1] = mrb_ary_new_capa(mrb, 0); } - if (o > 0 && argc >= m1+m2) - pc += (argc - m1 - m2)*3; + /* skip initailizer of passed arguments */ + if (o > 0 && argc-kargs >= m1+m2) + pc += (argc - kargs - m1 - m2)*3; } else { int rnum = 0; if (argv0 != argv) { - regs[len+1] = *blk; /* move block */ + regs[blk_pos] = *blk; /* move block */ + if (kd) regs[len + 1] = kdict; value_move(®s[1], argv, m1+o); } if (r) { mrb_value ary; - rnum = argc-m1-o-m2; + rnum = argc-m1-o-m2-kargs; ary = mrb_ary_new_from_values(mrb, rnum, argv+m1+o); regs[m1+o+1] = ary; } @@ -1775,29 +1821,60 @@ RETRY_TRY_BLOCK: } } if (argv0 == argv) { - regs[len+1] = *blk; /* move block */ + regs[blk_pos] = *blk; /* move block */ + if (kd) regs[len + 1] = kdict; } pc += o*3; } - mrb->c->ci->argc = len; + + /* format arguments for generated code */ + mrb->c->ci->argc = len + kd; + /* clear local (but non-argument) variables */ - if (irep->nlocals-len-2 > 0) { - stack_clear(®s[len+2], irep->nlocals-len-2); + if (irep->nlocals-blk_pos-1 > 0) { + stack_clear(®s[blk_pos+1], irep->nlocals-blk_pos-1); } JUMP; } CASE(OP_KARG, BB) { - /* not implemented yet */ + mrb_value k = mrb_symbol_value(syms[b]); + mrb_value kdict = regs[mrb->c->ci->argc]; + + if (!mrb_hash_key_p(mrb, kdict, k)) { + mrb_value str = mrb_format(mrb, "missing keyword: %S", k); + mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str)); + goto L_RAISE; + } + regs[a] = mrb_hash_get(mrb, kdict, k); + mrb_hash_delete_key(mrb, kdict, k); NEXT; } - CASE(OP_KARG2, BB) { - /* not implemented yet */ + + CASE(OP_KEY_P, BB) { + mrb_value k = mrb_symbol_value(syms[b]); + mrb_value kdict = regs[mrb->c->ci->argc]; + mrb_bool key_p = mrb_hash_key_p(mrb, kdict, k); + + regs[a] = mrb_bool_value(key_p); + NEXT; + } + + CASE(OP_KEYEND, Z) { + mrb_value kdict = regs[mrb->c->ci->argc]; + + if (mrb_hash_p(kdict) && !mrb_hash_empty_p(mrb, kdict)) { + mrb_value keys = mrb_hash_keys(mrb, kdict); + mrb_value key1 = RARRAY_PTR(keys)[0]; + mrb_value str = mrb_format(mrb, "unknown keyword: %S", key1); + mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str)); + goto L_RAISE; + } NEXT; } CASE(OP_KDICT, B) { - /* not implemented yet */ + regs[a] = regs[mrb->c->ci->argc]; NEXT; } @@ -2064,9 +2141,10 @@ RETRY_TRY_BLOCK: } CASE(OP_BLKPUSH, BS) { - int m1 = (b>>10)&0x3f; - int r = (b>>9)&0x1; - int m2 = (b>>4)&0x1f; + int m1 = (b>>11)&0x3f; + int r = (b>>10)&0x1; + int m2 = (b>>5)&0x1f; + int kd = (b>>4)&0x1; int lv = (b>>0)&0xf; mrb_value *stack; @@ -2084,7 +2162,7 @@ RETRY_TRY_BLOCK: localjump_error(mrb, LOCALJUMP_ERROR_YIELD); goto L_RAISE; } - regs[a] = stack[m1+r+m2]; + regs[a] = stack[m1+r+m2+kd]; NEXT; } diff --git a/test/t/syntax.rb b/test/t/syntax.rb index 299394557..6392509ec 100644 --- a/test/t/syntax.rb +++ b/test/t/syntax.rb @@ -403,6 +403,9 @@ assert('External command execution.') do assert_equal 'test dynamic `', t assert_equal ['test', 'test dynamic `', 'test', 'test dynamic `'], results + results = [] + assert_equal 'test sym test sym test', `test #{:sym} test #{:sym} test` + alias_method sym, :old_cmd end true @@ -466,3 +469,183 @@ this is a comment that has extra after =begin and =end with tabs after it =end xxxxxxxxxxxxxxxxxxxxxxxxxx assert_equal(line + 4, __LINE__) end + +assert 'keyword arguments' do + def m(a, b:) [a, b] end + assert_equal [1, 2], m(1, b: 2) + assert_raise(ArgumentError) { m b: 1 } + assert_raise(ArgumentError) { m 1 } + + def m(a:) a end + assert_equal 1, m(a: 1) + assert_raise(ArgumentError) { m } + assert_raise(ArgumentError) { m 'a' => 1, a: 1 } + h = { a: 1 } + assert_equal 1, m(h) + assert_equal({ a: 1 }, h) + + def m(a: 1) a end + assert_equal 1, m + assert_equal 2, m(a: 2) + assert_raise(ArgumentError) { m 1 } + + def m(**) end + assert_nil m + assert_nil m a: 1, b: 2 + assert_raise(ArgumentError) { m 2 } + + def m(a, **) a end + assert_equal 1, m(1) + assert_equal 1, m(1, a: 2, b: 3) + assert_equal({ 'a' => 1, b: 2 }, m('a' => 1, b: 2)) + + def m(a, **k) [a, k] end + assert_equal [1, {}], m(1) + assert_equal [1, {a: 2, b: 3}], m(1, a: 2, b: 3) + assert_equal [{'a' => 1, b: 2}, {}], m('a' => 1, b: 2) + + def m(a=1, **) a end + assert_equal 1, m + assert_equal 2, m(2, a: 1, b: 0) + assert_raise(ArgumentError) { m('a' => 1, a: 2) } + + def m(a=1, **k) [a, k] end + assert_equal [1, {}], m + assert_equal [2, {a: 1, b: 2}], m(2, a: 1, b: 2) + assert_equal [{a: 1}, {b: 2}], m({a: 1}, {b: 2}) + + def m(*, a:) a end + assert_equal 1, m(a: 1) + assert_equal 3, m(1, 2, a: 3) + assert_raise(ArgumentError) { m('a' => 1, a: 2) } + + def m(*a, b:) [a, b] end + assert_equal [[], 1], m(b: 1) + assert_equal [[1, 2], 3], m(1, 2, b: 3) + assert_raise(ArgumentError) { m('a' => 1, b: 2) } + + def m(*a, b: 1) [a, b] end + assert_equal [[], 1], m + assert_equal [[1, 2, 3], 4], m(1, 2, 3, b: 4) + assert_raise(ArgumentError) { m('a' => 1, b: 2) } + + def m(*, **) end + assert_nil m() + assert_nil m(a: 1, b: 2) + assert_nil m(1, 2, 3, a: 4, b: 5) + + def m(*a, **) a end + assert_equal [], m() + assert_equal [1, 2, 3], m(1, 2, 3, a: 4, b: 5) + assert_raise(ArgumentError) { m("a" => 1, a: 1) } + assert_equal [1], m(1, **{a: 2}) + + def m(*, **k) k end + assert_equal({}, m()) + assert_equal({a: 4, b: 5}, m(1, 2, 3, a: 4, b: 5)) + assert_raise(ArgumentError) { m("a" => 1, a: 1) } + + def m(a = nil, b = nil, **k) [a, k] end + assert_equal [nil, {}], m() + assert_equal([nil, {a: 1}], m(a: 1)) + assert_raise(ArgumentError) { m("a" => 1, a: 1) } + assert_equal([{"a" => 1}, {a: 1}], m({ "a" => 1 }, a: 1)) + assert_equal([{a: 1}, {}], m({a: 1}, {})) + assert_equal([nil, {}], m({})) + + def m(*a, **k) [a, k] end + assert_equal([[], {}], m()) + assert_equal([[1], {}], m(1)) + assert_equal([[], {a: 1, b: 2}], m(a: 1, b: 2)) + assert_equal([[1, 2, 3], {a: 2}], m(1, 2, 3, a: 2)) + assert_raise(ArgumentError) { m("a" => 1, a: 1) } + assert_raise(ArgumentError) { m("a" => 1) } + assert_equal([[], {a: 1}], m(a: 1)) + assert_raise(ArgumentError) { m("a" => 1, a: 1) } + assert_equal([[{"a" => 1}], {a: 1}], m({ "a" => 1 }, a: 1)) + assert_equal([[{a: 1}], {}], m({a: 1}, {})) + assert_raise(ArgumentError) { m({a: 1}, {"a" => 1}) } + + def m(a:, b:) [a, b] end + assert_equal([1, 2], m(a: 1, b: 2)) + assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } + + def m(a:, b: 1) [a, b] end + assert_equal([1, 1], m(a: 1)) + assert_equal([1, 2], m(a: 1, b: 2)) + assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } + + def m(a:, **) a end + assert_equal(1, m(a: 1)) + assert_equal(1, m(a: 1, b: 2)) + assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } + + def m(a:, **k) [a, k] end + assert_equal([1, {}], m(a: 1)) + assert_equal([1, {b: 2, c: 3}], m(a: 1, b: 2, c: 3)) + assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } + +=begin + def m(a:, &b) [a, b] end + assert_equal([1, nil], m(a: 1)) + assert_equal([1, l], m(a: 1, &(l = ->{}))) +=end + + def m(a: 1, b:) [a, b] end + assert_equal([1, 0], m(b: 0)) + assert_equal([3, 2], m(b: 2, a: 3)) + assert_raise(ArgumentError) { m a: 1 } + + def m(a: def m(a: 1) a end, b:) + [a, b] + end + assert_equal([2, 3], m(a: 2, b: 3)) + assert_equal([:m, 1], m(b: 1)) + # Note the default value of a: in the original method. + assert_equal(1, m()) + + def m(a: 1, b: 2) [a, b] end + assert_equal([1, 2], m()) + assert_equal([4, 3], m(b: 3, a: 4)) + + def m(a: 1, **) a end + assert_equal(1, m()) + assert_equal(2, m(a: 2, b: 1)) + + def m(a: 1, **k) [a, k] end + assert_equal([1, {b: 2, c: 3}], m(b: 2, c: 3)) + + def m(a:, **) yield end + assert_raise(ArgumentError) { m { :blk } } + assert_equal :blk, m(a: 1){ :blk } + + def m(a:, **k, &b) [b.call, k] end + assert_raise(ArgumentError) { m { :blk } } + assert_equal [:blk, {b: 2}], m(a: 1, b: 2){ :blk } + + def m(**k, &b) [k, b] end + assert_equal([{ a: 1, b: 2}, nil], m(a: 1, b: 2)) + assert_equal :blk, m{ :blk }[1].call + + def m(hsh = {}) hsh end + assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2)) + assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2)) + + def m(hsh) hsh end + assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2)) + assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2)) + +=begin + def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l) + [a, b, c, d, e, f, g, h, k, l] + end + result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{})) + assert_equal([9, 8, [7], [], 6, 5, 4, 3, {}, l], result) + + def m a, b=1, *c, d, e:, f: 2, g:, **k, &l + [a, b, c, d, e, f, g, k, l] + end + result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{})) + assert_equal([1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l], result) +=end +end |
