{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE Trustworthy #-}
module Data.YAML.Loader
( decodeLoader
, Loader(..)
, LoaderT
, NodeId
) where
import Control.Monad.State
import qualified Data.ByteString.Lazy as BS.L
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.YAML.Event (Tag)
import qualified Data.YAML.Event as YE
import Util
type NodeId = Word
data Loader m n = Loader
{ yScalar :: Tag -> YE.ScalarStyle -> Text -> LoaderT m n
, ySequence :: Tag -> [n] -> LoaderT m n
, yMapping :: Tag -> [(n,n)] -> LoaderT m n
, yAlias :: NodeId -> Bool -> n -> LoaderT m n
, yAnchor :: NodeId -> n -> LoaderT m n
}
type LoaderT m n = YE.Pos -> m (Either (YE.Pos,String) n)
{-# INLINEABLE decodeLoader #-}
decodeLoader :: forall n m . MonadFix m => Loader m n -> BS.L.ByteString -> m (Either (YE.Pos, String) [n])
decodeLoader Loader{..} bs0 = do
case sequence $ filter (not. isComment) (YE.parseEvents bs0) of
Left (pos,err) -> return $ Left (pos,err)
Right evs -> runParserT goStream evs
where
isComment evPos = case evPos of
Right (YE.EvPos {eEvent = (YE.Comment _), ePos = _}) -> True
_ -> False
goStream :: PT n m [n]
goStream = do
_ <- satisfy (== YE.StreamStart)
ds <- manyUnless (== YE.StreamEnd) goDoc
eof
return ds
goDoc :: PT n m n
goDoc = do
_ <- satisfy isDocStart
modify $ \s0 -> s0 { sDict = mempty, sCycle = mempty }
n <- goNode
_ <- satisfy isDocEnd
return n
getNewNid :: PT n m Word
getNewNid = state $ \s0 -> let i0 = sIdCnt s0
in (i0, s0 { sIdCnt = i0+1 })
returnNode :: YE.Pos -> Maybe YE.Anchor -> Either (YE.Pos, String) n -> PT n m n
returnNode _ _ (Left err) = throwError err
returnNode _ Nothing (Right node) = return node
returnNode pos (Just a) (Right node) = do
nid <- getNewNid
node' <- liftEither' =<< lift (yAnchor nid node pos)
modify $ \s0 -> s0 { sDict = Map.insert a (nid,node') (sDict s0) }
return node'
registerAnchor :: YE.Pos -> Maybe YE.Anchor -> PT n m n -> PT n m n
registerAnchor _ Nothing pn = pn
registerAnchor pos (Just a) pn = do
modify $ \s0 -> s0 { sCycle = Set.insert a (sCycle s0) }
nid <- getNewNid
mdo
modify $ \s0 -> s0 { sDict = Map.insert a (nid,n) (sDict s0) }
n0 <- pn
n <- liftEither' =<< lift (yAnchor nid n0 pos)
return n
exitAnchor :: Maybe YE.Anchor -> PT n m ()
exitAnchor Nothing = return ()
exitAnchor (Just a) = modify $ \s0 -> s0 { sCycle = Set.delete a (sCycle s0) }
goNode :: PT n m n
goNode = do
n <- anyEv
let pos = YE.ePos n
case YE.eEvent n of
YE.Scalar manc tag sty val -> do
exitAnchor manc
n' <- lift (yScalar tag sty val pos)
returnNode pos manc $! n'
YE.SequenceStart manc tag _ -> registerAnchor pos manc $ do
ns <- manyUnless (== YE.SequenceEnd) goNode
exitAnchor manc
liftEither' =<< lift (ySequence tag ns pos)
YE.MappingStart manc tag _ -> registerAnchor pos manc $ do
kvs <- manyUnless (== YE.MappingEnd) (liftM2 (,) goNode goNode)
exitAnchor manc
liftEither' =<< lift (yMapping tag kvs pos)
YE.Alias a -> do
d <- gets sDict
cy <- gets sCycle
case Map.lookup a d of
Nothing -> throwError (pos, ("anchor not found: " ++ show a))
Just (nid,n') -> liftEither' =<< lift (yAlias nid (Set.member a cy) n' pos)
_ -> throwError (pos, "goNode: unexpected event")
data S n = S { sEvs :: [YE.EvPos]
, sDict :: Map YE.Anchor (Word,n)
, sCycle :: Set YE.Anchor
, sIdCnt :: !Word
}
newtype PT n m a = PT (StateT (S n) (ExceptT (YE.Pos, String) m) a)
deriving ( Functor
, Applicative
, Monad
, MonadState (S n)
, MonadError (YE.Pos, String)
, MonadFix
)
instance MonadTrans (PT n) where
lift = PT . lift . lift
runParserT :: Monad m => PT n m a -> [YE.EvPos] -> m (Either (YE.Pos, String) a)
runParserT (PT act) s0 = runExceptT $ evalStateT act (S s0 mempty mempty 0)
satisfy :: Monad m => (YE.Event -> Bool) -> PT n m YE.EvPos
satisfy p = do
s0 <- get
case sEvs s0 of
[] -> throwError (fakePos, "satisfy: premature eof")
(ev:rest)
| p (YE.eEvent ev) -> do put (s0 { sEvs = rest})
return ev
| otherwise -> throwError (YE.ePos ev, ("satisfy: predicate failed " ++ show ev))
peek :: Monad m => PT n m (Maybe YE.EvPos)
peek = do
s0 <- get
case sEvs s0 of
[] -> return Nothing
(ev:_) -> return (Just ev)
peek1 :: Monad m => PT n m YE.EvPos
peek1 = maybe (throwError (fakePos,"peek1: premature eof")) return =<< peek
anyEv :: Monad m => PT n m YE.EvPos
anyEv = satisfy (const True)
eof :: Monad m => PT n m ()
eof = do
s0 <- get
case sEvs s0 of
[] -> return ()
(ev:_) -> throwError (YE.ePos ev, "eof expected")
manyUnless :: Monad m => (YE.Event -> Bool) -> PT n m a -> PT n m [a]
manyUnless p act = do
t0 <- peek1
if p (YE.eEvent t0)
then anyEv >> return []
else liftM2 (:) act (manyUnless p act)
isDocStart :: YE.Event -> Bool
isDocStart (YE.DocumentStart _) = True
isDocStart _ = False
isDocEnd :: YE.Event -> Bool
isDocEnd (YE.DocumentEnd _) = True
isDocEnd _ = False
fakePos :: YE.Pos
fakePos = YE.Pos { posByteOffset = -1 , posCharOffset = -1 , posLine = 1 , posColumn = 0 }