{"atom":"regression/before-assertion-group","family":"regression","slug":"before-assertion-group","header":{"atom":"regression/before-assertion-group","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/before-assertion-group.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/before-assertion-group.grid"],"records":[["0001","","# Regression: `<?before [ … ]>` — a bracket after an explicit `before`/`after`\n# keyword is a non-capturing GROUP, not a character class. The `<?[…]>` /\n# `<![…]>` class-assertion shorthand only applies to the keyword-less form, so\n# reading `<?before [ 'a' ]* 'b'>` as a class made the whole lookahead fail.\n# This is the YAMLish block-scalar (`key: >-` / `key: |`) parse: its\n# `block-string` rule opens with `<?before [ $indent <.space>* <.line-break> ]*\n# $indent $<sp>=' '+ … >`.\n# Contract: exit 0 + last line PASS.\nmy @fail;\n\n# the group form — zero reps, one rep, and an alternation inside\n@fail.push('before-group')      unless 'abc' ~~ / ^ <?before [ 'a' ] > 'abc' /;\n@fail.push('before-group-star') unless 'abc' ~~ / ^ <?before [ 'zz' ]* > 'abc' /;\n@fail.push('before-group-tail') unless 'abc' ~~ / ^ <?before [ 'zz' ]* 'a' > 'abc' /;\n@fail.push('before-group-opt')  unless 'abc' ~~ / ^ <?before [ 'zz' ]? > 'abc' /;\n@fail.push('before-group-alt')  unless 'abc' ~~ / ^ <?before [ 'a' | 'b' ] 'bc' > 'abc' /;\n@fail.push('before-group-plus') if     'abc' ~~ / ^ <?before [ 'zz' ]+ > 'abc' /;\n@fail.push('after-group')       unless 'xabc' ~~ / <?after [ 'x' ] > 'abc' /;\n\n# the keyword-less shorthand still reads its bracket as a character class\n@fail.push('class-assert')     unless 'abc' ~~ / ^ <?[a]> 'abc' /;\n@fail.push('class-assert-neg') unless 'abc' ~~ / ^ <![x]> 'abc' /;\n@fail.push('class-assert-rng') unless 'abc' ~~ / ^ <?+[a..z]> 'abc' /;\n@fail.push('class-assert-sub') unless 'abc' ~~ / ^ <?-[x..z]> 'abc' /;\n# a quote member inside the class must not open a string literal\n@fail.push('class-assert-quote') unless Q[say \"hi\"] ~~ / '\"' [ <![\"]> . ]* '\"' /;\n\n# the shape that broke YAMLish: an indent-parameterised lookahead whose leading\n# group matches zero times, feeding a `:my` var used later in the same token\ngrammar BlockScalar {\n    token space      { <[\\ \\t]> }\n    token line-break { \\n }\n    token TOP        { <body('')> }\n    token body(Str $indent) {\n        $<kind>=<[\\|\\>]> <.space>* <.line-break>\n        :my $new-indent;\n        <?before\n            [ $indent <.space>* <.line-break> ]*\n            $indent $<sp>=' '+ { $new-indent = $<sp> }\n        >\n        [ $indent $new-indent $<content>=[ \\N* ] ]+ % <.line-break>\n        <.line-break>?\n    }\n}\nmy $m = BlockScalar.parse(\"|\\n  one\\n  two\\n\");\n@fail.push('block-scalar') unless $m && $m<body><content>.join('|') eq 'one|two';\n\nif @fail { note \"FAILED: @fail[]\"; say 'FAIL' } else { say 'PASS' }","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}