From 9c8ba8467599e174d73241631add030478891f49 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Thu, 28 Sep 2023 12:51:33 -0400 Subject: [PATCH] Support if and unless guards on patterns --- prism_compile.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/prism_compile.c b/prism_compile.c index 7e0786c1f5..5dee86a2b6 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -630,15 +630,43 @@ pm_compile_pattern(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const re case PM_HASH_PATTERN_NODE: rb_bug("Hash pattern matching not yet supported."); break; - case PM_IF_NODE: - rb_bug("If guards on pattern matching not yet supported."); - break; - case PM_UNLESS_NODE: - rb_bug("Unless guards on pattern matching not yet supported."); - break; case PM_CAPTURE_PATTERN_NODE: rb_bug("Capture pattern matching not yet supported."); break; + case PM_IF_NODE: { + // If guards can be placed on patterns to further limit matches based on + // a dynamic predicate. This looks like: + // + // case foo + // in bar if baz + // end + // + pm_if_node_t *cast = (pm_if_node_t *) node; + + pm_compile_pattern(iseq, cast->statements->body.nodes[0], ret, src, compile_context, matched_label, unmatched_label, in_alternation_pattern); + PM_COMPILE_NOT_POPPED(cast->predicate); + + ADD_INSNL(ret, &dummy_line_node, branchunless, unmatched_label); + ADD_INSNL(ret, &dummy_line_node, jump, matched_label); + break; + } + case PM_UNLESS_NODE: { + // Unless guards can be placed on patterns to further limit matches + // based on a dynamic predicate. This looks like: + // + // case foo + // in bar unless baz + // end + // + pm_unless_node_t *cast = (pm_unless_node_t *) node; + + pm_compile_pattern(iseq, cast->statements->body.nodes[0], ret, src, compile_context, matched_label, unmatched_label, in_alternation_pattern); + PM_COMPILE_NOT_POPPED(cast->predicate); + + ADD_INSNL(ret, &dummy_line_node, branchif, unmatched_label); + ADD_INSNL(ret, &dummy_line_node, jump, matched_label); + break; + } case PM_LOCAL_VARIABLE_TARGET_NODE: { // Local variables can be targetted by placing identifiers in the place // of a pattern. For example, foo in bar. This results in the value @@ -651,9 +679,11 @@ pm_compile_pattern(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const re // it's ambiguous which value should be used. So instead we indicate // this with a compile error. if (in_alternation_pattern) { - const char *name = rb_id2name(pm_constant_id_lookup(compile_context, cast->name)); + ID id = pm_constant_id_lookup(compile_context, cast->name); + const char *name = rb_id2name(id); + if (name && strlen(name) > 0 && name[0] != '_') { - COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")", name); + COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")", rb_id2str(id)); return COMPILE_NG; } } @@ -733,6 +763,8 @@ pm_compile_pattern(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const re rb_bug("Unexpected node type in pattern matching expression: %s", pm_node_type_to_str(PM_NODE_TYPE(node))); break; } + + return COMPILE_OK; } /*