@@ -632,6 +632,16 @@ new_pat_var(parser_state *p, mrb_sym name)
632632return (node*)n;
633633}
634634635+/* Create pin pattern node (^var) */
636+static node*
637+new_pat_pin(parser_state *p, mrb_sym name)
638+{
639+struct mrb_ast_pat_pin_node *n = NEW_NODE(pat_pin, NODE_PAT_PIN);
640+ n->name = name;
641+/* Pin operator references existing variable, does not create new binding */
642+return (node*)n;
643+}
644+635645/* Create as pattern node (pattern => var) */
636646static node*
637647new_pat_as(parser_state *p, node *pattern, mrb_sym name)
@@ -2074,7 +2084,7 @@ prohibit_literals(parser_state *p, node *n)
20742084%type <id> f_label f_kwrest
2075208520762086/* pattern matching */
2077-%type <nd> in_clauses p_expr p_alt p_value p_var p_as p_array p_array_body p_array_elems p_rest p_hash p_hash_body p_hash_elems p_hash_elem p_kwrest
2087+%type <nd> in_clauses p_expr p_alt p_value p_var p_as p_array p_array_body p_array_elems p_rest p_hash p_hash_body p_hash_elems p_hash_elem p_kwrest p_args_head p_args_post
2078208820792089%token tUPLUS "unary plus"
20802090%token tUMINUS "unary minus"
@@ -3860,7 +3870,50 @@ in_clauses : opt_else
38603870 ;
3861387138623872/* Pattern expressions for case/in */
3873+/* Bracket-less array patterns: in 1, 2, x is same as in [1, 2, x] */
38633874p_expr : p_as
3875+| p_args_head p_as
3876+ {
3877+$$ = new_pat_array(p, push($1, $2), 0, 0);
3878+ }
3879+| p_args_head p_rest
3880+ {
3881+$$ = new_pat_array(p, $1, $2, 0);
3882+ }
3883+| p_args_head p_rest ',' p_args_post
3884+ {
3885+$$ = new_pat_array(p, $1, $2, $4);
3886+ }
3887+| p_rest
3888+ {
3889+$$ = new_pat_array(p, 0, $1, 0);
3890+ }
3891+| p_rest ',' p_args_post
3892+ {
3893+$$ = new_pat_array(p, 0, $1, $3);
3894+ }
3895+ ;
3896+3897+/* Comma-separated pattern list (prefix) */
3898+p_args_head : p_as ','
3899+ {
3900+$$ = list1($1);
3901+ }
3902+| p_args_head p_as ','
3903+ {
3904+$$ = push($1, $2);
3905+ }
3906+ ;
3907+3908+/* Comma-separated pattern list (suffix, no trailing comma) */
3909+p_args_post : p_as
3910+ {
3911+$$ = list1($1);
3912+ }
3913+| p_args_post ',' p_as
3914+ {
3915+$$ = push($1, $3);
3916+ }
38643917 ;
3865391838663919p_as : p_alt
@@ -3916,6 +3969,10 @@ p_value : p_var
39163969 }
39173970| p_array
39183971| p_hash
3972+| '^' tIDENTIFIER
3973+ {
3974+$$ = new_pat_pin(p, $2);
3975+ }
39193976 ;
3920397739213978/* Array pattern: [a, b, *rest, c] */
@@ -3957,12 +4014,12 @@ p_array_body : p_array_elems
39574014 }
39584015 ;
395940163960-/* Non-rest array pattern elements */
3961-p_array_elems : p_expr
4017+/* Non-rest array pattern elements - use p_as, not p_expr to avoid bracket-less recursion */
4018+p_array_elems : p_as
39624019 {
39634020$$ = list1($1);
39644021 }
3965-| p_array_elems ',' p_expr
4022+| p_array_elems ',' p_as
39664023 {
39674024$$ = push($1, $3);
39684025 }