{-# LANGUAGE OverloadedStrings #-} module SimpleParser.Examples.Json ( Json (..) , JsonF (..) , JsonParserC , JsonParserM , jsonParser , recJsonParser ) where import Control.Monad (void) import Data.Foldable (asum) import Data.Scientific (Scientific) import Data.Sequence (Seq) import Data.Text (Text) import Data.Void (Void) import SimpleParser.Chunked (TextualChunked (..)) import SimpleParser.Common (TextLabel, betweenParser, escapedStringParser, lexemeParser, scientificParser, sepByParser, signedNumStartPred, spaceParser) import SimpleParser.Input (matchChunk, matchToken, satisfyToken) import SimpleParser.Parser (Parser, commitParser, onEmptyParser, orParser) import SimpleParser.Stream (Stream (..), TextualStream) data JsonF a = JsonObject !(Seq (Text, a)) | JsonArray !(Seq a) | JsonString !Text | JsonBool !Bool | JsonNum !Scientific | JsonNull deriving (Eq, Show, Functor, Foldable, Traversable) newtype Json = Json { unJson :: JsonF Json } deriving (Eq, Show) type JsonParserC s = (TextualStream s, Eq (Chunk s)) type JsonParserM s a = Parser TextLabel s Void a jsonParser :: JsonParserC s => JsonParserM s Json jsonParser = let p = fmap Json (recJsonParser p) in p isBoolStartPred :: Char -> Bool isBoolStartPred c = c == 't' || c == 'f' recJsonParser :: JsonParserC s => JsonParserM s a -> JsonParserM s (JsonF a) recJsonParser root = onEmptyParser (asum opts) (fail "failed to parse json document") where pairP = objectPairP root opts = [ commitParser openBraceP (objectP pairP) , commitParser openBracketP (arrayP root) , commitParser openQuoteP stringP , commitParser (void (satisfyToken Nothing signedNumStartPred)) numP , commitParser (void (satisfyToken Nothing isBoolStartPred)) boolP , commitParser (void (matchToken 'n')) nullP ] spaceP :: JsonParserC s => JsonParserM s () spaceP = spaceParser tokL :: JsonParserC s => Char -> JsonParserM s () tokL c = lexemeParser spaceP (void (matchToken c)) chunkL :: JsonParserC s => Text -> JsonParserM s () chunkL cs = lexemeParser spaceP (void (matchChunk (unpackChunk cs))) openBraceP, closeBraceP, commaP, colonP, openBracketP, closeBracketP, closeQuoteP :: JsonParserC s => JsonParserM s () openBraceP = tokL '{' closeBraceP = tokL '}' commaP = tokL ',' colonP = tokL ':' openBracketP = tokL '[' closeBracketP = tokL ']' closeQuoteP = tokL '"' openQuoteP :: JsonParserC s => JsonParserM s () openQuoteP = void (matchToken '"') nullTokP, trueTokP, falseTokP :: JsonParserC s => JsonParserM s () nullTokP = chunkL "null" trueTokP = chunkL "true" falseTokP = chunkL "false" rawStringP :: JsonParserC s => JsonParserM s Text rawStringP = fmap packChunk (escapedStringParser '"') stringP :: JsonParserC s => JsonParserM s (JsonF a) stringP = fmap JsonString rawStringP nullP :: JsonParserC s => JsonParserM s (JsonF a) nullP = JsonNull <$ nullTokP boolP :: JsonParserC s => JsonParserM s (JsonF a) boolP = orParser (JsonBool True <$ trueTokP) (JsonBool False <$ falseTokP) numP :: JsonParserC s => JsonParserM s (JsonF a) numP = fmap JsonNum scientificParser objectPairP :: JsonParserC s => JsonParserM s a -> JsonParserM s (Text, a) objectPairP root = do name <- rawStringP colonP value <- root pure (name, value) objectP :: JsonParserC s => JsonParserM s (Text, a) -> JsonParserM s (JsonF a) objectP pairP = betweenParser openBraceP closeBraceP (fmap JsonObject (sepByParser pairP commaP)) arrayP :: JsonParserC s => JsonParserM s a -> JsonParserM s (JsonF a) arrayP root = betweenParser openBracketP closeBracketP (fmap JsonArray (sepByParser root commaP))