# JSON Parser ```idris module Parser.JSON import public Parser import public Parser.Numbers import Structures.Dependent.DList ``` ## JSON components Types a JSON value is allowed to have ```idris public export data JSONType : Type where TObject : JSONType TArray : JSONType TString : JSONType TNumber : JSONType TBool : JSONType TNull : JSONType %runElab derive "JSONType" [Generic, Meta, Eq, Ord, Show, DecEq] %name JSONType type, type2, type3 ``` A JSON value indexed by its type ```idris public export data JSONValue : JSONType -> Type where VObject : {types : List JSONType} -> DList JSONType (\t => (String, JSONValue t)) types -> JSONValue TObject VArray : {types : List JSONType} -> DList JSONType JSONValue types -> JSONValue TArray VString : (s : String) -> JSONValue TString VNumber : (d : Double) -> JSONValue TNumber VBool : (b : Bool) -> JSONValue TBool VNull : JSONValue TNull %name JSONValue value, value2, value3 ``` ## Parsers We are going to get mutually recursive here. Instead of using a `mutual` block, we will use the more modern style of declaring all our types ahead of our definitions. ```idris export object : Parser (JSONValue TObject) export array : Parser (JSONValue TArray) export string : Parser (JSONValue TString) export number : Parser (JSONValue TNumber) export bool : Parser (JSONValue TBool) export null : Parser (JSONValue TNull) ``` Define a `whitespace` character class based on the json specifications ```idris whitespace : Parser Char whitespace = do result <- parseChar (\x => any (== x) $ the (List _) [' ', '\n', '\r', '\t']) id case result of GotChar char => pure char GotError err => throwParseError "Expected whitespace, got: \{show err}" EndOfInput => throwParseError "End of Input" ``` Convenience function ```idris dpairize : {t : JSONType} -> Parser (JSONValue t) -> Parser (t' : JSONType ** JSONValue t') dpairize x = do x <- x pure (_ ** x) ``` Top level json value parser ```idris export value : Parser (t : JSONType ** JSONValue t) value = do surround whitespace $ oneOfE (throwParseError "Expected JSON Value") (the (List _) [ dpairize object , dpairize array , dpairize string , dpairize number , dpairize bool , dpairize null ]) ``` Now go through our json value types ```idris object = do oneOfE (throwParseError "Expected Object") (the (List _) [emptyObject, occupiedObject]) where emptyObject : Parser (JSONValue TObject) emptyObject = do delimited '{' '}' (nom whitespace) pure $ VObject Nil keyValue : Parser (t : JSONType ** (String, JSONValue t)) keyValue = do VString key <- surround whitespace string _ <- parseExactChar ':' (typ ** val) <- value pure (typ ** (key, val)) restKeyValue : Parser (t : JSONType ** (String, JSONValue t)) restKeyValue = do _ <- parseExactChar ',' keyValue pairs : Parser (List1 (t : JSONType ** (String, JSONValue t))) pairs = do first <- keyValue rest <- many restKeyValue pure $ first ::: rest occupiedObject : Parser (JSONValue TObject) occupiedObject = do val <- delimited '{' '}' pairs let (types ** xs) = DList.fromList (forget val) pure $ VObject xs ``` ```idris array = do oneOfE (throwParseError "Expected Array") (the (List _) [emptyArray, occupiedArray]) where emptyArray : Parser (JSONValue TArray) emptyArray = do _ <- parseExactChar '[' _ <- many whitespace _ <- parseExactChar ']' pure $ VArray Nil restValue : Parser (t : JSONType ** JSONValue t) restValue = do _ <- parseExactChar ',' value occupiedArray : Parser (JSONValue TArray) occupiedArray = do _ <- parseExactChar '[' first <- value rest <- many restValue _ <- parseExactChar ']' let (types ** xs) = DList.fromList (first :: rest) pure $ VArray xs ``` ```idris string = do _ <- parseExactChar '"' -- TODO: Handle control characters properly e1 <- parseError "Expected non-quote, got quote" e2 <- parseError "End of input" contents <- parseString . many $ parseCharE (not . (== '"')) (\_ => e1) e2 _ <- parseExactChar '"' pure $ VString contents ``` ```idris number = do d <- double pure $ VNumber d ``` ```idris bool = do oneOfE (throwParseError "Expected Bool") (the (List _) [true, false]) where true : Parser (JSONValue TBool) true = do _ <- exactString "true" pure $ VBool True false : Parser (JSONValue TBool) false = do _ <- exactString "false" pure $ VBool False ``` ```idris null = do _ <- exactString "null" pure VNull ``` ## Unit tests Quick smoke test ```idris -- @@test JSON Quick Smoke quickSmoke : IO Bool quickSmoke = do let input = "{\"string\":\"string\",\"number\":5,\"true\":true,\"false\":false,\"null\":null,\"array\":[1,2,3]}" putStrLn input Right parsed <- runFirstIO object input | Left err => do printLn err pure False putStrLn "Input: \{input}\nOutput: \{show parsed}" case parsed of VObject xs => ?quickSmoke_rhs_0 _ => pure False ```