@@ -4538,6 +4538,163 @@ codegen_pattern(codegen_scope *s, node *pattern, int target, uint32_t *fail_pos)
45384538 }
45394539break;
454045404541+case NODE_PAT_FIND:
4542+ {
4543+/* Find pattern: [*pre, elem1, elem2, ..., *post]
4544+ * Searches for elems anywhere in the array.
4545+ *
4546+ * Stack layout:
4547+ * arr_reg: deconstructed array (stable)
4548+ * idx_reg: current search index (stable)
4549+ *
4550+ * Loop bound is recomputed each iteration since OP_SEND clobbers registers.
4551+ */
4552+struct mrb_ast_pat_find_node *pat_find = pat_find_node(pattern);
4553+int elems_len = 0;
4554+node *elem;
4555+int arr_reg = cursp();
4556+int idx_reg;
4557+uint32_t loop_start, match_fail, loop_end;
4558+4559+/* Count middle elements */
4560+for (elem = pat_find->elems; elem; elem = elem->cdr) elems_len++;
4561+4562+/* Call deconstruct on target */
4563+gen_move(s, cursp(), target, 0);
4564+push();
4565+genop_3(s, OP_SEND, arr_reg, new_sym(s, MRB_SYM_2(s->mrb, deconstruct)), 0);
4566+4567+/* Check minimum length: arr.size >= elems_len */
4568+gen_move(s, cursp(), arr_reg, 0);
4569+push();
4570+genop_3(s, OP_SEND, cursp() - 1, new_sym(s, MRB_SYM_2(s->mrb, size)), 0);
4571+gen_int(s, cursp(), elems_len);
4572+push(); push(); pop(); pop(); pop();
4573+genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, ge)), 1);
4574+tmp = genjmp2(s, OP_JMPNOT, cursp(), *fail_pos, 1);
4575+*fail_pos = tmp;
4576+4577+/* Initialize index to 0 */
4578+idx_reg = cursp();
4579+gen_int(s, idx_reg, 0);
4580+push();
4581+4582+/* Loop: try matching at each position */
4583+loop_start = s->pc;
4584+match_fail = JMPLINK_START;
4585+4586+/* Check if idx <= arr.size - elems_len (i.e., idx < arr.size - elems_len + 1) */
4587+/* Compute: arr.size - elems_len */
4588+gen_move(s, cursp(), arr_reg, 0);
4589+push();
4590+genop_3(s, OP_SEND, cursp() - 1, new_sym(s, MRB_SYM_2(s->mrb, size)), 0);
4591+gen_int(s, cursp(), elems_len);
4592+push(); push(); pop(); pop(); pop();
4593+genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, sub)), 1);
4594+/* Now cursp() has (size - elems_len), compare: idx <= (size - elems_len) */
4595+gen_move(s, cursp() + 1, idx_reg, 0);
4596+push();
4597+push(); push(); pop(); pop(); pop();
4598+genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, ge)), 1);
4599+tmp = genjmp2(s, OP_JMPNOT, cursp(), *fail_pos, 1);
4600+*fail_pos = tmp;
4601+4602+/* Try to match each middle element at idx+offset */
4603+int offset = 0;
4604+for (elem = pat_find->elems; elem; elem = elem->cdr, offset++) {
4605+/* Get arr[idx + offset] */
4606+gen_move(s, cursp(), arr_reg, 0);
4607+push();
4608+if (offset == 0) {
4609+gen_move(s, cursp(), idx_reg, 0);
4610+ }
4611+else {
4612+gen_move(s, cursp(), idx_reg, 0);
4613+push();
4614+gen_int(s, cursp(), offset);
4615+push(); push(); pop(); pop(); pop();
4616+genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, add)), 1);
4617+ }
4618+push(); push(); pop(); pop(); pop();
4619+genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, aref)), 1);
4620+/* Match element pattern - on fail, try next index */
4621+codegen_pattern(s, elem->car, cursp(), &match_fail);
4622+ }
4623+4624+/* All elements matched - bind pre and post if named */
4625+if (pat_find->pre && pat_find->pre != (node*)-1) {
4626+struct mrb_ast_pat_var_node *pre_var = pat_var_node(pat_find->pre);
4627+if (pre_var->name) {
4628+int var_idx = lv_idx(s, pre_var->name);
4629+/* pre = arr[0...idx] (exclusive range) */
4630+/* Following the NODE_PAT_ARRAY pattern exactly */
4631+gen_move(s, cursp(), arr_reg, 0); /* arr at cursp */
4632+push();
4633+gen_int(s, cursp(), 0); /* start=0 at cursp */
4634+push();
4635+gen_move(s, cursp(), idx_reg, 0); /* end=idx at cursp */
4636+/* start at cursp-1, end at cursp; create exclusive range at cursp-1 */
4637+genop_1(s, OP_RANGE_EXC, cursp() - 1);
4638+/* arr at cursp-2, range at cursp-1 */
4639+pop(); /* cursp now at range position */
4640+pop(); /* cursp now at arr position */
4641+genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, aref)), 1);
4642+if (var_idx > 0) {
4643+gen_move(s, var_idx, cursp(), 1);
4644+ }
4645+ }
4646+ }
4647+4648+if (pat_find->post && pat_find->post != (node*)-1) {
4649+struct mrb_ast_pat_var_node *post_var = pat_var_node(pat_find->post);
4650+if (post_var->name) {
4651+int var_idx = lv_idx(s, post_var->name);
4652+/* post = arr[(idx+elems_len)..-1] (inclusive range) */
4653+/* Following the NODE_PAT_ARRAY pattern exactly */
4654+gen_move(s, cursp(), arr_reg, 0); /* arr at cursp */
4655+push();
4656+/* Compute idx + elems_len for start index */
4657+gen_move(s, cursp(), idx_reg, 0); /* idx at cursp */
4658+push();
4659+gen_int(s, cursp(), elems_len); /* elems_len at cursp */
4660+push(); push(); pop(); pop(); pop();
4661+genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, add)), 1);
4662+/* start index (idx+elems_len) now at cursp */
4663+push();
4664+gen_int(s, cursp(), -1); /* end=-1 at cursp */
4665+/* start at cursp-1, end at cursp; create inclusive range at cursp-1 */
4666+genop_1(s, OP_RANGE_INC, cursp() - 1);
4667+/* arr at cursp-2, range at cursp-1 */
4668+pop(); /* cursp now at range position */
4669+pop(); /* cursp now at arr position */
4670+genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, aref)), 1);
4671+if (var_idx > 0) {
4672+gen_move(s, var_idx, cursp(), 1);
4673+ }
4674+ }
4675+ }
4676+4677+/* Jump to success (end of find pattern) */
4678+loop_end = genjmp(s, OP_JMP, JMPLINK_START);
4679+4680+/* Match failed - increment index and try again */
4681+dispatch_linked(s, match_fail);
4682+gen_move(s, cursp(), idx_reg, 0);
4683+push();
4684+gen_int(s, cursp(), 1);
4685+push(); push(); pop(); pop(); pop();
4686+genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, add)), 1);
4687+gen_move(s, idx_reg, cursp(), 0);
4688+genjmp(s, OP_JMP, loop_start);
4689+4690+/* Success exit point */
4691+dispatch(s, loop_end);
4692+4693+pop(); /* idx_reg */
4694+pop(); /* arr_reg */
4695+ }
4696+break;
4697+45414698case NODE_PAT_HASH:
45424699 {
45434700struct mrb_ast_pat_hash_node *pat_hash = pat_hash_node(pattern);