summaryrefslogtreecommitdiff
path: root/parse.y
diff options
context:
space:
mode:
Diffstat (limited to 'parse.y')
-rw-r--r--parse.y64
1 files changed, 55 insertions, 9 deletions
diff --git a/parse.y b/parse.y
index 18c08db394..861ff4945a 100644
--- a/parse.y
+++ b/parse.y
@@ -174,6 +174,15 @@ vtable_add(struct vtable *tbl, ID id)
tbl->tbl[tbl->pos++] = id;
}
+#ifndef RIPPER
+static void
+vtable_pop(struct vtable *tbl, int n)
+{
+ if (tbl->pos < n) rb_bug("vtable_pop: unreachable");
+ tbl->pos -= n;
+}
+#endif
+
static int
vtable_included(const struct vtable * tbl, ID id)
{
@@ -9614,25 +9623,62 @@ new_args_tail_gen(struct parser_params *parser, NODE *k, ID kr, ID b)
{
int saved_line = ruby_sourceline;
struct rb_args_info *args;
- NODE *kw_rest_arg = 0;
NODE *node;
- int check = 0;
args = ZALLOC(struct rb_args_info);
node = NEW_NODE(NODE_ARGS, 0, 0, args);
args->block_arg = b;
args->kw_args = k;
- if (k && !kr) {
- check = 1;
- kr = internal_id();
+
+ if (k) {
+ /*
+ * def foo(k1: 1, kr1:, k2: 2, **krest, &b)
+ * variable order: k1, kr1, k2, &b, internal_id, krest
+ * #=> <reorder>
+ * variable order: kr1, k1, k2, internal_id, krest, &b
+ */
+ ID kw_bits;
+ NODE *kwn = k;
+ struct vtable *required_kw_vars = vtable_alloc(NULL);
+ struct vtable *kw_vars = vtable_alloc(NULL);
+ int i;
+
+ while (kwn) {
+ NODE *val_node = kwn->nd_body->nd_value;
+ ID vid = kwn->nd_body->nd_vid;
+
+ if (val_node == (NODE *)-1) {
+ vtable_add(required_kw_vars, vid);
+ }
+ else {
+ vtable_add(kw_vars, vid);
+ }
+
+ kwn = kwn->nd_next;
+ }
+
+ vtable_pop(lvtbl->args, vtable_size(required_kw_vars) + vtable_size(kw_vars) + (b != 0));
+
+ for (i=0; i<vtable_size(required_kw_vars); i++) arg_var(required_kw_vars->tbl[i]);
+ for (i=0; i<vtable_size(kw_vars); i++) arg_var(kw_vars->tbl[i]);
+ vtable_free(required_kw_vars);
+ vtable_free(kw_vars);
+
+ kw_bits = internal_id();
+ arg_var(kw_bits);
+ if (kr) arg_var(kr);
+ if (b) arg_var(b);
+
+ args->kw_rest_arg = NEW_DVAR(kw_bits);
+ args->kw_rest_arg->nd_cflag = kr;
}
- if (kr) {
+ else if (kr) {
+ if (b) vtable_pop(lvtbl->args, 1); /* reorder */
arg_var(kr);
- kw_rest_arg = NEW_DVAR(kr);
- kw_rest_arg->nd_cflag = check;
+ if (b) arg_var(b);
+ args->kw_rest_arg = NEW_DVAR(kr);
}
- args->kw_rest_arg = kw_rest_arg;
ruby_sourceline = saved_line;
return node;