Give NaN a token, so its printed form reads back (#906) - #910
Merged
Conversation
Stringize prints the NaN value as NaN, the grammar had no such token, so reading it back gave a
variable of that name -- which then behaved like any symbol. NaN - NaN simplified to 0,
NaN / NaN to `1 provided not NaN = 0`, and NaN * 0 to 0, each of them the ordinary rewrite for a
free variable. Nothing on the page distinguished the two, because a variable named NaN prints as
NaN as well.
Its two siblings already had tokens -- +oo and -oo both print and parse -- and Latexize has
emitted \mathrm{undefined} all along, which is the token CSharpMath decodes back to MathS.NaN, so
the LaTeX round trip was already closed and is untouched. Verified in CSharpMath's own source
rather than assumed: Evaluation.cs has
`case ("undefined", 0, FontStyle.Roman): @this = MathS.NaN; break;`.
Declared as a lexer rule above VARIABLE rather than as an inline literal, which is where BOOLEAN
sits and for the same reason: equal-length matches go to the earlier rule, and this word would
otherwise be an identifier. One spelling only. BOOLEAN carries two capitalisations of each word
because it has to read back its own output -- Entity.Boolean prints True where a caller types
true -- and NaN prints and reads the same way, so there is nothing to reconcile.
Reserving the word costs the identifier, as `mod` did in 2.0, and only the exact spelling: NaNx,
NaN_1 and aNaN are still variables, since the lexer takes the longest match.
The round trip is now tested in both directions. Every case in StringizeRoundTripTest began from a
string, so it could only reach expressions the parser already produces, and a value with no source
form was invisible to all of them however many cases were added -- which is why this defect lived
in an output. It now also enumerates the named constants of MathS and Entity.Number.Real by
reflection, prints each and reads it back, so a constant added later is covered without anyone
remembering the file exists. MathS.NaN and Real.NaN are the two that fail against the old grammar.
Regenerated with the committed antlr-4.13.1 jar and the post-processor. The unmodified grammar was
regenerated first and its diff confirmed empty, so nothing in the generated files here is a
toolchain difference. Syntax.md lists the new literal.
New tests fail 9 of 219 against master and pass 219 of 219 here. Suite 6451 passed, F# wrapper 130
passed; casbench 117/119 with 0 wrong; rootcheck 596/596; simpsweep 10463/10463; propcheck 1340
checks 0 failures; crashcheck 1652 cases 0 crashes; boundcheck 2 disagreements and 0 that did not
parse; docsamples 0 compile errors, its one output mismatch being the Solvers wiki page that #901
made stale.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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.
Closes #906, and takes the option both comments there converged on:
NaNbecomes a reserved word, likethe built-in function names.
"NaN - NaN".Simplify()0NaN"NaN / NaN".Simplify()1 provided not NaN = 0NaN"NaN * 0".Evaled0NaN"NaN * 2".Evaled,"NaN + NaN".EvaledNaNNaNas a variable nameNaNx,NaN_1,aNaNEvery "was" in that table is the ordinary rewrite for a free variable, which is what
NaNparsed to.Nothing on the page said so, because a variable named
NaNprints asNaNtoo.Latexizeneeded nothing, and that is measured rather than assumed@Happypig375's hedge in the issue — "CSharpMath uses
\mathrm{undefined}as the special token here, Ithink" — is right, in both directions. AngouriMath has emitted
\mathrm{undefined}for NaN all along(
Latex.Number.Classes.cs), and CSharpMath'sEvaluation.cscarriesso the LaTeX round trip was already closed. This PR does not touch
Latexize. Its three non-finitevalues (
\infty,-\infty,\mathrm{undefined}) were all fine; it wasStringizethat printed twonames the parser knew and one it did not.
The grammar choice
Declared as a lexer rule above
VARIABLE, which is whereBOOLEANsits and for the same reason:equal-length matches go to the earlier rule, and this word would otherwise be an identifier.
+ooand-ooare inline literals instead, which is safe for them because they cannot collide with a name.One spelling,
NaN.BOOLEANaccepts two capitalisations of each word because it has to read backits own output —
Entity.BooleanprintsTruewhere a caller typestrue— andNaNprints and readsthe same way, so there is nothing to reconcile and no reason to enlarge the reserved surface.
Reserving the word costs the identifier, exactly as
moddid in 2.0, and only the exact spelling: thelexer takes the longest match, so
NaNx,NaN_1andaNaNare all still variables.Syntax.mdliststhe new literal.
The round-trip test now runs in both directions
This is the part that catches the next one. Every case in
StringizeRoundTripTestbegan from astring, so it could only reach expressions the parser already produces — a value with no source form
was invisible to all of them, however many cases were added. That is why the defect lived in an output
for as long as it did.
It now also enumerates the named constants of
MathSandEntity.Number.Realby reflection, printseach and reads it back, so a constant added later is covered without anyone remembering the file exists.
MathS.NaNandReal.NaNare the two that fail against the old grammar, along with the computed cases(
0/0,1/0,+oo - +oo).Regeneration discipline
Regenerated with the committed
antlr-4.13.1-complete.jarand theAntlrPostProcessorReplacePublicWithInternalstep. The unmodified grammar was regenerated first and its diff confirmed empty, so nothing in the
generated files here is a toolchain difference rather than my rule.
Measured
New tests fail 9 of 219 against
masterand pass 219 of 219 here. Suite 6451 passed / 0 failed, F#wrapper 130 passed. casbench 117/119 with 0 wrong, rootcheck 596/596, simpsweep 10463/10463, propcheck
1340 checks with 0 failures, crashcheck 1652 cases with 0 crashes, boundcheck 2 disagreements and 0
that did not parse — worth naming for a grammar change.
docsampleshas 0 compile errors; its singleoutput mismatch is the Solvers wiki page that #901 made stale, which I am updating separately.
One more of the same shape, filed rather than bundled:
ToSympyfor aRealis=> Stringize(), so itemits
NaN,+ooand-oointo generated Python whose preamble is onlyimport sympy— none of thosenames is bound there. That is #909.
Cut from
masterat7c3c01ab.