@@ -1,278 +1,537 @@
1−{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
1+{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing #-}
2−{-# LANGUAGE BangPatterns, MagicHash, OverloadedStrings, UnboxedTuples #-}
2+{-# LANGUAGE BangPatterns, Rank2Types, OverloadedStrings, UnboxedTuples #-}
3module Json.Decode
3module Json.Decode
4− ( parse
4+ ( fromByteString
5− -- re-export from Json.Decode.Internals
5+ , fromFile
6− , Json.Decoder
6+ , Decoder
7− , Json.string, Json.text, Json.name, Json.bool, Json.int
7+ , string
8− , Json.list, Json.dict, Json.pairs, Json.maybe
8+ , bool
9− , Json.field, Json.at
9+ , int
10− , Json.pair
10+ , list, pair
11− , Json.map, Json.map2, Json.mapError
14+ , mapError
12− , Json.succeed, Json.fail
13− , Json.andThen, Json.oneOf
18−import Prelude hiding (length)
19−import qualified Data.ByteString.Char8 as Char8
20−import qualified Data.ByteString.Internal as B
19+import qualified Data.ByteString as B
21−import qualified Data.Text as Text
20+import qualified Data.List as List
22−import qualified Data.Text.Encoding as Text
21+import qualified Data.Map as Map
23−import Foreign.ForeignPtr (ForeignPtr)
22+import qualified Data.Utf8 as Utf8
24−import GHC.Word (Word8(..))
23+import Data.Word (Word8, Word16)
24+import Foreign.Ptr (Ptr, plusPtr, minusPtr)
26−import qualified Json.Decode.Error as DecodeError
26+import qualified Parse.Keyword as K
27−import qualified Json.Decode.Internals as Json
28import qualified Parse.Primitives as P
27import qualified Parse.Primitives as P
29−import qualified Parse.Primitives.Internals as I
30−import Parse.Primitives.Internals (Parser(..), State(..), noError)
31−import qualified Parse.Primitives.Keyword as Keyword
32−import qualified Parse.Primitives.Number as Number
33−import qualified Parse.Primitives.Symbol as Symbol
34−import qualified Reporting.Doc as D
35−import qualified Reporting.Error.Syntax as E
28+import qualified Reporting.Error.Json as E
36−import qualified Reporting.Region as R
37−import qualified Reporting.Render.Code as Code
44−parse :: String -> (e -> [D.Doc]) -> Json.Decoder e a -> B.ByteString -> Either D.Doc a
35+fromByteString :: Decoder e a -> B.ByteString -> Either (E.Error e) a
45−parse rootName userErrorToDocs (Json.Decoder run) bytestring =
36+fromByteString decoder src =
46− let
37+ fromAST decoder (P.fromByteString pFile src)
48− Code.toSource (Text.replace "\t" " " (Text.decodeUtf8 bytestring))
51− DecodeError.toDoc rootName source userErrorToDocs err
53− case P.run jsonFile bytestring of
55− Left (toDoc (DecodeError.BadJson err))
57− Right value ->
40+fromFile :: Decoder e a -> FilePath -> IO (Either (E.Error e) a)
58− case run value of
41+fromFile decoder path =
59− Left err ->
42+ fromAST decoder <$> P.fromFile pFile path
60− Left (toDoc (DecodeError.BadContent err))
45+fromAST :: Decoder e a -> P.Result E.Context E.ParseExpectation AST -> Either (E.Error e) a
46+fromAST (Decoder decode) parserResult =
49+ decode ast Right (Left . E.DecodeProblem)
51+ P.Err row col ctx expectation ->
52+ Left (E.ParseProblem row col ctx expectation)
70−jsonFile :: Parser Json.Value
56+-- DECODERS
79−jsonValue :: Parser Json.Value
59+newtype Decoder e a =
80−jsonValue =
60+ Decoder (forall b. AST -> (a -> b) -> (E.Problem e -> b) -> b)
81− do start <- P.getPosition
83− [ Json.String <$> string
87− , Keyword.jsonTrue >> return Json.TRUE
88− , Keyword.jsonFalse >> return Json.FALSE
89− , Keyword.jsonNull >> return Json.NULL
94−-- OBJECT
64+-- INSTANCES
97−object :: R.Position -> Parser Json.Value
67+instance Functor (Decoder e) where
98−object start =
68+ {-# INLINE fmap #-}
99− do Symbol.leftCurly
69+ fmap func (Decoder decodeA) =
100− P.inContext start E.ExprRecord $
70+ Decoder $ \ast ok err ->
102− I.oneOf
72+ ok' a = ok (func a)
103− [ do entry <- field
73+ in
104− spaces
74+ decodeA ast ok' err
106− , do Symbol.rightCurly
107− return (Json.Object [])
111−objectHelp :: [(Text.Text, Json.Value)] -> Parser Json.Value
77+instance Applicative (Decoder e) where
112−objectHelp revEntries =
78+ {-# INLINE pure #-}
113− I.oneOf
79+ pure = return
119− objectHelp (entry:revEntries)
121− do Symbol.rightCurly
122− return (Json.Object (reverse revEntries))
82+ (<*>) (Decoder decodeFunc) (Decoder decodeArg) =
83+ Decoder $ \ast ok err ->
87+ okA arg = ok (func arg)
89+ decodeArg ast okA err
91+ decodeFunc ast okF err
126−field :: Parser (Text.Text, Json.Value)
94+instance Monad (Decoder e) where
95+ {-# INLINE return #-}
100+ {-# INLINE (>>=) #-}
101+ (>>=) (Decoder decodeA) callback =
102+ Decoder $ \ast ok err ->
106+ Decoder decodeB -> decodeB ast ok err
140−array :: R.Position -> Parser Json.Value
112+-- STRINGS
142− do Symbol.leftSquare
143− P.inContext start E.ExprList $
146− [ do entry <- jsonValue
149− , do Symbol.rightSquare
150− return (Json.Array [])
154−arrayHelp :: Int -> [Json.Value] -> Parser Json.Value
115+string :: Decoder e Utf8.String
155−arrayHelp !length revEntries =
116+string =
156− I.oneOf
117+ Decoder $ \ast ok err ->
158− do Symbol.comma
119+ String bits ->
160− entry <- jsonValue
121+
162− arrayHelp (length + 1) (entry:revEntries)
123+ err (E.Expecting E.TString)
164− do Symbol.rightSquare
125+
165− return $ Json.Array $ reverse revEntries
126+
130+bool :: Decoder e Bool
132+ Decoder $ \ast ok err ->
141+ err (E.Expecting E.TBool)
150+ Decoder $ \ast ok err ->
156+ err (E.Expecting E.TInt)
170−-- NUMBER
160+-- ARRAYS
173−number :: Parser Json.Value
163+list :: Decoder e a -> Decoder e [a]
174−number =
164+list decoder =
175− Parser $ \(State fp offset terminal indent row col ctx) cok cerr _ eerr ->
165+ Decoder $ \ast ok err ->
176− if offset < terminal then
166+ case ast of
168+ listHelp decoder ok err 0 asts []
171+ err (E.Expecting E.TArray)
174+listHelp :: Decoder e a -> ([a] -> b) -> (E.Problem e -> b) -> Int -> [AST] -> [a] -> b
175+listHelp decoder@(Decoder decodeA) ok err !i asts revs =
179− !word = I.unsafeIndex fp offset
182+ ok' a = listHelp decoder ok err (i+1) asts (a:revs)
180− !offset1 = offset + 1
183+ err' e = err (E.Index i e)
182− if word <= 0x39 {- 9 -} && word >= 0x31 {- 1 -} then
185+ decodeA ast ok' err'
183− case Number.chompInt fp offset1 terminal (fromIntegral (word - 0x30 {- 0 -})) of
186+
184− Number.Err newOffset problem ->
185− cerr (E.ParseError row (col + (newOffset - offset)) problem)
187− Number.OkInt newOffset n ->
188+pair :: Decoder e a -> Decoder e b -> Decoder e (a,b)
189+pair (Decoder decodeA) (Decoder decodeB) =
190+ Decoder $ \ast ok err ->
189− !newState = State fp newOffset terminal indent row (col + (newOffset - offset)) ctx
196+ err0 e = err (E.Index 0 e)
199+ err1 e = err (E.Index 1 e)
202+ decodeB astB ok1 err1
191− cok (Json.Integer n) newState noError
204+ decodeA astA ok0 err0
207+ err (E.Expecting (E.TArrayPair (length vs)))
210+ err (E.Expecting E.TArray)
217+dict :: Decoder e a -> Decoder e (Map.Map Utf8.String a)
219+ Map.fromList <$> pairs decoder
193− Number.OkFloat newOffset ->
222+pairs :: Decoder e a -> Decoder e [(Utf8.String, a)]
224+ Decoder $ \ast ok err ->
227+ pairsHelp decoder ok err kvs []
230+ err (E.Expecting E.TObject)
233+pairsHelp :: Decoder e a -> ([(Utf8.String, a)] -> b) -> (E.Problem e -> b) -> [(Utf8.String, AST)] -> [(Utf8.String, a)] -> b
234+pairsHelp decoder@(Decoder decodeA) ok err kvs revs =
241+ ok' a = pairsHelp decoder ok err kvs ((key,a):revs)
242+ err' e = err (E.Field key e)
244+ decodeA ast ok' err'
247+field :: Utf8.String -> Decoder e a -> Decoder e a
248+field key (Decoder decodeA) =
249+ Decoder $ \ast ok err ->
252+ case List.lookup key kvs of
195− !length = newOffset - offset
255+ err' e = err (E.Field key e)
196− !scientific = read $ Char8.unpack $ B.PS fp offset length
197− !newState = State fp newOffset terminal indent row (col + length) ctx
199− cok (Json.Float scientific) newState noError
257+ decodeA v ok err'
201− else if word == 0x30 {- 0 -} then
259+ Nothing ->
202− cok (Json.Integer 0) (State fp offset1 terminal indent row (col + 1) ctx) noError
260+ err (E.Expecting (E.TObjectWith key))
263+ err (E.Expecting E.TObject)
270+oneOf :: [Decoder e a] -> Decoder e a
272+ Decoder $ \ast ok err ->
274+ Decoder decodeA : decoders ->
277+ oneOfHelp ast ok err decoders e []
282+ error "Ran into (Json.Decode.oneOf [])"
285+oneOfHelp :: AST -> (a -> b) -> (E.Problem e -> b) -> [Decoder e a] -> E.Problem e -> [E.Problem e] -> b
286+oneOfHelp ast ok err decoders e es =
288+ Decoder decodeA : decoders ->
291+ oneOfHelp ast ok err decoders e' (e:es)
296+ err (oneOfError [] e es)
299+oneOfError :: [E.Problem e] -> E.Problem e -> [E.Problem e] -> E.Problem e
300+oneOfError errors err es =
306+ oneOfError (err:errors) e es
313+failure :: e -> Decoder e a
315+ Decoder $ \_ _ err ->
323+mapError :: (e -> e') -> Decoder e a -> Decoder e' a
324+mapError func (Decoder decodeA) =
325+ Decoder $ \ast ok err ->
327+ err' e = err (mapErrorHelp func e)
332+mapErrorHelp :: (e -> e') -> E.Problem e -> E.Problem e'
333+mapErrorHelp func err =
335+ E.Field k e -> E.Field k (mapErrorHelp func e)
336+ E.Index i e -> E.Index i (mapErrorHelp func e)
337+ E.OneOf e es -> E.OneOf (mapErrorHelp func e) (map (mapErrorHelp func) es)
338+ E.Failure e -> E.Failure (func e)
339+ E.Expecting e -> E.Expecting e
348+ | Object [(Utf8.String, AST)]
349+ | String Utf8.String
361+ P.Parser E.Context E.ParseExpectation a
376+ [ fmap String pString
380+ , K.k4 0x74 0x72 0x75 0x65 E.Bool >> return TRUE
381+ , K.k5 0x66 0x61 0x6C 0x73 0x65 E.Bool >> return FALSE
382+ , K.k4 0x6E 0x75 0x6C 0x6C E.Null >> return NULL
386+endOfFile :: Parser ()
388+ P.Parser $ \state@(P.State pos end _ row col ctx) _ eok _ eerr ->
390+ eerr row col ctx E.EndOfFile
208− eerr noError
392+ eok () state
399+pObject :: Parser AST
401+ do P.word1 0x7B {- { -} E.ObjectStart
403+ P.oneOf E.ObjectMore
404+ [ do entry <- pField
407+ , do P.word1 0x7D {-}-} E.ObjectEnd
412+pObjectHelp :: [(Utf8.String, AST)] -> Parser AST
413+pObjectHelp revEntries =
414+ P.oneOf E.ObjectMore
416+ do P.word1 0x2C {-,-} E.ObjectMore
420+ pObjectHelp (entry:revEntries)
422+ do P.word1 0x7D {-}-} E.ObjectEnd
423+ return (Object (reverse revEntries))
427+pField :: Parser (Utf8.String, AST)
431+ P.word1 0x3A {-:-} E.Colon
443+ do P.word1 0x5B {-[-} E.ArrayStart
446+ [ do entry <- pValue
448+ pArrayHelp 1 [entry]
449+ , do P.word1 0x5D {-]-} E.ArrayEnd
454+pArrayHelp :: Int -> [AST] -> Parser AST
455+pArrayHelp !len revEntries =
458+ do P.word1 0x2C {-,-} E.ArrayMore
462+ pArrayHelp (len + 1) (entry:revEntries)
464+ do P.word1 0x5D {-]-} E.ArrayEnd
465+ return (Array (reverse revEntries))
216−string :: Parser Text.Text
473+pString :: Parser Utf8.String
217−string =
474+pString =
218− Parser $ \(State fp offset terminal indent row col ctx) cok cerr _ eerr ->
475+ P.Parser $ \(P.State pos end indent row col ctx) cok _ cerr eerr ->
219− if offset < terminal && I.unsafeIndex fp offset == 0x22 {- " -} then
476+ if pos < end && P.unsafeIndex pos == 0x22 {-"-} then
222− !offset1 = offset + 1
479+ !pos1 = plusPtr pos 1
224− case stringHelp fp offset1 terminal row (col + 1) of
228− Ok newOffset newRow newCol ->
481+ (# status, newPos, newRow, newCol #) =
482+ pStringHelp pos1 end row (col + 1)
230− !newState = State fp newOffset terminal indent newRow newCol ctx
487+ !newState = P.State newPos end indent newRow newCol ctx
231− !content = Text.decodeUtf8 (B.PS fp offset1 (newOffset - offset1 - 1))
488+ !content = Utf8.fromPtr pos1 (plusPtr newPos (-1))
233− cok content newState noError
490+ cok content newState
493+ cerr newRow newCol ctx E.StringEnd
236− eerr noError
496+ eerr row col ctx E.StringStart
239−data StringResult
499+data StringStatus
240− = Err E.ParseError
500+ = GoodString
241− | Ok !Int !Int !Int
501+ | BadStringEnd
244−stringHelp :: ForeignPtr Word8 -> Int -> Int -> Int -> Int -> StringResult
504+pStringHelp :: Ptr Word8 -> Ptr Word8 -> Word16 -> Word16 -> (# StringStatus, Ptr Word8, Word16, Word16 #)
245−stringHelp fp offset terminal row col =
505+pStringHelp pos end row col =
246− if offset >= terminal then
506+ if pos >= end then
247− Err (E.ParseError row col E.EndOfFile_String)
507+ (# BadStringEnd, pos, row, col #)
250− case I.unsafeIndex fp offset of
510+ case P.unsafeIndex pos of
251− 0x22 {- " -} ->
511+ 0x22 {-"-} ->
252− Ok (offset + 1) row (col + 1)
512+ (# GoodString, plusPtr pos 1, row, col + 1 #)
254− 0x0A {- \n -} ->
514+ 0x0A {-\n-} ->
255− Err (E.ParseError row col E.NewLineInString)
515+ (# BadStringEnd, pos, row, col #)
257− 0x5C {- \ -} ->
517+ 0x5C {-\-} ->
259− !offset1 = offset + 1
519+ !pos1 = plusPtr pos 1
261− if offset1 < terminal then
521+ if pos1 < end then
264− !word = I.unsafeIndex fp offset1
524+ !word = P.unsafeIndex pos1
265− !newOffset = offset1 + I.getCharWidth fp offset1 terminal word
525+ !newPos = plusPtr pos1 (P.getCharWidth pos1 end word)
267− stringHelp fp newOffset terminal row (col + 2)
527+ pStringHelp newPos end row (col + 2)
270− Err (E.ParseError (row + 1) col E.EndOfFile_String)
530+ (# BadStringEnd, pos1, row + 1, col #)
273− let !newOffset = offset + I.getCharWidth fp offset terminal word in
533+ let !newPos = plusPtr pos (P.getCharWidth pos end word) in
274− stringHelp fp newOffset terminal row (col + 1)
534+ pStringHelp newPos end row (col + 1)
@@ -281,27 +540,107 @@ stringHelp fp offset terminal row col =
282spaces :: Parser ()
541spaces :: Parser ()
284− Parser $ \(State fp offset terminal indent row col ctx) cok _ _ _ ->
543+ P.Parser $ \state@(P.State pos end indent row col ctx) cok eok _ _ ->
286− (# newOffset, newRow, newCol #) =
545+ (# newPos, newRow, newCol #) =
287− eatSpaces fp offset terminal row col
546+ eatSpaces pos end row col
290− State fp newOffset terminal indent newRow newCol ctx
548+ if pos == newPos then
553+ P.State newPos end indent newRow newCol ctx
292− cok () newState noError
555+ cok () newState
295−eatSpaces :: ForeignPtr Word8 -> Int -> Int -> Int -> Int -> (# Int, Int, Int #)
558+eatSpaces :: Ptr Word8 -> Ptr Word8 -> Word16 -> Word16 -> (# Ptr Word8, Word16, Word16 #)
296−eatSpaces fp offset terminal row col =
559+eatSpaces pos end row col =
297− if offset >= terminal then
560+ if pos >= end then
298− (# offset, row, col #)
561+ (# pos, row, col #)
301− case I.unsafeIndex fp offset of
564+ case P.unsafeIndex pos of
302− 0x20 {- -} -> eatSpaces fp (offset + 1) terminal row (col + 1)
565+ 0x20 {- -} -> eatSpaces (plusPtr pos 1) end row (col + 1)
303− 0x09 {- \t -} -> eatSpaces fp (offset + 1) terminal row (col + 1)
566+ 0x09 {-\t-} -> eatSpaces (plusPtr pos 1) end row (col + 1)
304− 0x0A {- \n -} -> eatSpaces fp (offset + 1) terminal (row + 1) 1
567+ 0x0A {-\n-} -> eatSpaces (plusPtr pos 1) end (row + 1) 1
305− 0x0D {- \r -} -> eatSpaces fp (offset + 1) terminal row col
568+ 0x0D {-\r-} -> eatSpaces (plusPtr pos 1) end row col
307− (# offset, row, col #)
570+ (# pos, row, col #)
579+ P.Parser $ \(P.State pos end indent row col ctx) cok _ cerr eerr ->
581+ eerr row col ctx E.IntStart
584+ let !word = P.unsafeIndex pos in
585+ if not (isDecimalDigit word) then
586+ eerr row col ctx E.IntStart
588+ else if word == 0x30 {-0-} then
591+ !pos1 = plusPtr pos 1
592+ !newState = P.State pos1 end indent row (col + 1) ctx
595+ let !word1 = P.unsafeIndex pos1 in
596+ if isDecimalDigit word1 then
597+ cerr row (col + 1) ctx E.NoLeadingZeros
598+ else if word1 == 0x2E {-.-} then
599+ cerr row (col + 1) ctx E.NoFloats
601+ cok (Int 0) newState
603+ cok (Int 0) newState
607+ (# status, n, newPos #) =
608+ chompInt (plusPtr pos 1) end (fromIntegral (word - 0x30 {-0-}))
610+ !len = fromIntegral (minusPtr newPos pos)
616+ P.State newPos end indent row (col + len) ctx
618+ cok (Int n) newState
621+ cerr row (col + len) ctx E.NoFloats
624+data IntStatus = GoodInt | BadIntEnd
627+chompInt :: Ptr Word8 -> Ptr Word8 -> Int -> (# IntStatus, Int, Ptr Word8 #)
630+ let !word = P.unsafeIndex pos in
631+ if isDecimalDigit word then
632+ let !m = 10 * n + fromIntegral (word - 0x30 {-0-}) in
633+ chompInt (plusPtr pos 1) end m
634+ else if word == 0x2E {-.-} || word == 0x65 {-e-} || word == 0x45 {-E-} then
635+ (# BadIntEnd, n, pos #)
637+ (# GoodInt, n, pos #)
640+ (# GoodInt, n, pos #)
643+{-# INLINE isDecimalDigit #-}
644+isDecimalDigit :: Word8 -> Bool
645+isDecimalDigit word =
646+ word <= 0x39 {-9-} && word >= 0x30 {-0-}