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
{{ message }}
Repository navigation
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
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.
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.
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.
Activity
docs(gap-discovery): roster rows for the P68 lexer harnesses (refs #2300
antonsynd commentedon Oct 22, 2026
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 (LexerReadNumberonly); #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 EVERYReadNumberpath — 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 printing3.14 100000.0— the integer suffix is silently dropped and the value isfloat64;y = .5f→ SPY0103 "Expected end of statement, got Identifier" while0.5fruns;.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 tableNumericLiteralTwinTests.SharpyOnlyRows_LexAsTheSpecSayspins 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.14Lran → refused;.5fSPY0103 → 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 adocs/deviations.yamlrow (identifier-letters-bmp-only). Rejected (R-HX): (b) a P73 residue group with #2337/#2338; (c) P22c (numeric, not the prefix table). Two-way.fix(lexer): a numeric suffix is keyed by the literal's kind and read …
antonsynd commentedon Oct 22, 2026
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 fiveReadNumberpaths (decimal, leading-dot, hex, binary, octal) after their digits.Direction (
run, e7e0a0e → this commit):print(3.14L, 1e5u)3.14 100000.0print(1.5ul)y = .5f0.5print(.5j)Matrix:
NumericLiteralTwinTests.SharpyOnlyRows_LexAsTheSpecSaysnow has 345 generated rows, up from 83.Mutations:
Every acceptance cell is met. Closing.
Merge pull request #2346 from antonsynd/dev