@@ -3848,29 +3848,31 @@ cases : opt_else
38483848 ;
3849384938503850/* Pattern matching in-clauses for case/in */
3851+/* in_kwarg is set by lexer when keyword_in is returned */
38513852in_clauses : opt_else
38523853 {
38533854$$ = $1 ? list1(new_in(p, NULL, NULL, $1, FALSE)) : 0;
38543855 }
3855-| keyword_in p_expr then compstmt in_clauses
3856+| keyword_in p_expr {p->in_kwarg--;} then compstmt in_clauses
38563857 {
3857- node *in_clause = new_in(p, $2, NULL, $4, FALSE);
3858-$$ = cons(in_clause, $5);
3858+ node *in_clause = new_in(p, $2, NULL, $5, FALSE);
3859+$$ = cons(in_clause, $6);
38593860 }
3860-| keyword_in p_expr modifier_if expr_value then compstmt in_clauses
3861+| keyword_in p_expr {p->in_kwarg--;} modifier_if expr_value then compstmt in_clauses
38613862 {
3862- node *in_clause = new_in(p, $2, $4, $6, FALSE);
3863-$$ = cons(in_clause, $7);
3863+ node *in_clause = new_in(p, $2, $5, $7, FALSE);
3864+$$ = cons(in_clause, $8);
38643865 }
3865-| keyword_in p_expr modifier_unless expr_value then compstmt in_clauses
3866+| keyword_in p_expr {p->in_kwarg--;} modifier_unless expr_value then compstmt in_clauses
38663867 {
3867- node *in_clause = new_in(p, $2, $4, $6, TRUE);
3868-$$ = cons(in_clause, $7);
3868+ node *in_clause = new_in(p, $2, $5, $7, TRUE);
3869+$$ = cons(in_clause, $8);
38693870 }
38703871 ;
3871387238723873/* Pattern expressions for case/in */
38733874/* Bracket-less array patterns: in 1, 2, x is same as in [1, 2, x] */
3875+/* Brace-less hash patterns: in a:, b: x is same as in {a:, b: x} */
38743876p_expr : p_as
38753877| p_args_head p_as
38763878 {
@@ -3892,6 +3894,21 @@ p_expr : p_as
38923894 {
38933895$$ = new_pat_array(p, 0, $1, $3);
38943896 }
3897+| p_hash_elems
3898+ {
3899+/* Brace-less hash pattern: in a:, b: x */
3900+$$ = new_pat_hash(p, $1, 0);
3901+ }
3902+| p_hash_elems ',' p_kwrest
3903+ {
3904+/* Brace-less hash pattern with kwrest: in a:, **rest */
3905+$$ = new_pat_hash(p, $1, $3);
3906+ }
3907+| p_kwrest
3908+ {
3909+/* Brace-less kwrest only: in **rest */
3910+$$ = new_pat_hash(p, 0, $1);
3911+ }
38953912 ;
3896391338973914/* Comma-separated pattern list (prefix) */
@@ -4075,7 +4092,8 @@ p_hash_elems : p_hash_elem
40754092 ;
4076409340774094/* Hash pattern element: key: pattern or key: (shorthand) */
4078-p_hash_elem : tIDENTIFIER tLABEL_TAG p_expr
4095+/* Use p_as, not p_expr to avoid brace-less recursion inside hash patterns */
4096+p_hash_elem : tIDENTIFIER tLABEL_TAG p_as
40794097 {
40804098/* {key: pattern} */
40814099$$ = cons(new_sym(p, $1), $3);
@@ -4085,7 +4103,7 @@ p_hash_elem : tIDENTIFIER tLABEL_TAG p_expr
40854103/* {key:} shorthand - binds to variable with same name */
40864104$$ = cons(new_sym(p, $1), new_pat_var(p, $1));
40874105 }
4088-| symbol tASSOC p_expr
4106+| symbol tASSOC p_as
40894107 {
40904108/* {:"key" => pattern} or {:key => pattern} */
40914109$$ = cons($1, $3);
@@ -5393,7 +5411,7 @@ toklen(parser_state *p)
53935411#define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN)
53945412#define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS)
53955413#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c))
5396-#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG())
5414+#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG() || p->lstate == EXPR_VALUE)
53975415#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1))
5398541653995417static int32_t
@@ -6855,7 +6873,8 @@ parser_yylex(parser_state *p)
68556873 }
68566874if (!space_seen && IS_END()) {
68576875pushback(p, c);
6858- p->lstate = EXPR_BEG;
6876+/* In pattern matching context, use EXPR_ARG so newlines are significant */
6877+ p->lstate = p->in_kwarg ? EXPR_ARG : EXPR_BEG;
68596878return tLABEL_TAG;
68606879 }
68616880if (IS_END() || ISSPACE(c) || c == '#') {
@@ -7357,6 +7376,10 @@ parser_yylex(parser_state *p)
73577376return keyword_do_block;
73587377return keyword_do;
73597378 }
7379+if (kw->id[0] == keyword_in) {
7380+/* Set in_kwarg for pattern matching context */
7381+ p->in_kwarg++;
7382+ }
73607383if (state == EXPR_BEG || state == EXPR_VALUE || state == EXPR_CLASS)
73617384return kw->id[0];
73627385else {