on master help sign in
Merge pull request #139 from snoyberg/135-hex-oct
18639b2Michael Snoyman
ChangeLog.md+2
@@ -3,6 +3,8 @@
3## 0.8.32
3## 0.8.32
4
4
5* Escape keys as necessary [#137](https://github.com/snoyberg/yaml/issues/137)
5* Escape keys as necessary [#137](https://github.com/snoyberg/yaml/issues/137)
6+* Support hexadecimal and octal number values [#135](https://github.com/snoyberg/yaml/issues/135)
7+* More resilient `isNumeric` (should reduce cases of unneeded quoting)
6* hpackify
8* hpackify
7* src subdir
9* src subdir
8
10
src/Data/Yaml/Internal.hs+15−12
@@ -11,11 +11,13 @@ module Data.Yaml.Internal
11 , decodeHelper_
11 , decodeHelper_
12 , specialStrings
12 , specialStrings
13 , isNumeric
13 , isNumeric
14+ , textToScientific
14 ) where
15 ) where
15
16
16#if !MIN_VERSION_base(4,8,0)
17#if !MIN_VERSION_base(4,8,0)
17import Control.Applicative ((<$>), Applicative(..))
18import Control.Applicative ((<$>), Applicative(..))
18#endif
19#endif
20+import Control.Applicative ((<|>))
19import Control.Exception
21import Control.Exception
20import Control.Monad (liftM, ap, unless)
22import Control.Monad (liftM, ap, unless)
21import Control.Monad.IO.Class (MonadIO, liftIO)
23import Control.Monad.IO.Class (MonadIO, liftIO)
@@ -25,8 +27,9 @@ import Control.Monad.Trans.State
25import Data.Aeson
27import Data.Aeson
26import Data.Aeson.Types hiding (parse)
28import Data.Aeson.Types hiding (parse)
27import qualified Data.Attoparsec.Text as Atto
29import qualified Data.Attoparsec.Text as Atto
30+import Data.Bits (shiftL, (.|.))
28import Data.ByteString (ByteString)
31import Data.ByteString (ByteString)
29−import Data.Char (toUpper)
32+import Data.Char (toUpper, ord)
30import Data.Conduit ((.|), ConduitM, runConduit)
33import Data.Conduit ((.|), ConduitM, runConduit)
31import qualified Data.Conduit.List as CL
34import qualified Data.Conduit.List as CL
32import qualified Data.HashMap.Strict as M
35import qualified Data.HashMap.Strict as M
@@ -176,7 +179,16 @@ textToValue _ _ t
176 where titleCased = toUpper (T.head ref) `T.cons` T.tail ref
179 where titleCased = toUpper (T.head ref) `T.cons` T.tail ref
177
180
178textToScientific :: Text -> Either String Scientific
181textToScientific :: Text -> Either String Scientific
179−textToScientific = Atto.parseOnly (Atto.scientific <* Atto.endOfInput)
182+textToScientific = Atto.parseOnly (num <* Atto.endOfInput)
183+ where
184+ num = (fromInteger <$> ("0x" *> Atto.hexadecimal))
185+ <|> (fromInteger <$> ("0o" *> octal))
186+ <|> Atto.scientific
187+
188+ octal = T.foldl' step 0 <$> Atto.takeWhile1 isOctalDigit
189+ where
190+ isOctalDigit c = (c >= '0' && c <= '7')
191+ step a c = (a `shiftL` 3) .|. fromIntegral (ord c - 48)
180
192
181parseO :: ConduitM Event o Parse Value
193parseO :: ConduitM Event o Parse Value
182parseO = do
194parseO = do
@@ -284,13 +296,4 @@ specialStrings = HashSet.fromList $ T.words
284 "y Y yes Yes YES n N no No NO true True TRUE false False FALSE on On ON off Off OFF null Null NULL ~ *"
296 "y Y yes Yes YES n N no No NO true True TRUE false False FALSE on On ON off Off OFF null Null NULL ~ *"
285
297
286isNumeric :: Text -> Bool
298isNumeric :: Text -> Bool
287−isNumeric t =
299+isNumeric = either (const False) (const True) . textToScientific
288− T.all isNumeric' t && T.any isNumber t
289− where
290− isNumber c = '0' <= c && c <= '9'
291− isNumeric' c = isNumber c
292− || c == 'e'
293− || c == 'E'
294− || c == '.'
295− || c == '-'
296− || c == '+'
test/Data/YamlSpec.hs+15−1
@@ -29,8 +29,9 @@ import Data.Maybe
29import qualified Data.HashMap.Strict as M
29import qualified Data.HashMap.Strict as M
30import qualified Data.Text as T
30import qualified Data.Text as T
31import Data.Aeson.TH
31import Data.Aeson.TH
32+import Data.Scientific (Scientific)
32import Data.Text (Text)
33import Data.Text (Text)
33−import Data.Text.Encoding (decodeUtf8)
34+import Data.Text.Encoding (decodeUtf8, encodeUtf8)
34import Data.Vector (Vector)
35import Data.Vector (Vector)
35import qualified Data.Vector as V
36import qualified Data.Vector as V
36import Data.HashMap.Strict (HashMap)
37import Data.HashMap.Strict (HashMap)
@@ -181,6 +182,19 @@ spec = do
181 , T.unpack text
182 , T.unpack text
182 ] :: IO ()
183 ] :: IO ()
183
184
185+ describe "non-decimal numbers #135" $ do
186+ let go str val = it str $ encodeUtf8 (T.pack str) `shouldDecode` val
187+ go "12345" (12345 :: Int)
188+ go "+12345" (12345 :: Int)
189+ go "0o14" (12 :: Int)
190+ go "0o123" (83 :: Int)
191+ go "0xC" (12 :: Int)
192+ go "0xc" (12 :: Int)
193+ go "0xdeadBEEF" (3735928559 :: Int)
194+ go "0xDEADBEEF" (3735928559 :: Int)
195+ go "1.23015e+3" (1230.15 :: Scientific)
196+ go "12.3015e+02" (1230.15 :: Scientific)
197+ go "1230.15" (1230.15 :: Scientific)
184
198
185specialStrings :: [T.Text]
199specialStrings :: [T.Text]
186specialStrings =
200specialStrings =