mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-compiler: add guard clauses to pattern matching
Add support for if/unless guards in case/in pattern matching: case value in x if x > 0 then :positive in x unless x == 0 then :non_zero end Uses modifier_if/modifier_unless tokens since guards appear after an expression. Disable peephole optimization for pattern variable binding to prevent gen_move() from being optimized away when failed guard jumps target the binding instruction. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4271,6 +4271,8 @@ codegen_case_match(codegen_scope *s, node *varnode, int val)
|
||||
while (current_in) {
|
||||
struct mrb_ast_in_node *in_n = in_node(current_in->car);
|
||||
node *pattern = in_n->pattern;
|
||||
node *guard = in_n->guard;
|
||||
mrb_bool guard_is_unless = in_n->guard_is_unless;
|
||||
node *body = in_n->body;
|
||||
|
||||
uint32_t fail_pos = JMPLINK_START;
|
||||
@@ -4280,6 +4282,21 @@ codegen_case_match(codegen_scope *s, node *varnode, int val)
|
||||
codegen_pattern(s, pattern, head, &fail_pos);
|
||||
}
|
||||
|
||||
/* Generate guard clause if present */
|
||||
if (guard) {
|
||||
codegen(s, guard, VAL);
|
||||
pop(); /* pop before jump - cursp() now points to guard result */
|
||||
if (guard_is_unless) {
|
||||
/* unless guard: fail if guard is true */
|
||||
tmp = genjmp2(s, OP_JMPIF, cursp(), fail_pos, 0);
|
||||
}
|
||||
else {
|
||||
/* if guard: fail if guard is false */
|
||||
tmp = genjmp2(s, OP_JMPNOT, cursp(), fail_pos, 0);
|
||||
}
|
||||
fail_pos = tmp;
|
||||
}
|
||||
|
||||
/* Generate in-clause body */
|
||||
codegen(s, body, val);
|
||||
if (val) pop();
|
||||
@@ -4348,7 +4365,7 @@ codegen_pattern(codegen_scope *s, node *pattern, int target, uint32_t *fail_pos)
|
||||
/* Bind the matched value to the variable */
|
||||
int idx = lv_idx(s, pat_var->name);
|
||||
if (idx > 0) {
|
||||
gen_move(s, idx, target, 0);
|
||||
gen_move(s, idx, target, 1); /* nopeep=1 to prevent optimization */
|
||||
}
|
||||
}
|
||||
/* Variable pattern always matches (wildcard if name is 0) */
|
||||
|
||||
@@ -3327,6 +3327,24 @@ primary : literal
|
||||
node *in_clause = new_in(p, $5, NULL, $7, FALSE);
|
||||
$$ = new_case_match(p, $2, cons(in_clause, $8));
|
||||
}
|
||||
| keyword_case expr_value opt_terms
|
||||
keyword_in p_expr modifier_if expr_value then
|
||||
compstmt
|
||||
in_clauses
|
||||
keyword_end
|
||||
{
|
||||
node *in_clause = new_in(p, $5, $7, $9, FALSE);
|
||||
$$ = new_case_match(p, $2, cons(in_clause, $10));
|
||||
}
|
||||
| keyword_case expr_value opt_terms
|
||||
keyword_in p_expr modifier_unless expr_value then
|
||||
compstmt
|
||||
in_clauses
|
||||
keyword_end
|
||||
{
|
||||
node *in_clause = new_in(p, $5, $7, $9, TRUE);
|
||||
$$ = new_case_match(p, $2, cons(in_clause, $10));
|
||||
}
|
||||
| keyword_for for_var keyword_in
|
||||
{COND_PUSH(1);}
|
||||
expr_value do
|
||||
@@ -3808,6 +3826,16 @@ in_clauses : opt_else
|
||||
node *in_clause = new_in(p, $2, NULL, $4, FALSE);
|
||||
$$ = cons(in_clause, $5);
|
||||
}
|
||||
| keyword_in p_expr modifier_if expr_value then compstmt in_clauses
|
||||
{
|
||||
node *in_clause = new_in(p, $2, $4, $6, FALSE);
|
||||
$$ = cons(in_clause, $7);
|
||||
}
|
||||
| keyword_in p_expr modifier_unless expr_value then compstmt in_clauses
|
||||
{
|
||||
node *in_clause = new_in(p, $2, $4, $6, TRUE);
|
||||
$$ = cons(in_clause, $7);
|
||||
}
|
||||
;
|
||||
|
||||
/* Pattern expressions for case/in */
|
||||
|
||||
+3172
-3109
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user