fix: parse YAML numeric literals invariantly [patch] - #74
Merged
Merged
Conversation
YamlDeserializer parsed Int32 and Double literals with the TryParse overloads that read against Thread.CurrentThread.CurrentCulture, while YamlSerializer writes them through YamlDotNet's scalar emitter, which is always dot-decimal. On a culture that groups with "." and separates decimals with "," — de-DE, fr-FR, es-ES, it-IT, ru-RU, pl-PL, nl-NL and the rest of continental Europe — double.TryParse's default NumberStyles of Float | AllowThousands read "3.14" as 314. No error was raised: the literal was silently corrupted, and round-tripping a .coder.yaml file changed the behaviour of the program it described. Pass NumberStyles and CultureInfo.InvariantCulture at both sites, matching the pattern the generators and the inspector already use (LanguageGeneratorBase, CSharpGenerator, AstFields.AssignInteger). bool.TryParse is left alone: it accepts only "true"/"false" and has no culture-sensitive overload. Fixes #73 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B1XQbfXNGfPRfGr4bRjsT3
MSTEST0046, flagged by SonarCloud on the new test file. StringAssert is superseded in MSTest 4; Assert.Contains takes the substring first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B1XQbfXNGfPRfGr4bRjsT3
|
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.



Fixes #73
What was wrong
YamlSerializerwrites numeric literals through YamlDotNet's scalar emitter, which is always invariant and dot-decimal (3.14).YamlDeserializerread them back with theTryParseoverloads that take no culture, so they parsed againstThread.CurrentThread.CurrentCulture.double.TryParse(string, out double)defaults toNumberStyles.Float | AllowThousands. On a culture where.is the group separator and,the decimal separator — de-DE, fr-FR, es-ES, it-IT, ru-RU, pl-PL, nl-NL and the rest of continental Europe —"3.14"parses as 314. Not an error, not a dropped node: a silently wrong value in the loaded AST, so round-tripping a.coder.yamlfile changed the behaviour of the program it described.The change
NumberStyles+CultureInfo.InvariantCultureat the two numeric parse sites inYamlDeserializer.cs:DeserializeLeafNode—Int32DeserializeLiteralExpression—Int32andDoubleThis matches the pattern the rest of the codebase already uses for source values (
LanguageGeneratorBase.cs:125,133,CSharpGenerator.cs:100,106,AstFields.AssignInteger).bool.TryParseis deliberately left alone. The issue suggested passing a culture there too, butbool.TryParseaccepts only"true"/"false"(case- and whitespace-insensitive) and has no culture-sensitive overload — it is already invariant by construction. The remainingTryParsecalls in the file areEnum.TryParse, which is likewise culture-independent.Test
New
Coder.Test/Serialization/CultureInvariantDeserializationTests.cs, covering the fraction round trip the issue's acceptance criteria asks for, plus negative fractions, a fraction nested in a function body, and integer/boolean contract guards.Two notes on how it is built:
new CultureInfo("de-DE")throwsCultureNotFoundException. The test instead clonesInvariantCultureand setsNumberDecimalSeparator = ","/NumberGroupSeparator = ".", which is the exact pairing that causes the corruption. That also keeps the test hermetic — it pins a fixed number format rather than whatever ICU data the host carries.LongRunningtask, so the mutated culture dies with its dedicated thread instead of being inherited by a pool thread later.Verification
Reverting
YamlDeserializer.cswhile keeping the new test file:With the fix restored: 6/6 pass, and the full suite is 867/867 green.
dotnet build Coder.slnsucceeds with no warnings across both target frameworks.To be straight about coverage: only the three
doublecases demonstrably regress today. TheInt32andBooleantests pass either way —int.TryParseis not currently broken by any culture .NET ships, since it special-cases the ASCII hyphen even where the culture's negative sign differs. They are kept as contract guards for the invariant round trip, not presented as reproductions.🤖 Generated with Claude Code
https://claude.ai/code/session_01B1XQbfXNGfPRfGr4bRjsT3
Generated by Claude Code