packages feed

yaml 0.8.31.1 → 0.8.32

raw patch · 24 files changed

+2210/−2098 lines, 24 filesdep −unsupported-ghc-versiondep ~aesondep ~basedep ~bytestring

Dependencies removed: unsupported-ghc-version

Dependency ranges changed: aeson, base, bytestring, conduit, resourcet

Files

ChangeLog.md view
@@ -1,3 +1,13 @@+# ChangeLog for yaml++## 0.8.32++* Escape keys as necessary [#137](https://github.com/snoyberg/yaml/issues/137)+* Support hexadecimal and octal number values [#135](https://github.com/snoyberg/yaml/issues/135)+* More resilient `isNumeric` (should reduce cases of unneeded quoting)+* hpackify+* src subdir+ ## 0.8.31.1  * Add a workaround for a cabal bug [haskell-infra/hackage-trustees#165](https://github.com/haskell-infra/hackage-trustees/issues/165)
− Data/Yaml.hs
@@ -1,219 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE OverloadedStrings #-}---- | Provides a high-level interface for processing YAML files.------ This module reuses most of the infrastructure from the @aeson@ package.--- This means that you can use all of the existing tools for JSON--- processing for processing YAML files. As a result, much of the--- documentation below mentions JSON; do not let that confuse you, it's--- intentional.------ For the most part, YAML content translates directly into JSON, and--- therefore there is very little data loss. If you need to deal with YAML--- more directly (e.g., directly deal with aliases), you should use the--- "Text.Libyaml" module instead.------ For documentation on the @aeson@ types, functions, classes, and--- operators, please see the @Data.Aeson@ module of the @aeson@ package.------ Look in the examples directory of the source repository for some initial--- pointers on how to use this library.--#if (defined (ghcjs_HOST_OS))-module Data.Yaml {-# WARNING "GHCJS is not supported yet (will break at runtime once called)." #-}-#else-module Data.Yaml-#endif-    ( -- * Encoding-      encode-    , encodeFile-      -- * Decoding-    , decodeEither'-    , decodeFileEither-    , decodeThrow-    , decodeFileThrow-      -- ** More control over decoding-    , decodeHelper-      -- * Types-    , Value (..)-    , Parser-    , Object-    , Array-    , ParseException(..)-    , prettyPrintParseException-    , YamlException (..)-    , YamlMark (..)-      -- * Constructors and accessors-    , object-    , array-    , (.=)-    , (.:)-    , (.:?)-    , (.!=)-      -- ** With helpers (since 0.8.23)-    , withObject-    , withText-    , withArray-    , withScientific-    , withBool-      -- * Parsing-    , parseMonad-    , parseEither-    , parseMaybe-      -- * Classes-    , ToJSON (..)-    , FromJSON (..)-      -- * Deprecated-    , decode-    , decodeFile-    , decodeEither-    ) where-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative((<$>))-#endif-import Control.Exception-import Control.Monad.IO.Class (MonadIO, liftIO)-import Control.Monad.Trans.Resource (MonadThrow, throwM)-import Data.Aeson-    ( Value (..), ToJSON (..), FromJSON (..), object-    , (.=) , (.:) , (.:?) , (.!=)-    , Object, Array-    , withObject, withText, withArray, withScientific, withBool-    )-#if MIN_VERSION_aeson(1,0,0)-import Data.Aeson.Text (encodeToTextBuilder)-#else-import Data.Aeson.Encode (encodeToTextBuilder)-#endif-import Data.Aeson.Types (Pair, parseMaybe, parseEither, Parser)-import Data.ByteString (ByteString)-import Data.Conduit ((.|), runConduitRes)-import qualified Data.Conduit.List as CL-import qualified Data.HashMap.Strict as M-import qualified Data.HashSet as HashSet-import Data.Text.Encoding (encodeUtf8)-import qualified Data.Text.Encoding as TE-import qualified Data.Text.Lazy as TL-import Data.Text.Lazy.Builder (toLazyText)-import qualified Data.Vector as V-import System.IO.Unsafe (unsafePerformIO)--import Data.Yaml.Internal-import Text.Libyaml hiding (encode, decode, encodeFile, decodeFile)-import qualified Text.Libyaml as Y---- | Encode a value into its YAML representation.-encode :: ToJSON a => a -> ByteString-encode obj = unsafePerformIO $ runConduitRes-    $ CL.sourceList (objToEvents $ toJSON obj)-   .| Y.encode---- | Encode a value into its YAML representation and save to the given file.-encodeFile :: ToJSON a => FilePath -> a -> IO ()-encodeFile fp obj = runConduitRes-    $ CL.sourceList (objToEvents $ toJSON obj)-   .| Y.encodeFile fp--objToEvents :: Value -> [Y.Event]-objToEvents o = (:) EventStreamStart-              . (:) EventDocumentStart-              $ objToEvents' o-              [ EventDocumentEnd-              , EventStreamEnd-              ]--{- FIXME-scalarToEvent :: YamlScalar -> Event-scalarToEvent (YamlScalar v t s) = EventScalar v t s Nothing--}--objToEvents' :: Value -> [Y.Event] -> [Y.Event]---objToEvents' (Scalar s) rest = scalarToEvent s : rest-objToEvents' (Array list) rest =-    EventSequenceStart Nothing-  : foldr objToEvents' (EventSequenceEnd : rest) (V.toList list)-objToEvents' (Object pairs) rest =-    EventMappingStart Nothing-  : foldr pairToEvents (EventMappingEnd : rest) (M.toList pairs)---- Empty strings need special handling to ensure they get quoted. This avoids:--- https://github.com/snoyberg/yaml/issues/24-objToEvents' (String "") rest = EventScalar "" NoTag SingleQuoted Nothing : rest--objToEvents' (String s) rest =-    event : rest-  where-    event-        -- Make sure that special strings are encoded as strings properly.-        -- See: https://github.com/snoyberg/yaml/issues/31-        | s `HashSet.member` specialStrings || isNumeric s = EventScalar (encodeUtf8 s) NoTag SingleQuoted Nothing-        | otherwise = EventScalar (encodeUtf8 s) StrTag PlainNoTag Nothing-objToEvents' Null rest = EventScalar "null" NullTag PlainNoTag Nothing : rest-objToEvents' (Bool True) rest = EventScalar "true" BoolTag PlainNoTag Nothing : rest-objToEvents' (Bool False) rest = EventScalar "false" BoolTag PlainNoTag Nothing : rest--- Use aeson's implementation which gets rid of annoying decimal points-objToEvents' n@Number{} rest = EventScalar (TE.encodeUtf8 $ TL.toStrict $ toLazyText $ encodeToTextBuilder n) IntTag PlainNoTag Nothing : rest--pairToEvents :: Pair -> [Y.Event] -> [Y.Event]-pairToEvents (k, v) rest =-    EventScalar (encodeUtf8 k) StrTag PlainNoTag Nothing-  : objToEvents' v rest--decode :: FromJSON a-       => ByteString-       -> Maybe a-decode bs = unsafePerformIO-          $ either (const Nothing) id-          <$> decodeHelper_ (Y.decode bs)-{-# DEPRECATED decode "Please use decodeEither or decodeThrow, which provide information on how the decode failed" #-}--decodeFile :: FromJSON a-           => FilePath-           -> IO (Maybe a)-decodeFile fp = decodeHelper (Y.decodeFile fp) >>= either throwIO (return . either (const Nothing) id)-{-# DEPRECATED decodeFile "Please use decodeFileEither, which does not confused type-directed and runtime exceptions." #-}---- | A version of 'decodeFile' which should not throw runtime exceptions.------ @since 0.8.4-decodeFileEither-    :: FromJSON a-    => FilePath-    -> IO (Either ParseException a)-decodeFileEither = decodeHelper_ . Y.decodeFile--decodeEither :: FromJSON a => ByteString -> Either String a-decodeEither bs = unsafePerformIO-                $ either (Left . prettyPrintParseException) id-                <$> decodeHelper (Y.decode bs)-{-# DEPRECATED decodeEither "Please use decodeEither' or decodeThrow, which provide more useful failures" #-}---- | More helpful version of 'decodeEither' which returns the 'YamlException'.------ @since 0.8.3-decodeEither' :: FromJSON a => ByteString -> Either ParseException a-decodeEither' = either Left (either (Left . AesonException) Right)-              . unsafePerformIO-              . decodeHelper-              . Y.decode---- | A version of 'decodeEither'' lifted to MonadThrow------ @since 0.8.31-decodeThrow :: (MonadThrow m, FromJSON a) => ByteString -> m a-decodeThrow = either throwM return . decodeEither'---- | A version of 'decodeFileEither' lifted to MonadIO------ @since 0.8.31-decodeFileThrow :: (MonadIO m, FromJSON a) => FilePath -> m a-decodeFileThrow f = liftIO $ decodeFileEither f >>= either throwIO return---- | Construct a new 'Value' from a list of 'Value's.-array :: [Value] -> Value-array = Array . V.fromList--parseMonad :: Monad m => (a -> Parser b) -> a -> m b-parseMonad p = either fail return . parseEither p
− Data/Yaml/Aeson.hs
@@ -1,7 +0,0 @@--- | Just a re-export of @Data.Yaml@. In the future, this will be the canonical--- name for that module\'s contents.-module Data.Yaml.Aeson-    ( module Data.Yaml-    ) where--import Data.Yaml
− Data/Yaml/Builder.hs
@@ -1,116 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE CPP #-}--- | NOTE: This module is a highly experimental preview release. It may change--- drastically, or be entirely removed, in a future release.-module Data.Yaml.Builder-    ( YamlBuilder (..)-    , ToYaml (..)-    , mapping-    , array-    , string-    , bool-    , null-    , scientific-    , number-    , toByteString-    , writeYamlFile-    , (.=)-    ) where--import Prelude hiding (null)--import Control.Arrow (second)-#if MIN_VERSION_aeson(1,0,0)-import Data.Aeson.Text (encodeToTextBuilder)-#else-import Data.Aeson.Encode (encodeToTextBuilder)-#endif-import Data.Aeson.Types (Value(..))-import Data.ByteString (ByteString)-import qualified Data.ByteString.Char8 as S8-import Data.Conduit-import qualified Data.HashSet as HashSet-import Data.Scientific (Scientific)-import Data.Text (Text)-import Data.Text.Encoding (encodeUtf8)-import qualified Data.Text.Encoding as TE-import qualified Data.Text.Lazy as TL-import Data.Text.Lazy.Builder (toLazyText)-import System.IO.Unsafe (unsafePerformIO)--import Data.Yaml.Internal-import Text.Libyaml--(.=) :: ToYaml a => Text -> a -> (Text, YamlBuilder)-k .= v = (k, toYaml v)--newtype YamlBuilder = YamlBuilder { unYamlBuilder :: [Event] -> [Event] }--class ToYaml a where-    toYaml :: a -> YamlBuilder-instance ToYaml YamlBuilder where-    toYaml = id-instance ToYaml a => ToYaml [(Text, a)] where-    toYaml = mapping . map (second toYaml)-instance ToYaml a => ToYaml [a] where-    toYaml = array . map toYaml-instance ToYaml Text where-    toYaml = string-instance ToYaml Int where-    toYaml i = YamlBuilder (EventScalar (S8.pack $ show i) IntTag PlainNoTag Nothing:)--mapping :: [(Text, YamlBuilder)] -> YamlBuilder-mapping pairs = YamlBuilder $ \rest ->-    EventMappingStart Nothing : foldr addPair (EventMappingEnd : rest) pairs-  where-    addPair (key, YamlBuilder value) after-        = EventScalar (encodeUtf8 key) StrTag PlainNoTag Nothing-        : value after--array :: [YamlBuilder] -> YamlBuilder-array bs =-    YamlBuilder $ (EventSequenceStart Nothing:) . flip (foldr go) bs . (EventSequenceEnd:)-  where-    go (YamlBuilder b) = b--string :: Text -> YamlBuilder--- Empty strings need special handling to ensure they get quoted. This avoids:--- https://github.com/snoyberg/yaml/issues/24-string ""  = YamlBuilder (EventScalar "" NoTag SingleQuoted Nothing :)-string s   =-    YamlBuilder (event :)-  where-    event-        -- Make sure that special strings are encoded as strings properly.-        -- See: https://github.com/snoyberg/yaml/issues/31-        | s `HashSet.member` specialStrings || isNumeric s = EventScalar (encodeUtf8 s) NoTag SingleQuoted Nothing-        | otherwise = EventScalar (encodeUtf8 s) StrTag PlainNoTag Nothing- --- Use aeson's implementation which gets rid of annoying decimal points-scientific :: Scientific -> YamlBuilder-scientific n = YamlBuilder (EventScalar (TE.encodeUtf8 $ TL.toStrict $ toLazyText $ encodeToTextBuilder (Number n)) IntTag PlainNoTag Nothing :)--{-# DEPRECATED number "Use scientific" #-}-number :: Scientific -> YamlBuilder-number = scientific--bool :: Bool -> YamlBuilder-bool True   = YamlBuilder (EventScalar "true" BoolTag PlainNoTag Nothing :)-bool False  = YamlBuilder (EventScalar "false" BoolTag PlainNoTag Nothing :)--null :: YamlBuilder-null = YamlBuilder (EventScalar "null" NullTag PlainNoTag Nothing :)--toEvents :: YamlBuilder -> [Event]-toEvents (YamlBuilder front) =-    EventStreamStart : EventDocumentStart : front [EventDocumentEnd, EventStreamEnd]--toSource :: (Monad m, ToYaml a) => a -> ConduitM i Event m ()-toSource = mapM_ yield . toEvents . toYaml--toByteString :: ToYaml a => a -> ByteString-toByteString yb = unsafePerformIO $ runConduitRes $ toSource yb .| encode--writeYamlFile :: ToYaml a => FilePath -> a -> IO ()-writeYamlFile fp yb = runConduitRes $ toSource yb .| encodeFile fp
− Data/Yaml/Config.hs
@@ -1,215 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}--- | Functionality for using YAML as configuration files------ In particular, merging environment variables with yaml values------ 'loadYamlSettings' is a high-level API for loading YAML and merging environment variables.--- A yaml value of @_env:ENV_VAR:default@ will lookup the environment variable @ENV_VAR@.------ On a historical note, this code was taken directly from the yesod web framework's configuration module.-module Data.Yaml.Config-    ( -- * High-level-      loadYamlSettings-    , loadYamlSettingsArgs-      -- ** EnvUsage-    , EnvUsage-    , ignoreEnv-    , useEnv-    , requireEnv-    , useCustomEnv-    , requireCustomEnv-      -- * Lower level-    , applyCurrentEnv-    , getCurrentEnv-    , applyEnvValue-    ) where---#if __GLASGOW_HASKELL__ < 710-import Control.Applicative ((<$>))-import Data.Monoid-#endif-import Data.Semigroup-import Data.List.NonEmpty (nonEmpty)-import Data.Aeson-import qualified Data.HashMap.Strict as H-import Data.Text (Text, pack)-import System.Environment (getArgs, getEnvironment)-import Control.Arrow ((***))-import Control.Monad (forM)-import Control.Exception (throwIO)-import Data.Text.Encoding (encodeUtf8)-import qualified Data.Yaml as Y-import qualified Data.Yaml.Include as YI-import Data.Maybe (fromMaybe)-import qualified Data.Text as T--newtype MergedValue = MergedValue { getMergedValue :: Value }--instance Semigroup MergedValue where-    MergedValue x <> MergedValue y = MergedValue $ mergeValues x y---- | Left biased-mergeValues :: Value -> Value -> Value-mergeValues (Object x) (Object y) = Object $ H.unionWith mergeValues x y-mergeValues x _ = x---- | Override environment variable placeholders in the given @Value@ with--- values from the environment.------ If the first argument is @True@, then all placeholders _must_ be provided by--- the actual environment. Otherwise, default values from the @Value@ will be--- used.------ @since 0.8.16-applyEnvValue :: Bool -- ^ require an environment variable to be present?-              -> H.HashMap Text Text -> Value -> Value-applyEnvValue requireEnv' env =-    goV-  where-    goV (Object o) = Object $ goV <$> o-    goV (Array a) = Array (goV <$> a)-    goV (String t1) = fromMaybe (String t1) $ do-        t2 <- T.stripPrefix "_env:" t1-        let (name, t3) = T.break (== ':') t2-            mdef = fmap parseValue $ T.stripPrefix ":" t3-        Just $ case H.lookup name env of-            Just val ->-                -- If the default value parses as a String, we treat the-                -- environment variable as a raw value and do not parse it.-                -- This means that things like numeric passwords just work.-                -- However, for originally numerical or boolean values (e.g.,-                -- port numbers), we still perform a normal YAML parse.-                ---                -- For details, see:-                -- https://github.com/yesodweb/yesod/issues/1061-                case mdef of-                    Just (String _) -> String val-                    _ -> parseValue val-            Nothing ->-                case mdef of-                    Just val | not requireEnv' -> val-                    _ -> Null-    goV v = v--    parseValue val = either-      (const (String val))-      id-      (Y.decodeThrow $ encodeUtf8 val)---- | Get the actual environment as a @HashMap@ from @Text@ to @Text@.------ @since 0.8.16-getCurrentEnv :: IO (H.HashMap Text Text)-getCurrentEnv = fmap (H.fromList . map (pack *** pack)) getEnvironment---- | A convenience wrapper around 'applyEnvValue' and 'getCurrentEnv'------ @since 0.8.16-applyCurrentEnv :: Bool -- ^ require an environment variable to be present?-                -> Value -> IO Value-applyCurrentEnv requireEnv' orig = flip (applyEnvValue requireEnv') orig <$> getCurrentEnv---- | Defines how we want to use the environment variables when loading a config--- file. Use the smart constructors provided by this module.------ @since 0.8.16-data EnvUsage = IgnoreEnv-              | UseEnv-              | RequireEnv-              | UseCustomEnv (H.HashMap Text Text)-              | RequireCustomEnv (H.HashMap Text Text)---- | Do not use any environment variables, instead relying on defaults values--- in the config file.------ @since 0.8.16-ignoreEnv :: EnvUsage-ignoreEnv = IgnoreEnv---- | Use environment variables when available, otherwise use defaults.------ @since 0.8.16-useEnv :: EnvUsage-useEnv = UseEnv---- | Do not use default values from the config file, but instead take all--- overrides from the environment. If a value is missing, loading the file will--- throw an exception.------ @since 0.8.16-requireEnv :: EnvUsage-requireEnv = RequireEnv---- | Same as 'useEnv', but instead of the actual environment, use the provided--- @HashMap@ as the environment.------ @since 0.8.16-useCustomEnv :: H.HashMap Text Text -> EnvUsage-useCustomEnv = UseCustomEnv---- | Same as 'requireEnv', but instead of the actual environment, use the--- provided @HashMap@ as the environment.------ @since 0.8.16-requireCustomEnv :: H.HashMap Text Text -> EnvUsage-requireCustomEnv = RequireCustomEnv---- | Load the settings from the following three sources:------ * Run time config files------ * Run time environment variables------ * The default compile time config file------ For example, to load up settings from @config/foo.yaml@ and allow overriding--- from the actual environment, you can use:------ > loadYamlSettings ["config/foo.yaml"] [] useEnv------ @since 0.8.16-loadYamlSettings-    :: FromJSON settings-    => [FilePath] -- ^ run time config files to use, earlier files have precedence-    -> [Value] -- ^ any other values to use, usually from compile time config. overridden by files-    -> EnvUsage-    -> IO settings-loadYamlSettings runTimeFiles compileValues envUsage = do-    runValues <- forM runTimeFiles $ \fp -> do-        eres <- YI.decodeFileEither fp-        case eres of-            Left e -> do-                putStrLn $ "loadYamlSettings: Could not parse file as YAML: " ++ fp-                throwIO e-            Right value -> return value--    value' <--        case nonEmpty $ map MergedValue $ runValues ++ compileValues of-            Nothing -> error "loadYamlSettings: No configuration provided"-            Just ne -> return $ getMergedValue $ sconcat ne-    value <--        case envUsage of-            IgnoreEnv            -> return $ applyEnvValue   False mempty value'-            UseEnv               ->          applyCurrentEnv False        value'-            RequireEnv           ->          applyCurrentEnv True         value'-            UseCustomEnv env     -> return $ applyEnvValue   False env    value'-            RequireCustomEnv env -> return $ applyEnvValue   True  env    value'--    case fromJSON value of-        Error s -> error $ "Could not convert to expected type: " ++ s-        Success settings -> return settings---- | Same as @loadYamlSettings@, but get the list of runtime config files from--- the command line arguments.------ @since 0.8.17-loadYamlSettingsArgs-    :: FromJSON settings-    => [Value] -- ^ any other values to use, usually from compile time config. overridden by files-    -> EnvUsage -- ^ use environment variables-    -> IO settings-loadYamlSettingsArgs values env = do-    args <- getArgs-    loadYamlSettings args values env
− Data/Yaml/Include.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE RankNTypes #-}-module Data.Yaml.Include (decodeFile, decodeFileEither) where--#if !MIN_VERSION_directory(1, 2, 3)-import Control.Exception (handleJust)-import Control.Monad (guard)-import System.IO.Error (ioeGetFileName, ioeGetLocation, isDoesNotExistError)-#endif--import Control.Exception (throwIO)-import Control.Monad (when)-import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Resource (MonadResource)-import Data.Aeson (FromJSON)-import Data.Conduit-import qualified Data.Conduit.List as CL-import Data.Text (unpack)-import Data.Text.Encoding (decodeUtf8)-import System.Directory-import System.FilePath--import Data.Yaml.Internal (ParseException(..), decodeHelper_, decodeHelper)-import Text.Libyaml hiding (decodeFile)-import qualified Text.Libyaml as Y--eventsFromFile-    :: MonadResource m-    => FilePath-    -> ConduitM i Event m ()-eventsFromFile = go []-  where-    go :: MonadResource m => [FilePath] -> FilePath -> ConduitM i Event m ()-    go seen fp = do-        cfp <- liftIO $ handleNotFound $ canonicalizePath fp-        when (cfp `elem` seen) $ do-            liftIO $ throwIO CyclicIncludes-        Y.decodeFile cfp .| do-            awaitForever $ \event -> case event of-                EventScalar f (UriTag "!include") _ _ -> do-                    let includeFile = takeDirectory cfp </> unpack (decodeUtf8 f)-                    go (cfp : seen) includeFile .| CL.filter (`notElem` irrelevantEvents)-                _ -> yield event--    irrelevantEvents = [EventStreamStart, EventDocumentStart, EventDocumentEnd, EventStreamEnd]--#if !MIN_VERSION_directory(1, 2, 3)-    handleNotFound = handleJust-        (\e -> do-            guard (isDoesNotExistError e)-            guard (ioeGetLocation e == "canonicalizePath")-            ioeGetFileName e)-        (throwIO . YamlException . ("Yaml file not found: " ++))-#else-    handleNotFound = id-#endif---- | Like `Data.Yaml.decodeFile` but with support for relative and absolute--- includes.------ The syntax for includes follows the form:------ > somekey: !include ./somefile.yaml-decodeFile-    :: FromJSON a-    => FilePath-    -> IO (Maybe a)-decodeFile fp = decodeHelper (eventsFromFile fp) >>= either throwIO (return . either (const Nothing) id)---- | Like `Data.Yaml.decodeFileEither` but with support for relative and--- absolute includes.------ The syntax for includes follows the form:------ > somekey: !include ./somefile.yaml-decodeFileEither-    :: FromJSON a-    => FilePath-    -> IO (Either ParseException a)-decodeFileEither = decodeHelper_ . eventsFromFile
− Data/Yaml/Internal.hs
@@ -1,296 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE PatternGuards #-}-{-# LANGUAGE OverloadedStrings #-}-module Data.Yaml.Internal-    (-      ParseException(..)-    , prettyPrintParseException-    , parse-    , decodeHelper-    , decodeHelper_-    , specialStrings-    , isNumeric-    ) where--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), Applicative(..))-#endif-import Control.Exception-import Control.Monad (liftM, ap, unless)-import Control.Monad.IO.Class (MonadIO, liftIO)-import Control.Monad.Trans.Class (MonadTrans, lift)-import Control.Monad.Trans.Resource (ResourceT, runResourceT)-import Control.Monad.Trans.State-import Data.Aeson-import Data.Aeson.Types hiding (parse)-import qualified Data.Attoparsec.Text as Atto-import Data.ByteString (ByteString)-import Data.Char (toUpper)-import Data.Conduit ((.|), ConduitM, runConduit)-import qualified Data.Conduit.List as CL-import qualified Data.HashMap.Strict as M-import qualified Data.HashSet as HashSet-import qualified Data.Map as Map-import Data.Scientific (Scientific)-import Data.Text (Text, pack)-import qualified Data.Text as T-import Data.Text.Encoding (decodeUtf8With)-import Data.Text.Encoding.Error (lenientDecode)-import Data.Typeable-import qualified Data.Vector as V--import qualified Text.Libyaml as Y-import Text.Libyaml hiding (encode, decode, encodeFile, decodeFile)--data ParseException = NonScalarKey-                    | UnknownAlias { _anchorName :: Y.AnchorName }-                    | UnexpectedEvent { _received :: Maybe Event-                                      , _expected :: Maybe Event-                                      }-                    | InvalidYaml (Maybe YamlException)-                    | AesonException String-                    | OtherParseException SomeException-                    | NonStringKeyAlias Y.AnchorName Value-                    | CyclicIncludes-    deriving (Show, Typeable)--instance Exception ParseException where-#if MIN_VERSION_base(4, 8, 0)-  displayException = prettyPrintParseException-#endif---- | Alternative to 'show' to display a 'ParseException' on the screen.---   Instead of displaying the data constructors applied to their arguments,---   a more textual output is returned. For example, instead of printing:------ > InvalidYaml (Just (YamlParseException {yamlProblem = "did not find expected ',' or '}'", yamlContext = "while parsing a flow mapping", yamlProblemMark = YamlMark {yamlIndex = 42, yamlLine = 2, yamlColumn = 12}})))------   It looks more pleasant to print:------ > YAML parse exception at line 2, column 12,--- > while parsing a flow mapping:--- > did not find expected ',' or '}'------ Since 0.8.11-prettyPrintParseException :: ParseException -> String-prettyPrintParseException pe = case pe of-  NonScalarKey -> "Non scalar key"-  UnknownAlias anchor -> "Unknown alias `" ++ anchor ++ "`"-  UnexpectedEvent { _expected = mbExpected, _received = mbUnexpected } -> unlines-    [ "Unexpected event: expected"-    , "  " ++ show mbExpected-    , "but received"-    , "  " ++ show mbUnexpected-    ]-  InvalidYaml mbYamlError -> case mbYamlError of-    Nothing -> "Unspecified YAML error"-    Just yamlError -> case yamlError of-      YamlException s -> "YAML exception:\n" ++ s-      YamlParseException problem context mark -> concat-        [ "YAML parse exception at line " ++ show (yamlLine mark) ++-          ", column " ++ show (yamlColumn mark)-        , case context of-            "" -> ":\n"-            -- The context seems to include a leading "while" or similar.-            _  -> ",\n" ++ context ++ ":\n"-        , problem-        ]-  AesonException s -> "Aeson exception:\n" ++ s-  OtherParseException exc -> "Generic parse exception:\n" ++ show exc-  NonStringKeyAlias anchor value -> unlines-    [ "Non-string key alias:"-    , "  Anchor name: " ++ anchor-    , "  Value: " ++ show value-    ]-  CyclicIncludes -> "Cyclic includes"--newtype PErrorT m a = PErrorT { runPErrorT :: m (Either ParseException a) }-instance Monad m => Functor (PErrorT m) where-    fmap = liftM-instance Monad m => Applicative (PErrorT m) where-    pure  = PErrorT . return . Right-    (<*>) = ap-instance Monad m => Monad (PErrorT m) where-    return = pure-    (PErrorT m) >>= f = PErrorT $ do-        e <- m-        case e of-            Left e' -> return $ Left e'-            Right a -> runPErrorT $ f a-instance MonadTrans PErrorT where-    lift = PErrorT . liftM Right-instance MonadIO m => MonadIO (PErrorT m) where-    liftIO = lift . liftIO--type Parse = StateT (Map.Map String Value) (ResourceT IO)--requireEvent :: Event -> ConduitM Event o Parse ()-requireEvent e = do-    f <- CL.head-    unless (f == Just e) $ liftIO $ throwIO $ UnexpectedEvent f $ Just e--parse :: ConduitM Event o Parse Value-parse = do-    streamStart <- CL.head-    case streamStart of-        Nothing ->-            -- empty string input-            return Null-        Just EventStreamStart -> do-            documentStart <- CL.head-            case documentStart of-                Just EventStreamEnd ->-                    -- empty file input, comment only string/file input-                    return Null-                Just EventDocumentStart -> do-                    res <- parseO-                    requireEvent EventDocumentEnd-                    requireEvent EventStreamEnd-                    return res-                _ -> liftIO $ throwIO $ UnexpectedEvent documentStart Nothing-        _ -> liftIO $ throwIO $ UnexpectedEvent streamStart Nothing--parseScalar :: ByteString -> Anchor -> Style -> Tag-            -> ConduitM Event o Parse Text-parseScalar v a style tag = do-    let res = decodeUtf8With lenientDecode v-    case a of-        Nothing -> return res-        Just an -> do-            lift $ modify (Map.insert an $ textToValue style tag res)-            return res--textToValue :: Style -> Tag -> Text -> Value-textToValue SingleQuoted _ t = String t-textToValue DoubleQuoted _ t = String t-textToValue _ StrTag t = String t-textToValue Folded _ t = String t-textToValue _ _ t-    | t `elem` ["null", "Null", "NULL", "~", ""] = Null-    | any (t `isLike`) ["y", "yes", "on", "true"] = Bool True-    | any (t `isLike`) ["n", "no", "off", "false"] = Bool False-    | Right x <- textToScientific t = Number x-    | otherwise = String t-  where x `isLike` ref = x `elem` [ref, T.toUpper ref, titleCased]-          where titleCased = toUpper (T.head ref) `T.cons` T.tail ref--textToScientific :: Text -> Either String Scientific-textToScientific = Atto.parseOnly (Atto.scientific <* Atto.endOfInput)--parseO :: ConduitM Event o Parse Value-parseO = do-    me <- CL.head-    case me of-        Just (EventScalar v tag style a) -> textToValue style tag <$> parseScalar v a style tag-        Just (EventSequenceStart a) -> parseS a id-        Just (EventMappingStart a) -> parseM a M.empty-        Just (EventAlias an) -> do-            m <- lift get-            case Map.lookup an m of-                Nothing -> liftIO $ throwIO $ UnknownAlias an-                Just v -> return v-        _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing--parseS :: Y.Anchor-       -> ([Value] -> [Value])-       -> ConduitM Event o Parse Value-parseS a front = do-    me <- CL.peek-    case me of-        Just EventSequenceEnd -> do-            CL.drop 1-            let res = Array $ V.fromList $ front []-            case a of-                Nothing -> return res-                Just an -> do-                    lift $ modify $ Map.insert an res-                    return res-        _ -> do-            o <- parseO-            parseS a $ front . (:) o--parseM :: Y.Anchor-       -> M.HashMap Text Value-       -> ConduitM Event o Parse Value-parseM a front = do-    me <- CL.peek-    case me of-        Just EventMappingEnd -> do-            CL.drop 1-            let res = Object front-            case a of-                Nothing -> return res-                Just an -> do-                    lift $ modify $ Map.insert an res-                    return res-        _ -> do-            CL.drop 1-            s <- case me of-                    Just (EventScalar v tag style a') -> parseScalar v a' style tag-                    Just (EventAlias an) -> do-                        m <- lift get-                        case Map.lookup an m of-                            Nothing -> liftIO $ throwIO $ UnknownAlias an-                            Just (String t) -> return t-                            Just v -> liftIO $ throwIO $ NonStringKeyAlias an v-                    _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing-            o <- parseO--            let al  = M.insert s o front-                al' = if s == pack "<<"-                         then case o of-                                  Object l  -> M.union front l-                                  Array l -> M.union front $ foldl merge' M.empty $ V.toList l-                                  _          -> al-                         else al-            parseM a al'-    where merge' al (Object om) = M.union al om-          merge' al _           = al--decodeHelper :: FromJSON a-             => ConduitM () Y.Event Parse ()-             -> IO (Either ParseException (Either String a))-decodeHelper src = do-    -- This used to be tryAny, but the fact is that catching async-    -- exceptions is fine here. We'll rethrow them immediately in the-    -- otherwise clause.-    x <- try $ runResourceT $ flip evalStateT Map.empty $ runConduit $ src .| parse-    case x of-        Left e-            | Just pe <- fromException e -> return $ Left pe-            | Just ye <- fromException e -> return $ Left $ InvalidYaml $ Just (ye :: YamlException)-            | otherwise -> throwIO e-        Right y -> return $ Right $ parseEither parseJSON y--decodeHelper_ :: FromJSON a-              => ConduitM () Event Parse ()-              -> IO (Either ParseException a)-decodeHelper_ src = do-    x <- try $ runResourceT $ flip evalStateT Map.empty $ runConduit $ src .| parse-    return $ case x of-        Left e-            | Just pe <- fromException e -> Left pe-            | Just ye <- fromException e -> Left $ InvalidYaml $ Just (ye :: YamlException)-            | otherwise -> Left $ OtherParseException e-        Right y -> either-            (Left . AesonException)-            Right-            (parseEither parseJSON y)---- | Strings which must be escaped so as not to be treated as non-string scalars.-specialStrings :: HashSet.HashSet Text-specialStrings = HashSet.fromList $ T.words-    "y Y yes Yes YES n N no No NO true True TRUE false False FALSE on On ON off Off OFF null Null NULL ~ *"--isNumeric :: Text -> Bool-isNumeric t =-    T.all isNumeric' t && T.any isNumber t-  where-    isNumber c = '0' <= c && c <= '9'-    isNumeric' c = isNumber c-                || c == 'e'-                || c == 'E'-                || c == '.'-                || c == '-'-                || c == '+'
− Data/Yaml/Parser.hs
@@ -1,201 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE OverloadedStrings #-}--- | NOTE: This module is a highly experimental preview release. It may change--- drastically, or be entirely removed, in a future release.-module Data.Yaml.Parser where--import Control.Applicative-import Control.Exception (Exception)-import Control.Monad (MonadPlus (..), liftM, ap)-import Control.Monad.Trans.Class (lift)-import Control.Monad.Trans.Resource (MonadThrow, throwM)-import Control.Monad.Trans.Writer.Strict (tell, WriterT)-import Data.ByteString (ByteString)-import Data.Conduit-import Data.Conduit.Lift (runWriterC)-import qualified Data.Map as Map-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid (Monoid (..))-#endif-#if !MIN_VERSION_base(4,11,0)-import Data.Semigroup (Semigroup(..))-#endif-import Data.Text (Text, pack, unpack)-import Data.Text.Encoding (decodeUtf8)-import Data.Text.Read (signed, decimal)-import Data.Typeable (Typeable)--import Text.Libyaml--newtype YamlParser a = YamlParser-    { unYamlParser :: AnchorMap -> Either Text a-    }-instance Functor YamlParser where-    fmap = liftM-instance Applicative YamlParser where-    pure = YamlParser . const . Right-    (<*>) = ap-instance Alternative YamlParser where-    empty = fail "empty"-    (<|>) = mplus-instance Semigroup (YamlParser a) where-    (<>) = mplus-instance Monoid (YamlParser a) where-    mempty = fail "mempty"-#if !MIN_VERSION_base(4,11,0)-    mappend = (<>)-#endif-instance Monad YamlParser where-    return = pure-    YamlParser f >>= g = YamlParser $ \am ->-        case f am of-            Left t -> Left t-            Right x -> unYamlParser (g x) am-    fail = YamlParser . const . Left . pack-instance MonadPlus YamlParser where-    mzero = fail "mzero"-    mplus a b = YamlParser $ \am ->-        case unYamlParser a am of-            Left _ -> unYamlParser b am-            x -> x--lookupAnchor :: AnchorName -> YamlParser (Maybe YamlValue)-lookupAnchor name = YamlParser $ Right . Map.lookup name--withAnchor :: AnchorName -> Text -> (YamlValue -> YamlParser a) -> YamlParser a-withAnchor name expected f = do-    mv <- lookupAnchor name-    case mv of-        Nothing -> fail $ unpack expected ++ ": unknown alias " ++ name-        Just v -> f v--withMapping :: Text -> ([(Text, YamlValue)] -> YamlParser a) -> YamlValue -> YamlParser a-withMapping _ f (Mapping m _) = f m-withMapping expected f (Alias an) = withAnchor an expected $ withMapping expected f-withMapping expected _ v = typeMismatch expected v--withSequence :: Text -> ([YamlValue] -> YamlParser a) -> YamlValue -> YamlParser a-withSequence _ f (Sequence s _) = f s-withSequence expected f (Alias an) = withAnchor an expected $ withSequence expected f-withSequence expected _ v = typeMismatch expected v--withText :: Text -> (Text -> YamlParser a) -> YamlValue -> YamlParser a-withText _ f (Scalar s _ _ _) = f $ decodeUtf8 s-withText expected f (Alias an) = withAnchor an expected $ withText expected f-withText expected _ v = typeMismatch expected v--typeMismatch :: Text -> YamlValue -> YamlParser a-typeMismatch expected v =-    fail $ concat-        [ "Expected "-        , unpack expected-        , ", but got: "-        , t-        ]-  where-    t = case v of-        Mapping _ _ -> "mapping"-        Sequence _ _ -> "sequence"-        Scalar _ _ _ _ -> "scalar"-        Alias _ -> "alias"--class FromYaml a where-    fromYaml :: YamlValue -> YamlParser a-instance FromYaml YamlValue where-    fromYaml = return-instance FromYaml a => FromYaml [a] where-    fromYaml = withSequence "[a]" (mapM fromYaml)-instance FromYaml Text where-    fromYaml = withText "Text" return-instance FromYaml Int where-    fromYaml =-        withText "Int" go-      where-        go t =-            case signed decimal t of-                Right (i, "") -> return i-                _ -> fail $ "Invalid Int: " ++ unpack t--data YamlValue-    = Mapping [(Text, YamlValue)] Anchor-    | Sequence [YamlValue] Anchor-    | Scalar ByteString Tag Style Anchor-    | Alias AnchorName-    deriving Show--type AnchorMap = Map.Map AnchorName YamlValue-data RawDoc = RawDoc YamlValue AnchorMap-    deriving Show--parseRawDoc :: (FromYaml a, MonadThrow m) => RawDoc -> m a-parseRawDoc (RawDoc val am) =-    case unYamlParser (fromYaml val) am of-        Left t -> throwM $ FromYamlException t-        Right x -> return x--(.:) :: FromYaml a => [(Text, YamlValue)] -> Text -> YamlParser a-o .: k =-    case lookup k o of-        Nothing -> fail $ "Key not found: " ++ unpack k-        Just v -> fromYaml v--data YamlParseException-    = UnexpectedEndOfEvents-    | UnexpectedEvent Event-    | FromYamlException Text-    deriving (Show, Typeable)-instance Exception YamlParseException--sinkValue :: MonadThrow m => ConduitM Event o (WriterT AnchorMap m) YamlValue-sinkValue =-    start-  where-    start = await >>= maybe (throwM UnexpectedEndOfEvents) go--    tell' Nothing val = return val-    tell' (Just name) val = do-        lift $ tell $ Map.singleton name val-        return val--    go EventStreamStart = start-    go EventDocumentStart = start-    go (EventAlias a) = return $ Alias a-    go (EventScalar a b c d) = tell' d $ Scalar a b c d-    go (EventSequenceStart mname) = do-        vals <- goS id-        let val = Sequence vals mname-        tell' mname val-    go (EventMappingStart mname) = do-        pairs <- goM id-        let val = Mapping pairs mname-        tell' mname val--    go e = throwM $ UnexpectedEvent e--    goS front = do-        me <- await-        case me of-            Nothing -> throwM UnexpectedEndOfEvents-            Just EventSequenceEnd -> return $ front []-            Just e -> do-                val <- go e-                goS (front . (val:))--    goM front = do-        mk <- await-        case mk of-            Nothing -> throwM UnexpectedEndOfEvents-            Just EventMappingEnd -> return $ front []-            Just (EventScalar a b c d) -> do-                _ <- tell' d $ Scalar a b c d-                let k = decodeUtf8 a-                v <- start-                goM (front . ((k, v):))-            Just e -> throwM $ UnexpectedEvent e--sinkRawDoc :: MonadThrow m => ConduitM Event o m RawDoc-sinkRawDoc = uncurry RawDoc <$> runWriterC sinkValue--readYamlFile :: FromYaml a => FilePath -> IO a-readYamlFile fp = runConduitRes (decodeFile fp .| sinkRawDoc) >>= parseRawDoc
− Data/Yaml/Pretty.hs
@@ -1,85 +0,0 @@-{-# LANGUAGE CPP #-}--- | Prettier YAML encoding.------ @since 0.8.13-module Data.Yaml.Pretty-    ( encodePretty-    , Config-    , getConfCompare-    , setConfCompare-    , getConfDropNull-    , setConfDropNull-    , defConfig-    ) where--import Prelude hiding (null)--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))-#endif-import Data.Aeson.Types-import Data.ByteString (ByteString)-import Data.Function (on)-import qualified Data.HashMap.Strict as HM-import Data.List (sortBy)-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid-#endif-import Data.Text (Text)-import qualified Data.Vector as V--import Data.Yaml.Builder---- |--- @since 0.8.13-data Config = Config-  { confCompare :: Text -> Text -> Ordering -- ^ Function used to sort keys in objects-  , confDropNull :: Bool-  }---- | The default configuration: do not sort objects or drop keys------ @since 0.8.13-defConfig :: Config-defConfig = Config mempty False---- |--- @since 0.8.13-getConfCompare :: Config -> Text -> Text -> Ordering-getConfCompare = confCompare---- | Sets ordering for object keys------ @since 0.8.13-setConfCompare :: (Text -> Text -> Ordering) -> Config -> Config-setConfCompare cmp c = c { confCompare = cmp }---- |--- @since 0.8.24-getConfDropNull :: Config -> Bool-getConfDropNull = confDropNull---- | Drop entries with `Null` value from objects, if set to `True`------ @since 0.8.24-setConfDropNull :: Bool -> Config -> Config-setConfDropNull m c = c { confDropNull = m }--pretty :: Config -> Value -> YamlBuilder-pretty cfg = go-  where go (Object o) = let sort = sortBy (confCompare cfg `on` fst)-                            select-                              | confDropNull cfg = HM.filter (/= Null)-                              | otherwise        = id-                        in mapping (sort $ HM.toList $ HM.map go $ select o)-        go (Array a)  = array (go <$> V.toList a)-        go Null       = null-        go (String s) = string s-        go (Number n) = scientific n-        go (Bool b)   = bool b---- | Configurable 'encode'.------ @since 0.8.13-encodePretty :: ToJSON a => Config -> a -> ByteString-encodePretty cfg = toByteString . pretty cfg . toJSON
− Data/Yaml/TH.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE CPP                 #-}-{-# LANGUAGE LambdaCase          #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Data.Yaml.TH-  ( -- * Decoding-    yamlQQ-#if MIN_VERSION_template_haskell(2,9,0)-  , decodeFile-#endif-    -- * Re-exports from "Data.Yaml"-  , Value (..)-  , Parser-  , Object-  , Array-  , object-  , array-  , (.=)-  , (.:)-  , (.:?)-  , (.!=)-  , FromJSON (..)-  ) where--import           Data.Text.Encoding-import qualified Data.Text as T-import           Language.Haskell.TH-import           Language.Haskell.TH.Syntax-import           Language.Haskell.TH.Quote--import           Data.Yaml hiding (decodeFile)---- | Decode a YAML file at compile time. Only available on GHC version @7.8.1@--- or higher.------ @since 0.8.19.0------ ==== __Examples__------ @--- {-\# LANGUAGE TemplateHaskell \#-}------ config :: Config--- config = $$('decodeFile' "config.yaml")--- @-decodeFile :: forall a. (Lift a, FromJSON a) => FilePath -> Q (TExp a)-decodeFile path = do-  addDependentFile path-  x <- runIO $ decodeFileThrow path-  fmap TExp (lift (x :: a))--yamlExp :: String -> Q Exp-yamlExp input = do-  val <- runIO $ decodeThrow $ encodeUtf8 $ T.pack input-  lift (val :: Value)---- | A @QuasiQuoter@ for YAML.------ @since 0.8.28.0------ ==== __Examples__------ @--- {-\# LANGUAGE QuasiQuotes \#-}--- import Data.Yaml.TH------ value :: Value--- value = [yamlQQ|--- name: John Doe--- age: 23--- |]--- @-yamlQQ :: QuasiQuoter-yamlQQ = QuasiQuoter {-  quoteExp  = yamlExp-, quotePat  = notDefined "quotePat"-, quoteType = notDefined "quoteType"-, quoteDec  = notDefined "quoteDec"-} where-    notDefined name _ = fail (name ++ " is not defined for yamlQQ")
README.md view
@@ -11,7 +11,7 @@  ### Examples -Usage examples can be found in the `Data.Yaml` documentation or in the [examples](./examples) directory.+Usage examples can be found in the `Data.Yaml` documentation or in the [examples](https://github.com/snoyberg/yaml/tree/master/examples) directory.  ### Additional modules 
− Text/Libyaml.hs
@@ -1,643 +0,0 @@-{-# LANGUAGE EmptyDataDecls #-}-{-# LANGUAGE ForeignFunctionInterface #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE TypeFamilies #-}---- | Low-level, streaming YAML interface. For a higher-level interface, see--- "Data.Yaml".-module Text.Libyaml-    ( -- * The event stream-      Event (..)-    , Style (..)-    , Tag (..)-    , AnchorName-    , Anchor-      -- * Encoding and decoding-    , encode-    , decode-    , encodeFile-    , decodeFile-      -- * Error handling-    , YamlException (..)-    , YamlMark (..)-    ) where--import Prelude hiding (pi)--import Data.Bits ((.|.))-import Foreign.C-import Foreign.Ptr-import Foreign.ForeignPtr-#if MIN_VERSION_base(4,7,0)-import Foreign.ForeignPtr.Unsafe-#endif-import Foreign.Marshal.Alloc-import qualified System.Posix.Internals as Posix--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-#endif-import Control.Exception (mask_, throwIO, Exception, finally)-import Control.Monad-import Control.Monad.IO.Class-import Control.Monad.Trans.Resource-import Data.Conduit hiding (Source, Sink, Conduit)-import Data.Data--import Data.ByteString (ByteString, packCStringLen)-import qualified Data.ByteString-import qualified Data.ByteString.Char8 as B8-import qualified Data.ByteString.Internal as B-import qualified Data.ByteString.Unsafe as BU--data Event =-      EventStreamStart-    | EventStreamEnd-    | EventDocumentStart-    | EventDocumentEnd-    | EventAlias !AnchorName-    | EventScalar !ByteString !Tag !Style !Anchor-    | EventSequenceStart !Anchor-    | EventSequenceEnd-    | EventMappingStart !Anchor-    | EventMappingEnd-    deriving (Show, Eq)--data Style = Any-           | Plain-           | SingleQuoted-           | DoubleQuoted-           | Literal-           | Folded-           | PlainNoTag-    deriving (Show, Read, Eq, Enum, Bounded, Ord, Data, Typeable)--data Tag = StrTag-         | FloatTag-         | NullTag-         | BoolTag-         | SetTag-         | IntTag-         | SeqTag-         | MapTag-         | UriTag String-         | NoTag-    deriving (Show, Eq, Read, Data, Typeable)--type AnchorName = String-type Anchor = Maybe AnchorName--tagToString :: Tag -> String-tagToString StrTag = "tag:yaml.org,2002:str"-tagToString FloatTag = "tag:yaml.org,2002:float"-tagToString NullTag = "tag:yaml.org,2002:null"-tagToString BoolTag = "tag:yaml.org,2002:bool"-tagToString SetTag = "tag:yaml.org,2002:set"-tagToString IntTag = "tag:yaml.org,2002:int"-tagToString SeqTag = "tag:yaml.org,2002:seq"-tagToString MapTag = "tag:yaml.org,2002:map"-tagToString (UriTag s) = s-tagToString NoTag = ""--bsToTag :: ByteString -> Tag-bsToTag = stringToTag . B8.unpack--stringToTag :: String -> Tag-stringToTag "tag:yaml.org,2002:str" = StrTag-stringToTag "tag:yaml.org,2002:float" = FloatTag-stringToTag "tag:yaml.org,2002:null" = NullTag-stringToTag "tag:yaml.org,2002:bool" = BoolTag-stringToTag "tag:yaml.org,2002:set" = SetTag-stringToTag "tag:yaml.org,2002:int" = IntTag-stringToTag "tag:yaml.org,2002:seq" = SeqTag-stringToTag "tag:yaml.org,2002:map" = MapTag-stringToTag "" = NoTag-stringToTag s = UriTag s--data ParserStruct-type Parser = Ptr ParserStruct-parserSize :: Int-parserSize = 480--data EventRawStruct-type EventRaw = Ptr EventRawStruct-eventSize :: Int-eventSize = 104--foreign import ccall unsafe "yaml_parser_initialize"-    c_yaml_parser_initialize :: Parser -> IO CInt--foreign import ccall unsafe "yaml_parser_delete"-    c_yaml_parser_delete :: Parser -> IO ()--foreign import ccall unsafe "yaml_parser_set_input_string"-    c_yaml_parser_set_input_string :: Parser-                                   -> Ptr CUChar-                                   -> CULong-                                   -> IO ()--foreign import ccall unsafe "yaml_parser_set_input_file"-    c_yaml_parser_set_input_file :: Parser-                                 -> File-                                 -> IO ()--data FileStruct-type File = Ptr FileStruct--#ifdef WINDOWS-foreign import ccall unsafe "_fdopen"-#else-foreign import ccall unsafe "fdopen"-#endif-    c_fdopen :: CInt-             -> Ptr CChar-             -> IO File-foreign import ccall unsafe "fclose"-    c_fclose :: File-             -> IO ()--foreign import ccall unsafe "fclose_helper"-    c_fclose_helper :: File -> IO ()--foreign import ccall unsafe "yaml_parser_parse"-    c_yaml_parser_parse :: Parser -> EventRaw -> IO CInt--foreign import ccall unsafe "yaml_event_delete"-    c_yaml_event_delete :: EventRaw -> IO ()--foreign import ccall "get_parser_error_problem"-    c_get_parser_error_problem :: Parser -> IO (Ptr CUChar)--foreign import ccall "get_parser_error_context"-    c_get_parser_error_context :: Parser -> IO (Ptr CUChar)--foreign import ccall unsafe "get_parser_error_index"-    c_get_parser_error_index :: Parser -> IO CULong--foreign import ccall unsafe "get_parser_error_line"-    c_get_parser_error_line :: Parser -> IO CULong--foreign import ccall unsafe "get_parser_error_column"-    c_get_parser_error_column :: Parser -> IO CULong--makeString :: MonadIO m => (a -> m (Ptr CUChar)) -> a -> m String-makeString f a = do-    cchar <- castPtr `liftM` f a-    if cchar == nullPtr-        then return ""-        else liftIO $ peekCString cchar--data EventType = YamlNoEvent-               | YamlStreamStartEvent-               | YamlStreamEndEvent-               | YamlDocumentStartEvent-               | YamlDocumentEndEvent-               | YamlAliasEvent-               | YamlScalarEvent-               | YamlSequenceStartEvent-               | YamlSequenceEndEvent-               | YamlMappingStartEvent-               | YamlMappingEndEvent-               deriving (Enum,Show)--foreign import ccall unsafe "get_event_type"-    c_get_event_type :: EventRaw -> IO CInt--foreign import ccall unsafe "get_scalar_value"-    c_get_scalar_value :: EventRaw -> IO (Ptr CUChar)--foreign import ccall unsafe "get_scalar_length"-    c_get_scalar_length :: EventRaw -> IO CULong--foreign import ccall unsafe "get_scalar_tag"-    c_get_scalar_tag :: EventRaw -> IO (Ptr CUChar)--foreign import ccall unsafe "get_scalar_tag_len"-    c_get_scalar_tag_len :: EventRaw -> IO CULong--foreign import ccall unsafe "get_scalar_style"-    c_get_scalar_style :: EventRaw -> IO CInt--foreign import ccall unsafe "get_scalar_anchor"-    c_get_scalar_anchor :: EventRaw -> IO CString--foreign import ccall unsafe "get_sequence_start_anchor"-    c_get_sequence_start_anchor :: EventRaw -> IO CString--foreign import ccall unsafe "get_mapping_start_anchor"-    c_get_mapping_start_anchor :: EventRaw -> IO CString--foreign import ccall unsafe "get_alias_anchor"-    c_get_alias_anchor :: EventRaw -> IO CString--getEvent :: EventRaw -> IO (Maybe Event)-getEvent er = do-    et <- c_get_event_type er-    case toEnum $ fromEnum et of-        YamlNoEvent -> return Nothing-        YamlStreamStartEvent -> return $ Just EventStreamStart-        YamlStreamEndEvent -> return $ Just EventStreamEnd-        YamlDocumentStartEvent -> return $ Just EventDocumentStart-        YamlDocumentEndEvent -> return $ Just EventDocumentEnd-        YamlAliasEvent -> do-            yanchor <- c_get_alias_anchor er-            anchor <- if yanchor == nullPtr-                          then error "got YamlAliasEvent with empty anchor"-                          else peekCString yanchor-            return $ Just $ EventAlias anchor-        YamlScalarEvent -> do-            yvalue <- c_get_scalar_value er-            ylen <- c_get_scalar_length er-            ytag <- c_get_scalar_tag er-            ytag_len <- c_get_scalar_tag_len er-            ystyle <- c_get_scalar_style er-            let ytag_len' = fromEnum ytag_len-            let yvalue' = castPtr yvalue-            let ytag' = castPtr ytag-            let ylen' = fromEnum ylen-            bs <- packCStringLen (yvalue', ylen')-            tagbs <--                if ytag_len' == 0-                    then return Data.ByteString.empty-                    else packCStringLen (ytag', ytag_len')-            let style = toEnum $ fromEnum ystyle-            yanchor <- c_get_scalar_anchor er-            anchor <- if yanchor == nullPtr-                          then return Nothing-                          else Just <$> peekCString yanchor-            return $ Just $ EventScalar bs (bsToTag tagbs) style anchor-        YamlSequenceStartEvent -> do-            yanchor <- c_get_sequence_start_anchor er-            anchor <- if yanchor == nullPtr-                          then return Nothing-                          else Just <$> peekCString yanchor-            return $ Just $ EventSequenceStart anchor-        YamlSequenceEndEvent -> return $ Just EventSequenceEnd-        YamlMappingStartEvent -> do-            yanchor <- c_get_mapping_start_anchor er-            anchor <- if yanchor == nullPtr-                          then return Nothing-                          else Just <$> peekCString yanchor-            return $ Just $ EventMappingStart anchor-        YamlMappingEndEvent -> return $ Just EventMappingEnd---- Emitter--data EmitterStruct-type Emitter = Ptr EmitterStruct-emitterSize :: Int-emitterSize = 432--foreign import ccall unsafe "yaml_emitter_initialize"-    c_yaml_emitter_initialize :: Emitter -> IO CInt--foreign import ccall unsafe "yaml_emitter_delete"-    c_yaml_emitter_delete :: Emitter -> IO ()--data BufferStruct-type Buffer = Ptr BufferStruct-bufferSize :: Int-bufferSize = 16--foreign import ccall unsafe "buffer_init"-    c_buffer_init :: Buffer -> IO ()--foreign import ccall unsafe "get_buffer_buff"-    c_get_buffer_buff :: Buffer -> IO (Ptr CUChar)--foreign import ccall unsafe "get_buffer_used"-    c_get_buffer_used :: Buffer -> IO CULong--foreign import ccall unsafe "my_emitter_set_output"-    c_my_emitter_set_output :: Emitter -> Buffer -> IO ()--#ifndef __NO_UNICODE__-foreign import ccall unsafe "yaml_emitter_set_unicode"-    c_yaml_emitter_set_unicode :: Emitter -> CInt -> IO ()-#endif--foreign import ccall unsafe "yaml_emitter_set_output_file"-    c_yaml_emitter_set_output_file :: Emitter -> File -> IO ()--foreign import ccall unsafe "yaml_emitter_emit"-    c_yaml_emitter_emit :: Emitter -> EventRaw -> IO CInt--foreign import ccall unsafe "yaml_stream_start_event_initialize"-    c_yaml_stream_start_event_initialize :: EventRaw -> CInt -> IO CInt--foreign import ccall unsafe "yaml_stream_end_event_initialize"-    c_yaml_stream_end_event_initialize :: EventRaw -> IO CInt--foreign import ccall unsafe "yaml_scalar_event_initialize"-    c_yaml_scalar_event_initialize-        :: EventRaw-        -> Ptr CUChar -- anchor-        -> Ptr CUChar -- tag-        -> Ptr CUChar -- value-        -> CInt       -- length-        -> CInt       -- plain_implicit-        -> CInt       -- quoted_implicit-        -> CInt       -- style-        -> IO CInt--foreign import ccall unsafe "simple_document_start"-    c_simple_document_start :: EventRaw -> IO CInt--foreign import ccall unsafe "yaml_document_end_event_initialize"-    c_yaml_document_end_event_initialize :: EventRaw -> CInt -> IO CInt--foreign import ccall unsafe "yaml_sequence_start_event_initialize"-    c_yaml_sequence_start_event_initialize-        :: EventRaw-        -> Ptr CUChar-        -> Ptr CUChar-        -> CInt-        -> CInt-        -> IO CInt--foreign import ccall unsafe "yaml_sequence_end_event_initialize"-    c_yaml_sequence_end_event_initialize :: EventRaw -> IO CInt--foreign import ccall unsafe "yaml_mapping_start_event_initialize"-    c_yaml_mapping_start_event_initialize-        :: EventRaw-        -> Ptr CUChar-        -> Ptr CUChar-        -> CInt-        -> CInt-        -> IO CInt--foreign import ccall unsafe "yaml_mapping_end_event_initialize"-    c_yaml_mapping_end_event_initialize :: EventRaw -> IO CInt--foreign import ccall unsafe "yaml_alias_event_initialize"-    c_yaml_alias_event_initialize-        :: EventRaw-        -> Ptr CUChar-        -> IO CInt--toEventRaw :: Event -> (EventRaw -> IO a) -> IO a-toEventRaw e f = allocaBytes eventSize $ \er -> do-    ret <- case e of-        EventStreamStart ->-            c_yaml_stream_start_event_initialize-                er-                0 -- YAML_ANY_ENCODING-        EventStreamEnd ->-            c_yaml_stream_end_event_initialize er-        EventDocumentStart ->-            c_simple_document_start er-        EventDocumentEnd ->-            c_yaml_document_end_event_initialize er 1-        EventScalar bs thetag style0 anchor -> do-            BU.unsafeUseAsCStringLen bs $ \(value, len) -> do-                let value' = castPtr value :: Ptr CUChar-                    len' = fromIntegral len :: CInt-                let thetag' = tagToString thetag-                withCString thetag' $ \tag' -> do-                    let (pi, style) =-                            case style0 of-                                PlainNoTag -> (1, Plain)-                                x -> (0, x)-                        style' = toEnum $ fromEnum style-                        tagP = castPtr tag'-                        qi = if null thetag' then 1 else 0-                    case anchor of-                        Nothing ->-                            c_yaml_scalar_event_initialize-                                er-                                nullPtr -- anchor-                                tagP    -- tag-                                value'  -- value-                                len'    -- length-                                pi      -- plain_implicit-                                qi      -- quoted_implicit-                                style'  -- style-                        Just anchor' ->-                            withCString anchor' $ \anchorP' -> do-                                let anchorP = castPtr anchorP'-                                c_yaml_scalar_event_initialize-                                    er-                                    anchorP -- anchor-                                    tagP    -- tag-                                    value'  -- value-                                    len'    -- length-                                    0       -- plain_implicit-                                    qi      -- quoted_implicit-                                    style'  -- style-        EventSequenceStart Nothing ->-            c_yaml_sequence_start_event_initialize-                er-                nullPtr-                nullPtr-                1-                0 -- YAML_ANY_SEQUENCE_STYLE-        EventSequenceStart (Just anchor) ->-            withCString anchor $ \anchor' -> do-                let anchorP = castPtr anchor'-                c_yaml_sequence_start_event_initialize-                    er-                    anchorP-                    nullPtr-                    1-                    0 -- YAML_ANY_SEQUENCE_STYLE-        EventSequenceEnd ->-            c_yaml_sequence_end_event_initialize er-        EventMappingStart Nothing ->-            c_yaml_mapping_start_event_initialize-                er-                nullPtr-                nullPtr-                1-                0 -- YAML_ANY_SEQUENCE_STYLE-        EventMappingStart (Just anchor) ->-            withCString anchor $ \anchor' -> do-                let anchorP = castPtr anchor'-                c_yaml_mapping_start_event_initialize-                    er-                    anchorP-                    nullPtr-                    1-                    0 -- YAML_ANY_SEQUENCE_STYLE-        EventMappingEnd ->-            c_yaml_mapping_end_event_initialize er-        EventAlias anchor ->-            withCString anchor $ \anchorP' -> do-                let anchorP = castPtr anchorP'-                c_yaml_alias_event_initialize-                    er-                    anchorP-    unless (ret == 1) $ throwIO $ ToEventRawException ret-    f er--newtype ToEventRawException = ToEventRawException CInt-    deriving (Show, Typeable)-instance Exception ToEventRawException--decode :: MonadResource m => B.ByteString -> ConduitM i Event m ()-decode bs | B8.null bs = return ()-decode bs =-    bracketP alloc cleanup (runParser . fst)-  where-    alloc = mask_ $ do-        ptr <- mallocBytes parserSize-        res <- c_yaml_parser_initialize ptr-        if res == 0-            then do-                c_yaml_parser_delete ptr-                free ptr-                throwIO $ YamlException "Yaml out of memory"-            else do-                let (bsfptr, offset, len) = B.toForeignPtr bs-                let bsptrOrig = unsafeForeignPtrToPtr bsfptr-                let bsptr = castPtr bsptrOrig `plusPtr` offset-                c_yaml_parser_set_input_string ptr bsptr (fromIntegral len)-                return (ptr, bsfptr)-    cleanup (ptr, bsfptr) = do-        touchForeignPtr bsfptr-        c_yaml_parser_delete ptr-        free ptr---- XXX copied from GHC.IO.FD-std_flags, read_flags, output_flags, write_flags :: CInt-std_flags    = Posix.o_NOCTTY-output_flags = std_flags    .|. Posix.o_CREAT .|. Posix.o_TRUNC-read_flags   = std_flags    .|. Posix.o_RDONLY-write_flags  = output_flags .|. Posix.o_WRONLY---- | Open a C FILE* from a file path, using internal GHC API to work correctly--- on all platforms, even on non-ASCII filenames. The opening mode must be--- indicated via both 'rawOpenFlags' and 'openMode'.-openFile :: FilePath -> CInt -> String -> IO File-openFile file rawOpenFlags openMode = do-  fd <- liftIO $ Posix.withFilePath file $ \file' ->-    Posix.c_open file' rawOpenFlags 0o666-  if fd /= (-1)-    then withCString openMode $ \openMode' -> c_fdopen fd openMode'-    else return nullPtr--decodeFile :: MonadResource m => FilePath -> ConduitM i Event m ()-decodeFile file =-    bracketP alloc cleanup (runParser . fst)-  where-    alloc = mask_ $ do-        ptr <- mallocBytes parserSize-        res <- c_yaml_parser_initialize ptr-        if res == 0-            then do-                c_yaml_parser_delete ptr-                free ptr-                throwIO $ YamlException "Yaml out of memory"-            else do-                file' <- openFile file read_flags "r"-                if file' == nullPtr-                    then do-                        c_yaml_parser_delete ptr-                        free ptr-                        throwIO $ YamlException-                                $ "Yaml file not found: " ++ file-                    else do-                        c_yaml_parser_set_input_file ptr file'-                        return (ptr, file')-    cleanup (ptr, file') = do-        c_fclose_helper file'-        c_yaml_parser_delete ptr-        free ptr--runParser :: MonadResource m => Parser -> ConduitM i Event m ()-runParser parser = do-    e <- liftIO $ parserParseOne' parser-    case e of-        Left err -> liftIO $ throwIO err-        Right Nothing -> return ()-        Right (Just ev) -> yield ev >> runParser parser--parserParseOne' :: Parser-                -> IO (Either YamlException (Maybe Event))-parserParseOne' parser = allocaBytes eventSize $ \er -> do-    res <- liftIO $ c_yaml_parser_parse parser er-    flip finally (c_yaml_event_delete er) $-      if res == 0-        then do-          problem <- makeString c_get_parser_error_problem parser-          context <- makeString c_get_parser_error_context parser-          index <- c_get_parser_error_index parser-          line <- c_get_parser_error_line parser-          column <- c_get_parser_error_column parser-          let problemMark = YamlMark (fromIntegral index) (fromIntegral line) (fromIntegral column)-          return $ Left $ YamlParseException problem context problemMark-        else Right <$> getEvent er--encode :: MonadResource m => ConduitM Event o m ByteString-encode =-    runEmitter alloc close-  where-    alloc emitter = do-        fbuf <- mallocForeignPtrBytes bufferSize-        withForeignPtr fbuf c_buffer_init-        withForeignPtr fbuf $ c_my_emitter_set_output emitter-        return fbuf-    close _ fbuf = withForeignPtr fbuf $ \b -> do-        ptr' <- c_get_buffer_buff b-        len <- c_get_buffer_used b-        fptr <- newForeignPtr_ $ castPtr ptr'-        return $ B.fromForeignPtr fptr 0 $ fromIntegral len--encodeFile :: MonadResource m-           => FilePath-           -> ConduitM Event o m ()-encodeFile filePath =-    bracketP getFile c_fclose $ \file -> runEmitter (alloc file) (\u _ -> return u)-  where-    getFile = do-        file <- openFile filePath write_flags "w"-        if file == nullPtr-            then throwIO $ YamlException $ "could not open file for write: " ++ filePath-            else return file--    alloc file emitter = c_yaml_emitter_set_output_file emitter file--runEmitter :: MonadResource m-           => (Emitter -> IO a) -- ^ alloc-           -> (() -> a -> IO b) -- ^ close-           -> ConduitM Event o m b-runEmitter allocI closeI =-    bracketP alloc cleanup go-  where-    alloc = mask_ $ do-        emitter <- mallocBytes emitterSize-        res <- c_yaml_emitter_initialize emitter-        when (res == 0) $ throwIO $ YamlException "c_yaml_emitter_initialize failed"-#ifndef __NO_UNICODE__-        c_yaml_emitter_set_unicode emitter 1-#endif-        a <- allocI emitter-        return (emitter, a)-    cleanup (emitter, _) = do-        c_yaml_emitter_delete emitter-        free emitter--    go (emitter, a) =-        loop-      where-        loop = await >>= maybe (close ()) push--        push e = do-            _ <- liftIO $ toEventRaw e $ c_yaml_emitter_emit emitter-            loop-        close u = liftIO $ closeI u a---- | The pointer position-data YamlMark = YamlMark { yamlIndex :: Int, yamlLine :: Int, yamlColumn :: Int }-    deriving Show--data YamlException = YamlException String-                   -- | problem, context, index, position line, position column-                   | YamlParseException { yamlProblem :: String, yamlContext :: String, yamlProblemMark :: YamlMark }-    deriving (Show, Typeable)-instance Exception YamlException
+ src/Data/Yaml.hs view
@@ -0,0 +1,217 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}++-- | Provides a high-level interface for processing YAML files.+--+-- This module reuses most of the infrastructure from the @aeson@ package.+-- This means that you can use all of the existing tools for JSON+-- processing for processing YAML files. As a result, much of the+-- documentation below mentions JSON; do not let that confuse you, it's+-- intentional.+--+-- For the most part, YAML content translates directly into JSON, and+-- therefore there is very little data loss. If you need to deal with YAML+-- more directly (e.g., directly deal with aliases), you should use the+-- "Text.Libyaml" module instead.+--+-- For documentation on the @aeson@ types, functions, classes, and+-- operators, please see the @Data.Aeson@ module of the @aeson@ package.+--+-- Look in the examples directory of the source repository for some initial+-- pointers on how to use this library.++#if (defined (ghcjs_HOST_OS))+module Data.Yaml {-# WARNING "GHCJS is not supported yet (will break at runtime once called)." #-}+#else+module Data.Yaml+#endif+    ( -- * Encoding+      encode+    , encodeFile+      -- * Decoding+    , decodeEither'+    , decodeFileEither+    , decodeThrow+    , decodeFileThrow+      -- ** More control over decoding+    , decodeHelper+      -- * Types+    , Value (..)+    , Parser+    , Object+    , Array+    , ParseException(..)+    , prettyPrintParseException+    , YamlException (..)+    , YamlMark (..)+      -- * Constructors and accessors+    , object+    , array+    , (.=)+    , (.:)+    , (.:?)+    , (.!=)+      -- ** With helpers (since 0.8.23)+    , withObject+    , withText+    , withArray+    , withScientific+    , withBool+      -- * Parsing+    , parseMonad+    , parseEither+    , parseMaybe+      -- * Classes+    , ToJSON (..)+    , FromJSON (..)+      -- * Deprecated+    , decode+    , decodeFile+    , decodeEither+    ) where+#if !MIN_VERSION_base(4,8,0)+import Control.Applicative((<$>))+#endif+import Control.Exception+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Trans.Resource (MonadThrow, throwM)+import Data.Aeson+    ( Value (..), ToJSON (..), FromJSON (..), object+    , (.=) , (.:) , (.:?) , (.!=)+    , Object, Array+    , withObject, withText, withArray, withScientific, withBool+    )+#if MIN_VERSION_aeson(1,0,0)+import Data.Aeson.Text (encodeToTextBuilder)+#else+import Data.Aeson.Encode (encodeToTextBuilder)+#endif+import Data.Aeson.Types (Pair, parseMaybe, parseEither, Parser)+import Data.ByteString (ByteString)+import Data.Conduit ((.|), runConduitRes)+import qualified Data.Conduit.List as CL+import qualified Data.HashMap.Strict as M+import qualified Data.HashSet as HashSet+import Data.Text.Encoding (encodeUtf8)+import qualified Data.Text.Encoding as TE+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy.Builder (toLazyText)+import qualified Data.Vector as V+import System.IO.Unsafe (unsafePerformIO)++import Data.Yaml.Internal+import Text.Libyaml hiding (encode, decode, encodeFile, decodeFile)+import qualified Text.Libyaml as Y++-- | Encode a value into its YAML representation.+encode :: ToJSON a => a -> ByteString+encode obj = unsafePerformIO $ runConduitRes+    $ CL.sourceList (objToEvents $ toJSON obj)+   .| Y.encode++-- | Encode a value into its YAML representation and save to the given file.+encodeFile :: ToJSON a => FilePath -> a -> IO ()+encodeFile fp obj = runConduitRes+    $ CL.sourceList (objToEvents $ toJSON obj)+   .| Y.encodeFile fp++objToEvents :: Value -> [Y.Event]+objToEvents o = (:) EventStreamStart+              . (:) EventDocumentStart+              $ objToEvents' o+              [ EventDocumentEnd+              , EventStreamEnd+              ]++{- FIXME+scalarToEvent :: YamlScalar -> Event+scalarToEvent (YamlScalar v t s) = EventScalar v t s Nothing+-}++objToEvents' :: Value -> [Y.Event] -> [Y.Event]+--objToEvents' (Scalar s) rest = scalarToEvent s : rest+objToEvents' (Array list) rest =+    EventSequenceStart Nothing+  : foldr objToEvents' (EventSequenceEnd : rest) (V.toList list)+objToEvents' (Object pairs) rest =+    EventMappingStart Nothing+  : foldr pairToEvents (EventMappingEnd : rest) (M.toList pairs)++-- Empty strings need special handling to ensure they get quoted. This avoids:+-- https://github.com/snoyberg/yaml/issues/24+objToEvents' (String "") rest = EventScalar "" NoTag SingleQuoted Nothing : rest++objToEvents' (String s) rest =+    event : rest+  where+    event+        -- Make sure that special strings are encoded as strings properly.+        -- See: https://github.com/snoyberg/yaml/issues/31+        | s `HashSet.member` specialStrings || isNumeric s = EventScalar (encodeUtf8 s) NoTag SingleQuoted Nothing+        | otherwise = EventScalar (encodeUtf8 s) StrTag PlainNoTag Nothing+objToEvents' Null rest = EventScalar "null" NullTag PlainNoTag Nothing : rest+objToEvents' (Bool True) rest = EventScalar "true" BoolTag PlainNoTag Nothing : rest+objToEvents' (Bool False) rest = EventScalar "false" BoolTag PlainNoTag Nothing : rest+-- Use aeson's implementation which gets rid of annoying decimal points+objToEvents' n@Number{} rest = EventScalar (TE.encodeUtf8 $ TL.toStrict $ toLazyText $ encodeToTextBuilder n) IntTag PlainNoTag Nothing : rest++pairToEvents :: Pair -> [Y.Event] -> [Y.Event]+pairToEvents (k, v) = objToEvents' (String k) . objToEvents' v++decode :: FromJSON a+       => ByteString+       -> Maybe a+decode bs = unsafePerformIO+          $ either (const Nothing) id+          <$> decodeHelper_ (Y.decode bs)+{-# DEPRECATED decode "Please use decodeEither or decodeThrow, which provide information on how the decode failed" #-}++decodeFile :: FromJSON a+           => FilePath+           -> IO (Maybe a)+decodeFile fp = decodeHelper (Y.decodeFile fp) >>= either throwIO (return . either (const Nothing) id)+{-# DEPRECATED decodeFile "Please use decodeFileEither, which does not confused type-directed and runtime exceptions." #-}++-- | A version of 'decodeFile' which should not throw runtime exceptions.+--+-- @since 0.8.4+decodeFileEither+    :: FromJSON a+    => FilePath+    -> IO (Either ParseException a)+decodeFileEither = decodeHelper_ . Y.decodeFile++decodeEither :: FromJSON a => ByteString -> Either String a+decodeEither bs = unsafePerformIO+                $ either (Left . prettyPrintParseException) id+                <$> decodeHelper (Y.decode bs)+{-# DEPRECATED decodeEither "Please use decodeEither' or decodeThrow, which provide more useful failures" #-}++-- | More helpful version of 'decodeEither' which returns the 'YamlException'.+--+-- @since 0.8.3+decodeEither' :: FromJSON a => ByteString -> Either ParseException a+decodeEither' = either Left (either (Left . AesonException) Right)+              . unsafePerformIO+              . decodeHelper+              . Y.decode++-- | A version of 'decodeEither'' lifted to MonadThrow+--+-- @since 0.8.31+decodeThrow :: (MonadThrow m, FromJSON a) => ByteString -> m a+decodeThrow = either throwM return . decodeEither'++-- | A version of 'decodeFileEither' lifted to MonadIO+--+-- @since 0.8.31+decodeFileThrow :: (MonadIO m, FromJSON a) => FilePath -> m a+decodeFileThrow f = liftIO $ decodeFileEither f >>= either throwIO return++-- | Construct a new 'Value' from a list of 'Value's.+array :: [Value] -> Value+array = Array . V.fromList++parseMonad :: Monad m => (a -> Parser b) -> a -> m b+parseMonad p = either fail return . parseEither p
+ src/Data/Yaml/Aeson.hs view
@@ -0,0 +1,7 @@+-- | Just a re-export of @Data.Yaml@. In the future, this will be the canonical+-- name for that module\'s contents.+module Data.Yaml.Aeson+    ( module Data.Yaml+    ) where++import Data.Yaml
+ src/Data/Yaml/Builder.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+-- | NOTE: This module is a highly experimental preview release. It may change+-- drastically, or be entirely removed, in a future release.+module Data.Yaml.Builder+    ( YamlBuilder (..)+    , ToYaml (..)+    , mapping+    , array+    , string+    , bool+    , null+    , scientific+    , number+    , toByteString+    , writeYamlFile+    , (.=)+    ) where++import Prelude hiding (null)++import Control.Arrow (second)+#if MIN_VERSION_aeson(1,0,0)+import Data.Aeson.Text (encodeToTextBuilder)+#else+import Data.Aeson.Encode (encodeToTextBuilder)+#endif+import Data.Aeson.Types (Value(..))+import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as S8+import Data.Conduit+import qualified Data.HashSet as HashSet+import Data.Scientific (Scientific)+import Data.Text (Text)+import Data.Text.Encoding (encodeUtf8)+import qualified Data.Text.Encoding as TE+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy.Builder (toLazyText)+import System.IO.Unsafe (unsafePerformIO)++import Data.Yaml.Internal+import Text.Libyaml++(.=) :: ToYaml a => Text -> a -> (Text, YamlBuilder)+k .= v = (k, toYaml v)++newtype YamlBuilder = YamlBuilder { unYamlBuilder :: [Event] -> [Event] }++class ToYaml a where+    toYaml :: a -> YamlBuilder+instance ToYaml YamlBuilder where+    toYaml = id+instance ToYaml a => ToYaml [(Text, a)] where+    toYaml = mapping . map (second toYaml)+instance ToYaml a => ToYaml [a] where+    toYaml = array . map toYaml+instance ToYaml Text where+    toYaml = string+instance ToYaml Int where+    toYaml i = YamlBuilder (EventScalar (S8.pack $ show i) IntTag PlainNoTag Nothing:)++mapping :: [(Text, YamlBuilder)] -> YamlBuilder+mapping pairs = YamlBuilder $ \rest ->+    EventMappingStart Nothing : foldr addPair (EventMappingEnd : rest) pairs+  where+    addPair (key, YamlBuilder value) after+        = EventScalar (encodeUtf8 key) StrTag PlainNoTag Nothing+        : value after++array :: [YamlBuilder] -> YamlBuilder+array bs =+    YamlBuilder $ (EventSequenceStart Nothing:) . flip (foldr go) bs . (EventSequenceEnd:)+  where+    go (YamlBuilder b) = b++string :: Text -> YamlBuilder+-- Empty strings need special handling to ensure they get quoted. This avoids:+-- https://github.com/snoyberg/yaml/issues/24+string ""  = YamlBuilder (EventScalar "" NoTag SingleQuoted Nothing :)+string s   =+    YamlBuilder (event :)+  where+    event+        -- Make sure that special strings are encoded as strings properly.+        -- See: https://github.com/snoyberg/yaml/issues/31+        | s `HashSet.member` specialStrings || isNumeric s = EventScalar (encodeUtf8 s) NoTag SingleQuoted Nothing+        | otherwise = EventScalar (encodeUtf8 s) StrTag PlainNoTag Nothing+ +-- Use aeson's implementation which gets rid of annoying decimal points+scientific :: Scientific -> YamlBuilder+scientific n = YamlBuilder (EventScalar (TE.encodeUtf8 $ TL.toStrict $ toLazyText $ encodeToTextBuilder (Number n)) IntTag PlainNoTag Nothing :)++{-# DEPRECATED number "Use scientific" #-}+number :: Scientific -> YamlBuilder+number = scientific++bool :: Bool -> YamlBuilder+bool True   = YamlBuilder (EventScalar "true" BoolTag PlainNoTag Nothing :)+bool False  = YamlBuilder (EventScalar "false" BoolTag PlainNoTag Nothing :)++null :: YamlBuilder+null = YamlBuilder (EventScalar "null" NullTag PlainNoTag Nothing :)++toEvents :: YamlBuilder -> [Event]+toEvents (YamlBuilder front) =+    EventStreamStart : EventDocumentStart : front [EventDocumentEnd, EventStreamEnd]++toSource :: (Monad m, ToYaml a) => a -> ConduitM i Event m ()+toSource = mapM_ yield . toEvents . toYaml++toByteString :: ToYaml a => a -> ByteString+toByteString yb = unsafePerformIO $ runConduitRes $ toSource yb .| encode++writeYamlFile :: ToYaml a => FilePath -> a -> IO ()+writeYamlFile fp yb = runConduitRes $ toSource yb .| encodeFile fp
+ src/Data/Yaml/Config.hs view
@@ -0,0 +1,215 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+-- | Functionality for using YAML as configuration files+--+-- In particular, merging environment variables with yaml values+--+-- 'loadYamlSettings' is a high-level API for loading YAML and merging environment variables.+-- A yaml value of @_env:ENV_VAR:default@ will lookup the environment variable @ENV_VAR@.+--+-- On a historical note, this code was taken directly from the yesod web framework's configuration module.+module Data.Yaml.Config+    ( -- * High-level+      loadYamlSettings+    , loadYamlSettingsArgs+      -- ** EnvUsage+    , EnvUsage+    , ignoreEnv+    , useEnv+    , requireEnv+    , useCustomEnv+    , requireCustomEnv+      -- * Lower level+    , applyCurrentEnv+    , getCurrentEnv+    , applyEnvValue+    ) where+++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative ((<$>))+import Data.Monoid+#endif+import Data.Semigroup+import Data.List.NonEmpty (nonEmpty)+import Data.Aeson+import qualified Data.HashMap.Strict as H+import Data.Text (Text, pack)+import System.Environment (getArgs, getEnvironment)+import Control.Arrow ((***))+import Control.Monad (forM)+import Control.Exception (throwIO)+import Data.Text.Encoding (encodeUtf8)+import qualified Data.Yaml as Y+import qualified Data.Yaml.Include as YI+import Data.Maybe (fromMaybe)+import qualified Data.Text as T++newtype MergedValue = MergedValue { getMergedValue :: Value }++instance Semigroup MergedValue where+    MergedValue x <> MergedValue y = MergedValue $ mergeValues x y++-- | Left biased+mergeValues :: Value -> Value -> Value+mergeValues (Object x) (Object y) = Object $ H.unionWith mergeValues x y+mergeValues x _ = x++-- | Override environment variable placeholders in the given @Value@ with+-- values from the environment.+--+-- If the first argument is @True@, then all placeholders _must_ be provided by+-- the actual environment. Otherwise, default values from the @Value@ will be+-- used.+--+-- @since 0.8.16+applyEnvValue :: Bool -- ^ require an environment variable to be present?+              -> H.HashMap Text Text -> Value -> Value+applyEnvValue requireEnv' env =+    goV+  where+    goV (Object o) = Object $ goV <$> o+    goV (Array a) = Array (goV <$> a)+    goV (String t1) = fromMaybe (String t1) $ do+        t2 <- T.stripPrefix "_env:" t1+        let (name, t3) = T.break (== ':') t2+            mdef = fmap parseValue $ T.stripPrefix ":" t3+        Just $ case H.lookup name env of+            Just val ->+                -- If the default value parses as a String, we treat the+                -- environment variable as a raw value and do not parse it.+                -- This means that things like numeric passwords just work.+                -- However, for originally numerical or boolean values (e.g.,+                -- port numbers), we still perform a normal YAML parse.+                --+                -- For details, see:+                -- https://github.com/yesodweb/yesod/issues/1061+                case mdef of+                    Just (String _) -> String val+                    _ -> parseValue val+            Nothing ->+                case mdef of+                    Just val | not requireEnv' -> val+                    _ -> Null+    goV v = v++    parseValue val = either+      (const (String val))+      id+      (Y.decodeThrow $ encodeUtf8 val)++-- | Get the actual environment as a @HashMap@ from @Text@ to @Text@.+--+-- @since 0.8.16+getCurrentEnv :: IO (H.HashMap Text Text)+getCurrentEnv = fmap (H.fromList . map (pack *** pack)) getEnvironment++-- | A convenience wrapper around 'applyEnvValue' and 'getCurrentEnv'+--+-- @since 0.8.16+applyCurrentEnv :: Bool -- ^ require an environment variable to be present?+                -> Value -> IO Value+applyCurrentEnv requireEnv' orig = flip (applyEnvValue requireEnv') orig <$> getCurrentEnv++-- | Defines how we want to use the environment variables when loading a config+-- file. Use the smart constructors provided by this module.+--+-- @since 0.8.16+data EnvUsage = IgnoreEnv+              | UseEnv+              | RequireEnv+              | UseCustomEnv (H.HashMap Text Text)+              | RequireCustomEnv (H.HashMap Text Text)++-- | Do not use any environment variables, instead relying on defaults values+-- in the config file.+--+-- @since 0.8.16+ignoreEnv :: EnvUsage+ignoreEnv = IgnoreEnv++-- | Use environment variables when available, otherwise use defaults.+--+-- @since 0.8.16+useEnv :: EnvUsage+useEnv = UseEnv++-- | Do not use default values from the config file, but instead take all+-- overrides from the environment. If a value is missing, loading the file will+-- throw an exception.+--+-- @since 0.8.16+requireEnv :: EnvUsage+requireEnv = RequireEnv++-- | Same as 'useEnv', but instead of the actual environment, use the provided+-- @HashMap@ as the environment.+--+-- @since 0.8.16+useCustomEnv :: H.HashMap Text Text -> EnvUsage+useCustomEnv = UseCustomEnv++-- | Same as 'requireEnv', but instead of the actual environment, use the+-- provided @HashMap@ as the environment.+--+-- @since 0.8.16+requireCustomEnv :: H.HashMap Text Text -> EnvUsage+requireCustomEnv = RequireCustomEnv++-- | Load the settings from the following three sources:+--+-- * Run time config files+--+-- * Run time environment variables+--+-- * The default compile time config file+--+-- For example, to load up settings from @config/foo.yaml@ and allow overriding+-- from the actual environment, you can use:+--+-- > loadYamlSettings ["config/foo.yaml"] [] useEnv+--+-- @since 0.8.16+loadYamlSettings+    :: FromJSON settings+    => [FilePath] -- ^ run time config files to use, earlier files have precedence+    -> [Value] -- ^ any other values to use, usually from compile time config. overridden by files+    -> EnvUsage+    -> IO settings+loadYamlSettings runTimeFiles compileValues envUsage = do+    runValues <- forM runTimeFiles $ \fp -> do+        eres <- YI.decodeFileEither fp+        case eres of+            Left e -> do+                putStrLn $ "loadYamlSettings: Could not parse file as YAML: " ++ fp+                throwIO e+            Right value -> return value++    value' <-+        case nonEmpty $ map MergedValue $ runValues ++ compileValues of+            Nothing -> error "loadYamlSettings: No configuration provided"+            Just ne -> return $ getMergedValue $ sconcat ne+    value <-+        case envUsage of+            IgnoreEnv            -> return $ applyEnvValue   False mempty value'+            UseEnv               ->          applyCurrentEnv False        value'+            RequireEnv           ->          applyCurrentEnv True         value'+            UseCustomEnv env     -> return $ applyEnvValue   False env    value'+            RequireCustomEnv env -> return $ applyEnvValue   True  env    value'++    case fromJSON value of+        Error s -> error $ "Could not convert to expected type: " ++ s+        Success settings -> return settings++-- | Same as @loadYamlSettings@, but get the list of runtime config files from+-- the command line arguments.+--+-- @since 0.8.17+loadYamlSettingsArgs+    :: FromJSON settings+    => [Value] -- ^ any other values to use, usually from compile time config. overridden by files+    -> EnvUsage -- ^ use environment variables+    -> IO settings+loadYamlSettingsArgs values env = do+    args <- getArgs+    loadYamlSettings args values env
+ src/Data/Yaml/Include.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE RankNTypes #-}+module Data.Yaml.Include (decodeFile, decodeFileEither) where++#if !MIN_VERSION_directory(1, 2, 3)+import Control.Exception (handleJust)+import Control.Monad (guard)+import System.IO.Error (ioeGetFileName, ioeGetLocation, isDoesNotExistError)+#endif++import Control.Exception (throwIO)+import Control.Monad (when)+import Control.Monad.IO.Class (liftIO)+import Control.Monad.Trans.Resource (MonadResource)+import Data.Aeson (FromJSON)+import Data.Conduit+import qualified Data.Conduit.List as CL+import Data.Text (unpack)+import Data.Text.Encoding (decodeUtf8)+import System.Directory+import System.FilePath++import Data.Yaml.Internal (ParseException(..), decodeHelper_, decodeHelper)+import Text.Libyaml hiding (decodeFile)+import qualified Text.Libyaml as Y++eventsFromFile+    :: MonadResource m+    => FilePath+    -> ConduitM i Event m ()+eventsFromFile = go []+  where+    go :: MonadResource m => [FilePath] -> FilePath -> ConduitM i Event m ()+    go seen fp = do+        cfp <- liftIO $ handleNotFound $ canonicalizePath fp+        when (cfp `elem` seen) $ do+            liftIO $ throwIO CyclicIncludes+        Y.decodeFile cfp .| do+            awaitForever $ \event -> case event of+                EventScalar f (UriTag "!include") _ _ -> do+                    let includeFile = takeDirectory cfp </> unpack (decodeUtf8 f)+                    go (cfp : seen) includeFile .| CL.filter (`notElem` irrelevantEvents)+                _ -> yield event++    irrelevantEvents = [EventStreamStart, EventDocumentStart, EventDocumentEnd, EventStreamEnd]++#if !MIN_VERSION_directory(1, 2, 3)+    handleNotFound = handleJust+        (\e -> do+            guard (isDoesNotExistError e)+            guard (ioeGetLocation e == "canonicalizePath")+            ioeGetFileName e)+        (throwIO . YamlException . ("Yaml file not found: " ++))+#else+    handleNotFound = id+#endif++-- | Like `Data.Yaml.decodeFile` but with support for relative and absolute+-- includes.+--+-- The syntax for includes follows the form:+--+-- > somekey: !include ./somefile.yaml+decodeFile+    :: FromJSON a+    => FilePath+    -> IO (Maybe a)+decodeFile fp = decodeHelper (eventsFromFile fp) >>= either throwIO (return . either (const Nothing) id)++-- | Like `Data.Yaml.decodeFileEither` but with support for relative and+-- absolute includes.+--+-- The syntax for includes follows the form:+--+-- > somekey: !include ./somefile.yaml+decodeFileEither+    :: FromJSON a+    => FilePath+    -> IO (Either ParseException a)+decodeFileEither = decodeHelper_ . eventsFromFile
+ src/Data/Yaml/Internal.hs view
@@ -0,0 +1,299 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE OverloadedStrings #-}+module Data.Yaml.Internal+    (+      ParseException(..)+    , prettyPrintParseException+    , parse+    , decodeHelper+    , decodeHelper_+    , specialStrings+    , isNumeric+    , textToScientific+    ) where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>), Applicative(..))+#endif+import Control.Applicative ((<|>))+import Control.Exception+import Control.Monad (liftM, ap, unless)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Trans.Class (MonadTrans, lift)+import Control.Monad.Trans.Resource (ResourceT, runResourceT)+import Control.Monad.Trans.State+import Data.Aeson+import Data.Aeson.Types hiding (parse)+import qualified Data.Attoparsec.Text as Atto+import Data.Bits (shiftL, (.|.))+import Data.ByteString (ByteString)+import Data.Char (toUpper, ord)+import Data.Conduit ((.|), ConduitM, runConduit)+import qualified Data.Conduit.List as CL+import qualified Data.HashMap.Strict as M+import qualified Data.HashSet as HashSet+import qualified Data.Map as Map+import Data.Scientific (Scientific)+import Data.Text (Text, pack)+import qualified Data.Text as T+import Data.Text.Encoding (decodeUtf8With)+import Data.Text.Encoding.Error (lenientDecode)+import Data.Typeable+import qualified Data.Vector as V++import qualified Text.Libyaml as Y+import Text.Libyaml hiding (encode, decode, encodeFile, decodeFile)++data ParseException = NonScalarKey+                    | UnknownAlias { _anchorName :: Y.AnchorName }+                    | UnexpectedEvent { _received :: Maybe Event+                                      , _expected :: Maybe Event+                                      }+                    | InvalidYaml (Maybe YamlException)+                    | AesonException String+                    | OtherParseException SomeException+                    | NonStringKeyAlias Y.AnchorName Value+                    | CyclicIncludes+    deriving (Show, Typeable)++instance Exception ParseException where+#if MIN_VERSION_base(4, 8, 0)+  displayException = prettyPrintParseException+#endif++-- | Alternative to 'show' to display a 'ParseException' on the screen.+--   Instead of displaying the data constructors applied to their arguments,+--   a more textual output is returned. For example, instead of printing:+--+-- > InvalidYaml (Just (YamlParseException {yamlProblem = "did not find expected ',' or '}'", yamlContext = "while parsing a flow mapping", yamlProblemMark = YamlMark {yamlIndex = 42, yamlLine = 2, yamlColumn = 12}})))+--+--   It looks more pleasant to print:+--+-- > YAML parse exception at line 2, column 12,+-- > while parsing a flow mapping:+-- > did not find expected ',' or '}'+--+-- Since 0.8.11+prettyPrintParseException :: ParseException -> String+prettyPrintParseException pe = case pe of+  NonScalarKey -> "Non scalar key"+  UnknownAlias anchor -> "Unknown alias `" ++ anchor ++ "`"+  UnexpectedEvent { _expected = mbExpected, _received = mbUnexpected } -> unlines+    [ "Unexpected event: expected"+    , "  " ++ show mbExpected+    , "but received"+    , "  " ++ show mbUnexpected+    ]+  InvalidYaml mbYamlError -> case mbYamlError of+    Nothing -> "Unspecified YAML error"+    Just yamlError -> case yamlError of+      YamlException s -> "YAML exception:\n" ++ s+      YamlParseException problem context mark -> concat+        [ "YAML parse exception at line " ++ show (yamlLine mark) +++          ", column " ++ show (yamlColumn mark)+        , case context of+            "" -> ":\n"+            -- The context seems to include a leading "while" or similar.+            _  -> ",\n" ++ context ++ ":\n"+        , problem+        ]+  AesonException s -> "Aeson exception:\n" ++ s+  OtherParseException exc -> "Generic parse exception:\n" ++ show exc+  NonStringKeyAlias anchor value -> unlines+    [ "Non-string key alias:"+    , "  Anchor name: " ++ anchor+    , "  Value: " ++ show value+    ]+  CyclicIncludes -> "Cyclic includes"++newtype PErrorT m a = PErrorT { runPErrorT :: m (Either ParseException a) }+instance Monad m => Functor (PErrorT m) where+    fmap = liftM+instance Monad m => Applicative (PErrorT m) where+    pure  = PErrorT . return . Right+    (<*>) = ap+instance Monad m => Monad (PErrorT m) where+    return = pure+    (PErrorT m) >>= f = PErrorT $ do+        e <- m+        case e of+            Left e' -> return $ Left e'+            Right a -> runPErrorT $ f a+instance MonadTrans PErrorT where+    lift = PErrorT . liftM Right+instance MonadIO m => MonadIO (PErrorT m) where+    liftIO = lift . liftIO++type Parse = StateT (Map.Map String Value) (ResourceT IO)++requireEvent :: Event -> ConduitM Event o Parse ()+requireEvent e = do+    f <- CL.head+    unless (f == Just e) $ liftIO $ throwIO $ UnexpectedEvent f $ Just e++parse :: ConduitM Event o Parse Value+parse = do+    streamStart <- CL.head+    case streamStart of+        Nothing ->+            -- empty string input+            return Null+        Just EventStreamStart -> do+            documentStart <- CL.head+            case documentStart of+                Just EventStreamEnd ->+                    -- empty file input, comment only string/file input+                    return Null+                Just EventDocumentStart -> do+                    res <- parseO+                    requireEvent EventDocumentEnd+                    requireEvent EventStreamEnd+                    return res+                _ -> liftIO $ throwIO $ UnexpectedEvent documentStart Nothing+        _ -> liftIO $ throwIO $ UnexpectedEvent streamStart Nothing++parseScalar :: ByteString -> Anchor -> Style -> Tag+            -> ConduitM Event o Parse Text+parseScalar v a style tag = do+    let res = decodeUtf8With lenientDecode v+    case a of+        Nothing -> return res+        Just an -> do+            lift $ modify (Map.insert an $ textToValue style tag res)+            return res++textToValue :: Style -> Tag -> Text -> Value+textToValue SingleQuoted _ t = String t+textToValue DoubleQuoted _ t = String t+textToValue _ StrTag t = String t+textToValue Folded _ t = String t+textToValue _ _ t+    | t `elem` ["null", "Null", "NULL", "~", ""] = Null+    | any (t `isLike`) ["y", "yes", "on", "true"] = Bool True+    | any (t `isLike`) ["n", "no", "off", "false"] = Bool False+    | Right x <- textToScientific t = Number x+    | otherwise = String t+  where x `isLike` ref = x `elem` [ref, T.toUpper ref, titleCased]+          where titleCased = toUpper (T.head ref) `T.cons` T.tail ref++textToScientific :: Text -> Either String Scientific+textToScientific = Atto.parseOnly (num <* Atto.endOfInput)+  where+    num = (fromInteger <$> ("0x" *> Atto.hexadecimal))+      <|> (fromInteger <$> ("0o" *> octal))+      <|> Atto.scientific++    octal = T.foldl' step 0 <$> Atto.takeWhile1 isOctalDigit+      where+        isOctalDigit c = (c >= '0' && c <= '7')+        step a c = (a `shiftL` 3) .|. fromIntegral (ord c - 48)++parseO :: ConduitM Event o Parse Value+parseO = do+    me <- CL.head+    case me of+        Just (EventScalar v tag style a) -> textToValue style tag <$> parseScalar v a style tag+        Just (EventSequenceStart a) -> parseS a id+        Just (EventMappingStart a) -> parseM a M.empty+        Just (EventAlias an) -> do+            m <- lift get+            case Map.lookup an m of+                Nothing -> liftIO $ throwIO $ UnknownAlias an+                Just v -> return v+        _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing++parseS :: Y.Anchor+       -> ([Value] -> [Value])+       -> ConduitM Event o Parse Value+parseS a front = do+    me <- CL.peek+    case me of+        Just EventSequenceEnd -> do+            CL.drop 1+            let res = Array $ V.fromList $ front []+            case a of+                Nothing -> return res+                Just an -> do+                    lift $ modify $ Map.insert an res+                    return res+        _ -> do+            o <- parseO+            parseS a $ front . (:) o++parseM :: Y.Anchor+       -> M.HashMap Text Value+       -> ConduitM Event o Parse Value+parseM a front = do+    me <- CL.peek+    case me of+        Just EventMappingEnd -> do+            CL.drop 1+            let res = Object front+            case a of+                Nothing -> return res+                Just an -> do+                    lift $ modify $ Map.insert an res+                    return res+        _ -> do+            CL.drop 1+            s <- case me of+                    Just (EventScalar v tag style a') -> parseScalar v a' style tag+                    Just (EventAlias an) -> do+                        m <- lift get+                        case Map.lookup an m of+                            Nothing -> liftIO $ throwIO $ UnknownAlias an+                            Just (String t) -> return t+                            Just v -> liftIO $ throwIO $ NonStringKeyAlias an v+                    _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing+            o <- parseO++            let al  = M.insert s o front+                al' = if s == pack "<<"+                         then case o of+                                  Object l  -> M.union front l+                                  Array l -> M.union front $ foldl merge' M.empty $ V.toList l+                                  _          -> al+                         else al+            parseM a al'+    where merge' al (Object om) = M.union al om+          merge' al _           = al++decodeHelper :: FromJSON a+             => ConduitM () Y.Event Parse ()+             -> IO (Either ParseException (Either String a))+decodeHelper src = do+    -- This used to be tryAny, but the fact is that catching async+    -- exceptions is fine here. We'll rethrow them immediately in the+    -- otherwise clause.+    x <- try $ runResourceT $ flip evalStateT Map.empty $ runConduit $ src .| parse+    case x of+        Left e+            | Just pe <- fromException e -> return $ Left pe+            | Just ye <- fromException e -> return $ Left $ InvalidYaml $ Just (ye :: YamlException)+            | otherwise -> throwIO e+        Right y -> return $ Right $ parseEither parseJSON y++decodeHelper_ :: FromJSON a+              => ConduitM () Event Parse ()+              -> IO (Either ParseException a)+decodeHelper_ src = do+    x <- try $ runResourceT $ flip evalStateT Map.empty $ runConduit $ src .| parse+    return $ case x of+        Left e+            | Just pe <- fromException e -> Left pe+            | Just ye <- fromException e -> Left $ InvalidYaml $ Just (ye :: YamlException)+            | otherwise -> Left $ OtherParseException e+        Right y -> either+            (Left . AesonException)+            Right+            (parseEither parseJSON y)++-- | Strings which must be escaped so as not to be treated as non-string scalars.+specialStrings :: HashSet.HashSet Text+specialStrings = HashSet.fromList $ T.words+    "y Y yes Yes YES n N no No NO true True TRUE false False FALSE on On ON off Off OFF null Null NULL ~ *"++isNumeric :: Text -> Bool+isNumeric = either (const False) (const True) . textToScientific
+ src/Data/Yaml/Parser.hs view
@@ -0,0 +1,201 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings #-}+-- | NOTE: This module is a highly experimental preview release. It may change+-- drastically, or be entirely removed, in a future release.+module Data.Yaml.Parser where++import Control.Applicative+import Control.Exception (Exception)+import Control.Monad (MonadPlus (..), liftM, ap)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Resource (MonadThrow, throwM)+import Control.Monad.Trans.Writer.Strict (tell, WriterT)+import Data.ByteString (ByteString)+import Data.Conduit+import Data.Conduit.Lift (runWriterC)+import qualified Data.Map as Map+#if !MIN_VERSION_base(4,8,0)+import Data.Monoid (Monoid (..))+#endif+#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup (Semigroup(..))+#endif+import Data.Text (Text, pack, unpack)+import Data.Text.Encoding (decodeUtf8)+import Data.Text.Read (signed, decimal)+import Data.Typeable (Typeable)++import Text.Libyaml++newtype YamlParser a = YamlParser+    { unYamlParser :: AnchorMap -> Either Text a+    }+instance Functor YamlParser where+    fmap = liftM+instance Applicative YamlParser where+    pure = YamlParser . const . Right+    (<*>) = ap+instance Alternative YamlParser where+    empty = fail "empty"+    (<|>) = mplus+instance Semigroup (YamlParser a) where+    (<>) = mplus+instance Monoid (YamlParser a) where+    mempty = fail "mempty"+#if !MIN_VERSION_base(4,11,0)+    mappend = (<>)+#endif+instance Monad YamlParser where+    return = pure+    YamlParser f >>= g = YamlParser $ \am ->+        case f am of+            Left t -> Left t+            Right x -> unYamlParser (g x) am+    fail = YamlParser . const . Left . pack+instance MonadPlus YamlParser where+    mzero = fail "mzero"+    mplus a b = YamlParser $ \am ->+        case unYamlParser a am of+            Left _ -> unYamlParser b am+            x -> x++lookupAnchor :: AnchorName -> YamlParser (Maybe YamlValue)+lookupAnchor name = YamlParser $ Right . Map.lookup name++withAnchor :: AnchorName -> Text -> (YamlValue -> YamlParser a) -> YamlParser a+withAnchor name expected f = do+    mv <- lookupAnchor name+    case mv of+        Nothing -> fail $ unpack expected ++ ": unknown alias " ++ name+        Just v -> f v++withMapping :: Text -> ([(Text, YamlValue)] -> YamlParser a) -> YamlValue -> YamlParser a+withMapping _ f (Mapping m _) = f m+withMapping expected f (Alias an) = withAnchor an expected $ withMapping expected f+withMapping expected _ v = typeMismatch expected v++withSequence :: Text -> ([YamlValue] -> YamlParser a) -> YamlValue -> YamlParser a+withSequence _ f (Sequence s _) = f s+withSequence expected f (Alias an) = withAnchor an expected $ withSequence expected f+withSequence expected _ v = typeMismatch expected v++withText :: Text -> (Text -> YamlParser a) -> YamlValue -> YamlParser a+withText _ f (Scalar s _ _ _) = f $ decodeUtf8 s+withText expected f (Alias an) = withAnchor an expected $ withText expected f+withText expected _ v = typeMismatch expected v++typeMismatch :: Text -> YamlValue -> YamlParser a+typeMismatch expected v =+    fail $ concat+        [ "Expected "+        , unpack expected+        , ", but got: "+        , t+        ]+  where+    t = case v of+        Mapping _ _ -> "mapping"+        Sequence _ _ -> "sequence"+        Scalar _ _ _ _ -> "scalar"+        Alias _ -> "alias"++class FromYaml a where+    fromYaml :: YamlValue -> YamlParser a+instance FromYaml YamlValue where+    fromYaml = return+instance FromYaml a => FromYaml [a] where+    fromYaml = withSequence "[a]" (mapM fromYaml)+instance FromYaml Text where+    fromYaml = withText "Text" return+instance FromYaml Int where+    fromYaml =+        withText "Int" go+      where+        go t =+            case signed decimal t of+                Right (i, "") -> return i+                _ -> fail $ "Invalid Int: " ++ unpack t++data YamlValue+    = Mapping [(Text, YamlValue)] Anchor+    | Sequence [YamlValue] Anchor+    | Scalar ByteString Tag Style Anchor+    | Alias AnchorName+    deriving Show++type AnchorMap = Map.Map AnchorName YamlValue+data RawDoc = RawDoc YamlValue AnchorMap+    deriving Show++parseRawDoc :: (FromYaml a, MonadThrow m) => RawDoc -> m a+parseRawDoc (RawDoc val am) =+    case unYamlParser (fromYaml val) am of+        Left t -> throwM $ FromYamlException t+        Right x -> return x++(.:) :: FromYaml a => [(Text, YamlValue)] -> Text -> YamlParser a+o .: k =+    case lookup k o of+        Nothing -> fail $ "Key not found: " ++ unpack k+        Just v -> fromYaml v++data YamlParseException+    = UnexpectedEndOfEvents+    | UnexpectedEvent Event+    | FromYamlException Text+    deriving (Show, Typeable)+instance Exception YamlParseException++sinkValue :: MonadThrow m => ConduitM Event o (WriterT AnchorMap m) YamlValue+sinkValue =+    start+  where+    start = await >>= maybe (throwM UnexpectedEndOfEvents) go++    tell' Nothing val = return val+    tell' (Just name) val = do+        lift $ tell $ Map.singleton name val+        return val++    go EventStreamStart = start+    go EventDocumentStart = start+    go (EventAlias a) = return $ Alias a+    go (EventScalar a b c d) = tell' d $ Scalar a b c d+    go (EventSequenceStart mname) = do+        vals <- goS id+        let val = Sequence vals mname+        tell' mname val+    go (EventMappingStart mname) = do+        pairs <- goM id+        let val = Mapping pairs mname+        tell' mname val++    go e = throwM $ UnexpectedEvent e++    goS front = do+        me <- await+        case me of+            Nothing -> throwM UnexpectedEndOfEvents+            Just EventSequenceEnd -> return $ front []+            Just e -> do+                val <- go e+                goS (front . (val:))++    goM front = do+        mk <- await+        case mk of+            Nothing -> throwM UnexpectedEndOfEvents+            Just EventMappingEnd -> return $ front []+            Just (EventScalar a b c d) -> do+                _ <- tell' d $ Scalar a b c d+                let k = decodeUtf8 a+                v <- start+                goM (front . ((k, v):))+            Just e -> throwM $ UnexpectedEvent e++sinkRawDoc :: MonadThrow m => ConduitM Event o m RawDoc+sinkRawDoc = uncurry RawDoc <$> runWriterC sinkValue++readYamlFile :: FromYaml a => FilePath -> IO a+readYamlFile fp = runConduitRes (decodeFile fp .| sinkRawDoc) >>= parseRawDoc
+ src/Data/Yaml/Pretty.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE CPP #-}+-- | Prettier YAML encoding.+--+-- @since 0.8.13+module Data.Yaml.Pretty+    ( encodePretty+    , Config+    , getConfCompare+    , setConfCompare+    , getConfDropNull+    , setConfDropNull+    , defConfig+    ) where++import Prelude hiding (null)++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>))+#endif+import Data.Aeson.Types+import Data.ByteString (ByteString)+import Data.Function (on)+import qualified Data.HashMap.Strict as HM+import Data.List (sortBy)+#if !MIN_VERSION_base(4,8,0)+import Data.Monoid+#endif+import Data.Text (Text)+import qualified Data.Vector as V++import Data.Yaml.Builder++-- |+-- @since 0.8.13+data Config = Config+  { confCompare :: Text -> Text -> Ordering -- ^ Function used to sort keys in objects+  , confDropNull :: Bool+  }++-- | The default configuration: do not sort objects or drop keys+--+-- @since 0.8.13+defConfig :: Config+defConfig = Config mempty False++-- |+-- @since 0.8.13+getConfCompare :: Config -> Text -> Text -> Ordering+getConfCompare = confCompare++-- | Sets ordering for object keys+--+-- @since 0.8.13+setConfCompare :: (Text -> Text -> Ordering) -> Config -> Config+setConfCompare cmp c = c { confCompare = cmp }++-- |+-- @since 0.8.24+getConfDropNull :: Config -> Bool+getConfDropNull = confDropNull++-- | Drop entries with `Null` value from objects, if set to `True`+--+-- @since 0.8.24+setConfDropNull :: Bool -> Config -> Config+setConfDropNull m c = c { confDropNull = m }++pretty :: Config -> Value -> YamlBuilder+pretty cfg = go+  where go (Object o) = let sort = sortBy (confCompare cfg `on` fst)+                            select+                              | confDropNull cfg = HM.filter (/= Null)+                              | otherwise        = id+                        in mapping (sort $ HM.toList $ HM.map go $ select o)+        go (Array a)  = array (go <$> V.toList a)+        go Null       = null+        go (String s) = string s+        go (Number n) = scientific n+        go (Bool b)   = bool b++-- | Configurable 'encode'.+--+-- @since 0.8.13+encodePretty :: ToJSON a => Config -> a -> ByteString+encodePretty cfg = toByteString . pretty cfg . toJSON
+ src/Data/Yaml/TH.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE LambdaCase          #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Data.Yaml.TH+  ( -- * Decoding+    yamlQQ+#if MIN_VERSION_template_haskell(2,9,0)+  , decodeFile+#endif+    -- * Re-exports from "Data.Yaml"+  , Value (..)+  , Parser+  , Object+  , Array+  , object+  , array+  , (.=)+  , (.:)+  , (.:?)+  , (.!=)+  , FromJSON (..)+  ) where++import           Data.Text.Encoding+import qualified Data.Text as T+import           Language.Haskell.TH+import           Language.Haskell.TH.Syntax+import           Language.Haskell.TH.Quote++import           Data.Yaml hiding (decodeFile)++-- | Decode a YAML file at compile time. Only available on GHC version @7.8.1@+-- or higher.+--+-- @since 0.8.19.0+--+-- ==== __Examples__+--+-- @+-- {-\# LANGUAGE TemplateHaskell \#-}+--+-- config :: Config+-- config = $$('decodeFile' "config.yaml")+-- @+decodeFile :: forall a. (Lift a, FromJSON a) => FilePath -> Q (TExp a)+decodeFile path = do+  addDependentFile path+  x <- runIO $ decodeFileThrow path+  fmap TExp (lift (x :: a))++yamlExp :: String -> Q Exp+yamlExp input = do+  val <- runIO $ decodeThrow $ encodeUtf8 $ T.pack input+  lift (val :: Value)++-- | A @QuasiQuoter@ for YAML.+--+-- @since 0.8.28.0+--+-- ==== __Examples__+--+-- @+-- {-\# LANGUAGE QuasiQuotes \#-}+-- import Data.Yaml.TH+--+-- value :: Value+-- value = [yamlQQ|+-- name: John Doe+-- age: 23+-- |]+-- @+yamlQQ :: QuasiQuoter+yamlQQ = QuasiQuoter {+  quoteExp  = yamlExp+, quotePat  = notDefined "quotePat"+, quoteType = notDefined "quoteType"+, quoteDec  = notDefined "quoteDec"+} where+    notDefined name _ = fail (name ++ " is not defined for yamlQQ")
+ src/Text/Libyaml.hs view
@@ -0,0 +1,643 @@+{-# LANGUAGE EmptyDataDecls #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TypeFamilies #-}++-- | Low-level, streaming YAML interface. For a higher-level interface, see+-- "Data.Yaml".+module Text.Libyaml+    ( -- * The event stream+      Event (..)+    , Style (..)+    , Tag (..)+    , AnchorName+    , Anchor+      -- * Encoding and decoding+    , encode+    , decode+    , encodeFile+    , decodeFile+      -- * Error handling+    , YamlException (..)+    , YamlMark (..)+    ) where++import Prelude hiding (pi)++import Data.Bits ((.|.))+import Foreign.C+import Foreign.Ptr+import Foreign.ForeignPtr+#if MIN_VERSION_base(4,7,0)+import Foreign.ForeignPtr.Unsafe+#endif+import Foreign.Marshal.Alloc+import qualified System.Posix.Internals as Posix++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative+#endif+import Control.Exception (mask_, throwIO, Exception, finally)+import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Trans.Resource+import Data.Conduit hiding (Source, Sink, Conduit)+import Data.Data++import Data.ByteString (ByteString, packCStringLen)+import qualified Data.ByteString+import qualified Data.ByteString.Char8 as B8+import qualified Data.ByteString.Internal as B+import qualified Data.ByteString.Unsafe as BU++data Event =+      EventStreamStart+    | EventStreamEnd+    | EventDocumentStart+    | EventDocumentEnd+    | EventAlias !AnchorName+    | EventScalar !ByteString !Tag !Style !Anchor+    | EventSequenceStart !Anchor+    | EventSequenceEnd+    | EventMappingStart !Anchor+    | EventMappingEnd+    deriving (Show, Eq)++data Style = Any+           | Plain+           | SingleQuoted+           | DoubleQuoted+           | Literal+           | Folded+           | PlainNoTag+    deriving (Show, Read, Eq, Enum, Bounded, Ord, Data, Typeable)++data Tag = StrTag+         | FloatTag+         | NullTag+         | BoolTag+         | SetTag+         | IntTag+         | SeqTag+         | MapTag+         | UriTag String+         | NoTag+    deriving (Show, Eq, Read, Data, Typeable)++type AnchorName = String+type Anchor = Maybe AnchorName++tagToString :: Tag -> String+tagToString StrTag = "tag:yaml.org,2002:str"+tagToString FloatTag = "tag:yaml.org,2002:float"+tagToString NullTag = "tag:yaml.org,2002:null"+tagToString BoolTag = "tag:yaml.org,2002:bool"+tagToString SetTag = "tag:yaml.org,2002:set"+tagToString IntTag = "tag:yaml.org,2002:int"+tagToString SeqTag = "tag:yaml.org,2002:seq"+tagToString MapTag = "tag:yaml.org,2002:map"+tagToString (UriTag s) = s+tagToString NoTag = ""++bsToTag :: ByteString -> Tag+bsToTag = stringToTag . B8.unpack++stringToTag :: String -> Tag+stringToTag "tag:yaml.org,2002:str" = StrTag+stringToTag "tag:yaml.org,2002:float" = FloatTag+stringToTag "tag:yaml.org,2002:null" = NullTag+stringToTag "tag:yaml.org,2002:bool" = BoolTag+stringToTag "tag:yaml.org,2002:set" = SetTag+stringToTag "tag:yaml.org,2002:int" = IntTag+stringToTag "tag:yaml.org,2002:seq" = SeqTag+stringToTag "tag:yaml.org,2002:map" = MapTag+stringToTag "" = NoTag+stringToTag s = UriTag s++data ParserStruct+type Parser = Ptr ParserStruct+parserSize :: Int+parserSize = 480++data EventRawStruct+type EventRaw = Ptr EventRawStruct+eventSize :: Int+eventSize = 104++foreign import ccall unsafe "yaml_parser_initialize"+    c_yaml_parser_initialize :: Parser -> IO CInt++foreign import ccall unsafe "yaml_parser_delete"+    c_yaml_parser_delete :: Parser -> IO ()++foreign import ccall unsafe "yaml_parser_set_input_string"+    c_yaml_parser_set_input_string :: Parser+                                   -> Ptr CUChar+                                   -> CULong+                                   -> IO ()++foreign import ccall unsafe "yaml_parser_set_input_file"+    c_yaml_parser_set_input_file :: Parser+                                 -> File+                                 -> IO ()++data FileStruct+type File = Ptr FileStruct++#ifdef WINDOWS+foreign import ccall unsafe "_fdopen"+#else+foreign import ccall unsafe "fdopen"+#endif+    c_fdopen :: CInt+             -> Ptr CChar+             -> IO File+foreign import ccall unsafe "fclose"+    c_fclose :: File+             -> IO ()++foreign import ccall unsafe "fclose_helper"+    c_fclose_helper :: File -> IO ()++foreign import ccall unsafe "yaml_parser_parse"+    c_yaml_parser_parse :: Parser -> EventRaw -> IO CInt++foreign import ccall unsafe "yaml_event_delete"+    c_yaml_event_delete :: EventRaw -> IO ()++foreign import ccall "get_parser_error_problem"+    c_get_parser_error_problem :: Parser -> IO (Ptr CUChar)++foreign import ccall "get_parser_error_context"+    c_get_parser_error_context :: Parser -> IO (Ptr CUChar)++foreign import ccall unsafe "get_parser_error_index"+    c_get_parser_error_index :: Parser -> IO CULong++foreign import ccall unsafe "get_parser_error_line"+    c_get_parser_error_line :: Parser -> IO CULong++foreign import ccall unsafe "get_parser_error_column"+    c_get_parser_error_column :: Parser -> IO CULong++makeString :: MonadIO m => (a -> m (Ptr CUChar)) -> a -> m String+makeString f a = do+    cchar <- castPtr `liftM` f a+    if cchar == nullPtr+        then return ""+        else liftIO $ peekCString cchar++data EventType = YamlNoEvent+               | YamlStreamStartEvent+               | YamlStreamEndEvent+               | YamlDocumentStartEvent+               | YamlDocumentEndEvent+               | YamlAliasEvent+               | YamlScalarEvent+               | YamlSequenceStartEvent+               | YamlSequenceEndEvent+               | YamlMappingStartEvent+               | YamlMappingEndEvent+               deriving (Enum,Show)++foreign import ccall unsafe "get_event_type"+    c_get_event_type :: EventRaw -> IO CInt++foreign import ccall unsafe "get_scalar_value"+    c_get_scalar_value :: EventRaw -> IO (Ptr CUChar)++foreign import ccall unsafe "get_scalar_length"+    c_get_scalar_length :: EventRaw -> IO CULong++foreign import ccall unsafe "get_scalar_tag"+    c_get_scalar_tag :: EventRaw -> IO (Ptr CUChar)++foreign import ccall unsafe "get_scalar_tag_len"+    c_get_scalar_tag_len :: EventRaw -> IO CULong++foreign import ccall unsafe "get_scalar_style"+    c_get_scalar_style :: EventRaw -> IO CInt++foreign import ccall unsafe "get_scalar_anchor"+    c_get_scalar_anchor :: EventRaw -> IO CString++foreign import ccall unsafe "get_sequence_start_anchor"+    c_get_sequence_start_anchor :: EventRaw -> IO CString++foreign import ccall unsafe "get_mapping_start_anchor"+    c_get_mapping_start_anchor :: EventRaw -> IO CString++foreign import ccall unsafe "get_alias_anchor"+    c_get_alias_anchor :: EventRaw -> IO CString++getEvent :: EventRaw -> IO (Maybe Event)+getEvent er = do+    et <- c_get_event_type er+    case toEnum $ fromEnum et of+        YamlNoEvent -> return Nothing+        YamlStreamStartEvent -> return $ Just EventStreamStart+        YamlStreamEndEvent -> return $ Just EventStreamEnd+        YamlDocumentStartEvent -> return $ Just EventDocumentStart+        YamlDocumentEndEvent -> return $ Just EventDocumentEnd+        YamlAliasEvent -> do+            yanchor <- c_get_alias_anchor er+            anchor <- if yanchor == nullPtr+                          then error "got YamlAliasEvent with empty anchor"+                          else peekCString yanchor+            return $ Just $ EventAlias anchor+        YamlScalarEvent -> do+            yvalue <- c_get_scalar_value er+            ylen <- c_get_scalar_length er+            ytag <- c_get_scalar_tag er+            ytag_len <- c_get_scalar_tag_len er+            ystyle <- c_get_scalar_style er+            let ytag_len' = fromEnum ytag_len+            let yvalue' = castPtr yvalue+            let ytag' = castPtr ytag+            let ylen' = fromEnum ylen+            bs <- packCStringLen (yvalue', ylen')+            tagbs <-+                if ytag_len' == 0+                    then return Data.ByteString.empty+                    else packCStringLen (ytag', ytag_len')+            let style = toEnum $ fromEnum ystyle+            yanchor <- c_get_scalar_anchor er+            anchor <- if yanchor == nullPtr+                          then return Nothing+                          else Just <$> peekCString yanchor+            return $ Just $ EventScalar bs (bsToTag tagbs) style anchor+        YamlSequenceStartEvent -> do+            yanchor <- c_get_sequence_start_anchor er+            anchor <- if yanchor == nullPtr+                          then return Nothing+                          else Just <$> peekCString yanchor+            return $ Just $ EventSequenceStart anchor+        YamlSequenceEndEvent -> return $ Just EventSequenceEnd+        YamlMappingStartEvent -> do+            yanchor <- c_get_mapping_start_anchor er+            anchor <- if yanchor == nullPtr+                          then return Nothing+                          else Just <$> peekCString yanchor+            return $ Just $ EventMappingStart anchor+        YamlMappingEndEvent -> return $ Just EventMappingEnd++-- Emitter++data EmitterStruct+type Emitter = Ptr EmitterStruct+emitterSize :: Int+emitterSize = 432++foreign import ccall unsafe "yaml_emitter_initialize"+    c_yaml_emitter_initialize :: Emitter -> IO CInt++foreign import ccall unsafe "yaml_emitter_delete"+    c_yaml_emitter_delete :: Emitter -> IO ()++data BufferStruct+type Buffer = Ptr BufferStruct+bufferSize :: Int+bufferSize = 16++foreign import ccall unsafe "buffer_init"+    c_buffer_init :: Buffer -> IO ()++foreign import ccall unsafe "get_buffer_buff"+    c_get_buffer_buff :: Buffer -> IO (Ptr CUChar)++foreign import ccall unsafe "get_buffer_used"+    c_get_buffer_used :: Buffer -> IO CULong++foreign import ccall unsafe "my_emitter_set_output"+    c_my_emitter_set_output :: Emitter -> Buffer -> IO ()++#ifndef __NO_UNICODE__+foreign import ccall unsafe "yaml_emitter_set_unicode"+    c_yaml_emitter_set_unicode :: Emitter -> CInt -> IO ()+#endif++foreign import ccall unsafe "yaml_emitter_set_output_file"+    c_yaml_emitter_set_output_file :: Emitter -> File -> IO ()++foreign import ccall unsafe "yaml_emitter_emit"+    c_yaml_emitter_emit :: Emitter -> EventRaw -> IO CInt++foreign import ccall unsafe "yaml_stream_start_event_initialize"+    c_yaml_stream_start_event_initialize :: EventRaw -> CInt -> IO CInt++foreign import ccall unsafe "yaml_stream_end_event_initialize"+    c_yaml_stream_end_event_initialize :: EventRaw -> IO CInt++foreign import ccall unsafe "yaml_scalar_event_initialize"+    c_yaml_scalar_event_initialize+        :: EventRaw+        -> Ptr CUChar -- anchor+        -> Ptr CUChar -- tag+        -> Ptr CUChar -- value+        -> CInt       -- length+        -> CInt       -- plain_implicit+        -> CInt       -- quoted_implicit+        -> CInt       -- style+        -> IO CInt++foreign import ccall unsafe "simple_document_start"+    c_simple_document_start :: EventRaw -> IO CInt++foreign import ccall unsafe "yaml_document_end_event_initialize"+    c_yaml_document_end_event_initialize :: EventRaw -> CInt -> IO CInt++foreign import ccall unsafe "yaml_sequence_start_event_initialize"+    c_yaml_sequence_start_event_initialize+        :: EventRaw+        -> Ptr CUChar+        -> Ptr CUChar+        -> CInt+        -> CInt+        -> IO CInt++foreign import ccall unsafe "yaml_sequence_end_event_initialize"+    c_yaml_sequence_end_event_initialize :: EventRaw -> IO CInt++foreign import ccall unsafe "yaml_mapping_start_event_initialize"+    c_yaml_mapping_start_event_initialize+        :: EventRaw+        -> Ptr CUChar+        -> Ptr CUChar+        -> CInt+        -> CInt+        -> IO CInt++foreign import ccall unsafe "yaml_mapping_end_event_initialize"+    c_yaml_mapping_end_event_initialize :: EventRaw -> IO CInt++foreign import ccall unsafe "yaml_alias_event_initialize"+    c_yaml_alias_event_initialize+        :: EventRaw+        -> Ptr CUChar+        -> IO CInt++toEventRaw :: Event -> (EventRaw -> IO a) -> IO a+toEventRaw e f = allocaBytes eventSize $ \er -> do+    ret <- case e of+        EventStreamStart ->+            c_yaml_stream_start_event_initialize+                er+                0 -- YAML_ANY_ENCODING+        EventStreamEnd ->+            c_yaml_stream_end_event_initialize er+        EventDocumentStart ->+            c_simple_document_start er+        EventDocumentEnd ->+            c_yaml_document_end_event_initialize er 1+        EventScalar bs thetag style0 anchor -> do+            BU.unsafeUseAsCStringLen bs $ \(value, len) -> do+                let value' = castPtr value :: Ptr CUChar+                    len' = fromIntegral len :: CInt+                let thetag' = tagToString thetag+                withCString thetag' $ \tag' -> do+                    let (pi, style) =+                            case style0 of+                                PlainNoTag -> (1, Plain)+                                x -> (0, x)+                        style' = toEnum $ fromEnum style+                        tagP = castPtr tag'+                        qi = if null thetag' then 1 else 0+                    case anchor of+                        Nothing ->+                            c_yaml_scalar_event_initialize+                                er+                                nullPtr -- anchor+                                tagP    -- tag+                                value'  -- value+                                len'    -- length+                                pi      -- plain_implicit+                                qi      -- quoted_implicit+                                style'  -- style+                        Just anchor' ->+                            withCString anchor' $ \anchorP' -> do+                                let anchorP = castPtr anchorP'+                                c_yaml_scalar_event_initialize+                                    er+                                    anchorP -- anchor+                                    tagP    -- tag+                                    value'  -- value+                                    len'    -- length+                                    0       -- plain_implicit+                                    qi      -- quoted_implicit+                                    style'  -- style+        EventSequenceStart Nothing ->+            c_yaml_sequence_start_event_initialize+                er+                nullPtr+                nullPtr+                1+                0 -- YAML_ANY_SEQUENCE_STYLE+        EventSequenceStart (Just anchor) ->+            withCString anchor $ \anchor' -> do+                let anchorP = castPtr anchor'+                c_yaml_sequence_start_event_initialize+                    er+                    anchorP+                    nullPtr+                    1+                    0 -- YAML_ANY_SEQUENCE_STYLE+        EventSequenceEnd ->+            c_yaml_sequence_end_event_initialize er+        EventMappingStart Nothing ->+            c_yaml_mapping_start_event_initialize+                er+                nullPtr+                nullPtr+                1+                0 -- YAML_ANY_SEQUENCE_STYLE+        EventMappingStart (Just anchor) ->+            withCString anchor $ \anchor' -> do+                let anchorP = castPtr anchor'+                c_yaml_mapping_start_event_initialize+                    er+                    anchorP+                    nullPtr+                    1+                    0 -- YAML_ANY_SEQUENCE_STYLE+        EventMappingEnd ->+            c_yaml_mapping_end_event_initialize er+        EventAlias anchor ->+            withCString anchor $ \anchorP' -> do+                let anchorP = castPtr anchorP'+                c_yaml_alias_event_initialize+                    er+                    anchorP+    unless (ret == 1) $ throwIO $ ToEventRawException ret+    f er++newtype ToEventRawException = ToEventRawException CInt+    deriving (Show, Typeable)+instance Exception ToEventRawException++decode :: MonadResource m => B.ByteString -> ConduitM i Event m ()+decode bs | B8.null bs = return ()+decode bs =+    bracketP alloc cleanup (runParser . fst)+  where+    alloc = mask_ $ do+        ptr <- mallocBytes parserSize+        res <- c_yaml_parser_initialize ptr+        if res == 0+            then do+                c_yaml_parser_delete ptr+                free ptr+                throwIO $ YamlException "Yaml out of memory"+            else do+                let (bsfptr, offset, len) = B.toForeignPtr bs+                let bsptrOrig = unsafeForeignPtrToPtr bsfptr+                let bsptr = castPtr bsptrOrig `plusPtr` offset+                c_yaml_parser_set_input_string ptr bsptr (fromIntegral len)+                return (ptr, bsfptr)+    cleanup (ptr, bsfptr) = do+        touchForeignPtr bsfptr+        c_yaml_parser_delete ptr+        free ptr++-- XXX copied from GHC.IO.FD+std_flags, read_flags, output_flags, write_flags :: CInt+std_flags    = Posix.o_NOCTTY+output_flags = std_flags    .|. Posix.o_CREAT .|. Posix.o_TRUNC+read_flags   = std_flags    .|. Posix.o_RDONLY+write_flags  = output_flags .|. Posix.o_WRONLY++-- | Open a C FILE* from a file path, using internal GHC API to work correctly+-- on all platforms, even on non-ASCII filenames. The opening mode must be+-- indicated via both 'rawOpenFlags' and 'openMode'.+openFile :: FilePath -> CInt -> String -> IO File+openFile file rawOpenFlags openMode = do+  fd <- liftIO $ Posix.withFilePath file $ \file' ->+    Posix.c_open file' rawOpenFlags 0o666+  if fd /= (-1)+    then withCString openMode $ \openMode' -> c_fdopen fd openMode'+    else return nullPtr++decodeFile :: MonadResource m => FilePath -> ConduitM i Event m ()+decodeFile file =+    bracketP alloc cleanup (runParser . fst)+  where+    alloc = mask_ $ do+        ptr <- mallocBytes parserSize+        res <- c_yaml_parser_initialize ptr+        if res == 0+            then do+                c_yaml_parser_delete ptr+                free ptr+                throwIO $ YamlException "Yaml out of memory"+            else do+                file' <- openFile file read_flags "r"+                if file' == nullPtr+                    then do+                        c_yaml_parser_delete ptr+                        free ptr+                        throwIO $ YamlException+                                $ "Yaml file not found: " ++ file+                    else do+                        c_yaml_parser_set_input_file ptr file'+                        return (ptr, file')+    cleanup (ptr, file') = do+        c_fclose_helper file'+        c_yaml_parser_delete ptr+        free ptr++runParser :: MonadResource m => Parser -> ConduitM i Event m ()+runParser parser = do+    e <- liftIO $ parserParseOne' parser+    case e of+        Left err -> liftIO $ throwIO err+        Right Nothing -> return ()+        Right (Just ev) -> yield ev >> runParser parser++parserParseOne' :: Parser+                -> IO (Either YamlException (Maybe Event))+parserParseOne' parser = allocaBytes eventSize $ \er -> do+    res <- liftIO $ c_yaml_parser_parse parser er+    flip finally (c_yaml_event_delete er) $+      if res == 0+        then do+          problem <- makeString c_get_parser_error_problem parser+          context <- makeString c_get_parser_error_context parser+          index <- c_get_parser_error_index parser+          line <- c_get_parser_error_line parser+          column <- c_get_parser_error_column parser+          let problemMark = YamlMark (fromIntegral index) (fromIntegral line) (fromIntegral column)+          return $ Left $ YamlParseException problem context problemMark+        else Right <$> getEvent er++encode :: MonadResource m => ConduitM Event o m ByteString+encode =+    runEmitter alloc close+  where+    alloc emitter = do+        fbuf <- mallocForeignPtrBytes bufferSize+        withForeignPtr fbuf c_buffer_init+        withForeignPtr fbuf $ c_my_emitter_set_output emitter+        return fbuf+    close _ fbuf = withForeignPtr fbuf $ \b -> do+        ptr' <- c_get_buffer_buff b+        len <- c_get_buffer_used b+        fptr <- newForeignPtr_ $ castPtr ptr'+        return $ B.fromForeignPtr fptr 0 $ fromIntegral len++encodeFile :: MonadResource m+           => FilePath+           -> ConduitM Event o m ()+encodeFile filePath =+    bracketP getFile c_fclose $ \file -> runEmitter (alloc file) (\u _ -> return u)+  where+    getFile = do+        file <- openFile filePath write_flags "w"+        if file == nullPtr+            then throwIO $ YamlException $ "could not open file for write: " ++ filePath+            else return file++    alloc file emitter = c_yaml_emitter_set_output_file emitter file++runEmitter :: MonadResource m+           => (Emitter -> IO a) -- ^ alloc+           -> (() -> a -> IO b) -- ^ close+           -> ConduitM Event o m b+runEmitter allocI closeI =+    bracketP alloc cleanup go+  where+    alloc = mask_ $ do+        emitter <- mallocBytes emitterSize+        res <- c_yaml_emitter_initialize emitter+        when (res == 0) $ throwIO $ YamlException "c_yaml_emitter_initialize failed"+#ifndef __NO_UNICODE__+        c_yaml_emitter_set_unicode emitter 1+#endif+        a <- allocI emitter+        return (emitter, a)+    cleanup (emitter, _) = do+        c_yaml_emitter_delete emitter+        free emitter++    go (emitter, a) =+        loop+      where+        loop = await >>= maybe (close ()) push++        push e = do+            _ <- liftIO $ toEventRaw e $ c_yaml_emitter_emit emitter+            loop+        close u = liftIO $ closeI u a++-- | The pointer position+data YamlMark = YamlMark { yamlIndex :: Int, yamlLine :: Int, yamlColumn :: Int }+    deriving Show++data YamlException = YamlException String+                   -- | problem, context, index, position line, position column+                   | YamlParseException { yamlProblem :: String, yamlContext :: String, yamlProblemMark :: YamlMark }+    deriving (Show, Typeable)+instance Exception YamlException
test/Data/YamlSpec.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TupleSections #-}  module Data.YamlSpec (main, spec) where @@ -28,7 +29,9 @@ import qualified Data.HashMap.Strict as M import qualified Data.Text as T import Data.Aeson.TH+import Data.Scientific (Scientific) import Data.Text (Text)+import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Data.Vector (Vector) import qualified Data.Vector as V import Data.HashMap.Strict (HashMap)@@ -166,6 +169,32 @@      it "truncates files" caseTruncatesFiles +    it "quoting keys #137" $ do+      let keys = T.words "true false NO YES 1.2 1e5 null"+          bs = D.encode $ M.fromList $ map (, ()) keys+          text = decodeUtf8 bs+      forM_ keys $ \key -> do+        let quoted = T.concat ["'", key, "'"]+        unless (quoted `T.isInfixOf` text) $ error $ concat+          [ "Could not find quoted key: "+          , T.unpack quoted+          , "\n\n"+          , T.unpack text+          ] :: IO ()++    describe "non-decimal numbers #135" $ do+      let go str val = it str $ encodeUtf8 (T.pack str) `shouldDecode` val+      go "12345" (12345 :: Int)+      go "+12345" (12345 :: Int)+      go "0o14" (12 :: Int)+      go "0o123" (83 :: Int)+      go "0xC" (12 :: Int)+      go "0xc" (12 :: Int)+      go "0xdeadBEEF" (3735928559 :: Int)+      go "0xDEADBEEF" (3735928559 :: Int)+      go "1.23015e+3" (1230.15 :: Scientific)+      go "12.3015e+02" (1230.15 :: Scientific)+      go "1230.15" (1230.15 :: Scientific)  specialStrings :: [T.Text] specialStrings =
yaml.cabal view
@@ -1,173 +1,245 @@-name:            yaml-version:         0.8.31.1-license:         BSD3-license-file:    LICENSE-author:          Michael Snoyman <michael@snoyman.com>, Anton Ageev <antage@gmail.com>,Kirill Simonov-maintainer:      Michael Snoyman <michael@snoyman.com>-synopsis:        Support for parsing and rendering YAML documents.-description:     README and API documentation are available at <https://www.stackage.org/package/yaml>-category:        Web-stability:       stable-cabal-version:   >= 1.8-build-type:      Simple-homepage:        http://github.com/snoyberg/yaml/-extra-source-files: c/helper.h-                    libyaml/yaml_private.h-                    libyaml/yaml.h-                    libyaml/LICENSE-                    test/largest-string.yaml-                    test/json.yaml-                    test/resources/foo.yaml-                    test/resources/bar.yaml-                    test/resources/baz.yaml-                    test/resources/accent/foo.yaml-                    test/resources/loop/foo.yaml-                    test/resources/loop/bar.yaml-                    test/resources/empty.yaml-                    test/resources/empty2.yaml-                    README.md-                    ChangeLog.md+-- This file has been generated from package.yaml by hpack version 0.28.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: 7a233a8f2ad181b26ff4049e7128d5380951fb67007e81166d2364c7dada6b95 -flag no-exe-  description: don't install the yaml2json or json2yaml executables-  default: True+name:           yaml+version:        0.8.32+synopsis:       Support for parsing and rendering YAML documents.+description:    README and API documentation are available at <https://www.stackage.org/package/yaml>+category:       Data+stability:      stable+homepage:       https://github.com/snoyberg/yaml#readme+bug-reports:    https://github.com/snoyberg/yaml/issues+author:         Michael Snoyman <michael@snoyman.com>, Anton Ageev <antage@gmail.com>,Kirill Simonov+maintainer:     Michael Snoyman <michael@snoyman.com>+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10+extra-source-files:+    c/helper.h+    ChangeLog.md+    libyaml/LICENSE+    libyaml/yaml.h+    libyaml/yaml_private.h+    README.md+    test/json.yaml+    test/largest-string.yaml+    test/resources/accent/foo.yaml+    test/resources/bar.yaml+    test/resources/baz.yaml+    test/resources/empty.yaml+    test/resources/empty2.yaml+    test/resources/foo.yaml+    test/resources/loop/bar.yaml+    test/resources/loop/foo.yaml +source-repository head+  type: git+  location: https://github.com/snoyberg/yaml+ flag no-examples   description: don't build the examples+  manual: False   default: True -flag system-libyaml-  description: Use the system-wide libyaml instead of the bundled copy-  default: False+flag no-exe+  description: don't install the yaml2json or json2yaml executables+  manual: False+  default: True  flag no-unicode   description: Don't enable unicode output. Instead, unicode characters will be escaped.+  manual: False   default: False -library-    other-extensions: LambdaCase-    if impl(ghc < 8.0.2)-      -- Disable building with GHC before 8.0.2.-      -- Due to a cabal bug, do not use buildable: False,-      -- but instead give it an impossible constraint.-      -- See: https://github.com/haskell-infra/hackage-trustees/issues/165-      build-depends: unsupported-ghc-version > 1 && < 1-    build-depends:   base >= 4.9.1 && < 5-                   , transformers >= 0.1-                   , bytestring >= 0.9.1.4-                   , conduit >= 1.2.8 && < 1.4-                   , resourcet >= 0.3 && < 1.3-                   , aeson >= 0.11-                   , containers-                   , unordered-containers-                   , vector-                   , text-                   , attoparsec >= 0.11.3.0-                   , scientific-                   , filepath-                   , directory-                   , semigroups-                   , template-haskell-    exposed-modules: Text.Libyaml-                     Data.Yaml-                     Data.Yaml.Aeson-                     Data.Yaml.Builder-                     Data.Yaml.Config-                     Data.Yaml.Include-                     Data.Yaml.Parser-                     Data.Yaml.Pretty-                     Data.Yaml.TH-    other-modules:-                     Data.Yaml.Internal-    ghc-options:     -Wall-    c-sources:       c/helper.c-    include-dirs:    c-    if flag(no-unicode)-            cpp-options:     -D__NO_UNICODE__-    if flag(system-libyaml)-            pkgconfig-depends: yaml-0.1-    else-            c-sources:       libyaml/api.c,-                             libyaml/dumper.c,-                             libyaml/emitter.c,-                             libyaml/loader.c,-                             libyaml/parser.c,-                             libyaml/reader.c,-                             libyaml/scanner.c,-                             libyaml/writer.c-            include-dirs:    libyaml-    if os(windows)-            cpp-options:     -DWINDOWS+flag system-libyaml+  description: Use the system-wide libyaml instead of the bundled copy+  manual: False+  default: False -executable yaml2json-    if flag(no-exe)-      Buildable: False-    else-      Buildable: True+library+  exposed-modules:+      Data.Yaml+      Data.Yaml.Aeson+      Data.Yaml.Builder+      Data.Yaml.Config+      Data.Yaml.Include+      Data.Yaml.Internal+      Data.Yaml.Parser+      Data.Yaml.Pretty+      Data.Yaml.TH+      Text.Libyaml+  other-modules:+      Paths_yaml+  hs-source-dirs:+      src+  other-extensions: LambdaCase+  ghc-options: -Wall+  include-dirs:+      c+  c-sources:+      c/helper.c+  build-depends:+      aeson >=0.11+    , attoparsec >=0.11.3.0+    , base >=4.9.1 && <5+    , bytestring >=0.9.1.4+    , conduit >=1.2.8 && <1.4+    , containers+    , directory+    , filepath+    , resourcet >=0.3 && <1.3+    , scientific+    , semigroups+    , template-haskell+    , text+    , transformers >=0.1+    , unordered-containers+    , vector+  if flag(no-unicode)+    cpp-options: -D__NO_UNICODE__+  if !(flag(system-libyaml))+    include-dirs:+        libyaml+    c-sources:+        libyaml/api.c+        libyaml/dumper.c+        libyaml/emitter.c+        libyaml/loader.c+        libyaml/parser.c+        libyaml/reader.c+        libyaml/scanner.c+        libyaml/writer.c+  if os(windows)+    cpp-options: -DWINDOWS+  default-language: Haskell2010 -    hs-source-dirs: exe-    main-is: yaml2json.hs-    build-depends: base >= 4 && < 5-                 , yaml-                 , bytestring >= 0.9.1.4-                 , aeson >= 0.7+executable examples+  main-is: Main.hs+  other-modules:+      Config+      Simple+      Paths_yaml+  hs-source-dirs:+      examples+  ghc-options: -Wall+  build-depends:+      aeson >=0.11+    , attoparsec >=0.11.3.0+    , base >=4.9.1 && <5+    , bytestring >=0.9.1.4+    , conduit >=1.2.8 && <1.4+    , containers+    , directory+    , filepath+    , resourcet >=0.3 && <1.3+    , scientific+    , semigroups+    , template-haskell+    , text+    , transformers >=0.1+    , unordered-containers+    , vector+  if flag(no-examples)+    buildable: False+  else+    build-depends:+        raw-strings-qq+      , yaml+  default-language: Haskell2010  executable json2yaml-    if flag(no-exe)-      Buildable: False-    else-      Buildable: True--    hs-source-dirs: exe-    main-is: json2yaml.hs-    build-depends: base >= 4 && < 5-                 , yaml-                 , bytestring >= 0.9.1.4-                 , aeson >= 0.7+  main-is: json2yaml.hs+  other-modules:+      Paths_yaml+  hs-source-dirs:+      exe+  build-depends:+      aeson >=0.11+    , attoparsec >=0.11.3.0+    , base >=4.9.1 && <5+    , bytestring >=0.9.1.4+    , conduit >=1.2.8 && <1.4+    , containers+    , directory+    , filepath+    , resourcet >=0.3 && <1.3+    , scientific+    , semigroups+    , template-haskell+    , text+    , transformers >=0.1+    , unordered-containers+    , vector+    , yaml+  if flag(no-exe)+    buildable: False+  default-language: Haskell2010 +executable yaml2json+  main-is: yaml2json.hs+  other-modules:+      Paths_yaml+  hs-source-dirs:+      exe+  build-depends:+      aeson >=0.11+    , attoparsec >=0.11.3.0+    , base >=4.9.1 && <5+    , bytestring >=0.9.1.4+    , conduit >=1.2.8 && <1.4+    , containers+    , directory+    , filepath+    , resourcet >=0.3 && <1.3+    , scientific+    , semigroups+    , template-haskell+    , text+    , transformers >=0.1+    , unordered-containers+    , vector+    , yaml+  if flag(no-exe)+    buildable: False+  default-language: Haskell2010  test-suite spec-    type: exitcode-stdio-1.0-    hs-source-dirs:  test-    main-is:         Spec.hs-    other-modules:   Data.YamlSpec-                     Data.Yaml.IncludeSpec-                     Data.Yaml.THSpec-    cpp-options:     -DTEST-    build-depends:   hspec >= 1.3-                   , HUnit-                   , base >= 4 && < 5-                   , transformers >= 0.1-                   , bytestring >= 0.9.1.4-                   , conduit-                   , yaml-                   , text-                   , aeson >= 0.7-                   , unordered-containers-                   , directory-                   , vector-                   , resourcet-                   , mockery-                   , base-compat-                   , temporary-    ghc-options:     -Wall--executable examples-   if flag(no-examples)-      Buildable: False-   else-      Buildable: True-      build-depends: base-                   , bytestring-                   , raw-strings-qq-                   , text-                   , yaml-   hs-source-dirs: examples-   main-is: Main.hs-   other-modules: Config-                  Simple-   ghc-options:   -Wall--source-repository head-  type:     git-  location: https://github.com/snoyberg/yaml+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Data.Yaml.IncludeSpec+      Data.Yaml.THSpec+      Data.YamlSpec+      Paths_yaml+  hs-source-dirs:+      test+  ghc-options: -Wall+  cpp-options: -DTEST+  build-depends:+      HUnit+    , aeson >=0.11+    , attoparsec >=0.11.3.0+    , base >=4.9.1 && <5+    , base-compat+    , bytestring >=0.9.1.4+    , conduit >=1.2.8 && <1.4+    , containers+    , directory+    , filepath+    , hspec >=1.3+    , mockery+    , resourcet >=0.3 && <1.3+    , scientific+    , semigroups+    , template-haskell+    , temporary+    , text+    , transformers >=0.1+    , unordered-containers+    , vector+    , yaml+  default-language: Haskell2010