What's wrong
LanguageGeneratorBase.FormatDouble (Coder/Languages/LanguageGeneratorBase.cs) formats with ToString("R") and, as its remarks say, leaves a non-finite value "as it is". CSharpGenerator writes {value.ToString(InvariantCulture)}d (Coder/Languages/CSharpGenerator.cs:105-106). Neither maps NaN or infinity to a spelling the target language understands.
Repro
gen.Generate(Literal.DecimalValue(double.NaN)), and the same for PositiveInfinity and NegativeInfinity:
| Target |
Emitted |
Result |
| C# |
NaNd, Infinityd, -Infinityd |
not a valid token |
| C / C++ |
NaN, Infinity |
gcc: 'NaN' undeclared |
| Go |
NaN |
undefined: NaN |
| Rust |
NaN |
E0425 cannot find value NaN |
| Python |
NaN |
NameError: name 'NaN' is not defined |
| JavaScript |
NaN, Infinity |
correct |
These results come from compiling the generated text with each real toolchain.
Why it matters
NaN and infinity are common sentinel and default values, for example const double Unset = NaN or an initial min = Infinity. Any AST that contains one produces code that fails to compile in 6 of the 7 targets, and nothing warns about it at generation time.
Suggested fix
Add a per-language hook for non-finite values, such as a virtual FormatDouble or a FormatNonFinite(double):
| Language |
NaN |
+Inf / −Inf |
| C# |
double.NaN |
double.PositiveInfinity / double.NegativeInfinity |
| C++ |
std::numeric_limits<double>::quiet_NaN() |
std::numeric_limits<double>::infinity() (negated) |
| C |
NAN |
INFINITY / -INFINITY (needs <math.h>) |
| Go |
math.NaN() |
math.Inf(1) / math.Inf(-1) (needs import "math") |
| Rust |
f64::NAN |
f64::INFINITY / f64::NEG_INFINITY |
| Python |
float("nan") |
float("inf") / float("-inf") |
| JavaScript |
NaN |
Infinity / -Infinity |
The C and Go spellings also need an include or import. If the generators can't add one yet, they should throw NotSupportedException rather than emit an undefined name.
Acceptance: a data-driven test covers NaN, +∞ and −∞ for each generator.
What's wrong
LanguageGeneratorBase.FormatDouble(Coder/Languages/LanguageGeneratorBase.cs) formats withToString("R")and, as its remarks say, leaves a non-finite value "as it is".CSharpGeneratorwrites{value.ToString(InvariantCulture)}d(Coder/Languages/CSharpGenerator.cs:105-106). Neither maps NaN or infinity to a spelling the target language understands.Repro
gen.Generate(Literal.DecimalValue(double.NaN)), and the same forPositiveInfinityandNegativeInfinity:NaNd,Infinityd,-InfinitydNaN,Infinity'NaN' undeclaredNaNundefined: NaNNaNE0425 cannot find value NaNNaNNameError: name 'NaN' is not definedNaN,InfinityThese results come from compiling the generated text with each real toolchain.
Why it matters
NaN and infinity are common sentinel and default values, for example
const double Unset = NaNor an initialmin = Infinity. Any AST that contains one produces code that fails to compile in 6 of the 7 targets, and nothing warns about it at generation time.Suggested fix
Add a per-language hook for non-finite values, such as a virtual
FormatDoubleor aFormatNonFinite(double):double.NaNdouble.PositiveInfinity/double.NegativeInfinitystd::numeric_limits<double>::quiet_NaN()std::numeric_limits<double>::infinity()(negated)NANINFINITY/-INFINITY(needs<math.h>)math.NaN()math.Inf(1)/math.Inf(-1)(needsimport "math")f64::NANf64::INFINITY/f64::NEG_INFINITYfloat("nan")float("inf")/float("-inf")NaNInfinity/-InfinityThe C and Go spellings also need an include or import. If the generators can't add one yet, they should throw
NotSupportedExceptionrather than emit an undefined name.Acceptance: a data-driven test covers NaN, +∞ and −∞ for each generator.