on master help sign in
Merge pull request #68 from bitonic/fix-number-parsing

Parse `Scientific` directly, avoiding loss in precision.

commiteb8b0c7335a24df1959ee9e8296b4dbf584542b2 authorMichael Snoyman <michael@snoyman.com> dateSun 6 Sep 2015, 08:20 (11 years ago) parentf28149e056b465
3 files changed+32−5
Data/Yaml/Internal.hs+8−4 test/Data/YamlSpec.hs+23 yaml.cabal+1−1
Data/Yaml/Internal.hs+8−4
@@ -37,10 +37,11 @@ import Data.Text.Encoding (decodeUtf8With)
3737import Data.Text.Encoding.Error (lenientDecode)
3838import qualified Data.HashMap.Strict as M
3939import Data.Typeable
40−import Data.Text.Read
4140#if MIN_VERSION_aeson(0, 7, 0)
42−import Data.Scientific (fromFloatDigits)
41+import qualified Data.Attoparsec.Text as Atto
42+import Data.Scientific (Scientific)
4343#else
44+import Data.Text.Read
4445import Data.Attoparsec.Number
4546#endif
4647import Control.Monad.Trans.Resource (ResourceT, runResourceT)
@@ -162,8 +163,7 @@ textToValue _ _ t
162163 | any (t `isLike`) ["y", "yes", "on", "true"] = Bool True
163164 | any (t `isLike`) ["n", "no", "off", "false"] = Bool False
164165#if MIN_VERSION_aeson(0, 7, 0)
165− | Right (x, "") <- signed decimal t = Number $ fromIntegral (x :: Integer)
166− | Right (x, "") <- double t = Number $ fromFloatDigits x
166+ | Right x <- textToScientific t = Number x
167167#else
168168 | Right (x, "") <- signed decimal t = Number $ I x
169169 | Right (x, "") <- double t = Number $ D x
@@ -172,6 +172,10 @@ textToValue _ _ t
172172 where x `isLike` ref = x `elem` [ref, T.toUpper ref, titleCased]
173173 where titleCased = toUpper (T.head ref) `T.cons` T.tail ref
174174
175+#if MIN_VERSION_aeson(0, 7, 0)
176+textToScientific :: Text -> Either String Scientific
177+textToScientific = Atto.parseOnly (Atto.scientific <* Atto.endOfInput)
178+#endif
175179
176180parseO :: C.Sink Event Parse Value
177181parseO = do
test/Data/YamlSpec.hs+23
@@ -148,6 +148,10 @@ spec = do
148148 it "serialization of +123 #64" $ do
149149 D.decode (D.encode ("+123" :: String)) `shouldBe` Just ("+123" :: String)
150150
151+#if MIN_VERSION_aeson(0, 7, 0)
152+ it "preserves Scientific precision" casePreservesScientificPrecision
153+#endif
154+
151155
152156specialStrings :: [T.Text]
153157specialStrings =
@@ -465,3 +469,22 @@ caseIssue49 =
465469 ])
466470 where
467471 src = "---\na:\n &id5 value: 1.0\nb:\n *id5: 1.2"
472+
473+#if MIN_VERSION_aeson(0, 7, 0)
474+-- | We cannot guarantee this before aeson started using 'Scientific'.
475+casePreservesScientificPrecision :: Assertion
476+casePreservesScientificPrecision = do
477+ D.decodeEither "x: 1e-100000" @?= Right (object
478+ [ "x" .= D.Number (read "1e-100000") ])
479+ -- Note that this ought to work also without 'Scientific', given
480+ -- that @read (show "9.78159610558926e-5") == 9.78159610558926e-5@.
481+ -- However, it didn't work (and still doesn't work with aeson < 0.7)
482+ -- for two reasons:
483+ --
484+ -- * We use 'Data.Text.Read.double', which is not as accurate as it
485+ -- can be;
486+ -- * Even if we used 'Data.Text.Read.rational' we would not get good
487+ -- results, because of <https://github.com/bos/text/issues/34>.
488+ D.decodeEither "x: 9.78159610558926e-5" @?= Right (object
489+ [ "x" .= D.Number (read "9.78159610558926e-5") ])
490+#endif
yaml.cabal+1−1
@@ -48,7 +48,7 @@ library
4848 , unordered-containers
4949 , vector
5050 , text
51− , attoparsec
51+ , attoparsec >= 0.11.3.0
5252 , scientific
5353 , filepath
5454 , directory