* 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
This commit is contained in:
nobu 2012-04-28 21:12:05 +00:00
Родитель 82fa2995b5
Коммит 3380974143
8 изменённых файлов: 104 добавлений и 5 удалений

Просмотреть файл

@ -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.

Просмотреть файл

@ -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);

Просмотреть файл

@ -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},

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");

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, "<=>"},

Просмотреть файл

@ -146,6 +146,12 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
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

Просмотреть файл

@ -90,6 +90,21 @@ class TestSyntax < Test::Unit::TestCase
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}

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;