on master help sign in
Initial commit
commit521bb8f68a12b3a4f55a69a242ebfcc2f17bccd1 authorBryan O'Sullivan <bos@mailrank.com> dateFri 21 Jan 2011, 13:40 (16 years ago)
7 files changed+387
.hgignore+20 Data/Aeson.hs+12 Data/Aeson/Parser.hs+20 Data/Aeson/Parser/Internal.hs+95 Data/Aeson/Types.hs+160 LICENSE+30 aeson.cabal+50
.hgignorenew file+20
@@ -0,0 +1,20 @@
1+^dist$
2+benchmarks/Arse
3+benchmarks/med.txt
4+benchmarks/tiny
5+
6+syntax: glob
7+*.aux
8+*.hi
9+*.hp
10+*.o
11+*.orig
12+*.out
13+*.pdf
14+*.prof
15+*.ps
16+*.rej
17+*~
18+.*.swp
19+.\#*
20+\#*
Data/Aeson.hsnew file+12
@@ -0,0 +1,12 @@
1+module Data.Aeson
2+ (
3+ Array
4+ , Object
5+ , Value(..)
6+ , JSON(..)
7+ , (.=)
8+ , (.:)
9+ , object
10+ ) where
11+
12+import Data.Aeson.Types
Data/Aeson/Parser.hsnew file+20
@@ -0,0 +1,20 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module Data.Aeson.Parser
4+ (
5+ json
6+ ) where
7+
8+import Control.Applicative as A
9+import Data.Attoparsec.Char8
10+import Data.Aeson.Types (Value(..))
11+import Data.Aeson.Parser.Internal (array, object)
12+
13+json :: Parser Value
14+json = do
15+ skipSpace
16+ c <- anyChar
17+ case c of
18+ '{' -> skipSpace *> object
19+ '[' -> skipSpace *> array
20+ _ -> fail "root value is not an object or array"
Data/Aeson/Parser/Internal.hsnew file+95
@@ -0,0 +1,95 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module Data.Aeson.Parser.Internal
4+ (
5+ value
6+ , object
7+ , array
8+ ) where
9+
10+import Control.Applicative as A
11+import Data.Attoparsec.Char8
12+import Data.Bits (shiftL)
13+import Data.ByteString as B
14+import Data.Char (chr)
15+import Data.Map as Map
16+import Data.Text as T
17+import Data.Text.Encoding (decodeUtf8, encodeUtf8)
18+import Data.Vector as Vector hiding ((++))
19+import Data.Word (Word8)
20+import qualified Data.Attoparsec as A
21+import Data.Aeson.Types (Value(..))
22+
23+object :: Parser Value
24+object = do
25+ let pair = liftA2 (,) (jstring <* skipSpace) (char8 ':' *> skipSpace *> value)
26+ vals <- (pair <* skipSpace) `sepBy` (char8 ',' *> skipSpace)
27+ _ <- char8 '}'
28+ return . Object $ Map.fromList vals
29+
30+array :: Parser Value
31+array = do
32+ vals <- (value <* skipSpace) `sepBy` (char8 ',' *> skipSpace)
33+ _ <- char8 ']'
34+ return . Array $ Vector.fromList vals
35+
36+value :: Parser Value
37+value = most <|> (Number <$> double)
38+ where
39+ most = do
40+ c <- anyChar
41+ case c of
42+ '{' -> skipSpace *> object
43+ '[' -> skipSpace *> array
44+ '"' -> String <$> jstring_
45+ 'f' -> string "alse" *> pure (Bool False)
46+ 't' -> string "rue" *> pure (Bool True)
47+ 'n' -> string "ull" *> pure Null
48+ _ -> A.empty
49+
50+doubleQuote :: Word8
51+doubleQuote = 34
52+
53+jstring :: Parser Text
54+jstring = A.word8 doubleQuote *> jstring_
55+
56+-- | Parse a string without a leading quote.
57+jstring_ :: Parser Text
58+jstring_ = do
59+ let backslash = 92
60+ s <- A.scan False $ \s c -> if s then Just False
61+ else if c == doubleQuote
62+ then Nothing
63+ else Just (c == backslash)
64+ _ <- A.word8 doubleQuote
65+ (decodeUtf8 . B.concat) <$> reparse unescape s
66+
67+reparse :: Parser a -> ByteString -> Parser a
68+reparse p s = case (case parse p s of {Partial k -> k ""; r -> r}) of
69+ Done "" r -> return r
70+ Fail _ _ msg -> fail msg
71+ _ -> fail "unexpected failure"
72+
73+unescape :: Parser [ByteString]
74+unescape = do
75+ let backslash = 92
76+ h <- A.takeWhile (/=backslash)
77+ let rest = do
78+ w <- A.word8 backslash *> A.satisfy (`B.elem` "\"\\/ntbrfu")
79+ case B.findIndex (==w) "\"\\/ntbrf" of
80+ Just i ->
81+ ([h,B.singleton $ B.index "\"\\/\n\t\b\r\f" i]++) <$> unescape
82+ Nothing -> do
83+ a <- reparse hexadecimal =<< A.take 4
84+ if a < 0xd800 || a > 0xdfff
85+ then ([h,encodeUtf8 . T.singleton . chr $ a]++) <$> unescape
86+ else do
87+ _ <- string "\\u"
88+ b <- reparse hexadecimal =<< A.take 4
89+ if a <= 0xdbff && b >= 0xdc00 && b <= 0xdfff
90+ then do
91+ let c = ((a - 0xd800) `shiftL` 10) + (b - 0xdc00) +
92+ 0x10000
93+ ([h,encodeUtf8 . T.singleton . chr $ c]++) <$> unescape
94+ else fail "invalid UTF-16 surrogates"
95+ rest <|> return [h]
Data/Aeson/Types.hsnew file+160
@@ -0,0 +1,160 @@
1+module Data.Aeson.Types
2+ (
3+ Array
4+ , Object
5+ , (.=)
6+ , (.:)
7+ , object
8+ , Value(..)
9+ , JSON(..)
10+ ) where
11+
12+import Control.Applicative
13+import Data.ByteString (ByteString)
14+import Data.Map (Map)
15+import Data.Text (Text, pack, unpack)
16+import Data.Text.Encoding (decodeUtf8, encodeUtf8)
17+import Data.Time.Clock (UTCTime)
18+import Data.Time.Format (formatTime, parseTime)
19+import Data.Vector (Vector)
20+import System.Locale (defaultTimeLocale)
21+import qualified Data.Map as M
22+import qualified Data.Vector as V
23+
24+type Object = Map Text Value
25+type Array = Vector Value
26+
27+data Value = Object Object
28+ | Array Array
29+ | String Text
30+ | Number Double
31+ | Bool !Bool
32+ | Null
33+ deriving (Eq, Show)
34+
35+(.=) :: JSON a => Text -> a -> Object
36+name .= value = M.singleton name (toJSON value)
37+{-# INLINE (.=) #-}
38+
39+(.:) :: (Alternative f, JSON a) => Object -> Text -> f a
40+obj .: key = case M.lookup key obj of
41+ Nothing -> empty
42+ Just v -> fromJSON v
43+{-# INLINE (.:) #-}
44+
45+object :: [Object] -> Value
46+object = Object . M.unions
47+{-# INLINE object #-}
48+
49+class JSON a where
50+ toJSON :: a -> Value
51+ fromJSON :: Alternative f => Value -> f a
52+
53+instance (JSON a) => JSON (Maybe a) where
54+ toJSON (Just a) = toJSON a
55+ toJSON Nothing = Null
56+ {-# INLINE toJSON #-}
57+
58+ fromJSON Null = pure Nothing
59+ fromJSON a = Just <$> fromJSON a
60+ {-# INLINE fromJSON #-}
61+
62+instance JSON Bool where
63+ toJSON = Bool
64+ {-# INLINE toJSON #-}
65+
66+ fromJSON (Bool b) = pure b
67+ fromJSON _ = empty
68+ {-# INLINE fromJSON #-}
69+
70+instance JSON Double where
71+ toJSON = Number
72+ {-# INLINE toJSON #-}
73+
74+ fromJSON (Number n) = pure n
75+ fromJSON _ = empty
76+ {-# INLINE fromJSON #-}
77+
78+instance JSON Int where
79+ toJSON = Number . fromIntegral
80+ {-# INLINE toJSON #-}
81+
82+ fromJSON (Number n) = pure (floor n)
83+ fromJSON _ = empty
84+ {-# INLINE fromJSON #-}
85+
86+instance JSON Text where
87+ toJSON = String
88+ {-# INLINE toJSON #-}
89+
90+ fromJSON (String t) = pure t
91+ fromJSON _ = empty
92+ {-# INLINE fromJSON #-}
93+
94+instance JSON ByteString where
95+ toJSON = String . decodeUtf8
96+
97+ fromJSON (String t) = pure (encodeUtf8 t)
98+ fromJSON _ = empty
99+
100+mapA :: (Applicative f) => (t -> f a) -> [t] -> f [a]
101+mapA f = go
102+ where
103+ go (a:as) = (:) <$> f a <*> go as
104+ go [] = pure []
105+
106+instance (JSON a) => JSON [a] where
107+ toJSON = Array . V.fromList . map toJSON
108+ {-# INLINE toJSON #-}
109+
110+ fromJSON (Array a) = mapA fromJSON (V.toList a)
111+ fromJSON _ = empty
112+ {-# INLINE fromJSON #-}
113+
114+instance (JSON a) => JSON (Vector a) where
115+ toJSON = Array . V.map toJSON
116+ {-# INLINE toJSON #-}
117+
118+ fromJSON (Array a) = V.fromList <$> mapA fromJSON (V.toList a)
119+ fromJSON _ = empty
120+ {-# INLINE fromJSON #-}
121+
122+instance JSON Value where
123+ toJSON a = a
124+ {-# INLINE toJSON #-}
125+
126+ fromJSON a = pure a
127+ {-# INLINE fromJSON #-}
128+
129+-- We happen to use the same JSON formatting for a UTCTime as .NET
130+-- does for a DateTime. How handy!
131+instance JSON UTCTime where
132+ toJSON t = String (pack (formatTime defaultTimeLocale "/Date(%s)/" t))
133+ {-# INLINE toJSON #-}
134+
135+ fromJSON (String t) =
136+ case parseTime defaultTimeLocale "/Date(%s)/" (unpack t) of
137+ Just d -> pure d
138+ _ -> empty
139+ fromJSON _ = empty
140+ {-# INLINE fromJSON #-}
141+
142+instance (JSON a, JSON b) => JSON (a,b) where
143+ toJSON (a,b) = toJSON [toJSON a, toJSON b]
144+ {-# INLINE toJSON #-}
145+
146+ fromJSON (Array ab) = case V.toList ab of
147+ [a,b] -> (,) <$> fromJSON a <*> fromJSON b
148+ _ -> empty
149+ fromJSON _ = empty
150+ {-# INLINE fromJSON #-}
151+
152+
153+
154+
155+
156+
157+
158+
159+
160+
LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright (c) 2011, MailRank, Inc.
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions
7+are met:
8+
9+1. Redistributions of source code must retain the above copyright
10+ notice, this list of conditions and the following disclaimer.
11+
12+2. Redistributions in binary form must reproduce the above copyright
13+ notice, this list of conditions and the following disclaimer in the
14+ documentation and/or other materials provided with the distribution.
15+
16+3. Neither the name of the author nor the names of his contributors
17+ may be used to endorse or promote products derived from this software
18+ without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
21+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
24+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30+POSSIBILITY OF SUCH DAMAGE.
aeson.cabalnew file+50
@@ -0,0 +1,50 @@
1+name: aeson
2+version: 0.0.0.0
3+license: BSD3
4+license-file: LICENSE
5+category: Text, Web, JSON
6+copyright: Copyright 2011 MailRank, Inc.
7+author: Bryan O'Sullivan <bos@mailrank.com>
8+maintainer: Bryan O'Sullivan <bos@mailrank.com>
9+stability: experimental
10+tested-with: GHC == 6.12.3
11+synopsis: Fast JSON parsing and generation
12+cabal-version: >= 1.8
13+homepage: http://github.com/mailrank/aeson
14+bug-reports: http://github.com/mailrank/aeson/issues
15+build-type: Simple
16+description:
17+ A fast JSON parsing and generation library.
18+extra-source-files:
19+ README.markdown
20+
21+flag developer
22+ description: operate in developer mode
23+ default: False
24+
25+library
26+ exposed-modules:
27+ Data.Aeson
28+ Data.Aeson.Parser
29+ Data.Aeson.Parser.Internal
30+ Data.Aeson.Types
31+
32+ build-depends:
33+ attoparsec >= 0.8.4.0,
34+ base == 4.*,
35+ bytestring,
36+ containers == 0.3.*,
37+ old-locale,
38+ text >= 0.11.0.0,
39+ time,
40+ vector >= 0.7
41+
42+ if flag(developer)
43+ ghc-options: -Werror
44+
45+ ghc-options: -Wall
46+ ghc-prof-options: -auto-all
47+
48+source-repository head
49+ type: git
50+ location: http://github.com/mailrank/aeson