Allow destructuring in formal arguments.

e.g.
```
def m(a,(b,c),d); p [a,b,c,d]; end
m(1,[2,3],4)  # => [1,2,3,4]
```

mruby limitation:
Destructured arguments (`b` and `c` in above example) cannot be accessed
from the default expression of optional arguments and keyword arguments,
since actual assignment is done after the evaluation of those default
expressions. Thus:

```
def f(a,(b,c),d=b)
  p [a,b,c,d]
end
f(1,[2,3])
```

raises `NoMethodError` for `b` in mruby.
parent e6b72b21
...@@ -734,15 +734,13 @@ lambda_body(codegen_scope *s, node *tree, int blk) ...@@ -734,15 +734,13 @@ lambda_body(codegen_scope *s, node *tree, int blk)
mrb_aspec a; mrb_aspec a;
int ma, oa, ra, pa, ka, kd, ba; int ma, oa, ra, pa, ka, kd, ba;
int pos, i; int pos, i;
node *n, *opt; node *opt;
node *margs, *pargs;
node *tail; node *tail;
/* mandatory arguments */ /* mandatory arguments */
ma = node_len(tree->car->car); ma = node_len(tree->car->car);
n = tree->car->car; margs = tree->car->car;
while (n) {
n = n->cdr;
}
tail = tree->car->cdr->cdr->cdr->cdr; tail = tree->car->cdr->cdr->cdr->cdr;
/* optional arguments */ /* optional arguments */
...@@ -751,6 +749,7 @@ lambda_body(codegen_scope *s, node *tree, int blk) ...@@ -751,6 +749,7 @@ lambda_body(codegen_scope *s, node *tree, int blk)
ra = tree->car->cdr->cdr->car ? 1 : 0; ra = tree->car->cdr->cdr->car ? 1 : 0;
/* mandatory arugments after rest argument */ /* mandatory arugments after rest argument */
pa = node_len(tree->car->cdr->cdr->cdr->car); pa = node_len(tree->car->cdr->cdr->cdr->car);
pargs = tree->car->cdr->cdr->cdr->car;
/* keyword arguments */ /* keyword arguments */
ka = tail? node_len(tail->cdr->car) : 0; ka = tail? node_len(tail->cdr->car) : 0;
/* keyword dictionary? */ /* keyword dictionary? */
...@@ -798,6 +797,7 @@ lambda_body(codegen_scope *s, node *tree, int blk) ...@@ -798,6 +797,7 @@ lambda_body(codegen_scope *s, node *tree, int blk)
dispatch(s, pos+i*3+1); dispatch(s, pos+i*3+1);
} }
/* keyword arguments */
if (tail) { if (tail) {
node *kwds = tail->cdr->car; node *kwds = tail->cdr->car;
int kwrest = 0; int kwrest = 0;
...@@ -836,7 +836,34 @@ lambda_body(codegen_scope *s, node *tree, int blk) ...@@ -836,7 +836,34 @@ lambda_body(codegen_scope *s, node *tree, int blk)
genop_0(s, OP_KEYEND); genop_0(s, OP_KEYEND);
} }
} }
/* argument destructuring */
if (margs) {
node *n = margs;
pos = 1;
while (n) {
if (nint(n->car->car) == NODE_MASGN) {
gen_vmassignment(s, n->car->cdr->car, pos, NOVAL);
}
pos++;
n = n->cdr;
}
}
if (pargs) {
node *n = margs;
pos = ma+oa+ra+1;
while (n) {
if (nint(n->car->car) == NODE_MASGN) {
gen_vmassignment(s, n->car->cdr->car, pos, NOVAL);
}
pos++;
n = n->cdr;
}
}
} }
codegen(s, tree->cdr->car, VAL); codegen(s, tree->cdr->car, VAL);
pop(); pop();
if (s->pc > 0) { if (s->pc > 0) {
...@@ -1066,6 +1093,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val) ...@@ -1066,6 +1093,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
idx = new_sym(s, nsym(tree)); idx = new_sym(s, nsym(tree));
genop_2(s, OP_SETGV, sp, idx); genop_2(s, OP_SETGV, sp, idx);
break; break;
case NODE_ARG:
case NODE_LVAR: case NODE_LVAR:
idx = lv_idx(s, nsym(tree)); idx = lv_idx(s, nsym(tree));
if (idx > 0) { if (idx > 0) {
...@@ -1173,7 +1201,7 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val) ...@@ -1173,7 +1201,7 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
pop_n(post+1); pop_n(post+1);
genop_3(s, OP_APOST, cursp(), n, post); genop_3(s, OP_APOST, cursp(), n, post);
n = 1; n = 1;
if (t->car) { /* rest */ if (t->car && t->car != (node*)-1) { /* rest */
gen_assignment(s, t->car, cursp(), NOVAL); gen_assignment(s, t->car, cursp(), NOVAL);
} }
if (t->cdr && t->cdr->car) { if (t->cdr && t->cdr->car) {
......
...@@ -682,6 +682,25 @@ new_arg(parser_state *p, mrb_sym sym) ...@@ -682,6 +682,25 @@ new_arg(parser_state *p, mrb_sym sym)
return cons((node*)NODE_ARG, nsym(sym)); return cons((node*)NODE_ARG, nsym(sym));
} }
static void
local_add_margs(parser_state *p, node *n)
{
while (n) {
if (n->car->car == (node*)NODE_MASGN) {
node *t = n->car->cdr->cdr;
n->car->cdr->cdr = NULL;
while (t) {
local_add_f(p, sym(t->car));
t = t->cdr;
}
local_add_margs(p, n->car->cdr->car->car);
local_add_margs(p, n->car->cdr->car->cdr->cdr->car);
}
n = n->cdr;
}
}
/* (m o r m2 tail) */ /* (m o r m2 tail) */
/* m: (a b c) */ /* m: (a b c) */
/* o: ((a . e1) (b . e2)) */ /* o: ((a . e1) (b . e2)) */
...@@ -693,6 +712,8 @@ new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, node *tail ...@@ -693,6 +712,8 @@ new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, node *tail
{ {
node *n; node *n;
local_add_margs(p, m);
local_add_margs(p, m2);
n = cons(m2, tail); n = cons(m2, tail);
n = cons(nsym(rest), n); n = cons(nsym(rest), n);
n = cons(opt, n); n = cons(opt, n);
...@@ -3275,9 +3296,15 @@ f_arg_item : f_norm_arg ...@@ -3275,9 +3296,15 @@ f_arg_item : f_norm_arg
{ {
$$ = new_arg(p, $1); $$ = new_arg(p, $1);
} }
| tLPAREN f_margs rparen | tLPAREN
{
$<nd>$ = local_switch(p);
}
f_margs rparen
{ {
$$ = new_masgn(p, $2, 0); $$ = new_masgn(p, $3, p->locals->car);
local_resume(p, $<nd>2);
local_add_f(p, 0);
} }
; ;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment