You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Perl's bare pattern match /^#/ — a match against the implicit $_ —
scores one ABC condition inside a boolean slot and zero outside one,
while the bound form $x =~ /^#/ scores one in both. Found while
landing #1461, which fixed the same asymmetry in five other languages
but could not use the same route here.
Measured
bca metrics --no-config, abc.conditions per sub, at 336ed709:
#1461 moved five languages' relational productions out of <lang>_bool_terminal_kinds!() into an unconditional arm, so the
construct scores wherever it is written. PatternMatcher (337) and PatternMatcherM (336) cannot take that route unchanged: the ABC walk
visits every node, so an unconditional arm would also fire on the
pattern inside $x =~ /^#/, whose EQTILDE token is already counted — bound_out would go to 2. That is the §5 double count
(.claude/rules/grammar-dispatch.md), and it is the same collision that
made Kotlin's as go through the wrapper peel rather than the terminal
set in #1459.
The existing note on perl_bool_terminal_kinds! explains why listing
the patterns double-counts nothing today: every walker that consumes
the set either breaks on the enclosing binary_expression or requires
the chain's list node to be the parent, so the pattern node is never
reached beside its own =~. An unconditional arm has no such
protection.
The shape a fix would take
A gated arm: count PatternMatcher / PatternMatcherM unless the
node's parent is a binary_expression whose operator token is =~ or !~. That is a different shape from the seven unconditional arms #1461
landed, which is why it was left out rather than folded in.
Whether the same gap exists in Ruby, whose =~ is likewise a token
arm gated on a binary parent. Ruby has no bare-match-against-$_
spelling, so it probably does not, but that is an assumption rather
than a measurement.
Corpus cost
None expected: no corpus carries a .pl / .pm file.
Summary
Perl's bare pattern match
/^#/— a match against the implicit$_—scores one ABC condition inside a boolean slot and zero outside one,
while the bound form
$x =~ /^#/scores one in both. Found whilelanding #1461, which fixed the same asymmetry in five other languages
but could not use the same route here.
Measured
bca metrics --no-config,abc.conditionsper sub, at336ed709:bare_outis the only row that disagrees with its controls.Why #1461's route does not work here
#1461 moved five languages' relational productions out of
<lang>_bool_terminal_kinds!()into an unconditional arm, so theconstruct scores wherever it is written.
PatternMatcher(337) andPatternMatcherM(336) cannot take that route unchanged: the ABC walkvisits every node, so an unconditional arm would also fire on the
pattern inside
$x =~ /^#/, whoseEQTILDEtoken is already counted —bound_outwould go to 2. That is the §5 double count(
.claude/rules/grammar-dispatch.md), and it is the same collision thatmade Kotlin's
asgo through the wrapper peel rather than the terminalset in #1459.
The existing note on
perl_bool_terminal_kinds!explains why listingthe patterns double-counts nothing today: every walker that consumes
the set either breaks on the enclosing
binary_expressionor requiresthe chain's list node to be the parent, so the pattern node is never
reached beside its own
=~. An unconditional arm has no suchprotection.
The shape a fix would take
A gated arm: count
PatternMatcher/PatternMatcherMunless thenode's parent is a
binary_expressionwhose operator token is=~or!~. That is a different shape from the seven unconditional arms #1461landed, which is why it was left out rather than folded in.
Two things to check before writing it:
substitution_pattern_s(s///) andtransliteration_tr_or_y(tr///) should move with it. Both areabsent from the terminal set today on the grounds that they evaluate
to a count rather than a bool — a judgement
perl_bool_terminal_kinds!records and decide: four uncounted boolean operands, and whether terminal sets should be token arms #1461 did not revisit.
=~is likewise a tokenarm gated on a
binaryparent. Ruby has no bare-match-against-$_spelling, so it probably does not, but that is an assumption rather
than a measurement.
Corpus cost
None expected: no corpus carries a
.pl/.pmfile.