mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-compiler: add one-line pattern matching
add support for one-line pattern matching syntax: - 'expr in pattern' returns true/false - 'expr => pattern' raises NoMatchingPatternError on mismatch add NODE_MATCH_PAT node type for both forms, distinguished by raise_on_fail flag. grammar rules placed at expr level to avoid conflict with rescue clause's exception variable syntax. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6118,6 +6118,61 @@ codegen(codegen_scope *s, node *tree, int val)
|
||||
codegen_case_match(s, tree, val);
|
||||
break;
|
||||
|
||||
case NODE_MATCH_PAT:
|
||||
{
|
||||
/* One-line pattern matching: expr in pattern / expr => pattern */
|
||||
struct mrb_ast_match_pat_node *mp = match_pat_node(tree);
|
||||
int head = cursp();
|
||||
uint32_t fail_pos = JMPLINK_START;
|
||||
uint32_t match_pos;
|
||||
|
||||
/* Evaluate the value */
|
||||
codegen(s, mp->value, VAL);
|
||||
|
||||
/* Generate pattern matching code */
|
||||
codegen_pattern(s, mp->pattern, head, &fail_pos);
|
||||
|
||||
/* Pattern matched - load true and jump to end */
|
||||
pop(); /* pop the value */
|
||||
if (val) {
|
||||
genop_1(s, OP_LOADT, cursp());
|
||||
push();
|
||||
}
|
||||
match_pos = genjmp(s, OP_JMP, JMPLINK_START);
|
||||
|
||||
/* Pattern failed */
|
||||
dispatch_linked(s, fail_pos);
|
||||
pop(); /* pop the value */
|
||||
if (mp->raise_on_fail) {
|
||||
/* expr => pattern: raise NoMatchingPatternError */
|
||||
int msg_off = new_lit_cstr(s, "pattern not matched");
|
||||
int exc_reg = cursp();
|
||||
/* Get NoMatchingPatternError class */
|
||||
genop_2(s, OP_GETCONST, exc_reg, new_sym(s, MRB_SYM_2(s->mrb, NoMatchingPatternError)));
|
||||
push();
|
||||
/* Create message string */
|
||||
genop_2(s, OP_STRING, cursp(), msg_off);
|
||||
push();
|
||||
/* Call NoMatchingPatternError.new(message) */
|
||||
pop(); /* pop argument */
|
||||
genop_3(s, OP_SEND, exc_reg, new_sym(s, MRB_SYM_2(s->mrb, new)), 1);
|
||||
/* Raise the exception */
|
||||
genop_1(s, OP_RAISEIF, exc_reg);
|
||||
if (val) push();
|
||||
}
|
||||
else {
|
||||
/* expr in pattern: return false */
|
||||
if (val) {
|
||||
genop_1(s, OP_LOADF, cursp());
|
||||
push();
|
||||
}
|
||||
}
|
||||
|
||||
/* End of pattern matching */
|
||||
dispatch(s, match_pos);
|
||||
}
|
||||
break;
|
||||
|
||||
case NODE_DEF:
|
||||
codegen_def(s, tree, val);
|
||||
break;
|
||||
|
||||
@@ -91,6 +91,7 @@ enum node_type {
|
||||
NODE_PAT_ALT, /* alternative pattern (pat1 | pat2) */
|
||||
NODE_PAT_ARRAY, /* array pattern [a, b, *rest] */
|
||||
NODE_PAT_HASH, /* hash pattern {a:, b: x} */
|
||||
NODE_MATCH_PAT, /* one-line pattern matching (expr in pat / expr => pat) */
|
||||
NODE_LAST
|
||||
};
|
||||
|
||||
@@ -340,6 +341,14 @@ struct mrb_ast_pat_hash_node {
|
||||
struct mrb_ast_node *rest; /* Rest pattern (NULL if none, -1 if **nil) */
|
||||
};
|
||||
|
||||
/* One-line pattern matching: expr in pattern / expr => pattern */
|
||||
struct mrb_ast_match_pat_node {
|
||||
struct mrb_ast_var_header header; /* 8 bytes */
|
||||
struct mrb_ast_node *value; /* Expression to match */
|
||||
struct mrb_ast_node *pattern; /* Pattern */
|
||||
mrb_bool raise_on_fail; /* TRUE for =>, FALSE for in */
|
||||
};
|
||||
|
||||
/* Variable-sized for node */
|
||||
struct mrb_ast_for_node {
|
||||
struct mrb_ast_var_header header; /* 8 bytes */
|
||||
@@ -451,6 +460,7 @@ struct mrb_ast_super_node {
|
||||
#define pat_alt_node(n) ((struct mrb_ast_pat_alt_node*)(n))
|
||||
#define pat_array_node(n) ((struct mrb_ast_pat_array_node*)(n))
|
||||
#define pat_hash_node(n) ((struct mrb_ast_pat_hash_node*)(n))
|
||||
#define match_pat_node(n) ((struct mrb_ast_match_pat_node*)(n))
|
||||
#define for_node(n) ((struct mrb_ast_for_node*)(n))
|
||||
#define asgn_node(n) ((struct mrb_ast_asgn_node*)(n))
|
||||
#define masgn_node(n) ((struct mrb_ast_masgn_node*)(n))
|
||||
|
||||
@@ -684,6 +684,17 @@ new_pat_hash(parser_state *p, node *pairs, node *rest)
|
||||
return (node*)n;
|
||||
}
|
||||
|
||||
/* Create one-line pattern matching node (expr in pattern / expr => pattern) */
|
||||
static node*
|
||||
new_match_pat(parser_state *p, node *value, node *pattern, mrb_bool raise_on_fail)
|
||||
{
|
||||
struct mrb_ast_match_pat_node *n = NEW_NODE(match_pat, NODE_MATCH_PAT);
|
||||
n->value = value;
|
||||
n->pattern = pattern;
|
||||
n->raise_on_fail = raise_on_fail;
|
||||
return (node*)n;
|
||||
}
|
||||
|
||||
/* Create in-clause node for case/in */
|
||||
static node*
|
||||
new_in(parser_state *p, node *pattern, node *guard, node *body, mrb_bool guard_is_unless)
|
||||
@@ -2313,12 +2324,6 @@ stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
|
||||
{
|
||||
$$ = new_masgn(p, $1, new_array(p, $3));
|
||||
}
|
||||
| arg tASSOC tIDENTIFIER
|
||||
{
|
||||
node *lhs = new_lvar(p, $3);
|
||||
assignable(p, lhs);
|
||||
$$ = new_asgn(p, lhs, $1);
|
||||
}
|
||||
| expr
|
||||
;
|
||||
|
||||
@@ -2419,6 +2424,18 @@ expr : command_call
|
||||
{
|
||||
$$ = call_uni_op(p, cond($2), "!");
|
||||
}
|
||||
| arg tASSOC {p->in_kwarg++;} p_expr
|
||||
{
|
||||
/* expr => pattern (raises NoMatchingPatternError on failure) */
|
||||
p->in_kwarg--;
|
||||
$$ = new_match_pat(p, $1, $4, TRUE);
|
||||
}
|
||||
| arg keyword_in {p->in_kwarg++;} p_expr
|
||||
{
|
||||
/* expr in pattern (returns true/false) */
|
||||
p->in_kwarg--;
|
||||
$$ = new_match_pat(p, $1, $4, FALSE);
|
||||
}
|
||||
| arg
|
||||
;
|
||||
|
||||
@@ -8894,6 +8911,16 @@ dump_node(mrb_state *mrb, node *tree, int offset)
|
||||
}
|
||||
break;
|
||||
|
||||
case NODE_MATCH_PAT:
|
||||
printf("NODE_MATCH_PAT%s:\n", match_pat_node(tree)->raise_on_fail ? " (=>)" : " (in)");
|
||||
dump_prefix(offset+1, lineno);
|
||||
printf("value:\n");
|
||||
dump_node(mrb, match_pat_node(tree)->value, offset+2);
|
||||
dump_prefix(offset+1, lineno);
|
||||
printf("pattern:\n");
|
||||
dump_node(mrb, match_pat_node(tree)->pattern, offset+2);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Fallback: unknown node type - skip like codegen.c does */
|
||||
printf("unknown node type %d (0x%x)\n", nodetype, (unsigned)nodetype);
|
||||
|
||||
+5645
-5514
File diff suppressed because it is too large
Load Diff
@@ -920,6 +920,7 @@ mrb_init_exception(mrb_state *mrb)
|
||||
mrb_define_class_id(mrb, MRB_SYM(SyntaxError), script_error); /* 15.2.38 */
|
||||
struct RClass *index_error = mrb_define_class_id(mrb, MRB_SYM(IndexError), E_STANDARD_ERROR); /* 15.2.33 */
|
||||
mrb_define_class_id(mrb, MRB_SYM(KeyError), index_error);
|
||||
mrb_define_class_id(mrb, MRB_SYM(NoMatchingPatternError), E_STANDARD_ERROR); /* pattern matching */
|
||||
struct RClass *stack_error = mrb_define_class_id(mrb, MRB_SYM(SystemStackError), exception);
|
||||
mrb->stack_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, stack_error, "stack level too deep"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user