Compile basic pattern matching expressions

This commit is contained in:
Kevin Newton 2023-09-28 12:14:38 -04:00
Родитель 223e9fcb27
Коммит 6e88a56f63
2 изменённых файлов: 137 добавлений и 0 удалений

Просмотреть файл

@ -611,6 +611,62 @@ pm_reg_flags(const pm_node_t *node) {
return flags;
}
/**
* Compile a pattern matching expression.
*/
static void
pm_compile_pattern(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, const uint8_t *src, pm_compile_context_t *compile_context, LABEL *matched_label, LABEL *unmatched_label)
{
int lineno = (int) pm_newline_list_line_column(&compile_context->parser->newline_list, node->location.start).line;
NODE dummy_line_node = generate_dummy_line_node(lineno, lineno);
switch (PM_NODE_TYPE(node)) {
case PM_ARRAY_NODE:
case PM_CLASS_VARIABLE_READ_NODE:
case PM_CONSTANT_PATH_NODE:
case PM_CONSTANT_READ_NODE:
case PM_FALSE_NODE:
case PM_FLOAT_NODE:
case PM_GLOBAL_VARIABLE_READ_NODE:
case PM_IMAGINARY_NODE:
case PM_INSTANCE_VARIABLE_READ_NODE:
case PM_INTEGER_NODE:
case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
case PM_INTERPOLATED_STRING_NODE:
case PM_INTERPOLATED_SYMBOL_NODE:
case PM_INTERPOLATED_X_STRING_NODE:
case PM_LAMBDA_NODE:
case PM_LOCAL_VARIABLE_READ_NODE:
case PM_NIL_NODE:
case PM_RANGE_NODE:
case PM_RATIONAL_NODE:
case PM_REGULAR_EXPRESSION_NODE:
case PM_SELF_NODE:
case PM_STRING_NODE:
case PM_SYMBOL_NODE:
case PM_TRUE_NODE:
case PM_X_STRING_NODE:
PM_COMPILE_NOT_POPPED(node);
ADD_INSN1(ret, &dummy_line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
ADD_INSNL(ret, &dummy_line_node, branchif, matched_label);
ADD_INSNL(ret, &dummy_line_node, jump, unmatched_label);
break;
case PM_PINNED_VARIABLE_NODE: {
pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node;
pm_compile_pattern(iseq, cast->variable, ret, src, compile_context, matched_label, unmatched_label);
break;
}
case PM_PINNED_EXPRESSION_NODE: {
pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node;
pm_compile_pattern(iseq, cast->expression, ret, src, compile_context, matched_label, unmatched_label);
break;
}
default:
rb_bug("Unexpected node type in pattern matching expression: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
break;
}
}
/*
* Compiles a prism node into instruction sequences
*
@ -938,6 +994,8 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
pm_constant_path_node_t *constant_path_node = (pm_constant_path_node_t*) node;
if (constant_path_node->parent) {
PM_COMPILE_NOT_POPPED(constant_path_node->parent);
} else {
ADD_INSN1(ret, &dummy_line_node, putobject, rb_cObject);
}
ADD_INSN1(ret, &dummy_line_node, putobject, Qfalse);
@ -1617,6 +1675,44 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
return;
}
case PM_MATCH_PREDICATE_NODE: {
pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node;
// First, allocate some stack space for the cached return value of any
// calls to #deconstruct.
ADD_INSN(ret, &dummy_line_node, putnil);
// Next, compile the expression that we're going to match against.
PM_COMPILE_NOT_POPPED(cast->value);
ADD_INSN(ret, &dummy_line_node, dup);
// Now compile the pattern that is going to be used to match against the
// expression.
LABEL *matched_label = NEW_LABEL(lineno);
LABEL *unmatched_label = NEW_LABEL(lineno);
LABEL *done_label = NEW_LABEL(lineno);
pm_compile_pattern(iseq, cast->pattern, ret, src, compile_context, matched_label, unmatched_label);
// If the pattern did not match, then compile the necessary instructions
// to handle pushing false onto the stack, then jump to the end.
ADD_LABEL(ret, unmatched_label);
ADD_INSN(ret, &dummy_line_node, pop);
ADD_INSN(ret, &dummy_line_node, pop);
if (!popped) ADD_INSN1(ret, &dummy_line_node, putobject, Qfalse);
ADD_INSNL(ret, &dummy_line_node, jump, done_label);
ADD_INSN(ret, &dummy_line_node, putnil);
// If the pattern did match, then compile the necessary instructions to
// handle pushing true onto the stack, then jump to the end.
ADD_LABEL(ret, matched_label);
ADD_INSN1(ret, &dummy_line_node, adjuststack, INT2FIX(2));
if (!popped) ADD_INSN1(ret, &dummy_line_node, putobject, Qtrue);
ADD_INSNL(ret, &dummy_line_node, jump, done_label);
ADD_LABEL(ret, done_label);
return;
}
case PM_MATCH_WRITE_NODE: {
pm_match_write_node_t *cast = (pm_match_write_node_t *)node;
LABEL *fail_label = NEW_LABEL(lineno);

Просмотреть файл

@ -334,6 +334,47 @@ module Prism
test_prism_eval("(1)")
end
############################################################################
# Pattern matching #
############################################################################
def test_MatchPredicateNode
test_prism_eval("1 in 1")
test_prism_eval("1.0 in 1.0")
test_prism_eval("1i in 1i")
test_prism_eval("1r in 1r")
test_prism_eval("\"foo\" in \"foo\"")
test_prism_eval("\"foo \#{1}\" in \"foo \#{1}\"")
test_prism_eval("false in false")
test_prism_eval("nil in nil")
test_prism_eval("self in self")
test_prism_eval("true in true")
test_prism_eval("5 in 0..10")
test_prism_eval("5 in 0...10")
test_prism_eval("module Prism; @@prism = 1; 1 in ^@@prism; end")
test_prism_eval("module Prism; @prism = 1; 1 in ^@prism; end")
test_prism_eval("$prism = 1; 1 in ^$prism")
test_prism_eval("prism = 1; 1 in ^prism")
test_prism_eval("[\"5\"] in %w[5]")
test_prism_eval("Prism in Prism")
test_prism_eval("Prism in ::Prism")
test_prism_eval(":prism in :prism")
test_prism_eval("%s[prism\#{1}] in %s[prism\#{1}]")
test_prism_eval("\"foo\" in /.../")
test_prism_eval("\"foo1\" in /...\#{1}/")
test_prism_eval("4 in ->(v) { v.even? }")
test_prism_eval("4 in ^(4)")
test_prism_eval("1 in 2")
end
private
def compare_eval(source)