Json.hs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. {-# LANGUAGE FlexibleInstances #-}
  2. {-# LANGUAGE ViewPatterns #-}
  3. module Json where
  4. import Control.Applicative
  5. import Data.Char
  6. import Numeric
  7. import System.Exit
  8. data Input = Input
  9. { inputLoc :: Int
  10. , inputStr :: String
  11. } deriving (Show, Eq)
  12. data JsonValue
  13. = JsonNull
  14. | JsonBool Bool
  15. | JsonNumber Double
  16. | JsonString String
  17. | JsonArray [JsonValue]
  18. | JsonObject [(String, JsonValue)]
  19. deriving (Show, Eq)
  20. data ParserError = ParserError Int String deriving (Show)
  21. newtype Parser a = Parser
  22. { runParser :: Input -> Either ParserError (Input, a)
  23. }
  24. instance Functor Parser where
  25. fmap f (Parser p) =
  26. Parser $ \input -> do
  27. (input', x) <- p input
  28. return (input', f x)
  29. instance Applicative Parser where
  30. pure x = Parser $ \input -> Right (input, x)
  31. (Parser p1) <*> (Parser p2) =
  32. Parser $ \input -> do
  33. (input', f) <- p1 input
  34. (input'', a) <- p2 input'
  35. return (input'', f a)
  36. instance Alternative (Either ParserError) where
  37. empty = Left $ ParserError 0 "empty"
  38. Left _ <|> e2 = e2
  39. e1 <|> _ = e1
  40. instance Alternative Parser where
  41. empty = Parser $ const empty
  42. (Parser p1) <|> (Parser p2) =
  43. Parser $ \input -> p1 input <|> p2 input
  44. -- | Pull the first character of the input if there is still input
  45. inputChar :: Input -> Maybe (Char, Input)
  46. inputChar (Input _ []) = Nothing
  47. inputChar (Input loc (x:xs)) = Just (x, Input (loc + 1) xs)
  48. -- | Parser for null json
  49. null :: Parser JsonValue
  50. null = JsonNull <$ parameterized_string "null"
  51. -- | Create a parser for a single specific character
  52. char :: Char -> Parser Char
  53. char x = Parser f
  54. where
  55. f input@(inputChar -> Just (y, ys))
  56. | y == x = Right (ys, x)
  57. | otherwise =
  58. Left $
  59. ParserError
  60. (inputLoc input)
  61. ("Expected '" ++ [x] ++ "', but found '" ++ [y] ++ "'")
  62. f input =
  63. Left $
  64. ParserError
  65. (inputLoc input)
  66. ("Expected '" ++ [x] ++ "', but reached end of string")
  67. -- | Create a parser for a specific string
  68. parameterized_string :: String -> Parser String
  69. parameterized_string str =
  70. Parser $ \input ->
  71. case runParser (traverse char str) input of
  72. Left _ ->
  73. Left $
  74. ParserError
  75. (inputLoc input)
  76. ("Expected \"" ++ str ++ "\", but found \"" ++ inputStr input ++ "\"")
  77. result -> result
  78. -- | Create a parser for boolean values
  79. bool :: Parser JsonValue
  80. bool = true <|> false
  81. where
  82. true = JsonBool True <$ parameterized_string "true"
  83. false = JsonBool False <$ parameterized_string "false"
  84. -- | Parser of a character that satisfies a predicate
  85. predicate_char :: String -> (Char -> Bool) -> Parser Char
  86. predicate_char desc f =
  87. Parser $ \input ->
  88. case input of
  89. (inputChar -> Just (y, ys))
  90. | f y -> Right (ys, y)
  91. | otherwise ->
  92. Left $
  93. ParserError
  94. (inputLoc input)
  95. ("Expected " ++ desc ++ ", but found '" ++ [y] ++ "'")
  96. _ ->
  97. Left $
  98. ParserError
  99. (inputLoc input)
  100. ("Expected " ++ desc ++ ", but reached end of string")
  101. -- | Parser of strings where all characters satifsfy a predicate
  102. predicate_string :: String -> (Char -> Bool) -> Parser String
  103. predicate_string desc = many . predicate_char desc
  104. {-
  105. See page 12 of
  106. http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
  107. -}
  108. -- | Parser for doubles
  109. double_literal :: Parser Double
  110. double_literal =
  111. double_from_parts
  112. <$> (minus <|> pure 1)
  113. <*> (read <$> digits)
  114. <*> ((read <$> (('0':) <$> ((:) <$> char '.' <*> digits))) <|> pure 0)
  115. <*> ((e *> ((*) <$> (plus <|> minus <|> pure 1) <*> (read <$> digits))) <|> pure 0)
  116. where
  117. digits = some $ predicate_char "digit" isDigit
  118. minus = (-1) <$ char '-'
  119. plus = 1 <$ char '+'
  120. e = char 'e' <|> char 'E'
  121. -- | Build a Double from its parts (sign, integral part, decimal part, exponent)
  122. double_from_parts :: Integer -- sign
  123. -> Integer -- integral part
  124. -> Double -- decimal part
  125. -> Integer -- exponent
  126. -> Double
  127. double_from_parts sign int dec expo =
  128. fromIntegral sign * (fromIntegral int + dec) * (10 ^^ expo)
  129. -- | Parser for json number values
  130. number :: Parser JsonValue
  131. number = JsonNumber <$> double_literal
  132. -- | Parser for hex encoded unicode characters in input
  133. escaped_unicode :: Parser Char
  134. escaped_unicode = chr . fst . head . readHex <$> sequenceA (replicate 4 (predicate_char "hex digit" isHexDigit))
  135. -- | Parser for escaped characters
  136. escaped_char :: Parser Char
  137. escaped_char = ('"' <$ parameterized_string "\\\"") <|>
  138. ('\\' <$ parameterized_string "\\\\") <|>
  139. ('/' <$ parameterized_string "\\/") <|>
  140. ('\b' <$ parameterized_string "\\b") <|>
  141. ('\f' <$ parameterized_string "\\f") <|>
  142. ('\n' <$ parameterized_string "\\n") <|>
  143. ('\r' <$ parameterized_string "\\r") <|>
  144. ('\t' <$ parameterized_string "\\t") <|>
  145. (parameterized_string "\\u" *> escaped_unicode)
  146. -- | Parser of a character that is not " or \\
  147. non_special_char :: Parser Char
  148. non_special_char = predicate_char "non-special character" ((&&) <$> (/= '"') <*> (/= '\\'))
  149. -- | Parser of a string that is between double quotes (not considering any double quots that are scaped)
  150. string_literal :: Parser String
  151. string_literal = char '"' *> many (non_special_char <|> escaped_char) <* char '"'
  152. -- | Parser of literal json string values
  153. string :: Parser JsonValue
  154. string = JsonString <$> string_literal
  155. -- | Parser for white spaces
  156. ws :: Parser String
  157. ws = predicate_string "whitespace character" isSpace
  158. -- | Creates a parser for a string of type "element1 sep1 element2 sep2 element3"
  159. -- from a parser for separators (sep1, sep2) and and a parser form elements (element1, element2, element3).
  160. separated_by :: Parser sep -> Parser el -> Parser [el]
  161. separated_by sep element = (:) <$> element <*> many (sep *> element) <|> pure []
  162. -- | Parser for json arrays
  163. array :: Parser JsonValue
  164. array = JsonArray <$> (char '[' *> ws *> (separated_by (ws *> char ',' <* ws) value) <* ws <* char ']')
  165. -- | Parser for json objects
  166. object :: Parser JsonValue
  167. object =
  168. JsonObject <$>
  169. (char '{' *> ws *> separated_by (ws *> char ',' <* ws) pair <* ws <* char '}')
  170. where
  171. pair = liftA2 (,) (string_literal <* ws <* char ':' <* ws) value
  172. -- | Parser for any json
  173. value :: Parser JsonValue
  174. value =
  175. Json.null <|> bool <|> number <|> string <|> array <|>
  176. object
  177. -- | Apply parser to content of file
  178. parse_file :: FilePath -> Parser a -> IO (Either ParserError a)
  179. parse_file fileName parser = do
  180. input <- readFile fileName
  181. case runParser parser $ Input 0 input of
  182. Left e -> return $ Left e
  183. Right (_, x) -> return $ Right x
  184. {-------------}
  185. test :: IO ()
  186. test = do
  187. putStrLn "[INFO] JSON:"
  188. putStrLn testJsonText
  189. case runParser value $ Input 0 testJsonText of
  190. Right (input, actualJsonAst) -> do
  191. putStrLn ("[INFO] Parsed as: " ++ show actualJsonAst)
  192. putStrLn
  193. ("[INFO] Remaining input (codes): " ++ show (map ord $ inputStr input))
  194. if actualJsonAst == expectedJsonAst
  195. then putStrLn "[SUCCESS] Parser produced expected result."
  196. else do
  197. putStrLn
  198. ("[ERROR] Parser produced unexpected result. Expected result was: " ++
  199. show expectedJsonAst)
  200. exitFailure
  201. Left (ParserError loc msg) -> do
  202. putStrLn $
  203. "[ERROR] Parser failed at character " ++ show loc ++ ": " ++ msg
  204. exitFailure
  205. where
  206. testJsonText =
  207. unlines
  208. [ "{"
  209. , " \"hello\": [false, true, null, 42, \"foo\\n\\u1234\\\"\", [1, -2, 3.1415, 4e-6, 5E6, 0.123e+1]],"
  210. , " \"world\": null"
  211. , "}"
  212. ]
  213. expectedJsonAst =
  214. JsonObject
  215. [ ( "hello"
  216. , JsonArray
  217. [ JsonBool False
  218. , JsonBool True
  219. , JsonNull
  220. , JsonNumber 42
  221. , JsonString "foo\n\4660\""
  222. , JsonArray
  223. [ JsonNumber 1.0
  224. , JsonNumber (-2.0)
  225. , JsonNumber 3.1415
  226. , JsonNumber 4e-6
  227. , JsonNumber 5000000
  228. , JsonNumber 1.23
  229. ]
  230. ])
  231. , ("world", JsonNull)
  232. ]
  233. -- >>> test
  234. -- [INFO] JSON:
  235. -- {
  236. -- "hello": [false, true, null, 42, "foo\n\u1234\"", [1, -2, 3.1415, 4e-6, 5E6, 0.123e+1]],
  237. -- "world": null
  238. -- }
  239. -- <BLANKLINE>
  240. -- [INFO] Parsed as: JsonObject [("hello",JsonArray [JsonBool False,JsonBool True,JsonNull,JsonNumber 42.0,JsonString "foo\n\4660\"",JsonArray [JsonNumber 1.0,JsonNumber (-2.0),JsonNumber 3.1415,JsonNumber 4.0e-6,JsonNumber 5000000.0,JsonNumber 1.23]]),("world",JsonNull)]
  241. -- [INFO] Remaining input (codes): [10]
  242. -- [SUCCESS] Parser produced expected result.
  243. --