Format numbers per the XPath 1.0 string() rules - #125
Merged
Conversation
strconv.FormatFloat(_, 'g', -1) emits scientific notation and Go's
+Inf/-Inf/-0 tokens, but XPath 1.0 (REC 4.2) requires finite numbers in
decimal form with no exponent, "Infinity"/"-Infinity" for the
infinities, "NaN", and "0" for negative zero. Add formatNumber
following that rule and use it in asString.
concat also silently dropped non-string arguments: its type switch only
handled string and node-set, so concat('id-', 5, '-x') returned 'id--x'.
Route each argument through asString so number and boolean arguments are
stringified the same way string() converts them.
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.
string()on a number — and every other place a number is coerced to text — goes throughasString, which usesstrconv.FormatFloat(v, 'g', -1, 64). Go's'g'verb switches to scientific notation whenever that is shorter and prints Go's own+Inf/-Inf/-0tokens, none of which are valid XPath. The XPath 1.0 REC §4.2 defines an exact algorithm: finite numbers render in decimal form with no exponent, the infinities becomeInfinity/-Infinity, NaN becomesNaN, and negative zero becomes0.concathas the same root cause from the other side — its type switch only handledstringand node-set arguments, so a numeric or boolean argument was silently dropped.Before:
Fix: add
formatNumberimplementing the §4.2 rule and use it inasString; route eachconcatargument throughasStringso all argument types are stringified exactly asstring()converts them.I checked the output against libxml2 (via lxml) across the common range of magnitudes and they now agree. Two notes on scope:
string(1e20)->1e+20), which §4.2 does not permit. This change follows §4.2 and always emits decimal form (100000000000000000000), so the spec arbitrates that range rather than libxml2.string(2 div 3)stays0.6666666666666666).FormatFloat(_, 'f', -1, 64)already emits the shortest round-tripping representation, which is what §4.2 asks for ("as many digits as are needed to uniquely distinguish the number").Regression cases added to
Test_func_stringandTest_func_concat; the full suite passes. The xmllint property tests in #114 are a complementary differential check, but their generators use integer-only literals and string/path arguments, so these particular edges aren't currently exercised.