summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--doc/opcode.md8
-rw-r--r--include/mruby/hash.h27
-rw-r--r--include/mruby/ops.h11
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c149
-rw-r--r--mrbgems/mruby-compiler/core/node.h4
-rw-r--r--mrbgems/mruby-compiler/core/parse.y493
-rw-r--r--mrbgems/mruby-objectspace/src/mruby_objectspace.c2
-rw-r--r--mrbgems/mruby-proc-ext/src/proc.c10
-rw-r--r--src/codedump.c50
-rw-r--r--src/hash.c86
-rw-r--r--src/kernel.c10
-rw-r--r--src/vm.c146
-rw-r--r--test/t/syntax.rb183
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;
diff --git a/src/vm.c b/src/vm.c
index b50f1ee9f..8aeb68fc2 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -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(&regs[1], argv, argc-mlen); /* m1 + o */
}
if (argc < m1) {
stack_clear(&regs[argc+1], m1-argc);
}
+ /* copy post mandatory arguments */
if (mlen) {
value_move(&regs[len-m2+1], &argv[argc-mlen], mlen);
}
if (mlen < m2) {
stack_clear(&regs[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(&regs[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(&regs[len+2], irep->nlocals-len-2);
+ if (irep->nlocals-blk_pos-1 > 0) {
+ stack_clear(&regs[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