Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- modified include/mruby.h
- @@ -109,6 +109,8 @@ typedef void* (*mrb_allocf) (struct mrb_state *mrb, void*, size_t, void *ud);
- typedef struct {
- mrb_sym mid;
- + mrb_bool use_kdict: 1;
- + mrb_bool need_kdict_dup: 1;
- struct RProc *proc;
- mrb_value *stackent;
- int nregs;
- @@ -120,6 +122,7 @@ typedef struct {
- int argc;
- int acc;
- struct RClass *target_class;
- + mrb_value *kwds;
- } mrb_callinfo;
- enum mrb_fiber_state {
- modified include/mruby/opcode.h
- @@ -96,8 +96,12 @@ enum {
- OP_SUPER,/* A C R(A) := super(R(A+1),... ,R(A+C+1)) */
- OP_ARGARY,/* A Bx R(A) := argument array (16=6:1:5:4) */
- OP_ENTER,/* Ax arg setup according to flags (23=5:5:1:5:5:1:1) */
- - OP_KARG,/* A B C R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */
- + OP_KARG,/* A B C R(A) := kdict[Syms(B)] */
- + /* R(A+1) := kdict.key?(Syms(B)) if C == 1 */
- + /* raise ArgumentError if C == 2 && !kdict.key?(Syms(B)) */
- + /* kdict.delete(Syms(B)) if C >= 1 */
- OP_KDICT,/* A C R(A) := kdict */
- + /* raise ArgumentError if C && !kdict.empty? */
- OP_RETURN,/* A B return R(A) (B=normal,in-block return/break) */
- OP_TAILCALL,/* A B C return call(R(A),Syms(B),*R(C)) */
- @@ -141,7 +145,11 @@ enum {
- OP_STOP,/* stop VM */
- OP_ERR,/* Bx raise RuntimeError with message Lit(Bx) */
- - OP_RSVD1,/* reserved instruction #1 */
- + OP_SENDK,/* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C), */
- + /* &R(A+C+1), */
- + /* R(A+C+2),R(A+C+2+1)..., */
- + /* R(A+C+2+2K),R(A+C+2+2K+1),nil) */
- +
- OP_RSVD2,/* reserved instruction #2 */
- OP_RSVD3,/* reserved instruction #3 */
- OP_RSVD4,/* reserved instruction #4 */
- modified mrbgems/mruby-compiler/core/codegen.c
- @@ -19,7 +19,7 @@
- #include <mruby/re.h>
- #include <mruby/throw.h>
- -typedef mrb_ast_node node;
- +typedef mrb_ast_node const* node;
- typedef struct mrb_parser_state parser_state;
- enum looptype {
- @@ -44,7 +44,7 @@ typedef struct scope {
- struct scope *prev;
- - node *lv;
- + node lv;
- int sp;
- int pc;
- @@ -75,16 +75,16 @@ typedef struct scope {
- parser_state* parser;
- } codegen_scope;
- -static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv);
- +static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node lv);
- static void scope_finish(codegen_scope *s);
- static struct loopinfo *loop_push(codegen_scope *s, enum looptype t);
- -static void loop_break(codegen_scope *s, node *tree);
- +static void loop_break(codegen_scope *s, node tree);
- static void loop_pop(codegen_scope *s, int val);
- -static void gen_assignment(codegen_scope *s, node *tree, int sp, int val);
- -static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val);
- +static void gen_assignment(codegen_scope *s, node tree, int sp, int val);
- +static void gen_vmassignment(codegen_scope *s, node tree, int rhs, int val);
- -static void codegen(codegen_scope *s, node *tree, int val);
- +static void codegen(codegen_scope *s, node tree, int val);
- static void raise_error(codegen_scope *s, const char *msg);
- static void
- @@ -556,7 +556,7 @@ new_sym(codegen_scope *s, mrb_sym sym)
- }
- static int
- -node_len(node *tree)
- +node_len(node tree)
- {
- int n = 0;
- @@ -572,7 +572,7 @@ node_len(node *tree)
- static int
- lv_idx(codegen_scope *s, mrb_sym id)
- {
- - node *lv = s->lv;
- + node lv = s->lv;
- int n = 1;
- while (lv) {
- @@ -584,12 +584,12 @@ lv_idx(codegen_scope *s, mrb_sym id)
- }
- static void
- -for_body(codegen_scope *s, node *tree)
- +for_body(codegen_scope *s, node tree)
- {
- codegen_scope *prev = s;
- int idx;
- struct loopinfo *lp;
- - node *n2;
- + node n2;
- mrb_code c;
- /* generate receiver */
- @@ -632,8 +632,22 @@ for_body(codegen_scope *s, node *tree)
- genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
- }
- +/* count optional keywords */
- static int
- -lambda_body(codegen_scope *s, node *tree, int blk)
- +kw_count_opt(node kws)
- +{
- + int ret = 0;
- +
- + while(kws) {
- + if (kws->car->cdr->cdr->car) ++ret;
- + kws = kws->cdr;
- + }
- +
- + return ret;
- +}
- +
- +static int
- +lambda_body(codegen_scope *s, node tree, int blk)
- {
- mrb_code c;
- codegen_scope *parent = s;
- @@ -653,33 +667,46 @@ lambda_body(codegen_scope *s, node *tree, int blk)
- mrb_aspec a;
- int ma, oa, ra, pa, ka, kd, ba;
- int pos, i;
- - node *n, *opt;
- + 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 arguments
- ra = tree->car->cdr->cdr->car ? 1 : 0;
- + // post required arguments
- pa = node_len(tree->car->cdr->cdr->cdr->car);
- - ka = kd = 0;
- - ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
- + // required keyword arguments
- + ka = tail? node_len(tail->cdr->car) - kw_count_opt(tail->cdr->car) : 0;
- + // keyword dictionary?
- + kd = tail && (tail->cdr->car || 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);
- + 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) << 6) /* (12bits = 6:1:5) */
- | ((ra & 1) << 5)
- | (pa & 0x1f);
- + // format arguments for this irep
- genop(s, MKOP_Ax(OP_ENTER, a));
- + // generate jump table for optional arguments initializer
- pos = new_label(s);
- for (i=0; i<oa; i++) {
- new_label(s);
- @@ -704,6 +731,40 @@ lambda_body(codegen_scope *s, node *tree, int blk)
- if (oa > 0) {
- dispatch(s, pos+i);
- }
- +
- + if (tail) {
- + int const kdict = 1 + ma + oa + ra + pa;
- + node const kwds = tail->cdr->car;
- + node const kwrest = tail->cdr->cdr->car;
- + // mrb_sym blk = sym(tail->cdr->cdr->cdr->car);
- + node k;
- +
- + mrb_assert((intptr_t)tail->car == NODE_ARGS_TAIL);
- + mrb_assert(node_len(tail) == 4);
- +
- + for (k = kwds; k; k = k->cdr) {
- + node kwd = k->car, def_arg = kwd->cdr->cdr->car;
- + mrb_sym kwd_sym = sym(kwd->cdr->car);
- +
- + mrb_assert((intptr_t)kwd->car == NODE_KW_ARG);
- +
- + if (def_arg) {
- + int jmpif_key_p;
- + genop(s, MKOP_ABC(OP_KARG, cursp(), new_msym(s, kwd_sym), 1));
- + jmpif_key_p = genop(s, MKOP_AsBx(OP_JMPIF, cursp() + 1, 0));
- + codegen(s, def_arg, VAL);
- + pop();
- + dispatch(s, jmpif_key_p);
- + genop(s, MKOP_AB(OP_MOVE, lv_idx(s, kwd_sym), cursp()));
- + } else {
- + genop(s, MKOP_ABC(OP_KARG, lv_idx(s, kwd_sym), new_msym(s, kwd_sym), 2));
- + }
- + }
- +
- + if (kwrest || kwds) {
- + genop(s, MKOP_ABC(OP_KDICT, kdict, 0, kwrest? 0 : 1));
- + }
- + }
- }
- codegen(s, tree->cdr->car, VAL);
- pop();
- @@ -727,7 +788,7 @@ lambda_body(codegen_scope *s, node *tree, int blk)
- }
- static int
- -scope_body(codegen_scope *s, node *tree, int val)
- +scope_body(codegen_scope *s, node tree, int val)
- {
- codegen_scope *scope = scope_new(s->mrb, s, tree->car);
- if (scope == NULL) {
- @@ -759,7 +820,7 @@ scope_body(codegen_scope *s, node *tree, int val)
- }
- static mrb_bool
- -nosplat(node *t)
- +nosplat(node t)
- {
- while (t) {
- if ((intptr_t)t->car->car == NODE_SPLAT) return FALSE;
- @@ -791,13 +852,37 @@ attrsym(codegen_scope *s, mrb_sym a)
- #define CALL_MAXARGS 127
- +// check NODE_KW_HASH with non-symbol key
- +static mrb_bool
- +check_symbol_only_kw_hash_p(node t)
- +{
- + if ((intptr_t)t->car == NODE_KW_HASH) {
- + node n;
- + int non_sym = 0;
- + for (n = t->cdr; n; n = n->cdr) {
- + switch ((intptr_t)n->car->car->car) {
- + case NODE_KW_REST_ARGS:
- + case NODE_SYM:
- + break;
- + default:
- + ++non_sym;
- + break;
- + }
- + }
- +
- + if (non_sym == 0) { return TRUE; }
- + }
- +
- + return FALSE;
- +}
- +
- static int
- -gen_values(codegen_scope *s, node *t, int val, int extra)
- +gen_values(codegen_scope *s, node t, int val, int extra)
- {
- int n = 0;
- int is_splat;
- - while (t) {
- + for (; t; t = t->cdr) {
- is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */
- if (
- n+extra >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */
- @@ -820,8 +905,9 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
- }
- }
- - t = t->cdr;
- - while (t) {
- + for (t = t->cdr; t; t = t->cdr) {
- + if (check_symbol_only_kw_hash_p(t->car)) { continue; }
- +
- push();
- codegen(s, t->car, VAL);
- pop(); pop();
- @@ -831,31 +917,34 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
- else {
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
- }
- - t = t->cdr;
- }
- }
- else {
- - while (t) {
- + for (; t; t = t->cdr) {
- + if (check_symbol_only_kw_hash_p(t->car)) { continue; }
- +
- codegen(s, t->car, NOVAL);
- - t = t->cdr;
- }
- }
- return -1;
- }
- +
- + if (check_symbol_only_kw_hash_p(t->car)) { continue; }
- +
- /* normal (no splat) mode */
- codegen(s, t->car, val);
- n++;
- - t = t->cdr;
- }
- return n;
- }
- static void
- -gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
- +gen_call(codegen_scope *s, node tree, mrb_sym name, int sp, int val, int safe)
- {
- mrb_sym sym = name ? name : sym(tree->cdr->car);
- int idx, skip = 0;
- int n = 0, noop = 0, sendv = 0, blk = 0;
- + node kwargs = NULL;
- codegen(s, tree->car, VAL); /* receiver */
- if (safe) {
- @@ -872,11 +961,58 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
- idx = new_msym(s, sym);
- tree = tree->cdr->cdr->car;
- if (tree) {
- + node kwrest = NULL, arg_n;
- +
- + mrb_int symlen;
- + const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
- +
- + mrb_bool skip_kwargs = FALSE;
- +
- + switch (symlen) {
- + case 1:
- + if (symname[0] == '+' || symname[0] == '-' ||
- + symname[0] == '*' || symname[0] == '/' ||
- + symname[0] == '<' || symname[0] == '>')
- + { skip_kwargs = TRUE; }
- + break;
- + case 2:
- + if ((symname[0] == '=' && symname[1] == '=') ||
- + (symname[0] == '>' && symname[1] == '=') ||
- + (symname[0] == '<' && symname[1] == '='))
- + { skip_kwargs = TRUE; }
- + break;
- + }
- +
- + if (!skip_kwargs) {
- + for (arg_n = tree->car; arg_n; arg_n = arg_n->cdr) {
- + if ((intptr_t)arg_n->car->car == NODE_KW_HASH) {
- + node n;
- + mrb_assert(!arg_n->cdr);
- + for (n = arg_n->car->cdr; n; n = n->cdr) {
- + switch ((intptr_t)n->car->car->car) {
- + case NODE_KW_REST_ARGS: kwrest = n->car; break;
- + case NODE_SYM: kwargs = arg_n->car->cdr; break;
- + }
- + }
- + }
- + }
- + }
- +
- n = gen_values(s, tree->car, VAL, sp?1:0);
- if (n < 0) {
- n = noop = sendv = 1;
- push();
- }
- +
- + if (kwrest) {
- + mrb_assert((intptr_t)kwrest->car->car == NODE_KW_REST_ARGS);
- + codegen(s, kwrest->cdr, VAL);
- + if (sendv) {
- + pop();
- + genop(s, MKOP_AB(OP_ARYPUSH, cursp() - 1, cursp()));
- + }
- + else { ++n; }
- + }
- }
- if (sp) {
- if (sendv) {
- @@ -932,12 +1068,34 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
- genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
- }
- else {
- - if (sendv) n = CALL_MAXARGS;
- - if (blk > 0) { /* no block */
- - genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
- + int const argc = sendv? CALL_MAXARGS : n;
- +
- + if (kwargs) {
- + int kwargs_count = 0;
- +
- + push_n(sendv? 2 : n + 2);
- + if (blk > 0) { genop(s, MKOP_A(OP_LOADNIL, cursp() - 1)); }
- +
- + for (; kwargs; kwargs = kwargs->cdr) {
- + if ((intptr_t)kwargs->car->car->car == NODE_SYM) {
- + ++kwargs_count;
- + codegen(s, kwargs->car->car, VAL);
- + codegen(s, kwargs->car->cdr, VAL);
- + }
- + }
- + // kwargs terminator
- + genop(s, MKOP_A(OP_LOADNIL, cursp()));
- +
- + pop_n(2 * kwargs_count);
- + pop_n(sendv? 2 : n + 2);
- +
- + genop(s, MKOP_ABC(OP_SENDK, cursp(), idx, argc));
- + }
- + else if (blk > 0) { /* no block */
- + genop(s, MKOP_ABC(OP_SEND, cursp(), idx, argc));
- }
- else {
- - genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n));
- + genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, argc));
- }
- }
- }
- @@ -950,7 +1108,7 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
- }
- static void
- -gen_assignment(codegen_scope *s, node *tree, int sp, int val)
- +gen_assignment(codegen_scope *s, node tree, int sp, int val)
- {
- int idx;
- int type = (intptr_t)tree->car;
- @@ -1032,10 +1190,10 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
- }
- static void
- -gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
- +gen_vmassignment(codegen_scope *s, node tree, int rhs, int val)
- {
- int n = 0, post = 0;
- - node *t, *p;
- + node t, p;
- if (tree->car) { /* pre */
- t = tree->car;
- @@ -1091,7 +1249,7 @@ gen_send_intern(codegen_scope *s)
- push();
- }
- static void
- -gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
- +gen_literal_array(codegen_scope *s, node tree, mrb_bool sym, int val)
- {
- if (val) {
- int i = 0, j = 0;
- @@ -1222,7 +1380,7 @@ readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_boo
- }
- static void
- -gen_retval(codegen_scope *s, node *tree)
- +gen_retval(codegen_scope *s, node tree)
- {
- if ((intptr_t)tree->car == NODE_SPLAT) {
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0));
- @@ -1238,7 +1396,7 @@ gen_retval(codegen_scope *s, node *tree)
- }
- static void
- -codegen(codegen_scope *s, node *tree, int val)
- +codegen(codegen_scope *s, node tree, int val)
- {
- int nt;
- @@ -1291,14 +1449,14 @@ codegen(codegen_scope *s, node *tree, int val)
- exend = 0;
- pos1 = 0;
- if (tree->car) {
- - node *n2 = tree->car;
- + node n2 = tree->car;
- int exc = cursp();
- genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
- push();
- while (n2) {
- - node *n3 = n2->car;
- - node *n4 = n3->car;
- + node n3 = n2->car;
- + node n4 = n3->car;
- if (pos1) dispatch(s, pos1);
- pos2 = 0;
- @@ -1403,7 +1561,7 @@ codegen(codegen_scope *s, node *tree, int val)
- case NODE_IF:
- {
- int pos1, pos2;
- - node *e = tree->cdr->cdr->car;
- + node e = tree->cdr->cdr->car;
- if (!tree->car) {
- codegen(s, e, val);
- @@ -1513,7 +1671,7 @@ codegen(codegen_scope *s, node *tree, int val)
- {
- int head = 0;
- int pos1, pos2, pos3, tmp;
- - node *n;
- + node n;
- pos3 = 0;
- if (tree->car) {
- @@ -1647,11 +1805,35 @@ codegen(codegen_scope *s, node *tree, int val)
- break;
- case NODE_HASH:
- + case NODE_KW_HASH:
- {
- int len = 0;
- mrb_bool update = FALSE;
- + if (nt == NODE_KW_HASH) {
- + node n;
- + int non_sym = 0;
- + for (n = tree; n; n = n->cdr) {
- + switch ((intptr_t)n->car->car->car) {
- + case NODE_KW_REST_ARGS:
- + case NODE_SYM:
- + break;
- + default:
- + ++non_sym;
- + break;
- + }
- + }
- + mrb_assert(non_sym > 0);
- + }
- +
- while (tree) {
- + if (nt == NODE_KW_HASH &&
- + ((intptr_t)tree->car->car->car == NODE_SYM ||
- + (intptr_t)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++;
- @@ -1693,7 +1875,7 @@ codegen(codegen_scope *s, node *tree, int val)
- case NODE_MASGN:
- {
- int len = 0, n = 0, post = 0;
- - node *t = tree->cdr, *p;
- + node t = tree->cdr, p;
- int rhs = cursp();
- if ((intptr_t)t->car == NODE_ARRAY && t->cdr && nosplat(t->cdr)) {
- @@ -1796,7 +1978,7 @@ codegen(codegen_scope *s, node *tree, int val)
- loop_pop(s, NOVAL);
- }
- else if ((intptr_t)tree->car->car == NODE_CALL) {
- - node *n = tree->car->cdr;
- + node n = tree->car->cdr;
- if (val) {
- vsp = cursp();
- @@ -1941,7 +2123,7 @@ codegen(codegen_scope *s, node *tree, int val)
- push(); /* room for receiver */
- if (tree) {
- - node *args = tree->car;
- + node args = tree->car;
- if (args) {
- n = gen_values(s, args, VAL, 0);
- if (n < 0) {
- @@ -2334,7 +2516,7 @@ codegen(codegen_scope *s, node *tree, int val)
- /* fall through */
- case NODE_DSTR:
- if (val) {
- - node *n = tree;
- + node n = tree;
- if (!n) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- @@ -2352,7 +2534,7 @@ codegen(codegen_scope *s, node *tree, int val)
- }
- }
- else {
- - node *n = tree;
- + node n = tree;
- while (n) {
- if ((intptr_t)n->car->car != NODE_STR) {
- @@ -2373,29 +2555,27 @@ codegen(codegen_scope *s, node *tree, int val)
- case NODE_DXSTR:
- {
- - node *n;
- + node n;
- int ai = mrb_gc_arena_save(s->mrb);
- - int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
- genop(s, MKOP_A(OP_LOADSELF, cursp()));
- push();
- codegen(s, tree->car, VAL);
- - n = tree->cdr;
- - while (n) {
- + for (n = tree->cdr; n; n = n->cdr) {
- if ((intptr_t)n->car->car == NODE_XSTR) {
- - n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR;
- - mrb_assert(!n->cdr); /* must be the end */
- + int str_lit = new_lit(s, mrb_str_new(s->mrb, (char const*)n->car->cdr->car,
- + (intptr_t)n->car->cdr->cdr));
- + genop(s, MKOP_ABx(OP_STRING, cursp(), str_lit));
- + push();
- +
- + mrb_assert(!n->cdr); // NODE_XSTR is the terminator
- }
- - codegen(s, n->car, VAL);
- - pop(); pop();
- - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
- - push();
- - n = n->cdr;
- + else { codegen(s, n->car, VAL); }
- + pop();
- + genop_peep(s, MKOP_AB(OP_STRCAT, cursp() - 1, cursp()), VAL);
- }
- - push(); /* for block */
- - pop_n(3);
- - sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
- - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
- + pop_n(2);
- + genop(s, MKOP_ABC(OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "`")), 1));
- if (val) push();
- mrb_gc_arena_restore(s->mrb, ai);
- }
- @@ -2406,16 +2586,12 @@ codegen(codegen_scope *s, node *tree, int val)
- char *p = (char*)tree->car;
- size_t len = (intptr_t)tree->cdr;
- int ai = mrb_gc_arena_save(s->mrb);
- - int off = new_lit(s, mrb_str_new(s->mrb, p, len));
- - int sym;
- genop(s, MKOP_A(OP_LOADSELF, cursp()));
- push();
- - genop(s, MKOP_ABx(OP_STRING, cursp(), off));
- - push(); push();
- - pop_n(3);
- - sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
- - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
- + genop(s, MKOP_ABx(OP_STRING, cursp(), new_lit(s, mrb_str_new(s->mrb, p, len))));
- + pop();
- + genop(s, MKOP_ABC(OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "`")), 1));
- if (val) push();
- mrb_gc_arena_restore(s->mrb, ai);
- }
- @@ -2464,7 +2640,7 @@ codegen(codegen_scope *s, node *tree, int val)
- case NODE_DREGX:
- if (val) {
- - node *n = tree->car;
- + node n = tree->car;
- int ai = mrb_gc_arena_save(s->mrb);
- int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
- int argc = 1;
- @@ -2515,7 +2691,7 @@ codegen(codegen_scope *s, node *tree, int val)
- push();
- }
- else {
- - node *n = tree->car;
- + node n = tree->car;
- while (n) {
- if ((intptr_t)n->car->car != NODE_STR) {
- @@ -2595,7 +2771,7 @@ codegen(codegen_scope *s, node *tree, int val)
- {
- int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method"));
- int num = 0;
- - node *t = tree;
- + node t = tree;
- genop(s, MKOP_A(OP_TCLASS, cursp()));
- push();
- @@ -2636,11 +2812,11 @@ codegen(codegen_scope *s, node *tree, int val)
- {
- int idx;
- - if (tree->car->car == (node*)0) {
- + if (tree->car->car == (node)0) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- }
- - else if (tree->car->car == (node*)1) {
- + else if (tree->car->car == (node)1) {
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- push();
- }
- @@ -2669,11 +2845,11 @@ codegen(codegen_scope *s, node *tree, int val)
- {
- int idx;
- - if (tree->car->car == (node*)0) {
- + if (tree->car->car == (node)0) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- }
- - else if (tree->car->car == (node*)1) {
- + else if (tree->car->car == (node)1) {
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- push();
- }
- @@ -2726,7 +2902,7 @@ codegen(codegen_scope *s, node *tree, int val)
- case NODE_SDEF:
- {
- - node *recv = tree->car;
- + node recv = tree->car;
- int sym = new_msym(s, sym(tree->cdr->car));
- int idx = lambda_body(s, tree->cdr->cdr, 0);
- @@ -2769,7 +2945,7 @@ scope_add_irep(codegen_scope *s, mrb_irep *irep)
- }
- static codegen_scope*
- -scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
- +scope_new(mrb_state *mrb, codegen_scope *prev, node lv)
- {
- static const codegen_scope codegen_scope_zero = { 0 };
- mrb_pool *pool = mrb_pool_open(mrb);
- @@ -2806,7 +2982,7 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
- p->sp += node_len(lv)+1; /* add self */
- p->nlocals = p->sp;
- if (lv) {
- - node *n = lv;
- + node n = lv;
- size_t i = 0;
- p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1));
- @@ -2902,7 +3078,7 @@ loop_push(codegen_scope *s, enum looptype t)
- }
- static void
- -loop_break(codegen_scope *s, node *tree)
- +loop_break(codegen_scope *s, node tree)
- {
- if (!s->loop) {
- codegen(s, tree, NOVAL);
- modified 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,
- modified mrbgems/mruby-compiler/core/parse.y
- @@ -560,6 +560,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)
- @@ -660,23 +667,54 @@ 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 = kws;
- +
- + /* 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 */
- + while (k) {
- + local_add_f(p, sym(k->car->cdr->car));
- + k = k->cdr;
- + }
- +
- + 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)
- @@ -1117,6 +1155,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 /* ** */
- @@ -1142,6 +1184,7 @@ heredoc_end(parser_state *p)
- %token tLBRACE /* { */
- %token tLBRACE_ARG /* { */
- %token tSTAR /* * */
- +%token tDSTAR /* ** */
- %token tAMPER /* & */
- %token tLAMBDA /* -> */
- %token tANDDOT /* &. */
- @@ -1716,6 +1759,7 @@ op : '|' { $$ = intern_c('|'); }
- | '/' { $$ = intern_c('/'); }
- | '%' { $$ = intern_c('%'); }
- | tPOW { $$ = intern("**",2); }
- + | tDSTAR { $$ = intern("**",2); }
- | '!' { $$ = intern_c('!'); }
- | '~' { $$ = intern_c('~'); }
- | tUPLUS { $$ = intern("+@",2); }
- @@ -1920,11 +1964,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);
- }
- ;
- @@ -1960,12 +2004,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);
- }
- ;
- @@ -1982,12 +2026,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
- @@ -2426,23 +2470,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);
- }
- @@ -2450,39 +2522,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);
- }
- @@ -2987,65 +3059,153 @@ f_arglist : '(' f_args rparen
- }
- ;
- -f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg
- +f_label : tLABEL
- + ;
- +
- +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);
- }
- ;
- @@ -3153,7 +3313,7 @@ f_rest_arg : restarg_mark tIDENTIFIER
- }
- | restarg_mark
- {
- - local_add_f(p, 0);
- + local_add_f(p, mrb_intern_lit(p->mrb, "*"));
- $$ = -1;
- }
- ;
- @@ -3164,7 +3324,6 @@ blkarg_mark : '&'
- f_block_arg : blkarg_mark tIDENTIFIER
- {
- - local_add_f(p, $2);
- $$ = $2;
- }
- ;
- @@ -3175,7 +3334,6 @@ opt_f_block_arg : ',' f_block_arg
- }
- | none
- {
- - local_add_f(p, 0);
- $$ = 0;
- }
- ;
- @@ -3228,15 +3386,16 @@ assocs : assoc
- }
- ;
- -assoc : arg tASSOC arg
- +assoc : arg_value tASSOC arg_value
- {
- + /* if ((intptr_t)$1->car == NODE_STR) */
- $$ = cons($1, $3);
- }
- - | tLABEL arg
- + | tLABEL arg_value
- {
- $$ = cons(new_sym(p, $1), $2);
- }
- - | tLABEL_END arg
- + | tLABEL_END arg_value
- {
- $$ = cons(new_sym(p, new_strsym(p, $1)), $2);
- }
- @@ -3248,6 +3407,10 @@ assoc : arg tASSOC arg
- {
- $$ = cons(new_dsym(p, push($2, $3)), $4);
- }
- + | tDSTAR arg_value
- + {
- + $$ = cons(cons((node*)NODE_KW_REST_ARGS, 0), $2);
- + }
- ;
- operation : tIDENTIFIER
- @@ -4325,7 +4488,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 == '=') {
- @@ -5841,6 +6013,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((intptr_t)n->car == NODE_ARGS_TAIL);
- + mrb_parser_dump(mrb, n, offset);
- + }
- +}
- +
- #endif
- void
- @@ -5912,7 +6126,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:
- @@ -5920,43 +6135,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");
- @@ -6491,43 +6670,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;
- @@ -6540,44 +6683,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);
- @@ -6593,6 +6699,37 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
- dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
- break;
- + case NODE_ARGS_TAIL:
- + printf("NODE_ARGS_TAIL:\n");
- + {
- + node *kws = tree->car;
- +
- + while (kws) {
- + mrb_parser_dump(mrb, kws->car, offset+1);
- + kws = kws->cdr;
- + }
- + }
- + tree = tree->cdr;
- + if (tree->car) {
- + mrb_assert((intptr_t)tree->car->car == NODE_KW_REST_ARGS);
- + mrb_parser_dump(mrb, tree->car, offset+1);
- + }
- + tree = tree->cdr;
- + if (tree->car) {
- + dump_prefix(tree, offset+1);
- + printf("block='%s'\n", mrb_sym2name(mrb, sym(tree->car)));
- + }
- + break;
- +
- + case NODE_KW_ARG:
- + printf("NODE_KW_ARG %s\n", mrb_sym2name(mrb, sym(tree->car)));
- + mrb_parser_dump(mrb, tree->cdr->car, offset + 1);
- + break;
- +
- + case NODE_KW_REST_ARGS:
- + printf("NODE_KW_REST_ARGS %s\n", mrb_sym2name(mrb, sym(tree)));
- + break;
- +
- default:
- printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype);
- break;
- modified mrbgems/mruby-proc-ext/src/proc.c
- @@ -104,7 +104,9 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
- {0, "opt"},
- {0, "rest"},
- {0, "req"},
- + {0, "kdict"},
- {0, "block"},
- + {0, "kwd"},
- {0, NULL}
- };
- const struct RProc *proc = mrb_proc_ptr(self);
- @@ -137,7 +139,9 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
- parameters_list[1].size = MRB_ASPEC_OPT(aspec);
- parameters_list[2].size = MRB_ASPEC_REST(aspec);
- parameters_list[3].size = MRB_ASPEC_POST(aspec);
- - parameters_list[4].size = MRB_ASPEC_BLOCK(aspec);
- + parameters_list[4].size = MRB_ASPEC_KDICT(aspec);
- + parameters_list[5].size = MRB_ASPEC_BLOCK(aspec);
- + parameters_list[6].size = MRB_ASPEC_KEY(aspec);
- parameters = mrb_ary_new_capa(mrb, irep->nlocals-1);
- @@ -145,9 +149,9 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
- if (p->size <= 0) continue;
- sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name));
- for (j = 0; j < p->size; i++, j++) {
- - mrb_value a = mrb_ary_new(mrb);
- + mrb_value a = mrb_ary_new_capa(mrb, 2);
- mrb_ary_push(mrb, a, sname);
- - if (irep->lv[i].name) {
- + if (irep->lv[i].name != mrb_intern_lit(mrb, "*")) {
- mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i].name));
- }
- mrb_ary_push(mrb, parameters, a);
- modified mrbgems/mruby-struct/test/struct.rb
- @@ -142,7 +142,7 @@ end
- assert('Struct#to_h') do
- s = Struct.new(:white, :red, :green).new('ruuko', 'yuzuki', 'hitoe')
- - assert_equal(:white => 'ruuko', :red => 'yuzuki', :green => 'hitoe') { s.to_h }
- + assert_equal({:white => 'ruuko', :red => 'yuzuki', :green => 'hitoe'}, s.to_h)
- end
- assert('Struct#values_at') do
- modified mrbgems/mruby-test/mrbgem.rake
- @@ -41,7 +41,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
- dep_list = build.gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions)
- file test_rbobj => g.test_rbireps
- - file g.test_rbireps => [g.test_rbfiles].flatten do |t|
- + file g.test_rbireps => [g.test_rbfiles, build.mrbcfile].flatten do |t|
- FileUtils.mkdir_p File.dirname(t.name)
- open(t.name, 'w') do |f|
- g.print_gem_test_header(f)
- modified src/class.c
- @@ -15,6 +15,8 @@
- #include <mruby/error.h>
- #include <mruby/data.h>
- #include <mruby/istruct.h>
- +#include <mruby/opcode.h>
- +#include <mruby/hash.h>
- KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal)
- @@ -582,6 +584,25 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
- (array_argv ? mrb_ary_ptr(mrb->c->stack[1])->ptr : (mrb->c->stack + 1))
- while ((c = *format++)) {
- + if ((c == 'H' || c == 'o') &&
- + i == argc && mrb->c->ci->pc && GET_OPCODE(mrb->c->ci->pc[-1]) == OP_SENDK) {
- + mrb_value const *kw = mrb->c->stack + mrb->c->ci->argc + 2;
- + mrb_value hsh = mrb_hash_new(mrb);
- + mrb_value *p = va_arg(ap, mrb_value*);
- +
- + for (; !mrb_nil_p(*kw); kw += 2) {
- + mrb_assert(mrb_symbol_p(kw[0]));
- + mrb_hash_set(mrb, hsh, kw[0], kw[1]);
- + }
- + mrb_assert(mrb_nil_p(*kw));
- +
- + i++; argc++;
- +
- + *p = hsh;
- +
- + continue;
- + }
- +
- switch (c) {
- case '|': case '*': case '&': case '?':
- break;
- modified src/codedump.c
- @@ -64,6 +64,15 @@ 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) {
- + printf("local variable names:\n");
- + printf(" R%d:%s\n", 0, "self");
- + 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 : "");
- + }
- + }
- +
- for (i = 0; i < (int)irep->ilen; i++) {
- ai = mrb_gc_arena_save(mrb);
- @@ -198,14 +207,22 @@ codedump(mrb_state *mrb, mrb_irep *irep)
- printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
- break;
- case OP_SEND:
- - printf("OP_SEND\tR%d\t:%s\t%d\n", GETARG_A(c),
- + printf("OP_SEND\tR%d\t:%s\t%d", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- + print_lv(mrb, irep, c, RA);
- break;
- case OP_SENDB:
- - printf("OP_SENDB\tR%d\t:%s\t%d\n", GETARG_A(c),
- + printf("OP_SENDB\tR%d\t:%s\t%d", GETARG_A(c),
- + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- + GETARG_C(c));
- + print_lv(mrb, irep, c, RA);
- + break;
- + case OP_SENDK:
- + printf("OP_SENDK\tR%d\t:%s\t%d", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- + print_lv(mrb, irep, c, RA);
- break;
- case OP_TAILCALL:
- printf("OP_TAILCALL\tR%d\t:%s\t%d\n", GETARG_A(c),
- @@ -444,6 +461,16 @@ codedump(mrb_state *mrb, mrb_irep *irep)
- printf("OP_EPOP\t%d\n", GETARG_A(c));
- break;
- + case OP_KARG:
- + printf("OP_KARG\tR%d\t:%s\t%d", GETARG_A(c),
- + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), GETARG_C(c));
- + print_lv(mrb, irep, c, RAB);
- + break;
- + case OP_KDICT:
- + printf("OP_KDICT\tR%d\t%d", GETARG_A(c), GETARG_C(c));
- + print_lv(mrb, irep, c, RA);
- + break;
- +
- default:
- printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c),
- GETARG_A(c), GETARG_B(c), GETARG_C(c));
- modified src/kernel.c
- @@ -1143,6 +1143,10 @@ mrb_local_variables(mrb_state *mrb, mrb_value self)
- mrb_value vars;
- struct mrb_irep *irep;
- size_t i;
- + mrb_sym const
- + blk = mrb_intern_lit(mrb, "&"),
- + rest = mrb_intern_lit(mrb, "*"),
- + kwrest = mrb_intern_lit(mrb, "**");
- proc = mrb->c->ci[-1].proc;
- @@ -1156,8 +1160,9 @@ mrb_local_variables(mrb_state *mrb, mrb_value self)
- }
- vars = mrb_hash_new(mrb);
- 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 const n = irep->lv[i].name;
- + if (n && n != blk && n != rest && n != kwrest) {
- + mrb_hash_set(mrb, vars, mrb_symbol_value(n), mrb_true_value());
- }
- }
- if (proc->env) {
- @@ -1169,8 +1174,9 @@ mrb_local_variables(mrb_state *mrb, mrb_value self)
- irep = mrb->c->cibase[e->cioff].proc->body.irep;
- if (irep->lv) {
- 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 const n = irep->lv[i].name;
- + if (n && n != blk && n != rest && n != kwrest) {
- + mrb_hash_set(mrb, vars, mrb_symbol_value(n), mrb_true_value());
- }
- }
- }
- modified src/vm.c
- @@ -249,6 +249,9 @@ cipush(mrb_state *mrb)
- ci->err = 0;
- ci->proc = 0;
- ci->acc = 0;
- + ci->use_kdict = FALSE;
- + ci->need_kdict_dup = TRUE;
- + ci->kwds = NULL;
- return ci;
- }
- @@ -890,6 +893,7 @@ mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
- &&L_OP_CLASS, &&L_OP_MODULE, &&L_OP_EXEC,
- &&L_OP_METHOD, &&L_OP_SCLASS, &&L_OP_TCLASS,
- &&L_OP_DEBUG, &&L_OP_STOP, &&L_OP_ERR,
- + &&L_OP_SENDK,
- };
- #endif
- @@ -1210,6 +1214,14 @@ RETRY_TRY_BLOCK:
- NEXT;
- }
- + CASE(OP_SENDK) {
- + /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C), */
- + /* &R(A+C+1), */
- + /* R(A+C+2),R(A+C+2+1)..., */
- + /* R(A+C+2+2K),R(A+C+2+2K+1),nil) */
- + /* fall through */
- + };
- +
- CASE(OP_SENDB) {
- /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/
- /* fall through */
- @@ -1234,10 +1246,7 @@ RETRY_TRY_BLOCK:
- else {
- bidx = a+n+1;
- }
- - if (GET_OPCODE(i) != OP_SENDB) {
- - SET_NIL_VALUE(regs[bidx]);
- - }
- - else {
- + if (GET_OPCODE(i) == OP_SENDB || GET_OPCODE(i) == OP_SENDK) {
- mrb_value blk = regs[bidx];
- if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
- if (bidx >= mrb->c->ci->nregs) {
- @@ -1248,6 +1257,7 @@ RETRY_TRY_BLOCK:
- regs[bidx] = result;
- }
- }
- + else { SET_NIL_VALUE(regs[bidx]); }
- c = mrb_class(mrb, recv);
- m = mrb_method_search_vm(mrb, &c, mid);
- if (!m) {
- @@ -1615,73 +1625,194 @@ RETRY_TRY_BLOCK:
- int o = MRB_ASPEC_OPT(ax);
- int r = MRB_ASPEC_REST(ax);
- int m2 = MRB_ASPEC_POST(ax);
- + int const kw = MRB_ASPEC_KEY(ax);
- + int const kd = (kw > 0 || MRB_ASPEC_KDICT(ax))? 1 : 0;
- /* unused
- - int k = MRB_ASPEC_KEY(ax);
- - int kd = MRB_ASPEC_KDICT(ax);
- int b = MRB_ASPEC_BLOCK(ax);
- */
- 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;
- + mrb_bool const is_sendk = mrb->c->ci->pc && GET_OPCODE(mrb->c->ci->pc[-1]) == OP_SENDK;
- + int const len = m1 + o + r + m2;
- + int blk_pos = len + 1;
- mrb_value *blk = &argv[argc < 0 ? 1 : argc];
- + mrb_value kdict = mrb_undef_value();
- + mrb_bool separate_kdict_p = FALSE, dont_separate_kdict;
- + // arguments is passed with Array
- if (argc < 0) {
- struct RArray *ary = mrb_ary_ptr(regs[1]);
- argv = ary->ptr;
- argc = ary->len;
- 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)) {
- - argnum_error(mrb, m1+m2);
- - goto L_RAISE;
- - }
- + int const kd_append = (MRB_ASPEC_KDICT(ax) && kw == 0) || is_sendk? 0 : kd;
- + if (argc >= 0 &&
- + (argc < m1 + m2 + kd_append || (r == 0 && argc > len + kd_append))) {
- + argnum_error(mrb, m1+m2+kd_append);
- + 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 = mrb_ary_ptr(argv[0])->len;
- argv = mrb_ary_ptr(argv[0])->ptr;
- }
- +
- + dont_separate_kdict = argc == 1 && (m1 + m2 == 1) && MRB_ASPEC_KDICT(ax);
- + mrb->c->ci->use_kdict = MRB_ASPEC_KDICT(ax)? TRUE : FALSE;
- + if (is_sendk) {
- + mrb_value *kw = regs + 2 + (mrb->c->ci->argc < 0? 1 : mrb->c->ci->argc);
- +
- + if (kd && !dont_separate_kdict) {
- + mrb_value const *kw_beg = kw;
- + int kdict_size = 0;
- + for (; !mrb_nil_p(kw[0]); kw += 2, ++kdict_size) { mrb_assert(mrb_symbol_p(kw[0])); }
- + kdict = mrb_ary_new_from_values(mrb, 2 * kdict_size + 1, kw_beg);
- + mrb->c->ci->kwds = mrb_ary_ptr(kdict)->ptr;
- + mrb_assert(mrb_nil_p(RARRAY_PTR(kdict)[RARRAY_LEN(kdict) - 1]));
- + }
- + // store to last argument hash when proc doesn't use keyword arguments
- + else {
- + mrb_value last_hash = mrb_hash_p(argv[argc - 1])? argv[argc - 1] : mrb_hash_new(mrb);
- + kdict = !mrb_hash_p(argv[argc - 1])? last_hash : mrb_hash_new(mrb);
- +
- + for (; !mrb_nil_p(kw[0]); kw += 2) {
- + mrb_assert(mrb_symbol_p(kw[0]));
- + mrb_hash_set(mrb, last_hash, kw[0], kw[1]);
- + }
- + }
- +
- + mrb_assert(mrb_nil_p(*kw));
- + }
- + else if (kd) {
- + blk_pos += kd;
- +
- + if (argc == 0 && kw > 0) {
- + mrb_value str = mrb_format(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;
- + }
- +
- + // check last arguments is hash if method takes keyword arguments
- + if (mrb_hash_p(argv[argc - 1])) { kdict = argv[argc - 1]; }
- + else if (mrb_respond_to(mrb, argv[argc - 1], mrb_intern_lit(mrb, "to_hash"))) {
- + kdict = argv[argc - 1] =
- + mrb_convert_type(mrb, argv[argc - 1], MRB_TT_HASH, "Hash", "to_hash");
- + }
- + else {
- + // check optional keyword arguments
- + if (kw > 0) {
- + mrb_value str = mrb_format(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;
- + }
- +
- + kdict = mrb_hash_new(mrb);
- + separate_kdict_p = TRUE;
- + }
- +
- + if (!separate_kdict_p && dont_separate_kdict) {
- + kdict = mrb_hash_new(mrb);
- + separate_kdict_p = TRUE;
- + }
- +
- + if (!separate_kdict_p) {
- + khash_t(ht) *h = RHASH_TBL(kdict);
- + khiter_t k;
- + mrb_int non_sym_count = 0;
- +
- + // count non symbol key
- + for (k = kh_begin(h); k != kh_end(h); ++k) {
- + if (kh_exist(h, k) && !mrb_symbol_p(kh_key(h, k))) { ++non_sym_count; }
- + }
- +
- + // move keyword arguments to seperate hash
- + if (non_sym_count > 0) {
- + mrb_value const sep = mrb_hash_new_capa(mrb, kh_size(h) - non_sym_count);
- + mrb_value kv;
- +
- + // duplicate hash
- + kdict = argv[argc - 1] = mrb_obj_dup(mrb, kdict);
- + mrb->c->ci->need_kdict_dup = FALSE;
- +
- + for (k = kh_begin(h); k != kh_end(h); ++k) {
- + if (!kh_exist(h, k)) { continue; }
- +
- + kv = kh_key(h, k);
- +
- + if (mrb_symbol_p(kv)) {
- + mrb_hash_set(mrb, sep, kv, kh_value(h, k).v);
- + mrb_hash_delete_key(mrb, kdict, kv);
- + }
- + }
- +
- + separate_kdict_p = TRUE;
- + kdict = sep;
- + }
- + // ignore passed hash as optional argument when hash splitting didn't occur
- + else if (o > 0) { --argc; }
- + }
- +
- + // recheck arguments count when kdict is separate hash
- + if (separate_kdict_p && !dont_separate_kdict &&
- + mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc) &&
- + argc >= 0 && (argc < m1 + m2 || (r == 0 && argc > len))) {
- + argnum_error(mrb, m1+m2);
- + goto L_RAISE;
- + }
- +
- + mrb_assert(mrb_hash_p(kdict));
- + mrb_assert(!mrb->c->ci->kwds);
- + }
- +
- + // 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);
- }
- + // skip optional arguments
- if (o == 0 || argc < m1+m2) pc++;
- + // skip initailizer of passed arguments
- else
- pc += argc - m1 - m2 + 1;
- }
- 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) {
- - rnum = argc-m1-o-m2;
- + rnum = argc-m1-o-m2-(is_sendk || separate_kdict_p? 0 : kd);
- regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o);
- }
- if (m2) {
- @@ -1690,11 +1821,12 @@ 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 + 1;
- }
- - mrb->c->ci->argc = len;
- + 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);
- @@ -1702,18 +1834,123 @@ RETRY_TRY_BLOCK:
- JUMP;
- }
- +#define DUP_KDICT(kdict) \
- + do { \
- + if (mrb->c->ci->need_kdict_dup) { \
- + mrb->c->ci->need_kdict_dup = FALSE; \
- + *kdict = mrb_obj_dup(mrb, *kdict); \
- + } \
- + } while(FALSE) \
- +
- CASE(OP_KARG) {
- - /* A B C R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */
- - /* if C == 2; raise unless kdict.empty? */
- - /* OP_JMP should follow to skip init code */
- + /* A B C R(A) := kdict[Syms(B)] */
- + /* R(A+1) := kdict.key?(Syms(B)) if C == 1 */
- + /* raise ArgumentError if C == 2 && !kdict.key?(Syms(B)) */
- + /* kdict.delete(Syms(B)) if C >= 1 */
- + int a = GETARG_A(i), c = GETARG_C(i);
- + mrb_value k = mrb_symbol_value(syms[GETARG_B(i)]);
- + if (mrb->c->ci->kwds) {
- + mrb_value *kw;
- +
- + mrb_assert(mrb_array_p(regs[mrb->c->ci->argc]));
- + mrb_assert(RARRAY_PTR(regs[mrb->c->ci->argc]) == mrb->c->ci->kwds);
- +
- + regs[a] = mrb_undef_value();
- + for (kw = mrb->c->ci->kwds; !mrb_nil_p(*kw); kw += 2) {
- + mrb_assert(mrb_symbol_p(kw[0]));
- + if (mrb_symbol(kw[0]) == mrb_symbol(k)) {
- + regs[a] = kw[1];
- + if (c == 1) { regs[a + 1] = mrb_true_value(); }
- + if (c) { kw[0] = mrb_symbol_value(0); }
- + break;
- + }
- + }
- + if (!mrb_undef_p(regs[a])) { NEXT; }
- + }
- + else {
- + mrb_value *kdict = ®s[mrb->c->ci->argc];
- +
- + mrb_assert(mrb_hash_p(*kdict));
- + regs[a] = mrb_hash_fetch(mrb, *kdict, k, mrb_undef_value());
- +
- + if (!mrb_undef_p(regs[a])) {
- + if (c == 1) { regs[a + 1] = mrb_true_value(); }
- + if (c && mrb->c->ci->use_kdict) {
- + DUP_KDICT(kdict);
- + mrb_hash_delete_key(mrb, *kdict, k);
- + }
- + NEXT;
- + }
- + }
- +
- + if (c == 2) {
- + mrb_value str = mrb_format(mrb, "keyword argument '%S' not passed", k);
- + mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
- + goto L_RAISE;
- + }
- + if (c == 1) { regs[a + 1] = mrb_false_value(); }
- + regs[a] = mrb_nil_value(); // clear undef value
- +
- NEXT;
- }
- CASE(OP_KDICT) {
- /* A C R(A) := kdict */
- + /* raise ArgumentError if C && !kdict.empty? */
- + int a = GETARG_A(i), c = GETARG_C(i);
- + mrb_value *kdict = ®s[mrb->c->ci->argc], str;
- +
- + if (mrb->c->ci->kwds) {
- + mrb_value *kw;
- +
- + mrb_assert(mrb_array_p(*kdict));
- +
- + if (c) {
- + mrb_int restkwd = 0;
- + for (kw = mrb->c->ci->kwds; !mrb_nil_p(*kw); kw += 2) {
- + if (mrb_symbol(kw[0]) != 0) { ++restkwd; }
- + }
- + if (restkwd > 0) { goto L_RAISE_KDICT_ERROR; }
- +
- + if (kdict == (regs + a)) {
- + *kdict = mrb_nil_value();
- + NEXT;
- + }
- + else { regs[a] = mrb_hash_new(mrb); }
- + }
- + else {
- + mrb_value res = mrb_hash_new(mrb);
- + for (kw = mrb->c->ci->kwds; !mrb_nil_p(*kw); kw += 2) {
- + if (mrb_symbol(kw[0]) == 0) { continue; }
- + mrb_assert(mrb_symbol_p(kw[0]));
- + mrb_hash_set(mrb, res, kw[0], kw[1]);
- + }
- + mrb_assert(mrb_nil_p(*kw));
- + *kdict = regs[a] = res;
- + }
- +
- + mrb->c->ci->kwds = NULL;
- + }
- + else {
- + if (c && kh_size(RHASH_TBL(*kdict)) > 0) { goto L_RAISE_KDICT_ERROR; }
- +
- + DUP_KDICT(kdict);
- + regs[a] = *kdict;
- + }
- +
- + mrb_assert(mrb_hash_p(*kdict));
- + mrb_assert(mrb_hash_p(regs[a]));
- +
- NEXT;
- +
- + L_RAISE_KDICT_ERROR:
- + str = mrb_format(mrb, "unhandled keyword arguments found: %S", *kdict);
- + mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
- + goto L_RAISE;
- }
- +#undef DUP_KDICT
- +
- L_RETURN:
- i = MKOP_AB(OP_RETURN, GETARG_A(i), OP_R_NORMAL);
- /* fall through */
- modified 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,215 @@ 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:) 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_equal({'a' => 1}, 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)
- +
- + def m(*, a:) a end
- + assert_equal 1, m(a: 1)
- + assert_equal 3, m(1, 2, a: 3)
- + assert_equal 2, 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_equal [[{'a' => 1}], 2], 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_equal [[{'a' => 1}], 2], m('a' => 1, b: 2)
- + splat_val = Object.new
- + assert_false splat_val.respond_to? :to_ary
- + assert_equal [[splat_val], 1], m(*splat_val)
- +
- + def m(*, **) end
- + assert_nil m()
- + assert_nil m(a: 1, b: 2)
- + assert_nil m(1, 2, 3, a: 4, b: 5)
- + h = Object.new
- + def h.to_hash; { a: 1 } end
- + assert_true h.respond_to? :to_hash
- + assert_nil m(h)
- + h = Object.new
- + def h.to_hash; raise end
- + assert_raise(RuntimeError) { m(h) }
- +
- + def m(*a, **) a end
- + assert_equal [], m()
- + assert_equal [1, 2, 3], m(1, 2, 3, a: 4, b: 5)
- + assert_equal [{"a" => 1}], m("a" => 1, a: 1)
- + assert_equal [1], m(1, **{a: 2})
- + h = Object.new
- + def h.to_hash; nil end
- + assert_true h.respond_to?(:to_hash)
- + assert_raise(TypeError) { m(**h) }
- +
- + def m(*, **k) k end
- + assert_equal({}, m())
- + assert_equal({a: 4, b: 5}, m(1, 2, 3, a: 4, b: 5))
- + assert_equal({a: 1}, m("a" => 1, a: 1))
- + h = Object.new
- + def h.to_hash; {a: 1} end
- + assert_true h.respond_to?(:to_hash)
- + assert_equal({a: 1}, m(h))
- +
- + def m(a = nil, b = nil, **k) [a, k] end
- + assert_equal [nil, {}], m()
- + assert_equal [{"a" => 1}, {}], m("a" => 1)
- + assert_equal([nil, {a: 1}], m(a: 1))
- + assert_equal([{"a" => 1}, {a: 1}], m("a" => 1, a: 1))
- + assert_equal([{"a" => 1}, {a: 1}], m({ "a" => 1 }, a: 1))
- + assert_equal([{a: 1}, {}], m({a: 1}, {}))
- + h = {"a" => 1, b: 2}
- + assert_equal([{"a" => 1}, {b: 2}], m(h))
- + assert_equal({"a" => 1, b: 2}, h)
- + h = {"a" => 1}
- + assert_equal(h, m(h).first)
- + assert_equal([nil, {}], m({}))
- + hh = {}
- + h = Object.new
- + h.define_singleton_method(:to_hash) { hh }
- + assert_equal([nil, {}], m(h))
- + h = Object.new
- + def h.to_hash; {"a" => 1, a: 2} end
- + assert_equal([{"a" => 1}, {a: 2}], m(h))
- +
- + 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_equal([[{"a" => 1}], {}], m("a" => 1))
- + assert_equal([[], {a: 1}], m(a: 1))
- + assert_equal([[{"a" => 1}], {a: 1}], 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([[{a: 1}, {"a" => 1}], {}], m({a: 1}, {"a" => 1}))
- + bo = BasicObject.new
- + def bo.to_a; [1, 2, 3]; end
- + def bo.to_hash; {b: 2, c: 3}; end
- + assert_equal([[1, 2, 3], {:b => 2, :c => 3}], m(*bo, **bo))
- +
- +=begin
- + def m(*, &b) b end
- + m().should be_nil
- + m(1, 2, 3, 4).should be_nil
- + m(&(l = ->{})).should equal(l)
- +
- + def m(*a, &b) [a, b] end
- + assert_equal([[], nil], m())
- + assert_equal([[1], nil], m(1))
- + assert_equal([[1, 2, 3], l], m(1, 2, 3, &(l = -> {})))
- +=end
- +
- + 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))
- +
- +=begin
- + def m(a: 1, &b) [a, b] end
- + assert_equal([1, l], m(&(l = ->{})))
- + assert_equal([1, nil], m())
- +
- + def m(**, &b) b end
- + assert_equal(l, m(a: 1, b: 2, &(l = ->{})))
- +=end
- +
- + def m(**k, &b) [k, b] end
- + assert_equal([{ a: 1, b: 2}, nil], 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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement