* 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:
parent
82fa2995b5
commit
3380974143
10
ChangeLog
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>
|
Sat Apr 28 18:39:40 2012 Koichi Sasada <ko1@atdot.net>
|
||||||
|
|
||||||
* vm_core.h (rb_thread_t#yielding): add a field.
|
* vm_core.h (rb_thread_t#yielding): add a field.
|
||||||
|
19
compile.c
19
compile.c
@ -2296,21 +2296,28 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
|
|||||||
|
|
||||||
while (node) {
|
while (node) {
|
||||||
NODE *start_node = node, *end_node;
|
NODE *start_node = node, *end_node;
|
||||||
|
NODE *kw = 0;
|
||||||
const int max = 0x100;
|
const int max = 0x100;
|
||||||
DECL_ANCHOR(anchor);
|
DECL_ANCHOR(anchor);
|
||||||
INIT_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) {
|
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)));
|
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) {
|
if (opt_p && nd_type(node->nd_head) != NODE_LIT) {
|
||||||
opt_p = 0;
|
opt_p = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
COMPILE_(anchor, "array element", node->nd_head, poped);
|
COMPILE_(anchor, "array element", node->nd_head, poped);
|
||||||
node = node->nd_next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt_p && type != COMPILE_ARRAY_TYPE_ARGS) {
|
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));
|
ADD_INSN1(anchor, line, newhash, INT2FIX(i));
|
||||||
APPEND_LIST(ret, anchor);
|
APPEND_LIST(ret, anchor);
|
||||||
}
|
}
|
||||||
else {
|
else if (i > 0) {
|
||||||
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
|
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
|
||||||
ADD_INSN(ret, line, swap);
|
ADD_INSN(ret, line, swap);
|
||||||
APPEND_LIST(ret, anchor);
|
APPEND_LIST(ret, anchor);
|
||||||
ADD_SEND(ret, line, ID2SYM(id_core_hash_merge_ptr), INT2FIX(i + 1));
|
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;
|
break;
|
||||||
case COMPILE_ARRAY_TYPE_ARGS:
|
case COMPILE_ARRAY_TYPE_ARGS:
|
||||||
APPEND_LIST(ret, anchor);
|
APPEND_LIST(ret, anchor);
|
||||||
|
@ -233,6 +233,7 @@ static const struct token_assoc {
|
|||||||
{tRPAREN, &ripper_id_rparen},
|
{tRPAREN, &ripper_id_rparen},
|
||||||
{tRSHFT, &ripper_id_op},
|
{tRSHFT, &ripper_id_op},
|
||||||
{tSTAR, &ripper_id_op},
|
{tSTAR, &ripper_id_op},
|
||||||
|
{tDSTAR, &ripper_id_op},
|
||||||
{tSTRING_BEG, &ripper_id_tstring_beg},
|
{tSTRING_BEG, &ripper_id_tstring_beg},
|
||||||
{tSTRING_CONTENT, &ripper_id_tstring_content},
|
{tSTRING_CONTENT, &ripper_id_tstring_content},
|
||||||
{tSTRING_DBEG, &ripper_id_embexpr_beg},
|
{tSTRING_DBEG, &ripper_id_embexpr_beg},
|
||||||
|
1
id.c
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_from_ary, "core#hash_from_ary");
|
||||||
REGISTER_SYMID(id_core_hash_merge_ary, "core#hash_merge_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_ptr, "core#hash_merge_ptr");
|
||||||
|
REGISTER_SYMID(id_core_hash_merge_kwd, "core#hash_merge_kwd");
|
||||||
|
|
||||||
REGISTER_SYMID(idEach, "each");
|
REGISTER_SYMID(idEach, "each");
|
||||||
REGISTER_SYMID(idLength, "length");
|
REGISTER_SYMID(idLength, "length");
|
||||||
|
32
parse.y
32
parse.y
@ -759,6 +759,7 @@ static void token_info_pop(struct parser_params*, const char *token);
|
|||||||
%token tLBRACE /* { */
|
%token tLBRACE /* { */
|
||||||
%token tLBRACE_ARG /* { */
|
%token tLBRACE_ARG /* { */
|
||||||
%token tSTAR /* * */
|
%token tSTAR /* * */
|
||||||
|
%token tDSTAR /* ** */
|
||||||
%token tAMPER /* & */
|
%token tAMPER /* & */
|
||||||
%token tLAMBDA /* -> */
|
%token tLAMBDA /* -> */
|
||||||
%token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG
|
%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_from_ary
|
||||||
%nonassoc id_core_hash_merge_ary
|
%nonassoc id_core_hash_merge_ary
|
||||||
%nonassoc id_core_hash_merge_ptr
|
%nonassoc id_core_hash_merge_ptr
|
||||||
|
%nonassoc id_core_hash_merge_kwd
|
||||||
|
|
||||||
%token tLAST_TOKEN
|
%token tLAST_TOKEN
|
||||||
|
|
||||||
@ -1918,6 +1920,7 @@ op : '|' { ifndef_ripper($$ = '|'); }
|
|||||||
| '/' { ifndef_ripper($$ = '/'); }
|
| '/' { ifndef_ripper($$ = '/'); }
|
||||||
| '%' { ifndef_ripper($$ = '%'); }
|
| '%' { ifndef_ripper($$ = '%'); }
|
||||||
| tPOW { ifndef_ripper($$ = tPOW); }
|
| tPOW { ifndef_ripper($$ = tPOW); }
|
||||||
|
| tDSTAR { ifndef_ripper($$ = tDSTAR); }
|
||||||
| '!' { ifndef_ripper($$ = '!'); }
|
| '!' { ifndef_ripper($$ = '!'); }
|
||||||
| '~' { ifndef_ripper($$ = '~'); }
|
| '~' { ifndef_ripper($$ = '~'); }
|
||||||
| tUPLUS { ifndef_ripper($$ = tUPLUS); }
|
| 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;
|
$$ = $2;
|
||||||
}
|
}
|
||||||
@ -4923,6 +4930,16 @@ assoc : arg_value tASSOC arg_value
|
|||||||
$$ = dispatch2(assoc_new, $1, $2);
|
$$ = dispatch2(assoc_new, $1, $2);
|
||||||
%*/
|
%*/
|
||||||
}
|
}
|
||||||
|
| tDSTAR arg_value
|
||||||
|
{
|
||||||
|
/*%%%*/
|
||||||
|
$$ = list_append(NEW_LIST(0), $2);
|
||||||
|
/*%
|
||||||
|
$$ = dispatch1(assoc_splat, $2);
|
||||||
|
%*/
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
operation : tIDENTIFIER
|
operation : tIDENTIFIER
|
||||||
@ -6890,7 +6907,17 @@ parser_yylex(struct parser_params *parser)
|
|||||||
return tOP_ASGN;
|
return tOP_ASGN;
|
||||||
}
|
}
|
||||||
pushback(c);
|
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 {
|
else {
|
||||||
if (c == '=') {
|
if (c == '=') {
|
||||||
@ -9701,6 +9728,7 @@ static const struct {
|
|||||||
{'+', "+(binary)"},
|
{'+', "+(binary)"},
|
||||||
{'-', "-(binary)"},
|
{'-', "-(binary)"},
|
||||||
{tPOW, "**"},
|
{tPOW, "**"},
|
||||||
|
{tDSTAR, "**"},
|
||||||
{tUPLUS, "+@"},
|
{tUPLUS, "+@"},
|
||||||
{tUMINUS, "-@"},
|
{tUMINUS, "-@"},
|
||||||
{tCMP, "<=>"},
|
{tCMP, "<=>"},
|
||||||
|
@ -146,6 +146,12 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
|
|||||||
assert_equal true, thru_assoc_new
|
assert_equal true, thru_assoc_new
|
||||||
end
|
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
|
def test_aref_field
|
||||||
assert_equal '[assign(aref_field(vcall(a),[1]),2)]', parse('a[1]=2')
|
assert_equal '[assign(aref_field(vcall(a),[1]),2)]', parse('a[1]=2')
|
||||||
end
|
end
|
||||||
|
@ -90,6 +90,21 @@ class TestSyntax < Test::Unit::TestCase
|
|||||||
assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989)
|
assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989)
|
||||||
end
|
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
|
def test_warn_grouped_expression
|
||||||
assert_warn("test:2: warning: (...) interpreted as grouped expression\n") do
|
assert_warn("test:2: warning: (...) interpreted as grouped expression\n") do
|
||||||
assert_valid_syntax("foo \\\n(\n true)", "test") {$VERBOSE = true}
|
assert_valid_syntax("foo \\\n(\n true)", "test") {$VERBOSE = true}
|
||||||
|
25
vm.c
25
vm.c
@ -2068,6 +2068,30 @@ m_core_hash_merge_ptr(int argc, VALUE *argv, VALUE recv)
|
|||||||
return hash;
|
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 VALUE *rb_gc_stack_start;
|
||||||
extern size_t rb_gc_stack_maxsize;
|
extern size_t rb_gc_stack_maxsize;
|
||||||
#ifdef __ia64
|
#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_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_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_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_obj_freeze(fcore);
|
||||||
rb_gc_register_mark_object(fcore);
|
rb_gc_register_mark_object(fcore);
|
||||||
rb_mRubyVMFrozenCore = fcore;
|
rb_mRubyVMFrozenCore = fcore;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user