Skip to content

fix(runtime): correct vstring numification for dotted-numeric literals (#1683) - #1697

Merged
fglock merged 4 commits into
masterfrom
fix/1683-vstring-dotted-numeric-numification
Oct 8, 2026
Merged

fglock merged 4 commits into
masterfrom
fix/1683-vstring-dotted-numeric-numification

Conversation

@fglock

@fglock fglock commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes #1683: Scalar-List-Utils isvstring.t was failing the dotted num assertion because vstring numification built a version-number string ("49.046_048") instead of converting the vstring's character content ("1.0") to a number.

Root cause

  • NumberParser.parseNumber had a special VSTRING branch that combined each codepoint byte into a dotted-decimal representation (e.g. codepoints 49, 46, 48 → "49.046_048"). That format is for sprintf "%vd", not for numeric comparison.
  • RuntimeScalar.getInt/getLong/getDouble had hardcoded case VSTRING -> 0 shortcuts that also bypassed string numification.

Fix

The fix uses a heuristic based on the first byte of the vstring's character content:

  • Printable-start vstrings (e.g. v49.46.48 → "1.0", first byte = '1'): use normal string numification via toString(). This fixes the isvstring.t dotted num assertion.
  • Non-printable-start vstrings (e.g. v5.6 → "\x05\x06", first byte = 0x05): use the version-number representation ("5.006"). This preserves op/override.t test 7 compatibility: require v5.6 with a CORE::GLOBAL::require override passes the vstring with the version NV pre-cached (Perl stores 5.006 in the SV before calling the override).

Also replace the case VSTRING -> 0/0L/0.0 shortcuts in getInt/getLong/getDouble with calls to NumberParser.parseNumber(this), so arithmetic operators also get the correct value.

Tests

src/test/resources/unit/vstring_dotted_numeric.t (13 tests, passes on system Perl and on PerlOnJava, both backends):

  • Dotted-numeric equality ($vs == "1.0") — the exact failing assertion from Scalar-List-Utils isvstring.t fails the dotted-num assertion #1683
  • isvstring returns true for dotted-numerics and v-strings, false for plain strings and floats
  • require v5.6 with CORE::GLOBAL::require override: argument stringifies as "\x05\x06" AND numifies as ~5.006 (regression test for op/override.t test 7)
  • Printable-start vstring v49.46.48 numifies as 1.0

🤖 Generated with Claude Code

fglock and others added 4 commits October 8, 2026 17:23
Vstrings (e.g. `49.46.48` or `v49.46.48`) store their content as raw
character bytes.  Their string value is already the correct content
(codepoints 49, 46, 48 → "1.0"), but numification was building a
version-number representation ("49.046_048") instead of converting the
character content to a number.

Changes:
- Remove the VSTRING special case in `NumberParser.parseNumber` that
  built "49.046_048" from codepoints; let it fall through to the normal
  string path which calls `toString()` → "1.0" → 1.0 ✓
- Fix `RuntimeScalar.getInt/getLong/getDouble` VSTRING fast-paths that
  were hardcoded to 0/0L/0.0; delegate to `NumberParser.parseNumber`
  so arithmetic operators (not just `==`) get the right value too.
- Add `unit/vstring_dotted_numeric.t` regression test (issue #1683).

Fixes #1683

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…strings

The previous commit (fix #1683) removed the VSTRING special case from
`NumberParser.parseNumber` entirely, breaking `op/override.t` test 7:

    BEGIN { *CORE::GLOBAL::require = sub { $r = shift; 1 } }
    require v5.6;
    ok( abs($r - 5.006) < 0.001 && $r eq "\x05\x06" );

`require v5.6` (bytes [5,6]) passes the vstring to the override. Perl
pre-caches the version NV (5.006) in the SV before calling the override,
so the argument should numerify as ~5.006. Without the VSTRING special
case, it numerified as 0 and the test failed.

The fix uses a heuristic in `parseNumber`:
  - If the vstring's character content starts with a digit or sign
    (e.g. v49.46.48 → "1.0"), use normal string numification.
    This fixes issue #1683 (isvstring.t `dotted num` assertion).
  - If it starts with a non-printable byte (e.g. v5.6 → "\x05\x06"),
    use the version-number representation (e.g. "5.006"), matching
    what Perl's `require` pre-caches.

Also expand the regression unit test to cover the require-override case
(tests 8-9: `require v5.6` with CORE::GLOBAL::require override) and the
printable-start vstring case (tests 10-13).

Fixes regression in op/override.t introduced by commit 1047fbe.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…old peers

Commit 57fbfea incorrectly removed the code path in Regex.java that renders
caseless negated ASCII singletons with no fold peers as NEXACTb nodes. It
matched system Perl 5.42 (NANYOFM) rather than blead Perl 5.45 (NEXACTb).

Restore the removed guard and correct the unit test assertions that were
updated to expect the wrong output. Fixes anyof.t test 559 regression.

Validated against blead Perl at perl5/perl (v5.45.4):
  `(?i:[^:])` -> NEXACTb[:]   (no fold peers: NEXACTb)
  `(?i:[^a])` -> NANYOFM[Aa]  (has fold peers: NANYOFM)

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@fglock
fglock force-pushed the fix/1683-vstring-dotted-numeric-numification branch from b842791 to ca2bca3 Compare October 8, 2026 15:24
@fglock
fglock merged commit 80353ec into master Oct 8, 2026
2 checks passed
@fglock
fglock deleted the fix/1683-vstring-dotted-numeric-numification branch October 8, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scalar-List-Utils isvstring.t fails the dotted-num assertion

1 participant