diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index f44245bcb..9d3b1acbe 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -4985,31 +4985,34 @@ codegen_pattern(codegen_scope *s, node *pattern, int target, uint32_t *fail_pos, genop_3(s, OP_SEND, hash_reg, sym_idx(s, MRB_SYM_2(s->mrb, deconstruct_keys)), 1); pop(); - /* Match each key-pattern pair */ - for (pair = pat_hash->pairs; pair; pair = pair->cdr) { - node *key = pair->car->car; - node *pat = pair->car->cdr; - - /* Check key existence: hash.key?(key) */ - gen_move(s, cursp(), hash_reg, 0); + /* Check all keys exist and get values via __pat_values */ + if (num_keys > 0) { + int vals_reg = cursp(); + gen_move(s, vals_reg, hash_reg, 0); push(); - gen_pat_key(s, key); - push(); push(); pop(); pop(); pop(); - genop_3(s, OP_SEND, cursp(), sym_idx(s, MRB_SYM_Q_2(s->mrb, key)), 1); - tmp = genjmp2(s, OP_JMPNOT, cursp(), *fail_pos, 1); + gen_pat_keys_ary(s, pat_hash->pairs, num_keys); + genop_3(s, OP_SEND, vals_reg, sym_idx(s, MRB_SYM_2(s->mrb, __pat_values)), 1); + pop(); /* keys_ary */ + /* vals_reg = values array or false; fail if false */ + tmp = genjmp2(s, OP_JMPNOT, vals_reg, *fail_pos, 1); *fail_pos = tmp; - /* Get value: hash[key] */ - gen_move(s, cursp(), hash_reg, 0); - push(); - gen_pat_key(s, key); - push(); push(); pop(); pop(); pop(); - genop_3(s, OP_SEND, cursp(), sym_idx(s, MRB_OPSYM_2(s->mrb, aref)), 1); - push(); + /* Match each value against its pattern */ + int i = 0; + for (pair = pat_hash->pairs; pair; pair = pair->cdr, i++) { + node *pat = pair->car->cdr; - /* Match pattern against value */ - codegen_pattern(s, pat, cursp() - 1, fail_pos, -1); - pop(); + gen_move(s, cursp(), vals_reg, 0); + push(); + gen_int(s, cursp(), i); + push(); push(); pop(); pop(); pop(); + genop_3(s, OP_SEND, cursp(), sym_idx(s, MRB_OPSYM_2(s->mrb, aref)), 1); + push(); + + codegen_pattern(s, pat, cursp() - 1, fail_pos, -1); + pop(); + } + pop(); /* vals_reg */ } /* Handle rest pattern */ diff --git a/src/hash.c b/src/hash.c index b29244094..f21de5813 100644 --- a/src/hash.c +++ b/src/hash.c @@ -2042,6 +2042,36 @@ mrb_hash_compact(mrb_state *mrb, mrb_value hash) return hash; } +/* + * Internal method for pattern matching key check + value extraction. + * Returns an array of values if all keys exist, false otherwise. + * + * {a: 1, b: 2}.__pat_values([:a, :b]) #=> [1, 2] + * {a: 1}.__pat_values([:a, :b]) #=> false + */ +static mrb_value +mrb_hash_pat_values(mrb_state *mrb, mrb_value hash) +{ + mrb_value keys; + mrb_get_args(mrb, "A", &keys); + + const mrb_value *ary = RARRAY_PTR(keys); + mrb_int klen = RARRAY_LEN(keys); + struct RHash *h = mrb_hash_ptr(hash); + mrb_value result = mrb_ary_new_capa(mrb, klen); + int ai = mrb_gc_arena_save(mrb); + + for (mrb_int i = 0; i < klen; i++) { + mrb_value val; + if (!h_get(mrb, h, ary[i], &val)) { + return mrb_false_value(); + } + mrb_ary_push(mrb, result, val); + mrb_gc_arena_restore(mrb, ai); + } + return result; +} + /* * Internal method for pattern matching **rest. * Returns a new hash excluding keys in the given array. @@ -2314,6 +2344,7 @@ mrb_init_hash(mrb_state *mrb) mrb_define_method_id(mrb, h, MRB_SYM(rassoc), mrb_hash_rassoc, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, h, MRB_SYM(__merge), mrb_hash_merge_m, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, h, MRB_SYM(__compact), mrb_hash_compact, MRB_ARGS_NONE()); /* implementation of Hash#compact! */ + mrb_define_method_id(mrb, h, MRB_SYM(__pat_values), mrb_hash_pat_values, MRB_ARGS_REQ(1)); /* for pattern matching keys */ mrb_define_method_id(mrb, h, MRB_SYM(__except), mrb_hash_except_keys, MRB_ARGS_REQ(1)); /* for pattern matching **rest */ } #undef lesser