Use new match write targets

This commit is contained in:
Kevin Newton 2023-11-20 16:09:51 -05:00
parent e269096d15
commit 914640eadd

View File

@ -2953,66 +2953,83 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
return; return;
} }
case PM_MATCH_WRITE_NODE: { case PM_MATCH_WRITE_NODE: {
// Match write nodes are specialized call nodes that have a regular
// expression with valid named capture groups on the left, the =~
// operator, and some value on the right. The nodes themselves simply
// wrap the call with the local variable targets that will be written
// when the call is executed.
pm_match_write_node_t *cast = (pm_match_write_node_t *) node; pm_match_write_node_t *cast = (pm_match_write_node_t *) node;
LABEL *fail_label = NEW_LABEL(lineno); LABEL *fail_label = NEW_LABEL(lineno);
LABEL *end_label = NEW_LABEL(lineno); LABEL *end_label = NEW_LABEL(lineno);
size_t capture_count = cast->locals.size;
VALUE r;
pm_constant_id_t *locals = ALLOCV_N(pm_constant_id_t, r, capture_count);
for (size_t i = 0; i < capture_count; i++) {
locals[i] = cast->locals.ids[i];
}
// First, we'll compile the call so that all of its instructions are
// present. Then we'll compile all of the local variable targets.
PM_COMPILE_NOT_POPPED((pm_node_t *) cast->call); PM_COMPILE_NOT_POPPED((pm_node_t *) cast->call);
VALUE global_variable_name = rb_id2sym(idBACKREF);
ADD_INSN1(ret, &dummy_line_node, getglobal, global_variable_name); // Now, check if the match was successful. If it was, then we'll
// continue on and assign local variables. Otherwise we'll skip over the
// assignment code.
ADD_INSN1(ret, &dummy_line_node, getglobal, rb_id2sym(idBACKREF));
PM_DUP; PM_DUP;
ADD_INSNL(ret, &dummy_line_node, branchunless, fail_label); ADD_INSNL(ret, &dummy_line_node, branchunless, fail_label);
if (capture_count == 1) { // If there's only a single local variable target, we can skip some of
int local_index = pm_lookup_local_index(iseq, scope_node, *locals); // the bookkeeping, so we'll put a special branch here.
size_t targets_count = cast->targets.size;
DECL_ANCHOR(nom); if (targets_count == 1) {
INIT_ANCHOR(nom); pm_node_t *target = cast->targets.nodes[0];
assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
ADD_INSNL(nom, &dummy_line_node, jump, end_label); pm_local_variable_target_node_t *local_target = (pm_local_variable_target_node_t *) target;
ADD_LABEL(nom, fail_label); int index = pm_lookup_local_index(iseq, scope_node, local_target->name);
ADD_LABEL(nom, end_label);
ADD_INSN1(ret, &dummy_line_node, putobject, rb_id2sym(pm_constant_id_lookup(scope_node, *locals))); ADD_INSN1(ret, &dummy_line_node, putobject, rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name)));
ADD_SEND(ret, &dummy_line_node, idAREF, INT2FIX(1)); ADD_SEND(ret, &dummy_line_node, idAREF, INT2FIX(1));
ADD_SETLOCAL(nom, &dummy_line_node, local_index, 0); ADD_LABEL(ret, fail_label);
ADD_SETLOCAL(ret, &dummy_line_node, index, (int) local_target->depth);
PM_POP_IF_POPPED; PM_POP_IF_POPPED;
ADD_SEQ(ret, nom);
return; return;
} }
for (size_t index = 0; index < capture_count; index++) { // Otherwise there is more than one local variable target, so we'll need
int local_index = pm_lookup_local_index(iseq, scope_node, locals[index]); // to do some bookkeeping.
for (size_t targets_index = 0; targets_index < targets_count; targets_index++) {
pm_node_t *target = cast->targets.nodes[targets_index];
assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
if (index < (capture_count - 1)) { pm_local_variable_target_node_t *local_target = (pm_local_variable_target_node_t *) target;
int index = pm_lookup_local_index(iseq, scope_node, local_target->name);
if (((size_t) targets_index) < (targets_count - 1)) {
PM_DUP; PM_DUP;
} }
ADD_INSN1(ret, &dummy_line_node, putobject, rb_id2sym(pm_constant_id_lookup(scope_node, locals[index]))); ADD_INSN1(ret, &dummy_line_node, putobject, rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name)));
ADD_SEND(ret, &dummy_line_node, idAREF, INT2FIX(1)); ADD_SEND(ret, &dummy_line_node, idAREF, INT2FIX(1));
ADD_SETLOCAL(ret, &dummy_line_node, local_index, 0); ADD_SETLOCAL(ret, &dummy_line_node, index, (int) local_target->depth);
} }
// Since we matched successfully, now we'll jump to the end.
ADD_INSNL(ret, &dummy_line_node, jump, end_label); ADD_INSNL(ret, &dummy_line_node, jump, end_label);
ADD_LABEL(ret, fail_label);
PM_POP_UNLESS_POPPED;
for (size_t index = 0; index < capture_count; index++) { // In the case that the match failed, we'll loop through each local
pm_constant_id_t constant = cast->locals.ids[index]; // variable target and set all of them to `nil`.
int local_index = pm_lookup_local_index(iseq, scope_node, constant); ADD_LABEL(ret, fail_label);
PM_POP;
for (size_t targets_index = 0; targets_index < targets_count; targets_index++) {
pm_node_t *target = cast->targets.nodes[targets_index];
assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
pm_local_variable_target_node_t *local_target = (pm_local_variable_target_node_t *) target;
int index = pm_lookup_local_index(iseq, scope_node, local_target->name);
PM_PUTNIL; PM_PUTNIL;
ADD_SETLOCAL(ret, &dummy_line_node, local_index, 0); ADD_SETLOCAL(ret, &dummy_line_node, index, (int) local_target->depth);
PM_POP_IF_POPPED;
} }
// Finally, we can push the end label for either case.
PM_POP_IF_POPPED;
ADD_LABEL(ret, end_label); ADD_LABEL(ret, end_label);
return; return;
} }