From aae1e0751cca795d0bf388bc69778b1b1a9df620 Mon Sep 17 00:00:00 2001
From: Yusuke Endoh <mame@tsg.ne.jp>
Date: Mon, 17 Oct 2011 23:49:46 +0900
Subject: [PATCH 4/5] * rough implementation of keyword arguments.

def foo(str: "foo", num: 424242)
  p [str, num]
end
foo(str: "bar") #=> ["bar", 424242]
---
 compile.c       |   30 ++++++++++++++++++++++++++----
 node.c          |   11 +++++++++++
 parse.y         |    8 ++++++--
 vm_core.h       |    3 +++
 vm_insnhelper.c |   16 ++++++++++++++--
 5 files changed, 60 insertions(+), 8 deletions(-)

diff --git a/compile.c b/compile.c
index 9f1552c..fa2e54c 100644
--- a/compile.c
+++ b/compile.c
@@ -1093,9 +1093,6 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
 	    iseq->arg_post_len = args->post_args_num;
 	}
 
-	if (args->kw_args) {
-	    rb_notimplement();
-	}
 	if (args->opt_args) {
 	    NODE *node = args->opt_args;
 	    LABEL *label;
@@ -1129,6 +1126,30 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
 	    iseq->arg_opts = 0;
 	}
 
+	if (args->kw_args) {
+	    NODE *node = args->kw_args;
+	    VALUE keywords = rb_ary_tmp_new(1);
+	    int i = 0, j;
+	    while (node) {
+		rb_ary_push(keywords, INT2FIX(node->nd_body->nd_head->nd_vid));
+		COMPILE_POPED(optargs, "kwarg", node->nd_body);
+		node = node->nd_next;
+		i += 1;
+	    }
+	    iseq->arg_keywords = i;
+	    iseq->arg_keyword_table = ALLOC_N(ID, i);
+	    iseq->arg_keyword_vars = ALLOC_N(int, i);
+	    for (j = 0; j < i; j++) {
+		ID id = FIX2INT(RARRAY_PTR(keywords)[j]);
+		iseq->arg_keyword_table[j] = id;
+		iseq->arg_keyword_vars[j] = get_dyna_var_idx_at_raw(iseq, id);
+	    }
+	    rb_ary_clear(keywords);
+	}
+	else {
+	    iseq->arg_keywords = 0;
+	}
+
 	if (args->pre_init) { /* m_init */
 	    COMPILE_POPED(optargs, "init arguments (m)", args->pre_init);
 	}
@@ -1153,7 +1174,8 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
 	}
 
 	if (iseq->arg_opts != 0 || iseq->arg_post_len != 0 ||
-	    iseq->arg_rest != -1 || iseq->arg_block != -1) {
+	    iseq->arg_rest != -1 || iseq->arg_block != -1 ||
+	    iseq->arg_keywords != 0) {
 	    iseq->arg_simple = 0;
 
 	    /* set arg_size: size of arguments */
diff --git a/node.c b/node.c
index eca8981..cfeb562 100644
--- a/node.c
+++ b/node.c
@@ -823,6 +823,15 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
 	F_NODE(nd_next, "next");
 	break;
 
+      case NODE_KW_ARG:
+	ANN("keyword arguments");
+	ANN("format: def method_name([nd_body=some], [nd_next..])");
+	ANN("example: def foo(a:1, b:2); end");
+	F_NODE(nd_body, "body");
+	LAST_NODE;
+	F_NODE(nd_next, "next");
+	break;
+
       case NODE_POSTARG:
 	ANN("post arguments");
 	ANN("format: *[nd_1st], [nd_2nd..] = ..");
@@ -849,6 +858,8 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
 	F_ID(nd_ainfo->rest_arg, "rest argument");
 	F_ID(nd_ainfo->block_arg, "block argument");
 	F_NODE(nd_ainfo->opt_args, "optional arguments");
+	LAST_NODE;
+	F_NODE(nd_ainfo->kw_args, "keyword arguments");
 	break;
 
       case NODE_SCOPE:
diff --git a/parse.y b/parse.y
index 085cf84..f200b0a 100644
--- a/parse.y
+++ b/parse.y
@@ -4821,8 +4821,10 @@ f_arg		: f_arg_item
 
 f_kw		: tLABEL arg_value
 		    {
+			NODE *lasgn;
 			arg_var(formal_argument(get_id($1)));
-			$$ = assignable($1, $2);
+			lasgn = assignable($1, $2);
+			$$ = NEW_OP_ASGN_OR(gettable(lasgn->nd_vid), lasgn);
 		    /*%%%*/
 			$$ = NEW_KW_ARG(0, $$);
 		    /*%
@@ -4833,8 +4835,10 @@ f_kw		: tLABEL arg_value
 
 f_block_kw	: tLABEL primary_value
 		    {
+			NODE *lasgn;
 			arg_var(formal_argument(get_id($1)));
-			$$ = assignable($1, $2);
+			lasgn = assignable($1, $2);
+			$$ = NEW_OP_ASGN_OR(gettable(lasgn->nd_vid), lasgn);
 		    /*%%%*/
 			$$ = NEW_KW_ARG(0, $$);
 		    /*%
diff --git a/vm_core.h b/vm_core.h
index e8dbdd9..d9a3bdc 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -220,6 +220,9 @@ struct rb_iseq_struct {
     int arg_post_start;
     int arg_size;
     VALUE *arg_opt_table;
+    int arg_keywords;
+    ID *arg_keyword_table;
+    int *arg_keyword_vars;
 
     size_t stack_max; /* for stack overflow check */
 
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 250ea3e..874b890 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -156,6 +156,16 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq,
 
     th->mark_stack_len = argc + iseq->arg_size;
 
+    if (iseq->arg_keywords && argc > 0 && TYPE(argv[argc-1]) == T_HASH) {
+	VALUE hash = argv[argc-1];
+	int i;
+	argc--;
+	for (i = 0; i < iseq->arg_keywords; i++) {
+	    VALUE val = rb_hash_aref(hash, ID2SYM(iseq->arg_keyword_table[i]));
+	    orig_argv[iseq->arg_keyword_vars[i]] = val;
+	}
+    }
+
     /* mandatory */
     if (argc < (m + iseq->arg_post_len)) { /* check with post arg */
 	argument_error(iseq, argc, m + iseq->arg_post_len);
@@ -475,7 +485,8 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
 		      iseq->local_size, iseq->arg_size);
 
 	/* clear local variables */
-	for (i = 0; i < iseq->local_size - iseq->arg_size; i++) {
+	sp += iseq->arg_keywords;
+	for (i = iseq->arg_keywords; i < iseq->local_size - iseq->arg_size; i++) {
 	    *sp++ = Qnil;
 	}
 
@@ -498,7 +509,8 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
 	sp -= rsp - p_rsp;
 
 	/* clear local variables */
-	for (i = 0; i < iseq->local_size - iseq->arg_size; i++) {
+	sp += iseq->arg_keywords;
+	for (i = iseq->arg_keywords; i < iseq->local_size - iseq->arg_size; i++) {
 	    *sp++ = Qnil;
 	}
 
-- 
1.7.5.4

