fix: Specify how a numeric string's leading and trailing zeros render - #180
Open
leongdl wants to merge 2 commits into
Open
fix: Specify how a numeric string's leading and trailing zeros render#180leongdl wants to merge 2 commits into
leongdl wants to merge 2 commits into
Conversation
seant-aws
previously approved these changes
Sep 1, 2026
Contributor
Author
|
Pending fixing openjd py and rs. |
leongdl
force-pushed
the
fix/numeric-string-zero-handling
branch
from
September 4, 2026 22:30
ae3f433 to
6dce253
Compare
wyongzhi
previously approved these changes
Sep 8, 2026
`<intstring>` and `<floatstring>` are defined only as "a string whose value is the string representation of" a number, which says what such an element denotes but not what it renders as. Implementations read it both ways, and both readings were already pinned by landed conformance fixtures pulling in opposite directions: base/jobs/2.3--int-param-intstring-default-resolves asserts COUNT:7 from an INT default of '007' -- the text is not preserved. EXPR/jobs/expr1.3.4--float-passthrough asserts PARAM:3.500 from a FLOAT default of "3.500" -- the text is preserved. Both are right, because the two kinds of zero are not the same thing. Add §7.5 Numeric strings to say so, and cross-reference it from the four places `<intstring>`/`<floatstring>` are defined. Leading zeros are redundant to the value and are removed: '02' and '2' ask for the same thing, and forwarding the text invokes a renderer with `--frame 02`. The decimal places a <floatstring> is written with are preserved: '2.50' renders 2.50, because a fixed number of decimal places is a thing renderers require and the string form is the only way a Template can ask for one -- a <float> literal cannot, since 2.50 and 2.5 are the same literal after parsing. Exponent notation and an explicit leading '+' are called out as unspecified in this revision rather than left to be inferred. Implementations disagree on both and no fixture pins either: measured across two implementations and two surfaces, '1E+2' renders as 1E+2 in three cells and 100.0 in one, and '+2.50' renders as +2.50 in two and 2.50 in two. The new fixture pins both rules on the range-element surface, for INT and FLOAT. '0.50' is included because its leading zero is not redundant -- it is the whole integer part -- so a text-based strip must still leave one digit behind. The fixture brackets each value (`W[2.50]`) instead of delimiting it with a colon. The runner matches expected output as a substring, so an assertion of `W:2.5` is satisfied by a line reading `W:2.50`; the closing bracket is what makes the `forbidden` entries forbid anything. base/jobs/3.4.1.2 in OpenJobDescription#179 has exactly that defect and reports a pass against either behaviour. Verification. The fixture passes against openjd-model-for-python#345 and fails against openjd-rs at 1b58c03, which renders W[2.5], W[3.5] and W[0.5] -- so it discriminates rather than just describing one implementation. Both CI conformance jobs install released versions (`pip install openjd-cli`, `cargo install openjd-cli`) and will fail this fixture until both implementations ship the fix; released Python 0.11.6 fails it on the INT side too. Zero-valued <floatstring>s ('0.00') are left out: openjd-rs collapses the value to 0.0 and loses the written scale, which is a separate pre-existing defect that also affects FLOAT parameter defaults. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
leongdl
force-pushed
the
fix/numeric-string-zero-handling
branch
from
September 9, 2026 00:05
6dce253 to
75c5ddc
Compare
mwiebe
reviewed
Sep 9, 2026
mwiebe
reviewed
Sep 9, 2026
Review asked for a choice on the two cases §7.5 left unspecified, rather than
leaving a reader to infer one. Both follow the split the section already makes,
so state them inside the existing two rules rather than as exceptions:
An exponent is notation the value was written in, like its decimal places, so
it is preserved. '1E+2' renders 1E+2, keeping the case of its marker and the
sign inside it.
A leading '+' is redundant to the value, like a redundant leading zero, so it
is removed. '+2.50' renders 2.50.
§7.5 goes from 36 lines to 24 in the process. The "left unspecified" list is
gone; only the sign of a zero is still open, and that is now one sentence.
Extend the fixture to both rulings, and trim its header from 34 comment lines
to 10 -- every other fixture in that directory has at most 4.
The exponent elements are spelled with a dot ('2.50E+2', not '1E+2') because
the runner re-dumps the template through PyYAML, which leaves a dotless
exponent unquoted. A YAML 1.2 parser then reads it as a <float> literal, which
makes no request about rendering at all, so the assertion would have tested
scalar resolution rather than §7.5. Measured: openjd-rs renders 100.0 for an
unquoted 1E+2 and 2.50E+2 for the quoted form -- same input, different YAML
dialect.
Measured against openjd-rs at mainline d67bfb8, full 2023-09 suite: 1178
passed, 1 failed. The one failure is this fixture, on the leading '+' alone --
it renders W[+4.25] where §7.5 now requires W[4.25]. openjd-model 0.11.9 with
openjd-cli 0.7.7 fails the fixture on exactly the same element and nothing
else. So the exponent ruling ratifies what both already ship on this surface,
and only the '+' ruling needs code.
Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds §7.5 Numeric strings, which says what an
<intstring>/<floatstring>renders as, and one conformance fixture that pins it for range elements.The rule, in one line: leading zeros go, trailing zeros stay.
Why the spec needs to say this
<intstring>and<floatstring>are defined only as "a string whose value is the string representation of" a number. That says what the element denotes; it does not say what it renders as. Implementations read it both ways, and two already-landed fixtures pull in opposite directions:base/jobs/2.3--int-param-intstring-default-resolvesCOUNT:7from INTdefault: '007'EXPR/jobs/expr1.3.4--float-passthroughPARAM:3.500from FLOATdefault: "3.500"Both are right, and the reason is that the two kinds of zero are not the same thing:
'02'and'2'ask for the same thing, and forwarding the text invokes a renderer with--frame 02.<float>literal cannot, because2.50and2.5are the same literal after parsing.This came out of review on openjd-model-for-python#342, which normalized range elements to the number they denote and so dropped the trailing zeros. openjd-model-for-python#345 reverts that half; this PR is the spec statement behind it.
base/jobs/3.4.1.2--float-range-floatstring-elements-normalizedin #179 states the fully-normalizing reading for a FLOAT range element. Its own comment flagged that it andexpr1.3.4--float-passthroughcould not both be right and that a ruling was needed. This is the ruling, and 3.4.1.2 is the one that moves — see the note on #179 below.Rulings added in review
Review asked for a specific choice on the two cases §7.5 first left unspecified, rather than leaving a reader to infer one. Both follow the split the section already makes, so they landed inside the existing two rules rather than as exceptions:
'1E+2'renders1E+2, keeping the case of its marker and the sign inside it+'+2.50'renders2.50§7.5 went from 36 lines to 24 in the process. Only the sign of a zero is still open, and that is now one sentence rather than a bulleted list.
The fixture
base/jobs/7.5--numeric-string-zeros-in-range-elements.test.yaml, covering INT and FLOAT range elements in one template.'0.50'is in there because its leading zero is not redundant, it is the whole integer part, so an implementation that strips leading zeros by text has to leave one digit behind. Header trimmed from 34 comment lines to 10; every other fixture in that directory has at most 4.Two details worth flagging for reviewers.
The assertions bracket each value (
W[2.50]) rather than delimiting with a colon. The runner matches expected output as a substring, so an assertion ofW:2.5is satisfied by a line readingW:2.50. The closing bracket is what makes theforbiddenentries forbid anything.base/jobs/3.4.1.2in #179 has exactly that defect and reports a pass against either behaviour.The exponent elements are spelled with a dot (
'2.50E+2', not'1E+2') because the runner re-dumps the template through PyYAML, whose YAML 1.1 resolver needs a dot before an exponent and so leaves1E+2unquoted. A YAML 1.2 parser then reads that plain scalar as a number, i.e. a<float>literal, which makes no request about rendering at all. Measured: openjd-rs renders100.0for an unquoted1E+2and2.50E+2for the quoted form. Asserting on'1E+2'here would have tested scalar resolution rather than §7.5.Verification
Measured against both implementations at their current mainline, after the rulings above:
d67bfb82023-09/*2023-09/base/*In both, the single failure is this fixture and nothing else, and in both it fails on the leading
+alone: they renderW[+4.25]where §7.5 now requiresW[4.25]. Every other element passes, including both exponent elements, so the exponent ruling ratifies what both already ship on this surface and only the+ruling needs code.The Python full suite was not run to completion: it wedges on an unrelated EXPR range-cap fixture (a
LIST[INT]range supplied beyond the list cap) that ran for minutes without finishing. Nothing to do with this change, but it is why the Python row is scoped tobase.Per-element, both surfaces:
'2.50'2.502.502.50'03.500'3.5003.5003.500'0.50'0.500.500.50'2.50E+2'2.50E+22.50E+22.50E+2'1.0e-3'1.0e-31.0e-31.0e-3'+4.25'+4.25+4.254.25'02','003','+7'2,3,72,3,7'+2.50'+2.502.502.50'03.500'03.5003.5003.500'2.50E+2'2.50E+22502.50E+2'1.0e-3'1.0e-30.00101.0e-3'+7'777The fixture pins the range rows only. The default rows are unpinned by any fixture and are filed against the implementations rather than widened into this PR: openjd-rs applies no §7.5 rule 1 at all on that surface (
coerce_from_str,create_job/parameters.rs), and Python renders it withstr(Decimal(...))semantics, which keep significant digits but not the written notation.CI will fail until both implementations release
Both conformance workflows install released versions (
pip install openjd-cli,cargo install openjd-cli), so this fixture fails there until the+fix ships in each. Released Python 0.11.6 additionally predates openjd-model-for-python#345 and fails on the leading-zero side too.One unrelated failure appears in the Python job on Windows,
3.4--path-parameter. It fails onmainlinetoo, so it is a pre-existing baseline failure and not a regression from this PR.Also left out
The sign of a zero. Both implementations render zero's sign away but differ on whether the decimal places survive it, so §7.5 says the case is unspecified rather than guessing.
Related
Float64::with_strguard that was dropping the decimal places of any zero.3.4.1.1and3.4.1.2.3.4.1.1(INT) agrees with §7.5 as written.3.4.1.2assertsW:2.5from'02.50'and needs to becomeW:2.50, with a terminator so substring matching cannot hide it. Flagging rather than editing that branch from here.