Skip to content

lexer: numeric suffixes are not keyed by literal kind or applied on every ReadNumber path — 3.14L runs as float64 with the suffix dropped, .5f is SPY0103 while 0.5f runs #2339

Closed

Description

@antonsynd

Class. The decimal ReadNumber path does not apply the numeric-suffix rule uniformly across literal kinds and lexing paths. The violated contract: a suffix is accepted exactly when the spec gives it to that literal's kind, and on every path that lexes that kind. The spec gives integer suffixes (L/u/ul/…) to integers in integer_literals.md, and f/d/m to floats in float_literals.md. float_literals.md also says .5 is the "Same as 0.5". Every other suffix is refused with SPY0023.

Found by the P68 numeric twin (NumericLiteralTwinTests, plan-d31acd, #2300) at 220259b. The lead confirmed it with the built sharpyc.

# Cell Program Observed Expected Lane
1 integer suffix on a decimal float x = 3.14L (also 3.14u, 1e5L) runs. The literal types float64 (a: int64 = 3.14L → SPY0220 "Cannot assign type 'float64'"), and the suffix is silently dropped SPY0023 (C# refuses 3.14L too) missing-refusal
2 float suffix on a leading-dot float x = .5f (also .5m, .5d, .5e3f) SPY0103 "Expected end of statement, got Identifier". The leading-dot branch of ReadNumber (Lexer.Literals.cs ~:723–788) returns before the suffix arm, so f lexes as an Identifier. 0.5f runs accepted as float32, the same as 0.5f false-refusal
3 sibling of 2 .5j SPY0103 SPY0023, the same as 1j message

The sites are the suffix roster at Lexer.Literals.cs ~:923, which is not keyed by literal kind, and the leading-dot early return. The hex/bin/oct path already keys its suffixes by kind through IntegerSuffixes, from the #2208 fix.

Matrix to sweep at fix time. Cross the literal kind (decimal int, decimal float, leading-dot float, exponent-only float 1e5, hex/bin/oct) with each suffix in the roster, under both of its cases. The numeric twin's SharpyOnlyRows_LexAsTheSpecSays table is the place to pin the matrix.

refs #2300

Activity

  1. added
    lane:false-refusalA valid program (spec or python) is refused
    reach:programAn ordinary program a Python programmer would write hits it
    class-trackerThe one issue per class: carries the contract and the cell table
    on Oct 21, 2026
  2. antonsynd commented on Oct 22, 2026

    @antonsynd
    OwnerAuthor

    Batching 2026-10-21 @ e7e0a0e — charter .claude/plans/remediation-round-2026-09-20-batching.md §3.0, chores band (order 0, one commit, no plan; rides P21e's gate; lands BEFORE P22c, which rewrites the same literal reader). Ruling R-HX (owner, 2026-10-21): (a) by class — #2339 → a chore in the current band (Lexer ReadNumber only); #2337 → P22c; #2338 → P67.

    Contract (integer_literals.md / float_literals.md): a numeric suffix is keyed by the literal's KIND (integer vs float) and consulted on EVERY ReadNumber path — every suffix the spec lists is accepted on every spelling of its kind and refused on every other kind. Measured @ e7e0a0e (built CLI, lock-free): print(3.14L, 1e5u) RUNS printing 3.14 100000.0 — the integer suffix is silently dropped and the value is float64; y = .5f → SPY0103 "Expected end of statement, got Identifier" while 0.5f runs; .5j → SPY0103 where the spec says SPY0023. Lane / reach: false-refusal + missing-refusal · program (the first cell is silent-wrong in effect). Class: the parallel-path meta-class (contract §1) — the hex/bin/oct path has been keyed by kind since #2208; the decimal and leading-dot paths are not (grep-only; the chore measures). Cure: one suffix table keyed by kind, consulted by every path after the digits; the twin's hand table NumericLiteralTwinTests.SharpyOnlyRows_LexAsTheSpecSays pins kind {int, float} × spelling {0.5, .5, 5., 1e5, hex/bin/oct, underscored} × suffix {every integer suffix, every float suffix, j, none} × case; direction record against the prior commit (3.14L ran → refused; .5f SPY0103 → runs; .5j → SPY0023); mutation (unkey one path → its rows red) in the commit body; fixture-hit count for both spellings FIRST (zero expected). Blast radius: Compiler.Tests lexer + FrontEndParity; no snapshots. Class tracker: this issue.

    Also recorded here for the chore's convenience (not a requirement): R-HW's lone-surrogate message (𝒳 = 1 → SPY0015 "Unexpected character: '�'") may print the full code point — one line in the same reader; the surface itself is a docs/deviations.yaml row (identifier-letters-bmp-only). Rejected (R-HX): (b) a P73 residue group with #2337/#2338; (c) P22c (numeric, not the prefix table). Two-way.

  3. antonsynd commented on Oct 22, 2026

    @antonsynd
    OwnerAuthor

    Landed (chores band 2026-10-22) on dev @ 4d22212, PR #2346 to mainline. Whole-solution gate 81669/0/7 @ 4d22212 (measured), against 81093/0/7 @ be4664e.

    Commit 0b30fa9. One suffix table, Lexer.NumericSuffixes, keyed by kind {Integer, Float}. One reader, ReadNumericSuffix, is called by all five ReadNumber paths (decimal, leading-dot, hex, binary, octal) after their digits.

    Direction (run, e7e0a0e → this commit):

    Program Before After
    print(3.14L, 1e5u) 3.14 100000.0 SPY0023
    print(1.5ul) SPY0909 ICE SPY0023
    y = .5f SPY0103 0.5
    print(.5j) SPY0104 SPY0023

    Matrix: NumericLiteralTwinTests.SharpyOnlyRows_LexAsTheSpecSays now has 345 generated rows, up from 83.

    Mutations:

    • The leading-dot path skipping the reader turned 54 rows red.
    • A float spelling accepting integer suffixes turned 72 rows red.
    • Restored, all green.

    Every acceptance cell is met. Closing.

  4. added a commit that references this issue on Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    class-trackerThe one issue per class: carries the contract and the cell tablelane:false-refusalA valid program (spec or python) is refusedlane:missing-refusalAn invalid program is acceptedreach:programAn ordinary program a Python programmer would write hits it

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions