Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

* parse.y (assoc, parser_yylex): add syntax to splat keyword hash.

  [ruby-core:44591][Feature #6353]
* compile.c (compile_array_): generate keyword splat insns.
* vm.c (m_core_hash_merge_kwd): merge keyword hash into intermediate
  hash.  leftward argument is prior currently.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35489 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
  • Loading branch information...
commit 3380974143d3fdf1721d9e28d6b2d42036f03bd2 1 parent 82fa299
@nobu nobu authored
View
10 ChangeLog
@@ -1,3 +1,13 @@
+Sun Apr 29 06:12:02 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * parse.y (assoc, parser_yylex): add syntax to splat keyword hash.
+ [ruby-core:44591][Feature #6353]
+
+ * compile.c (compile_array_): generate keyword splat insns.
+
+ * vm.c (m_core_hash_merge_kwd): merge keyword hash into intermediate
+ hash. leftward argument is prior currently.
+
Sat Apr 28 18:39:40 2012 Koichi Sasada <ko1@atdot.net>
* vm_core.h (rb_thread_t#yielding): add a field.
View
19 compile.c
@@ -2296,21 +2296,28 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
while (node) {
NODE *start_node = node, *end_node;
+ NODE *kw = 0;
const int max = 0x100;
DECL_ANCHOR(anchor);
INIT_ANCHOR(anchor);
- for (i=0; i<max && node; i++, len++) {
+ for (i=0; i<max && node; i++, len++, node = node->nd_next) {
if (CPDEBUG > 0 && nd_type(node) != NODE_ARRAY) {
rb_bug("compile_array: This node is not NODE_ARRAY, but %s", ruby_node_name(nd_type(node)));
}
+ if (type == COMPILE_ARRAY_TYPE_HASH && !node->nd_head) {
+ opt_p = 0;
+ kw = node->nd_next;
+ node = kw->nd_next;
+ kw = kw->nd_head;
+ break;
+ }
if (opt_p && nd_type(node->nd_head) != NODE_LIT) {
opt_p = 0;
}
COMPILE_(anchor, "array element", node->nd_head, poped);
- node = node->nd_next;
}
if (opt_p && type != COMPILE_ARRAY_TYPE_ARGS) {
@@ -2378,12 +2385,18 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
ADD_INSN1(anchor, line, newhash, INT2FIX(i));
APPEND_LIST(ret, anchor);
}
- else {
+ else if (i > 0) {
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
ADD_INSN(ret, line, swap);
APPEND_LIST(ret, anchor);
ADD_SEND(ret, line, ID2SYM(id_core_hash_merge_ptr), INT2FIX(i + 1));
}
+ if (kw) {
+ ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ ADD_INSN(ret, line, swap);
+ COMPILE(ret, "keyword splat", kw);
+ ADD_SEND(ret, line, ID2SYM(id_core_hash_merge_kwd), INT2FIX(2));
+ }
break;
case COMPILE_ARRAY_TYPE_ARGS:
APPEND_LIST(ret, anchor);
View
1  ext/ripper/eventids2.c
@@ -233,6 +233,7 @@ static const struct token_assoc {
{tRPAREN, &ripper_id_rparen},
{tRSHFT, &ripper_id_op},
{tSTAR, &ripper_id_op},
+ {tDSTAR, &ripper_id_op},
{tSTRING_BEG, &ripper_id_tstring_beg},
{tSTRING_CONTENT, &ripper_id_tstring_content},
{tSTRING_DBEG, &ripper_id_embexpr_beg},
View
1  id.c
@@ -34,6 +34,7 @@ Init_id(void)
REGISTER_SYMID(id_core_hash_from_ary, "core#hash_from_ary");
REGISTER_SYMID(id_core_hash_merge_ary, "core#hash_merge_ary");
REGISTER_SYMID(id_core_hash_merge_ptr, "core#hash_merge_ptr");
+ REGISTER_SYMID(id_core_hash_merge_kwd, "core#hash_merge_kwd");
REGISTER_SYMID(idEach, "each");
REGISTER_SYMID(idLength, "length");
View
32 parse.y
@@ -759,6 +759,7 @@ static void token_info_pop(struct parser_params*, const char *token);
%token tLBRACE /* { */
%token tLBRACE_ARG /* { */
%token tSTAR /* * */
+%token tDSTAR /* ** */
%token tAMPER /* & */
%token tLAMBDA /* -> */
%token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG
@@ -805,6 +806,7 @@ static void token_info_pop(struct parser_params*, const char *token);
%nonassoc id_core_hash_from_ary
%nonassoc id_core_hash_merge_ary
%nonassoc id_core_hash_merge_ptr
+%nonassoc id_core_hash_merge_kwd
%token tLAST_TOKEN
@@ -1918,6 +1920,7 @@ op : '|' { ifndef_ripper($$ = '|'); }
| '/' { ifndef_ripper($$ = '/'); }
| '%' { ifndef_ripper($$ = '%'); }
| tPOW { ifndef_ripper($$ = tPOW); }
+ | tDSTAR { ifndef_ripper($$ = tDSTAR); }
| '!' { ifndef_ripper($$ = '!'); }
| '~' { ifndef_ripper($$ = '~'); }
| tUPLUS { ifndef_ripper($$ = tUPLUS); }
@@ -4699,7 +4702,11 @@ f_kwarg : f_kw
}
;
-f_kwrest : tPOW tIDENTIFIER
+kwrest_mark : tPOW
+ | tDSTAR
+ ;
+
+f_kwrest : kwrest_mark tIDENTIFIER
{
$$ = $2;
}
@@ -4923,6 +4930,16 @@ assoc : arg_value tASSOC arg_value
$$ = dispatch2(assoc_new, $1, $2);
%*/
}
+ | tDSTAR arg_value
+ {
+ /*%%%*/
+ $$ = list_append(NEW_LIST(0), $2);
+ /*%
+ $$ = dispatch1(assoc_splat, $2);
+ %*/
+ }
+ ;
+
;
operation : tIDENTIFIER
@@ -6890,7 +6907,17 @@ parser_yylex(struct parser_params *parser)
return tOP_ASGN;
}
pushback(c);
- c = tPOW;
+ if (IS_SPCARG(c)) {
+ rb_warning0("`**' interpreted as argument prefix");
+ c = tDSTAR;
+ }
+ else if (IS_BEG()) {
+ c = tDSTAR;
+ }
+ else {
+ warn_balanced("**", "argument prefix");
+ c = tPOW;
+ }
}
else {
if (c == '=') {
@@ -9701,6 +9728,7 @@ static const struct {
{'+', "+(binary)"},
{'-', "-(binary)"},
{tPOW, "**"},
+ {tDSTAR, "**"},
{tUPLUS, "+@"},
{tUMINUS, "-@"},
{tCMP, "<=>"},
View
6 test/ripper/test_parser_events.rb
@@ -146,6 +146,12 @@ def test_assoc_new
assert_equal true, thru_assoc_new
end
+ def test_assoc_splat
+ thru_assoc_splat = false
+ parse('m(**h)', :on_assoc_splat) {thru_assoc_splat = true}
+ assert_equal true, thru_assoc_splat
+ end
+
def test_aref_field
assert_equal '[assign(aref_field(vcall(a),[1]),2)]', parse('a[1]=2')
end
View
15 test/ruby/test_syntax.rb
@@ -90,6 +90,21 @@ def o.kw(**a) a end
assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989)
end
+ def test_keyword_splat
+ assert_valid_syntax("foo(**h)", __FILE__)
+ o = Object.new
+ def o.kw(k1: 1, k2: 2) [k1, k2] end
+ h = {k1: 11, k2: 12}
+ assert_equal([11, 12], o.kw(**h))
+ assert_equal([11, 22], o.kw(k2: 22, **h))
+ assert_equal([11, 12], o.kw(**h, **{k2: 22}))
+ assert_equal([11, 22], o.kw(**{k2: 22}, **h))
+ h = {k3: 31}
+ assert_raise(ArgumentError) {o.kw(**h)}
+ h = {"k1"=>11, k2: 12}
+ assert_raise(TypeError) {o.kw(**h)}
+ end
+
def test_warn_grouped_expression
assert_warn("test:2: warning: (...) interpreted as grouped expression\n") do
assert_valid_syntax("foo \\\n(\n true)", "test") {$VERBOSE = true}
View
25 vm.c
@@ -2068,6 +2068,30 @@ m_core_hash_merge_ptr(int argc, VALUE *argv, VALUE recv)
return hash;
}
+static int
+kwmerge_ii(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
+{
+ if (existing) return ST_STOP;
+ *value = arg;
+ return ST_CONTINUE;
+}
+
+static int
+kwmerge_i(VALUE key, VALUE value, VALUE hash)
+{
+ if (!SYMBOL_P(key)) Check_Type(key, T_SYMBOL);
+ st_update(RHASH_TBL(hash), key, kwmerge_ii, (st_data_t)value);
+ return ST_CONTINUE;
+}
+
+static VALUE
+m_core_hash_merge_kwd(VALUE recv, VALUE hash, VALUE kw)
+{
+ kw = rb_convert_type(kw, T_HASH, "Hash", "to_hash");
+ rb_hash_foreach(kw, kwmerge_i, hash);
+ return hash;
+}
+
extern VALUE *rb_gc_stack_start;
extern size_t rb_gc_stack_maxsize;
#ifdef __ia64
@@ -2134,6 +2158,7 @@ Init_VM(void)
rb_define_method_id(klass, id_core_hash_from_ary, m_core_hash_from_ary, 1);
rb_define_method_id(klass, id_core_hash_merge_ary, m_core_hash_merge_ary, 2);
rb_define_method_id(klass, id_core_hash_merge_ptr, m_core_hash_merge_ptr, -1);
+ rb_define_method_id(klass, id_core_hash_merge_kwd, m_core_hash_merge_kwd, 2);
rb_obj_freeze(fcore);
rb_gc_register_mark_object(fcore);
rb_mRubyVMFrozenCore = fcore;
Please sign in to comment.
Something went wrong with that request. Please try again.