module JSON (Value (..), json, parse, stringify) where import Control.Monad hiding (join) import Text.ParserCombinators.Parsec hiding (parse) import qualified Text.ParserCombinators.Parsec as P import Text.Printf (printf) import Data.Char (ord, isControl) import Data.List (intersperse) import qualified Data.Map as M data Value = String String | Number !Double | Object !(M.Map String Value) | Array [Value] | Bool !Bool | Null deriving (Eq, Show) json :: Parser Value json = spaces >> value tok :: Parser a -> Parser a tok p = do{ x <- p; spaces; return x } value :: Parser Value value = msum [ liftM String str , liftM Number number , liftM Object object , liftM Array array , string "true" >> return (Bool True) , string "false" >> return (Bool False) , string "null" >> return Null ] str :: Parser String str = between (char '"') (char '"') $ many c1 where c1 = satisfy (\c -> not (c=='"' || c=='\\' || isControl c)) <|> (char '\\' >> c2) c2 = msum [ char '"' , char '\\' , char '/' , char 'b' >> return '\b' , char 'f' >> return '\f' , char 'n' >> return '\n' , char 'r' >> return '\r' , char 't' >> return '\t' , char 'u' >> do xs <- count 4 hexDigit return $ read $ "'\\x"++xs++"'" ] (>>+) :: Monad m => m [a] -> m [a] -> m [a] (>>+) = liftM2 (++) (>>:) :: Monad m => m a -> m [a] -> m [a] (>>:) = liftM2 (:) number :: Parser Double number = liftM read $ int >>+ option "" frac >>+ option "" exp where digits = many1 digit int = do s <- option "" (string "-") ds <- mplus (try (oneOf ['1'..'9'] >>: digits)) (digit >>= \d -> return [d]) return (s++ds) frac = char '.' >>: digits exp = e >>+ digits e = oneOf "eE" >>: option "" (string "+" <|> string "-") object :: Parser (M.Map String Value) object = liftM M.fromList $ between (tok (char '{')) (char '}') $ tok member `sepBy` tok (char ',') where member = do k <- tok str tok (char ':') v <- value return (k,v) array :: Parser [Value] array = between (tok (char '[')) (char ']') $ tok value `sepBy` tok (char ',') parse :: String -> Maybe Value parse s = case P.parse json "JSON.parse" s of Left err -> Nothing Right v -> Just v stringify :: Value -> String stringify (String s) = stringifyString s stringify (Number x) | isInfinite x = error "can't stringify infinity" | isNaN x = error "can't stringify NaN" | otherwise = show x stringify (Object m) = "{ " ++ join ", " [stringifyString k ++ ": " ++ stringify v | (k,v) <- M.toList m] ++ " }" stringify (Array xs) = "[ " ++ join ", " (map stringify xs) ++ " ]" stringify (Bool b) = if b then "true" else "false" stringify Null = "null" stringifyString :: String -> String stringifyString s = "\"" ++ concatMap f s ++ "\"" where f '"' = "\\\"" f '\\' = "\\\\" f '\b' = "\\b" f '\f' = "\\f" f '\n' = "\\n" f '\r' = "\\r" f '\t' = "\\t" f c | isControl c = printf "\\u%04x" c | otherwise = [c] join :: String -> [String] -> String join s = concat . intersperse s