Do not remove hash duplicated keys in parse.y
When hash keys are duplicated, e.g. `h = {k: 1, l: 2, k: 3}`, parser changes node structure for correct compilation. This generates tricky AST. This commit removes AST manipulation from parser to keep AST structure simple.
This commit is contained in:
parent
9d3dcb86d1
commit
83c98ead4e
37
compile.c
37
compile.c
@ -4588,6 +4588,28 @@ keyword_node_p(const NODE *const node)
|
|||||||
return nd_type_p(node, NODE_HASH) && (RNODE_HASH(node)->nd_brace & HASH_BRACE) != HASH_BRACE;
|
return nd_type_p(node, NODE_HASH) && (RNODE_HASH(node)->nd_brace & HASH_BRACE) != HASH_BRACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
node_hash_unique_key_index(rb_node_hash_t *node_hash, int *count_ptr)
|
||||||
|
{
|
||||||
|
NODE *node = node_hash->nd_head;
|
||||||
|
VALUE hash = rb_hash_new();
|
||||||
|
VALUE ary = rb_ary_new();
|
||||||
|
|
||||||
|
for (int i = 0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
|
||||||
|
VALUE key = RNODE_LIT(RNODE_LIST(node)->nd_head)->nd_lit;
|
||||||
|
VALUE idx = rb_hash_aref(hash, key);
|
||||||
|
if (!NIL_P(idx)) {
|
||||||
|
rb_ary_store(ary, FIX2INT(idx), Qfalse);
|
||||||
|
(*count_ptr)--;
|
||||||
|
}
|
||||||
|
rb_hash_aset(hash, key, INT2FIX(i));
|
||||||
|
rb_ary_store(ary, i, Qtrue);
|
||||||
|
(*count_ptr)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ary;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
|
compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
|
||||||
const NODE *const root_node,
|
const NODE *const root_node,
|
||||||
@ -4630,11 +4652,13 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
|
|||||||
/* may be keywords */
|
/* may be keywords */
|
||||||
node = RNODE_HASH(root_node)->nd_head;
|
node = RNODE_HASH(root_node)->nd_head;
|
||||||
{
|
{
|
||||||
int len = (int)RNODE_LIST(node)->as.nd_alen / 2;
|
int len = 0;
|
||||||
|
VALUE key_index = node_hash_unique_key_index(RNODE_HASH(root_node), &len);
|
||||||
struct rb_callinfo_kwarg *kw_arg =
|
struct rb_callinfo_kwarg *kw_arg =
|
||||||
rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
|
rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
|
||||||
VALUE *keywords = kw_arg->keywords;
|
VALUE *keywords = kw_arg->keywords;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
kw_arg->references = 0;
|
kw_arg->references = 0;
|
||||||
kw_arg->keyword_len = len;
|
kw_arg->keyword_len = len;
|
||||||
|
|
||||||
@ -4643,10 +4667,15 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
|
|||||||
for (i=0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
|
for (i=0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
|
||||||
const NODE *key_node = RNODE_LIST(node)->nd_head;
|
const NODE *key_node = RNODE_LIST(node)->nd_head;
|
||||||
const NODE *val_node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
|
const NODE *val_node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
|
||||||
keywords[i] = RNODE_LIT(key_node)->nd_lit;
|
int popped = TRUE;
|
||||||
NO_CHECK(COMPILE(ret, "keyword values", val_node));
|
if (rb_ary_entry(key_index, i)) {
|
||||||
|
keywords[j] = RNODE_LIT(key_node)->nd_lit;
|
||||||
|
j++;
|
||||||
|
popped = FALSE;
|
||||||
|
}
|
||||||
|
NO_CHECK(COMPILE_(ret, "keyword values", val_node, popped));
|
||||||
}
|
}
|
||||||
assert(i == len);
|
assert(j == len);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
parse.y
40
parse.y
@ -14951,21 +14951,6 @@ dsym_node(struct parser_params *p, NODE *node, const YYLTYPE *loc)
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
append_literal_keys(st_data_t k, st_data_t v, st_data_t h)
|
|
||||||
{
|
|
||||||
NODE *node = (NODE *)v;
|
|
||||||
NODE **result = (NODE **)h;
|
|
||||||
RNODE_LIST(node)->as.nd_alen = 2;
|
|
||||||
RNODE_LIST(RNODE_LIST(node)->nd_next)->as.nd_end = RNODE_LIST(node)->nd_next;
|
|
||||||
RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next = 0;
|
|
||||||
if (*result)
|
|
||||||
list_concat(*result, node);
|
|
||||||
else
|
|
||||||
*result = node;
|
|
||||||
return ST_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nd_type_st_key_enable_p(NODE *node)
|
nd_type_st_key_enable_p(NODE *node)
|
||||||
{
|
{
|
||||||
@ -15026,8 +15011,8 @@ nd_st_key_val(struct parser_params *p, NODE *node)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static NODE *
|
static void
|
||||||
remove_duplicate_keys(struct parser_params *p, NODE *hash)
|
warn_duplicate_keys(struct parser_params *p, NODE *hash)
|
||||||
{
|
{
|
||||||
struct st_hash_type literal_type = {
|
struct st_hash_type literal_type = {
|
||||||
literal_cmp,
|
literal_cmp,
|
||||||
@ -15035,50 +15020,31 @@ remove_duplicate_keys(struct parser_params *p, NODE *hash)
|
|||||||
};
|
};
|
||||||
|
|
||||||
st_table *literal_keys = st_init_table_with_size(&literal_type, RNODE_LIST(hash)->as.nd_alen / 2);
|
st_table *literal_keys = st_init_table_with_size(&literal_type, RNODE_LIST(hash)->as.nd_alen / 2);
|
||||||
NODE *result = 0;
|
|
||||||
NODE *last_expr = 0;
|
|
||||||
rb_code_location_t loc = hash->nd_loc;
|
|
||||||
while (hash && RNODE_LIST(hash)->nd_next) {
|
while (hash && RNODE_LIST(hash)->nd_next) {
|
||||||
NODE *head = RNODE_LIST(hash)->nd_head;
|
NODE *head = RNODE_LIST(hash)->nd_head;
|
||||||
NODE *value = RNODE_LIST(hash)->nd_next;
|
NODE *value = RNODE_LIST(hash)->nd_next;
|
||||||
NODE *next = RNODE_LIST(value)->nd_next;
|
NODE *next = RNODE_LIST(value)->nd_next;
|
||||||
st_data_t key = (st_data_t)head;
|
st_data_t key = (st_data_t)head;
|
||||||
st_data_t data;
|
st_data_t data;
|
||||||
RNODE_LIST(value)->nd_next = 0;
|
|
||||||
if (!head) {
|
if (!head) {
|
||||||
key = (st_data_t)value;
|
key = (st_data_t)value;
|
||||||
}
|
}
|
||||||
else if (nd_type_st_key_enable_p(head) &&
|
else if (nd_type_st_key_enable_p(head) &&
|
||||||
st_delete(literal_keys, (key = (st_data_t)nd_st_key(p, head), &key), &data)) {
|
st_delete(literal_keys, (key = (st_data_t)nd_st_key(p, head), &key), &data)) {
|
||||||
NODE *dup_value = (RNODE_LIST((NODE *)data))->nd_next;
|
|
||||||
rb_compile_warn(p->ruby_sourcefile, nd_line((NODE *)data),
|
rb_compile_warn(p->ruby_sourcefile, nd_line((NODE *)data),
|
||||||
"key %+"PRIsVALUE" is duplicated and overwritten on line %d",
|
"key %+"PRIsVALUE" is duplicated and overwritten on line %d",
|
||||||
nd_st_key_val(p, head), nd_line(head));
|
nd_st_key_val(p, head), nd_line(head));
|
||||||
if (dup_value == last_expr) {
|
|
||||||
RNODE_LIST(value)->nd_head = block_append(p, RNODE_LIST(dup_value)->nd_head, RNODE_LIST(value)->nd_head);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
RNODE_LIST(last_expr)->nd_head = block_append(p, RNODE_LIST(dup_value)->nd_head, RNODE_LIST(last_expr)->nd_head);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
st_insert(literal_keys, (st_data_t)key, (st_data_t)hash);
|
st_insert(literal_keys, (st_data_t)key, (st_data_t)hash);
|
||||||
last_expr = !head || nd_type_st_key_enable_p(head) ? value : head;
|
|
||||||
hash = next;
|
hash = next;
|
||||||
}
|
}
|
||||||
st_foreach(literal_keys, append_literal_keys, (st_data_t)&result);
|
|
||||||
st_free_table(literal_keys);
|
st_free_table(literal_keys);
|
||||||
if (hash) {
|
|
||||||
if (!result) result = hash;
|
|
||||||
else list_concat(result, hash);
|
|
||||||
}
|
|
||||||
result->nd_loc = loc;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static NODE *
|
static NODE *
|
||||||
new_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc)
|
new_hash(struct parser_params *p, NODE *hash, const YYLTYPE *loc)
|
||||||
{
|
{
|
||||||
if (hash) hash = remove_duplicate_keys(p, hash);
|
if (hash) warn_duplicate_keys(p, hash);
|
||||||
return NEW_HASH(hash, loc);
|
return NEW_HASH(hash, loc);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user