mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
hash.c: add __except method for pattern matching **rest
Add Hash#__except that returns a new hash excluding specified keys, used by the compiler for **rest capture in hash patterns. Takes keys as direct arguments to avoid array allocation. The compiler passes matched key symbols directly on the stack via OP_SEND. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5026,34 +5026,38 @@ codegen_pattern(codegen_scope *s, node *pattern, int target, uint32_t *fail_pos,
|
||||
pop();
|
||||
}
|
||||
else if (pat_hash->rest && pat_hash->rest != (node*)-2) {
|
||||
/* **var: capture remaining keys (hash minus matched keys) */
|
||||
/* **var: capture remaining keys via hash.__except(key1, key2, ...) */
|
||||
struct mrb_ast_pat_var_node *rest_var = pat_var_node(pat_hash->rest);
|
||||
if (rest_var->name) {
|
||||
int var_idx = lv_idx(s, rest_var->name);
|
||||
int rest_reg = cursp();
|
||||
/* rest = hash.dup */
|
||||
gen_move(s, cursp(), hash_reg, 0);
|
||||
int recv = cursp();
|
||||
gen_move(s, recv, hash_reg, 0);
|
||||
push();
|
||||
genop_3(s, OP_SEND, rest_reg, sym_idx(s, MRB_SYM_2(s->mrb, dup)), 0);
|
||||
/* Delete each matched key from the copy */
|
||||
for (pair = pat_hash->pairs; pair; pair = pair->cdr) {
|
||||
node *key = pair->car->car;
|
||||
gen_move(s, cursp(), rest_reg, 0);
|
||||
push();
|
||||
if (node_type(key) == NODE_SYM) {
|
||||
genop_2(s, OP_LOADSYM, cursp(), sym_idx(s, sym_node(key)->symbol));
|
||||
if (num_keys > 0) {
|
||||
/* Pass matched keys directly as arguments */
|
||||
int i = 0;
|
||||
for (pair = pat_hash->pairs; pair; pair = pair->cdr, i++) {
|
||||
node *key = pair->car->car;
|
||||
if (node_type(key) == NODE_SYM) {
|
||||
genop_2(s, OP_LOADSYM, cursp(), sym_idx(s, sym_node(key)->symbol));
|
||||
}
|
||||
else {
|
||||
codegen(s, key, VAL);
|
||||
}
|
||||
push();
|
||||
}
|
||||
else {
|
||||
codegen(s, key, VAL);
|
||||
}
|
||||
push(); push(); pop(); pop(); pop();
|
||||
genop_3(s, OP_SEND, cursp(), sym_idx(s, MRB_SYM_2(s->mrb, __delete)), 1);
|
||||
genop_3(s, OP_SEND, recv, sym_idx(s, MRB_SYM_2(s->mrb, __except)), num_keys);
|
||||
for (i = 0; i < num_keys; i++) pop();
|
||||
}
|
||||
else {
|
||||
/* No keys to exclude: rest = hash.dup */
|
||||
genop_3(s, OP_SEND, recv, sym_idx(s, MRB_SYM_2(s->mrb, dup)), 0);
|
||||
}
|
||||
/* Assign to variable */
|
||||
if (var_idx > 0) {
|
||||
gen_move(s, var_idx, rest_reg, 1);
|
||||
gen_move(s, var_idx, recv, 1);
|
||||
}
|
||||
pop(); /* rest_reg */
|
||||
pop(); /* recv */
|
||||
}
|
||||
}
|
||||
/* ** (anonymous rest) or partial match: nothing extra */
|
||||
|
||||
+34
@@ -2042,6 +2042,39 @@ mrb_hash_compact(mrb_state *mrb, mrb_value hash)
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal method for pattern matching **rest.
|
||||
* Returns a new hash excluding specified keys.
|
||||
*
|
||||
* {a: 1, b: 2, c: 3}.__except(:a, :c) #=> {b: 2}
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_hash_except_keys(mrb_state *mrb, mrb_value hash)
|
||||
{
|
||||
const mrb_value *argv;
|
||||
mrb_int argc;
|
||||
mrb_get_args(mrb, "*", &argv, &argc);
|
||||
|
||||
mrb_value result = mrb_hash_new(mrb);
|
||||
struct RHash *h = mrb_hash_ptr(hash);
|
||||
int ai = mrb_gc_arena_save(mrb);
|
||||
|
||||
H_EACH(h, entry) {
|
||||
mrb_bool found = FALSE;
|
||||
for (mrb_int i = 0; i < argc; i++) {
|
||||
if (mrb_equal(mrb, entry->key, argv[i])) {
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
mrb_hash_set(mrb, result, entry->key, entry->val);
|
||||
}
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* hash.to_s -> string
|
||||
@@ -2280,5 +2313,6 @@ 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(__except), mrb_hash_except_keys, MRB_ARGS_ANY()); /* for pattern matching **rest */
|
||||
}
|
||||
#undef lesser
|
||||
|
||||
Reference in New Issue
Block a user