From: Nobuyoshi Nakada <nobu@...>
Date: 2008-07-06T16:17:50+09:00
Subject: [ruby-dev:35379] [Feature:1.9] rb_scan_args() for keyword arguments

なかだです。

rb_scan_args()でキーワード引数に対応するパッチを発掘しました。

  rb_scan_args(argc, argv, "11:foo", &mandatory, &optional, &foo)
とか
  rb_scan_args(argc, argv, "11::", &mandatory, &optional, rb_intern("foo"), &foo, (ID)0)
とか書けるようになるはずだったと思います。


Index: class.c
===================================================================
--- class.c	(revision 17903)
+++ class.c	(working copy)
@@ -871,4 +871,5 @@ rb_scan_args(int argc, const VALUE *argv
     const char *p = fmt;
     VALUE *var;
+    VALUE opt = 0;
     va_list vargs;
 
@@ -876,4 +877,13 @@ rb_scan_args(int argc, const VALUE *argv
 
     if (*p == '*') goto rest_arg;
+    if (strchr(fmt, ':')) {
+	if (argc > 0 && TYPE(opt = argv[argc-1]) == T_HASH) {
+	    --argc;
+	}
+	else {
+	    opt = 0;
+	}
+	if (*p == ':') goto keyword;
+    }
 
     if (ISDIGIT(*p)) {
@@ -895,9 +905,6 @@ rb_scan_args(int argc, const VALUE *argv
 	for (; i<n; i++) {
 	    var = va_arg(vargs, VALUE*);
-	    if (argc > i) {
-		if (var) *var = argv[i];
-	    }
-	    else {
-		if (var) *var = Qnil;
+	    if (var) {
+		*var = argc > i ? argv[i] : Qnil;
 	    }
 	}
@@ -905,5 +912,32 @@ rb_scan_args(int argc, const VALUE *argv
     }
 
-    if(*p == '*') {
+  keyword:
+    if (*p == ':') {
+	if (p[1] == ':') {
+	    ID name;
+	    while ((name = va_arg(vargs, ID)) != 0) {
+		var = va_arg(vargs, VALUE*);
+		if (var) {
+		    *var = opt ? rb_hash_lookup(opt, name) : Qnil;
+		}
+	    }
+	}
+	else {
+	    do {
+		const char *beg = ++p;
+		if (*p != '_' && !ISALPHA(*p)) goto error;
+		while (*p == '_' || ISALNUM(*p)) p++;
+		if (*p && !strchr(":*&", *p)) goto error;
+		var = va_arg(vargs, VALUE*);
+		if (var) {
+		    *var = opt ?
+			rb_hash_lookup(opt, rb_intern2(beg, p - beg)) :
+			Qnil;
+		}
+	    } while (*p == ':');
+	}
+    }
+
+    if (*p == '*') {
       rest_arg:
 	var = va_arg(vargs, VALUE*);


-- 
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
    中田 伸悦