mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
hash.c: add __pat_values() for pattern matching optimization
Add Hash#__pat_values(keys) that returns an array of values if all keys exist, or false if any key is missing. This replaces per-key key?() + []() calls (2N hash lookups) with a single method call (N hash lookups). The compiler generates __pat_values() followed by array indexing to extract each value for pattern matching. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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 */
|
||||
|
||||
+31
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user