@@ -6,6 +6,7 @@ module Data.Yaml.Internal
7 ParseException(..)
7 ParseException(..)
8 , prettyPrintParseException
8 , prettyPrintParseException
10 , decodeHelper
11 , decodeHelper
11 , decodeHelper_
12 , decodeHelper_
@@ -19,23 +20,25 @@ import Control.Applicative ((<$>), Applicative(..))
20import Control.Applicative ((<|>))
21import Control.Applicative ((<|>))
21import Control.Exception
22import Control.Exception
22−import Control.Monad (liftM, ap, unless)
23+import Control.Monad (when, unless)
23−import Control.Monad.IO.Class (MonadIO, liftIO)
24−import Control.Monad.Trans.Class (MonadTrans, lift)
25import Control.Monad.Trans.Resource (ResourceT, runResourceT)
24import Control.Monad.Trans.Resource (ResourceT, runResourceT)
26−import Control.Monad.Trans.State
25+import Control.Monad.RWS
27import Data.Aeson
26import Data.Aeson
27+import Data.Aeson.Internal (JSONPath, JSONPathElement(..))
28import Data.Aeson.Types hiding (parse)
28import Data.Aeson.Types hiding (parse)
29import qualified Data.Attoparsec.Text as Atto
29import qualified Data.Attoparsec.Text as Atto
30import Data.Bits (shiftL, (.|.))
30import Data.Bits (shiftL, (.|.))
31import Data.ByteString (ByteString)
31import Data.ByteString (ByteString)
32import Data.Char (toUpper, ord)
32import Data.Char (toUpper, ord)
33import Data.Conduit ((.|), ConduitM, runConduit)
34import Data.Conduit ((.|), ConduitM, runConduit)
34import qualified Data.Conduit.List as CL
35import qualified Data.Conduit.List as CL
35import qualified Data.HashMap.Strict as M
36import qualified Data.HashMap.Strict as M
36import qualified Data.HashSet as HashSet
37import qualified Data.HashSet as HashSet
37import Data.Map (Map)
38import Data.Map (Map)
38import qualified Data.Map as Map
39import qualified Data.Map as Map
41+import qualified Data.Set as Set
39import Data.Scientific (Scientific)
42import Data.Scientific (Scientific)
40import Data.Text (Text, pack)
43import Data.Text (Text, pack)
41import qualified Data.Text as T
44import qualified Data.Text as T
@@ -109,31 +112,19 @@ prettyPrintParseException pe = case pe of
110 CyclicIncludes -> "Cyclic includes"
113 CyclicIncludes -> "Cyclic includes"
112−newtype PErrorT m a = PErrorT { runPErrorT :: m (Either ParseException a) }
113−instance Monad m => Functor (PErrorT m) where
115−instance Monad m => Applicative (PErrorT m) where
116− pure = PErrorT . return . Right
118−instance Monad m => Monad (PErrorT m) where
120− (PErrorT m) >>= f = PErrorT $ do
123− Left e' -> return $ Left e'
124− Right a -> runPErrorT $ f a
125−instance MonadTrans PErrorT where
126− lift = PErrorT . liftM Right
127−instance MonadIO m => MonadIO (PErrorT m) where
128− liftIO = lift . liftIO
130defineAnchor :: Value -> String -> ConduitM e o Parse ()
115defineAnchor :: Value -> String -> ConduitM e o Parse ()
131−defineAnchor value name = lift $ modify $ Map.insert name value
116+defineAnchor value name = modify $ Map.insert name value
133lookupAnchor :: String -> ConduitM e o Parse (Maybe Value)
118lookupAnchor :: String -> ConduitM e o Parse (Maybe Value)
134−lookupAnchor name = lift $ gets (Map.lookup name)
119+lookupAnchor name = gets (Map.lookup name)
121+data Warning = DuplicateKey JSONPath
124+addWarning :: Warning -> ConduitM e o Parse ()
125+addWarning = tell . return
136−type Parse = StateT (Map String Value) (ResourceT IO)
127+type Parse = RWST JSONPath [Warning] (Map String Value) (ResourceT IO)
138requireEvent :: Event -> ConduitM Event o Parse ()
129requireEvent :: Event -> ConduitM Event o Parse ()
139requireEvent e = do
130requireEvent e = do
@@ -199,8 +190,8 @@ parseO = do
199 me <- CL.head
190 me <- CL.head
200 case me of
191 case me of
201 Just (EventScalar v tag style a) -> textToValue style tag <$> parseScalar v a style tag
192 Just (EventScalar v tag style a) -> textToValue style tag <$> parseScalar v a style tag
202− Just (EventSequenceStart _ _ a) -> parseS a id
193+ Just (EventSequenceStart _ _ a) -> parseS 0 a id
203− Just (EventMappingStart _ _ a) -> parseM a M.empty
194+ Just (EventMappingStart _ _ a) -> parseM mempty a M.empty
204 Just (EventAlias an) -> do
195 Just (EventAlias an) -> do
205 m <- lookupAnchor an
196 m <- lookupAnchor an
206 case m of
197 case m of
@@ -208,10 +199,11 @@ parseO = do
208 Just v -> return v
199 Just v -> return v
209 _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing
200 _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing
211−parseS :: Y.Anchor
203+ -> Y.Anchor
212 -> ([Value] -> [Value])
204 -> ([Value] -> [Value])
213 -> ConduitM Event o Parse Value
205 -> ConduitM Event o Parse Value
214−parseS a front = do
206+parseS n a front = do
215 me <- CL.peek
207 me <- CL.peek
216 case me of
208 case me of
217 Just EventSequenceEnd -> do
209 Just EventSequenceEnd -> do
@@ -220,13 +212,14 @@ parseS a front = do
220 mapM_ (defineAnchor res) a
212 mapM_ (defineAnchor res) a
221 return res
213 return res
223− o <- parseO
215+ o <- local (Index n :) parseO
224− parseS a $ front . (:) o
216+ parseS (succ n) a $ front . (:) o
226−parseM :: Y.Anchor
219+ -> Y.Anchor
227 -> M.HashMap Text Value
220 -> M.HashMap Text Value
228 -> ConduitM Event o Parse Value
221 -> ConduitM Event o Parse Value
229−parseM a front = do
222+parseM mergedKeys a front = do
230 me <- CL.head
223 me <- CL.head
231 case me of
224 case me of
232 Just EventMappingEnd -> do
225 Just EventMappingEnd -> do
@@ -243,48 +236,55 @@ parseM a front = do
243 Just (String t) -> return t
236 Just (String t) -> return t
244 Just v -> liftIO $ throwIO $ NonStringKeyAlias an v
237 Just v -> liftIO $ throwIO $ NonStringKeyAlias an v
245 _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing
238 _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing
240+ (mergedKeys', al') <- local (Key s :) $ do
243+ when (M.member s front && Set.notMember s mergedKeys) $ do
244+ path <- reverse <$> ask
245+ addWarning (DuplicateKey path)
248− let al = M.insert s o front
246+ return (Set.delete s mergedKeys, M.insert s o front)
249− al' = if s == pack "<<"
247+ if s == pack "<<"
250 then case o of
248 then case o of
251− Object l -> M.union front l
249+ Object l -> return (merge l)
252− Array l -> M.union front $ foldl mergeObjects M.empty $ V.toList l
250+ Array l -> return $ merge $ foldl mergeObjects M.empty $ V.toList l
255− parseM a al'
253+ parseM mergedKeys' a al'
256 where mergeObjects al (Object om) = M.union al om
254 where mergeObjects al (Object om) = M.union al om
257 mergeObjects al _ = al
255 mergeObjects al _ = al
257+ merge xs = (Set.fromList (M.keys xs \\ M.keys front), M.union front xs)
259decodeHelper :: FromJSON a
259decodeHelper :: FromJSON a
260 => ConduitM () Y.Event Parse ()
260 => ConduitM () Y.Event Parse ()
261− -> IO (Either ParseException (Either String a))
261+ -> IO (Either ParseException ([Warning], Either String a))
262decodeHelper src = do
262decodeHelper src = do
263 -- This used to be tryAny, but the fact is that catching async
263 -- This used to be tryAny, but the fact is that catching async
264 -- exceptions is fine here. We'll rethrow them immediately in the
264 -- exceptions is fine here. We'll rethrow them immediately in the
265 -- otherwise clause.
265 -- otherwise clause.
266− x <- try $ runResourceT $ flip evalStateT Map.empty $ runConduit $ src .| parse
266+ x <- try $ runResourceT $ evalRWST (runConduit $ src .| parse) [] Map.empty
267 case x of
267 case x of
269 | Just pe <- fromException e -> return $ Left pe
269 | Just pe <- fromException e -> return $ Left pe
270 | Just ye <- fromException e -> return $ Left $ InvalidYaml $ Just (ye :: YamlException)
270 | Just ye <- fromException e -> return $ Left $ InvalidYaml $ Just (ye :: YamlException)
271 | otherwise -> throwIO e
271 | otherwise -> throwIO e
272− Right y -> return $ Right $ parseEither parseJSON y
272+ Right (y, warnings) -> return $ Right (warnings, parseEither parseJSON y)
274decodeHelper_ :: FromJSON a
274decodeHelper_ :: FromJSON a
275 => ConduitM () Event Parse ()
275 => ConduitM () Event Parse ()
276− -> IO (Either ParseException a)
276+ -> IO (Either ParseException ([Warning], a))
277decodeHelper_ src = do
277decodeHelper_ src = do
278− x <- try $ runResourceT $ flip evalStateT Map.empty $ runConduit $ src .| parse
278+ x <- try $ runResourceT $ evalRWST (runConduit $ src .| parse) [] Map.empty
279 return $ case x of
279 return $ case x of
281 | Just pe <- fromException e -> Left pe
281 | Just pe <- fromException e -> Left pe
282 | Just ye <- fromException e -> Left $ InvalidYaml $ Just (ye :: YamlException)
282 | Just ye <- fromException e -> Left $ InvalidYaml $ Just (ye :: YamlException)
283 | otherwise -> Left $ OtherParseException e
283 | otherwise -> Left $ OtherParseException e
284− Right y -> either
284+ Right (y, warnings) -> either
285 (Left . AesonException)
285 (Left . AesonException)
287− (parseEither parseJSON y)
287+ ((,) warnings <$> parseEither parseJSON y)
289-- | Strings which must be escaped so as not to be treated as non-string scalars.
289-- | Strings which must be escaped so as not to be treated as non-string scalars.
290specialStrings :: HashSet.HashSet Text
290specialStrings :: HashSet.HashSet Text