{-# LANGUAGE ScopedTypeVariables
, ParallelListComp
#-}{-| Parse UTF-8 JSON into native Haskell types.
-}moduleText.JSONb.DecodewhereimportData.CharimportData.Ratio((%))importPreludehiding(length,null,last,takeWhile)importData.ByteString(length,append,empty,ByteString)importData.ByteString.Char8(snoc,cons,pack)importControl.Applicativehiding(empty)importqualifiedData.ByteString.UTF8asUTF8importqualifiedData.Trie.ConvenienceasTrieimportData.Attoparsec(eitherResult)importData.Attoparsec.Char8(choice,char,Parser,option,takeWhile1,takeWhile,skipMany,satisfy,signed,decimal,Result(..))importqualifiedData.Attoparsec.Char8asAttoparsecimportData.ByteString.Nums.CarelessimportText.JSONb.Simple{-| Interpret a 'ByteString' as any JSON literal.
-}decode::ByteString->EitherStringJSONdecodebytes=(eitherResult.Attoparsec.parsejson)bytes{-| Split out the first parseable JSON literal from the input, returning
the result of the attempt along with the remainder of the input or the
whole input if no parseable item was discovered.
-}break::ByteString->(EitherStringJSON,ByteString)breakbytes=caseAttoparsec.parsejsonbytesofDoneremainderresult->(Rightresult,remainder)Fail__s->(Lefts,bytes)Partial_->(Left"Partial",bytes){-| Tries to parse any JSON literal.
-}json::ParserJSONjson=dowhitespacechoice[object,array,string,number,boolean,null]{-| Parse a JSON object (dictionary).
-}object::ParserJSONobject=dochar'{'whitespaceObject.Trie.fromListS<$>choice[whitespace>>char'}'>>return[],properties[]]wherepropertiesacc=dokey<-string_literalwhitespacechar':'something<-jsonwhitespaceletacc'=(key,something):accchoice[char','>>whitespace>>choice[char'}'>>returnacc',propertiesacc'],char'}'>>returnacc']{-| Parse a JSON array.
-}array::ParserJSONarray=dochar'['Array<$>choice[whitespace>>char']'>>return[],elements[]]whereelementsacc=dosomething<-jsonwhitespaceletacc'=something:accfinish=char']'>>return(reverseacc')choice[char','>>whitespace>>choice[finish,elementsacc'],finish]{-| Parses a string literal, unescaping as it goes.
-}string::ParserJSONstring=String<$>string_literal{-| Parses a numeric literal to a @Rational@.
-}number::ParserJSONnumber=Number<$>do(sign::Rational)<-(char'-'*>pure(-1))<|>pure1i<-just_zero<|>positive_numberf<-option0fractionale<-option0(exponentialE*>signeddecimal)return(sign*(i+f)*(10^^e))whereexponentialE=char'e'<|>char'E'fractional=doc<-char'.'digits<-takeWhile1isDigitreturn(intdigits%(10^(lengthdigits)))just_zero=char'0'*>pure0positive_number=pure((int.).cons)<*>satisfyhi<*>takeWhileisDigitwherehid=d>'0'&&d<='9'{-| Parse a JSON Boolean literal.
-}boolean::ParserJSONboolean=Boolean<$>choice[s_as_b"true">>pureTrue,s_as_b"false">>pureFalse]{-| Parse a JSON null literal.
-}null::ParserJSONnull=s_as_b"null">>returnNull{-| Per RFC 4627, section 2 "JSON Grammar", only a limited set of whitespace
characters actually count as insignificant whitespace.
-}whitespace::Parser()whitespace=skipMany(satisfyw)wherew' '=True-- ASCII space.w'\n'=True-- Newline.w'\r'=True-- Carriage return.w'\t'=True-- Horizontal tab.w_=False-- Not a JSON space.{-| Parse a JSON string literal and unescape it but don't wrap it in a string
constructor (we might wrap it as a dict key instead).
-}string_literal::ParserByteStringstring_literal=char'"'>>recurseemptywhererecurseacc=dotext<-takeWhile(not.(`elem`"\\\""))choice[char'"'>>return(acc`append`text),dochar'\\'c<-escape_sequencerecurse(acc`append`text`append`UTF8.fromString[c])]whereescape_sequence=dochoice[c>>r|c<-fmapchar"n/\"rfbt\\u"|r<-fmapreturn"\n/\"\r\f\b\t\\"++[u]]whereu=do(a,b,c,d)<-(,,,)<$>hex<*>hex<*>hex<*>hexreturn.toEnum$a*0x1000+b*0x100+c*0x10+d*0x1wherehex=choicedigitswhereprep(n,chars)=fmap(fmap((+n).ord).char)charsdigits=concatMapprep[(-48,['0'..'9']),(-55,['A'..'F']),(-87,['a'..'f'])]s_as_bs=Attoparsec.string(packs)