packages feed

AttoJson (empty) → 0.5

raw patch · 4 files changed

+301/−0 lines, 4 filesdep +attoparsecdep +basedep +bytestringsetup-changed

Dependencies added: attoparsec, base, bytestring, bytestring-show, containers, mtl, utf8-string

Files

+ AttoJson.cabal view
@@ -0,0 +1,64 @@+-- AttoJson.cabal auto-generated by cabal init. For additional+-- options, see+-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.+-- The name of the package.+Name:                AttoJson++-- The package version. See the Haskell package versioning policy+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for+-- standards guiding when and how versions should be incremented.+Version:             0.5++-- A short (one-line) description of the package.+Synopsis:            Simple lightweight JSON parser based on ByteString++-- A longer description of the package.+Description: Simple Lightweight JSON parser based on ByteString++-- URL for the project homepage or repository.+Homepage:            http://github.com/konn/AttoJSON++-- The license under which the package is released.+License:             BSD3++-- The file containing the license text.+License-file:        LICENSE++-- The package author(s).+Author:              Hiromi ISHII++-- An email address to which users can send suggestions, bug reports,+-- and patches.+Maintainer:          mr_konn _at_ jcom.home.ne.jp++-- A copyright notice.+-- Copyright:           ++Category:            Text++Build-type:          Simple++-- Extra files to be distributed with the package, such as examples or+-- a README.+-- Extra-source-files:  ++-- Constraint on the version of Cabal needed to build this package.+Cabal-version:       >=1.6++source-repository head+  type: git+  location: http://github.com/konn/AttoJSON/tree/master++Library+  -- Modules exported by the library.+  Exposed-modules:     Text.JSON.AttoJSON+  +  -- Packages needed in order to build this package.+  Build-depends: containers >=0.1, base >= 4 && < 5, bytestring-show >= 0.1, utf8-string >= 0.1, attoparsec >= 0.8, bytestring >= 0.9, mtl >= 1.0+  +  -- Modules not exported by this package.+  -- Other-modules:       +  +  -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.+  -- Build-tools:         +  
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2010, Hiromi ISHII++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Hiromi ISHII nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Text/JSON/AttoJSON.hs view
@@ -0,0 +1,205 @@+{-# LANGUAGE OverloadedStrings, FlexibleInstances, UndecidableInstances, TypeSynonymInstances #-}+{-# OPTIONS_GHC -fwarn-unused-imports #-}+module Text.JSON.AttoJSON (JSValue (..), getField, getFields, JSON(..), parseJSON, readJSON, showJSON) where+import Control.Applicative hiding (many)+import qualified Data.ByteString.Lazy as L (unpack)+import Control.Monad.Identity (runIdentity)+import Data.ByteString ( append, ByteString, replicate, unfoldr, take, reverse+                       , singleton, pack, intercalate, concat, unpack+                       )+import Data.Attoparsec ( maybeResult, parse, string, try, Parser, endOfInput+                       , parseWith, eitherResult, inClass, many, count, sepBy+                       , satisfy, option +                       )+import Data.Attoparsec.Char8 (char8, decimal, skipSpace, signed)+import Prelude hiding (concatMap, reverse, replicate, take, Show(..), concat)+import qualified Prelude as P (Show, reverse, map)+import Data.Foldable (foldrM)+import Data.Bits (shiftR, (.&.))+import Codec.Binary.UTF8.String (encode, decode)+import Data.Word (Word8)+import Text.Show.ByteString (show)+import Data.Ratio (denominator, numerator)+import Numeric (readHex)+import Data.Map (Map, fromList, elems, mapWithKey)+import qualified Data.Map as M (lookup)++fromLazy = pack . L.unpack++data JSValue = JSString {fromJSString :: !ByteString}+             | JSNumber Rational+             | JSObject (Map ByteString JSValue)+             | JSArray [JSValue]+             | JSBool !Bool+             | JSNull deriving (P.Show, Eq)++getField :: JSON a => ByteString -> JSValue -> Maybe a+getField key (JSObject dic) = M.lookup key dic >>= fromJSON+getField _ _                = Nothing++getFields :: JSON a => [ByteString] -> JSValue -> Maybe a+getFields keys jso = fromJSON =<< foldrM getField jso (P.reverse keys)++class JSON a where+    fromJSON :: JSValue -> Maybe a+    toJSON   :: a -> JSValue++instance JSON JSValue where+    fromJSON = return+    toJSON   = id++instance JSON Int where+    fromJSON (JSNumber a) = fromInteger <$> fromJSON (JSNumber a)+    fromJSON (JSString a) = maybeResult $ parse (signed decimal) a+    fromJSON _            = Nothing+    toJSON a              = JSNumber (fromIntegral a)++instance JSON Integer where+    fromJSON (JSNumber a) | denominator a == 1 = Just $ numerator a+                          | otherwise          = Nothing+    fromJSON (JSString a) = maybeResult $ parse (signed decimal) a+    fromJSON _            = Nothing+    toJSON a              = JSNumber (fromIntegral a)++instance JSON Double where+    fromJSON (JSNumber a) = Just $ fromRational a+    fromJSON _            = Nothing+    toJSON a              = JSNumber $ toRational a++instance JSON ByteString where+    fromJSON (JSString a) = Just a+    fromJSON _            = Nothing+    toJSON str            = JSString str++instance JSON String where+    fromJSON (JSString a) = Just $ w8sToStr $ unpack a+    fromJSON _            = Nothing+    toJSON str            = JSString $ pack $ strToW8s str++lexeme p = skipSpace *> p+symbol s = lexeme $ string s ++value :: Parser JSValue+value = jsString <|> number <|> object <|> array <|> try bool <|> try jsNull++parseJSON :: ByteString -> Either String JSValue+parseJSON = eitherResult . runIdentity . parseWith (return "") (value <* skipSpace <* endOfInput)+readJSON :: ByteString -> Maybe JSValue+readJSON = maybeResult . runIdentity . parseWith (return "") (value <* skipSpace <* endOfInput)++jsNull = lexeme (JSNull <$ symbol "null")+bool   = lexeme $+           JSBool True  <$ symbol "true"+       <|> JSBool False <$ symbol "false"+number = lexeme $ do+  sig   <- option 1 $ (-1 <$ char8 '-')+  int   <- decimal+  float <- option 0 frac+  pow  <- option 1 power+  return $ JSNumber (sig*(fromIntegral int+float)*pow)++frac = do+  char8 '.'+  dgs <- fromIntegral <$> decimal+  return (dgs / (10^^ceiling(logBase 10 (fromRational dgs))) )++power = do+  string "E" <|> string "e"+  sgn <- option 1 ((1 <$ char8 '+') <|> (-1 <$ char8 '-'))+  pow <- decimal+  return (10^^(sgn*pow))++array  = lexeme (symbol "[" *> (JSArray <$> (value `sepBy` (symbol ","))) <* symbol "]")++isControlChar ch = (0 <= ch && ch <= 31) || ch == 127+isNotHadaka ch = ch == 34 || ch == 92++w8sToStr :: [Word8] -> String+w8sToStr = map (toEnum . fromEnum)++strToW8s :: String -> [Word8]+strToW8s = map (toEnum . fromEnum)++chToW8 :: Char -> Word8+chToW8 = toEnum . fromEnum++w8ToCh :: Char -> Word8+w8ToCh = toEnum . fromEnum++jsString :: Parser JSValue+jsString = lexeme ( string "\"" *> (JSString . concat <$> many jsChar) <* string "\"")+jsChar :: Parser ByteString+jsChar = singleton <$> satisfy (\a -> not (isControlChar a || isNotHadaka a))+     <|> string "\\" *> (escapeChar <|> hex4digit)++escapeChar :: Parser ByteString+escapeChar = string "\"" +         <|> string "\\" +         <|> "\b" <$ char8 'b'+         <|> "\f" <$ char8 'f'+         <|> "/"  <$ char8 '/'+         <|> "\n" <$ char8 'n'+         <|> "\r" <$ char8 'r'+         <|> "\t" <$ char8 't'++hex4digit :: Parser ByteString+hex4digit = do+  string "u"+  ((hex, _):_) <- readHex . w8sToStr <$> count 4 (satisfy $ inClass "0-9a-zA-Z")+  return $ pack $ encode $ [toEnum hex]+object = lexeme ( symbol "{" *> (JSObject . fromList <$> (objMember `sepBy` symbol ",")) <* symbol "}")++objMember :: Parser (ByteString, JSValue)+objMember = lexeme ((,) <$> (fromJSString <$> jsString) <*> (symbol ":" *> value))++showJSON :: JSValue -> ByteString+showJSON (JSObject dic) = "{" `append` intercalate "," mems `append` "}"+  where+    mems = elems $ mapWithKey (\k v -> showJSString k `append` ":" `append` showJSON v) dic+showJSON (JSString jss)     = showJSString jss+showJSON (JSNumber jsn) | denominator jsn == 1 = fromLazy $ show $ numerator jsn+                        | otherwise            = fromLazy $ show (fromRational jsn :: Double)+showJSON (JSArray jss)      = "[" `append` intercalate ", " (P.map showJSON jss) `append` "]"+showJSON (JSNull)           = "null"+showJSON (JSBool True)      = "true"+showJSON (JSBool False)     = "false"++showJSString :: ByteString -> ByteString+showJSString js = "\"" `append` escape js `append` "\""+  where+    escape :: ByteString -> ByteString+    escapeCh :: Char -> ByteString+    escape = concat . P.map escapeCh . decode . unpack+    escapeCh '\\' = "\\\\"+    escapeCh '"'  = "\\\""+    escapeCh '\b' = "\\b"+    escapeCh '\f' = "\\f"+    escapeCh '/'  = "\\/"+    escapeCh '\n' = "\\n"+    escapeCh '\r' = "\\r"+    escapeCh '\t' = "\\t"+    escapeCh ch | mustEscape ch    = escapeHex $ fromEnum ch+                | otherwise        = singleton $ chToW8 ch++unfoldrStep :: (a -> Bool) -> (a -> Word8) -> (a -> a) -> a -> Maybe (Word8, a)+unfoldrStep p f g a | p a       = Nothing+                    | otherwise = Just (f a, g a)++mustEscape ch = ch < ' ' || ch == '\x7f' || ch > '\xff'++escapeHex :: Int -> ByteString+escapeHex c | c < 0x10000 = show4Hex c+            | otherwise   = astral c++show4Hex :: Int -> ByteString+show4Hex = ("\\u" `append`) . reverse . take 4 . (`append` replicate 4 48) . unfoldr (unfoldrStep (==0) (toHexWord8 .(`mod` 16)) (`div` 16)) ++astral :: Int -> ByteString+astral n = show4Hex (a + 0xd800) `append` show4Hex (b + 0xdc00)+  where+    a = (n `shiftR` 10) .&. 0x3ff+    b = n .&. 0x3ff++toHexWord8 :: Int -> Word8+toHexWord8 d | 0 <= d && d <= 9 = 48 + toEnum d+             | 10 <= d          = 87 + toEnum d