{"atom":"regression/regex-code-blocks","family":"regression","slug":"regex-code-blocks","header":{"atom":"regression/regex-code-blocks","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/regex-code-blocks.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/regex-code-blocks.grid"],"records":[["0001","","# Regression: `{…}` code blocks inside a PLAIN regex (the grammar path already had them).\n#   * a block runs when the ENGINE REACHES IT, not when the match is accepted — so a\n#     branch the engine later abandons still ran its block, and a retried branch runs\n#     it again. `/a+ {…} b/` against 'aaa' fires six times and matches nothing.\n#   * `$/` and `$¢` inside the block are the CURSOR: the match as far as the engine\n#     has got, positional and named captures included. `$¢` exists only for the\n#     block's duration — outside a regex it is Nil.\n#   * `.pos` is where the engine has got to, i.e. the cursor's end.\n#   * a `$var` inside the block belongs to the BLOCK. Interpolating `$var` into the\n#     pattern text (which is right for a `$var` ATOM) rewrote `{ $c = 42 }` into\n#     `{ 1 = 42 }`, so every write died with X::Assignment::RO and every read saw a\n#     stale copy. Blocks and `<?{…}>` assertions are now skipped when interpolating.\n#   * the hooks are wired before the `:g`/`:nth` loops, not after, so those run\n#     blocks too.\n# Contract: exit 0 + last line PASS.\nmy @fail;\nsub check($got, $want, $what) { @fail.push(\"$what: got $got want $want\") unless $got eq $want }\n\n# a block fires on every branch the engine walks, accepted or not\nmy @log;\n'aaa' ~~ / a+ { @log.push('x') } b /;\ncheck(@log.elems, '6', 'an abandoned branch still ran its block');\n@log = ();\n'aab' ~~ / a+ { @log.push('x') } b /;\ncheck(@log.elems, '1', 'a match that succeeds first try runs it once');\n@log = ();\n'abcabc' ~~ m:g/ a { @log.push('g') } b /;\ncheck(@log.elems, '2', ':g runs the block per match');\n\n# $/ and $¢ are the cursor\nmy $c;\n'abc' ~~ /.$${ $c = $¢ }/;\ncheck($c.Str, 'c', 'the cursor is the match so far');\n'camelia' ~~ /<[ l m ]> { $c = $¢ }/;\ncheck($c.Str,  'm', 'and starts where THIS attempt started, not at 0');\ncheck($/.Str,  'm', 'the finished match agrees');\nmy @seen;\n'123' ~~ / (\\d) { @seen.push($0.Str); @seen.push($/.Str) } \\d+ /;\ncheck(@seen.join(','), '1,1', 'a block sees the captures made so far');\ncheck($¢.defined, 'False', 'the cursor is gone once the match is over');\n\n# .pos\nmy $p;\n'abcdef' ~~ /b. { $p = $/.pos } ../;\ncheck($p, '3', 'pos is the end of the cursor');\n\n# a $var in a block is the block's own variable\nmy $q = 1;\n'camelia' ~~ /m { $q = 42 }/;\ncheck($q, '42', 'a block assigns to an outer lexical');\nmy $r = 'zz';\n'camelia' ~~ /m { $r = $r ~ '!' }/;\ncheck($r, 'zz!', 'and reads it live rather than as pattern text');\n# a `$var` ATOM still interpolates — that is what the skip must not break\nmy $atom = 'mel';\ncheck(('camelia' ~~ /$atom/).Str, 'mel', 'a variable atom still matches its value');\ncheck(('ca.melia' ~~ /$atom/).Str, 'mel', 'and is still quotemeta-ed');\nmy @alts = <xx lia>;\ncheck(('camelia' ~~ /@alts/).Str, 'lia', 'an array atom still alternates');\n\n# make inside a plain regex still reaches .made\nmy $m = 'abc' ~~ / (\\w) { make $0.Str.uc } /;\ncheck($m.made, 'A', 'an inline make still lands on the match');\n\nif @fail { note \"FAILED: @fail.join('; ')\"; say 'FAIL' } else { say 'PASS' }","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}