yaml-unscrambler (empty) → 0.1
raw patch · 19 files changed
+1502/−0 lines, 19 filesdep +QuickCheckdep +accdep +attoparsecsetup-changed
Dependencies added: QuickCheck, acc, attoparsec, attoparsec-data, attoparsec-time, base, base64, bytestring, conduit, containers, foldl, hashable, libyaml, mtl, neat-interpolation, quickcheck-instances, rerebase, scientific, selective, tasty, tasty-hunit, tasty-quickcheck, text, text-builder, time, transformers, unordered-containers, uuid, vector, yaml, yaml-unscrambler
Files
- LICENSE +22/−0
- Setup.hs +2/−0
- library/YamlUnscrambler.hs +610/−0
- library/YamlUnscrambler/AsciiAtto.hs +23/−0
- library/YamlUnscrambler/CompactErrRendering.hs +128/−0
- library/YamlUnscrambler/Err.hs +78/−0
- library/YamlUnscrambler/Expectations.hs +115/−0
- library/YamlUnscrambler/Model.hs +24/−0
- library/YamlUnscrambler/Prelude.hs +149/−0
- library/YamlUnscrambler/Util/ByteString.hs +19/−0
- library/YamlUnscrambler/Util/HashMap.hs +10/−0
- library/YamlUnscrambler/Util/List.hs +11/−0
- library/YamlUnscrambler/Util/Maybe.hs +11/−0
- library/YamlUnscrambler/Util/Text.hs +18/−0
- library/YamlUnscrambler/Util/Vector.hs +10/−0
- library/YamlUnscrambler/Util/Word8.hs +14/−0
- library/YamlUnscrambler/Util/Yaml.hs +18/−0
- test/Main.hs +160/−0
- yaml-unscrambler.cabal +80/−0
+ LICENSE view
@@ -0,0 +1,22 @@+Copyright (c) 2020 Nikita Volkov++Permission is hereby granted, free of charge, to any person+obtaining a copy of this software and associated documentation+files (the "Software"), to deal in the Software without+restriction, including without limitation the rights to use,+copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the+Software is furnished to do so, subject to the following+conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ library/YamlUnscrambler.hs view
@@ -0,0 +1,610 @@+module YamlUnscrambler+(+ -- * Execution+ parseText,+ parseByteString,+ getExpectations,+ -- * DSL+ -- ** Value+ Value,+ value,+ nullableValue,+ -- *** Helpers+ sequenceValue,+ mappingValue,+ scalarsValue,+ -- ** Scalar+ Scalar,+ stringScalar,+ nullScalar,+ boolScalar,+ scientificScalar,+ doubleScalar,+ rationalScalar,+ boundedIntegerScalar,+ unboundedIntegerScalar,+ timestampScalar,+ dayScalar,+ timeScalar,+ uuidScalar,+ binaryScalar,+ -- ** Mapping+ Mapping,+ foldMapping,+ byKeyMapping,+ -- ** Sequence+ Sequence,+ foldSequence,+ byOrderSequence,+ byKeySequence,+ -- ** String+ String,+ textString,+ enumString,+ formattedString,+ attoparsedString,+ -- ** ByKey+ ByKey,+ atByKey,+ atOneOfByKey,+ -- ** ByOrder+ ByOrder,+ fetchByOrder,+ -- * Value types+ MaxInputSize(..),+ Signed(..),+ NumeralSystem(..),+ CaseSensitive(..),+)+where++import YamlUnscrambler.Prelude hiding (String)+import YamlUnscrambler.Model+import qualified YamlUnscrambler.Err as Err+import qualified Attoparsec.Time.ByteString as AsciiAtto+import qualified Control.Foldl as Fold+import qualified Data.Attoparsec.ByteString.Char8 as AsciiAtto+import qualified Data.Attoparsec.Text as TextAtto+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Base64 as Base64+import qualified Data.HashMap.Strict as HashMap+import qualified Data.List as List+import qualified Data.Map.Strict as Map+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text+import qualified Data.UUID as UUID+import qualified Data.Vector as Vector+import qualified Data.Vector.Generic as GenericVector+import qualified Data.Yaml.Parser as Yaml+import qualified Text.Libyaml as Libyaml+import qualified YamlUnscrambler.AsciiAtto as AsciiAtto+import qualified YamlUnscrambler.CompactErrRendering as CompactErrRendering+import qualified YamlUnscrambler.Expectations as Ex+import qualified YamlUnscrambler.Util.ByteString as ByteString+import qualified YamlUnscrambler.Util.HashMap as HashMap+import qualified YamlUnscrambler.Util.Text as Text+import qualified YamlUnscrambler.Util.Vector as Vector+import qualified YamlUnscrambler.Util.Yaml as Yaml+++-- * Execution+-------------------------++parseText :: Value a -> Text -> Either Text a+parseText value =+ parseByteString value . Text.encodeUtf8++parseByteString :: Value a -> ByteString -> Either Text a+parseByteString (Value {..}) input =+ do+ Yaml.RawDoc value map <- Yaml.parseByteStringToRawDoc input+ valueParser value map & first CompactErrRendering.renderErrAtPath++{-|+Get a tree of expectations, which can then be converted into+documentation for people working with the YAML document or+into one of the spec formats (e.g., YAML Spec, JSON Spec).+-}+getExpectations :: Value a -> Ex.Value+getExpectations =+ valueExpectation+++-- *+-------------------------++data Value a =+ Value {+ valueExpectation :: Ex.Value,+ valueParser :: Yaml.YamlValue -> Yaml.AnchorMap -> Either Err.ErrAtPath a+ }+ deriving (Functor)++value :: [Scalar a] -> Maybe (Mapping a) -> Maybe (Sequence a) -> Value a+value scalars mappings sequences =+ Value expectations parse+ where+ expectations =+ Ex.Value+ scalarExpectations+ (fmap mappingExpectation mappings)+ (fmap sequenceExpectation sequences)+ scalarExpectations =+ fmap scalarExpectation scalars+ parse input anchorMap =+ case input of+ Yaml.Scalar bytes tag style _ ->+ case scalars of+ [] ->+ Left (Err.ErrAtPath [] (Err.UnexpectedScalarErr expectations))+ _ ->+ runExcept (asum (fmap parse scalars)) &+ first convErr+ where+ parse scalar =+ except $ first (Last . Just) $ scalarParser scalar bytes tag style+ convErr (Last msg) =+ Err.ErrAtPath [] (Err.ScalarErr scalarExpectations bytes tag style msg)+ Yaml.Mapping input _ ->+ case mappings of+ Just mapping ->+ mappingParser mapping input anchorMap+ Nothing ->+ Left (Err.ErrAtPath [] (Err.UnexpectedMappingErr expectations))+ Yaml.Sequence input _ ->+ case sequences of+ Just sequence ->+ sequenceParser sequence input anchorMap+ Nothing ->+ Left (Err.ErrAtPath [] (Err.UnexpectedSequenceErr expectations))+ Yaml.Alias anchorName ->+ case Map.lookup anchorName anchorMap of+ Just value ->+ parse value anchorMap+ Nothing ->+ Left (Err.ErrAtPath [] (Err.UnknownAnchorErr (fromString anchorName)))++nullableValue :: [Scalar a] -> Maybe (Mapping a) -> Maybe (Sequence a) -> Value (Maybe a)+nullableValue scalars mappings sequences =+ value+ ((nullScalar Nothing) : fmap (fmap Just) scalars)+ (fmap (fmap Just) mappings)+ (fmap (fmap Just) sequences)++-- ** Helpers+-------------------------++sequenceValue :: Sequence a -> Value a+sequenceValue sequence =+ value [] Nothing (Just sequence)++mappingValue :: Mapping a -> Value a+mappingValue mapping =+ value [] (Just mapping) Nothing++scalarsValue :: [Scalar a] -> Value a+scalarsValue scalars =+ value scalars Nothing Nothing+++-- *+-------------------------++data Scalar a =+ Scalar {+ scalarExpectation :: Ex.Scalar,+ scalarParser :: ByteString -> Libyaml.Tag -> Libyaml.Style -> Either Text a+ }+ deriving (Functor)++bytesParsingScalar :: Ex.Scalar -> (ByteString -> Either Text a) -> Scalar a+bytesParsingScalar expectation parser =+ Scalar expectation (\ bytes _ _ -> parser bytes)++attoparsedScalar :: Ex.Scalar -> AsciiAtto.Parser a -> Scalar a+attoparsedScalar expectation parser =+ bytesParsingScalar expectation $+ first (const "") . AsciiAtto.parseOnly (parser <* AsciiAtto.endOfInput)++sizedScalar :: MaxInputSize -> Scalar a -> Scalar a+sizedScalar (MaxInputSize maxInputSize) (Scalar {..}) =+ Scalar scalarExpectation $ \ bytes tag style ->+ if ByteString.length bytes <= maxInputSize+ then+ scalarParser bytes tag style+ else+ Left ("Input is longer then the expected maximum of " <> showAsText maxInputSize <> " bytes")++stringScalar :: String a -> Scalar a+stringScalar (String exp parse) =+ bytesParsingScalar+ (Ex.StringScalar exp)+ (\ bytes -> first showAsText (Text.decodeUtf8' bytes) >>= parse)++nullScalar :: a -> Scalar a+nullScalar a =+ Scalar Ex.NullScalar $ \ bytes tag _ ->+ if+ tag == Libyaml.NullTag ||+ ByteString.null bytes ||+ bytes == "~" ||+ ByteString.saysNullInCiAscii bytes+ then+ Right a+ else+ Left "Not null"++boolScalar :: Scalar Bool+boolScalar =+ bytesParsingScalar Ex.BoolScalar $ \ bytes ->+ if ByteString.length bytes <= 5+ then let+ lowercased =+ ByteString.lowercaseInAscii bytes+ in if elem lowercased ["y", "yes", "on", "true", "t", "1"]+ then+ return True+ else if elem lowercased ["n", "no", "off", "false", "f", "0"]+ then+ return False+ else+ Left "Not a boolean"+ else+ Left "Not a boolean"++scientificScalar :: Scalar Scientific+scientificScalar =+ attoparsedScalar Ex.ScientificScalar AsciiAtto.scientific++doubleScalar :: Scalar Double+doubleScalar =+ attoparsedScalar Ex.DoubleScalar AsciiAtto.double++rationalScalar :: MaxInputSize -> Scalar Rational+rationalScalar a =+ sizedScalar a $+ attoparsedScalar (Ex.RationalScalar a) AsciiAtto.rational++{-|+E.g., 'Int', 'Int64', 'Word', but not 'Integer'.+-}+boundedIntegerScalar :: (Integral a, FiniteBits a) => Signed -> NumeralSystem -> Scalar a+boundedIntegerScalar a b =+ attoparsedScalar (Ex.BoundedIntegerScalar a b) (AsciiAtto.integralScalar a b)++unboundedIntegerScalar :: MaxInputSize -> Signed -> NumeralSystem -> Scalar Integer+unboundedIntegerScalar a b c =+ sizedScalar a $+ attoparsedScalar (Ex.UnboundedIntegerScalar a b c) (AsciiAtto.integralScalar b c)++timestampScalar :: Scalar UTCTime+timestampScalar =+ attoparsedScalar Ex.Iso8601TimestampScalar AsciiAtto.utcTimeInISO8601++dayScalar :: Scalar Day+dayScalar =+ attoparsedScalar Ex.Iso8601DayScalar AsciiAtto.dayInISO8601++timeScalar :: Scalar TimeOfDay+timeScalar =+ attoparsedScalar Ex.Iso8601TimeScalar AsciiAtto.timeOfDayInISO8601++uuidScalar :: Scalar UUID+uuidScalar =+ bytesParsingScalar Ex.UuidScalar $ \ bytes ->+ case UUID.fromASCIIBytes bytes of+ Just uuid ->+ return uuid+ Nothing ->+ Left "Invalid UUID"++binaryScalar :: Scalar ByteString+binaryScalar =+ bytesParsingScalar Ex.Base64BinaryScalar $ \ bytes ->+ let+ bytesWithoutNewlines =+ ByteString.filter (/= 10) bytes+ in case Base64.decodeBase64 bytesWithoutNewlines of+ Right res ->+ return res+ Left err ->+ Left err+++-- *+-------------------------++data Mapping a =+ Mapping {+ mappingExpectation :: Ex.Mapping,+ mappingParser :: [(Text, Yaml.YamlValue)] -> Yaml.AnchorMap -> Either Err.ErrAtPath a+ }+ deriving (Functor)++foldMapping :: (key -> val -> assoc) -> Fold assoc a -> String key -> Value val -> Mapping a+foldMapping zip (Fold foldStep foldInit foldExtract) key val =+ Mapping+ (Ex.MonomorphicMapping (stringExpectation key) (valueExpectation val))+ parser+ where+ parser input anchorMap =+ foldM step foldInit input &+ fmap foldExtract+ where+ step state (keyInput, valInput) =+ do+ parsedKey <- first keyErr (stringParser key keyInput)+ parsedVal <- first (Err.atSegment keyInput) (valueParser val valInput anchorMap)+ return $! foldStep state (zip parsedKey parsedVal)+ where+ keyErr =+ Err.ErrAtPath [] .+ Err.KeyErr (stringExpectation key) keyInput++byKeyMapping :: CaseSensitive -> ByKey Text a -> Mapping a+byKeyMapping caseSensitive byKey =+ Mapping expectation parser+ where+ expectation =+ Ex.ByKeyMapping caseSensitive (byKeyExpectation byKey)+ parser input =+ either Left (first keysErr) . runExceptT . parser+ where+ parser =+ if coerce caseSensitive+ then let+ map =+ HashMap.fromList input+ lookup k =+ HashMap.lookup k map+ lookupFirst kl =+ HashMap.lookupFirst kl map+ in byKeyParser byKey id lookup lookupFirst+ else let+ map =+ HashMap.fromList (fmap (first Text.toLower) input)+ lookup k =+ HashMap.lookup (Text.toLower k) map+ lookupFirst kl =+ HashMap.lookupFirst (fmap Text.toLower kl) map+ in byKeyParser byKey id lookup lookupFirst+ keysErr keys =+ Err.ErrAtPath [] $+ Err.NoneOfMappingKeysFoundErr (byKeyExpectation byKey) caseSensitive keysAvail (toList keys)+ where+ keysAvail =+ fmap fst input+++-- *+-------------------------++data Sequence a =+ Sequence {+ sequenceExpectation :: Ex.Sequence,+ sequenceParser :: [Yaml.YamlValue] -> Yaml.AnchorMap -> Either Err.ErrAtPath a+ }+ deriving (Functor)++foldSequence :: Fold a b -> Value a -> Sequence b+foldSequence (Fold foldStep foldInit foldExtract) value =+ Sequence+ (Ex.MonomorphicSequence (valueExpectation value))+ parser+ where+ parser input anchorMap =+ foldM step (0 :: Int, foldInit) input &+ fmap (foldExtract . snd)+ where+ step (!index, !state) input =+ valueParser value input anchorMap &+ first (Err.atSegment (showAsText index)) &+ fmap (\ a -> (succ index, foldStep state a))++byOrderSequence :: ByOrder a -> Sequence a+byOrderSequence (ByOrder {..}) =+ Sequence+ (Ex.ByOrderSequence byOrderExpectation)+ parser+ where+ parser input anchorMap =+ runExceptT (runReaderT (evalStateT byOrderParser (0, input)) anchorMap) &+ either Left (first mapErr)+ where+ mapErr =+ \ case+ NotEnoughElementsByOrderErr a ->+ Err.ErrAtPath [] $+ Err.NotEnoughElementsErr byOrderExpectation a++byKeySequence :: ByKey Int a -> Sequence a+byKeySequence (ByKey {..}) =+ Sequence expectation parser+ where+ expectation =+ Ex.ByKeySequence byKeyExpectation+ parser input =+ let+ vector =+ Vector.fromList input+ lookup k =+ vector Vector.!? k+ lookupFirst kl =+ Vector.lookupFirst kl vector+ in \ anchorMap ->+ runExceptT (byKeyParser showAsText lookup lookupFirst anchorMap) &+ either Left (first keysErr)+ where+ keysErr keys =+ Err.ErrAtPath [] $+ Err.NoneOfSequenceKeysFoundErr byKeyExpectation (toList keys)+++-- *+-------------------------++data String a =+ String {+ stringExpectation :: Ex.String,+ stringParser :: Text -> Either Text a+ }+ deriving (Functor)++textString :: String Text+textString =+ String Ex.AnyString return++enumString :: CaseSensitive -> [(Text, a)] -> String a+enumString (CaseSensitive caseSensitive) assocList =+ String expectation parser+ where+ expectation =+ Ex.OneOfString (CaseSensitive caseSensitive) (fmap fst assocList)+ {-# NOINLINE lookup #-}+ lookup =+ if length assocList > 512+ then if caseSensitive+ then let+ hashMap =+ HashMap.fromList assocList+ in flip HashMap.lookup hashMap+ else let+ hashMap =+ HashMap.fromList (fmap (first Text.toLower) assocList)+ in flip HashMap.lookup hashMap . Text.toLower+ else if caseSensitive+ then+ flip List.lookup assocList+ else+ flip List.lookup (fmap (first Text.toLower) assocList) . Text.toLower+ parser text =+ case lookup text of+ Just a -> return a+ _ -> Left "Unexpected value"++formattedString :: Text -> (Text -> Either Text a) -> String a+formattedString format parser =+ String+ (Ex.FormattedString format)+ parser++attoparsedString :: Text -> TextAtto.Parser a -> String a+attoparsedString format parser =+ String+ (Ex.FormattedString format)+ (first fromString . TextAtto.parseOnly parser)+++-- *+-------------------------++data ByKey key a =+ ByKey {+ byKeyExpectation :: Ex.ByKey key,+ byKeyParser ::+ (key -> Text) ->+ (key -> Maybe Yaml.YamlValue) ->+ ([key] -> Maybe (key, Yaml.YamlValue)) ->+ Yaml.AnchorMap ->+ ExceptT (Acc key) (Either Err.ErrAtPath) a+ }+ deriving (Functor)++instance Applicative (ByKey key) where+ pure =+ ByKey Ex.AnyByKey . const . const . const . const . pure+ (<*>) (ByKey le lp) (ByKey re rp) =+ ByKey+ (Ex.BothByKey le re)+ (\ a b c d -> lp a b c d <*> rp a b c d)++instance Selective (ByKey key) where+ select (ByKey le lp) (ByKey re rp) =+ ByKey+ (Ex.BothByKey le re)+ (\ a b c d -> select (lp a b c d) (rp a b c d))++instance Alternative (ByKey key) where+ empty =+ ByKey+ Ex.NoByKey+ (const (const (const (const empty))))+ (<|>) (ByKey le lp) (ByKey re rp) =+ ByKey+ (Ex.EitherByKey le re)+ (\ a b c d -> lp a b c d <|> rp a b c d)++atByKey :: key -> Value a -> ByKey key a+atByKey key valueSpec =+ ByKey+ (Ex.LookupByKey [key] (valueExpectation valueSpec))+ parser+ where+ parser renderKey lookup _ env =+ case lookup key of+ Just val ->+ lift $ first (Err.atSegment (renderKey key)) $+ valueParser valueSpec val env+ Nothing ->+ throwE (pure key)++atOneOfByKey :: [key] -> Value a -> ByKey key a+atOneOfByKey keys valueSpec =+ ByKey+ (Ex.LookupByKey keys (valueExpectation valueSpec))+ parser+ where+ parser renderKey _ lookup env =+ case lookup keys of+ Just (key, val) ->+ lift $ first (Err.atSegment (renderKey key)) $+ valueParser valueSpec val env+ Nothing ->+ throwE (fromList keys)+++-- *+-------------------------++data ByOrderErr =+ NotEnoughElementsByOrderErr+ Int++data ByOrder a =+ ByOrder {+ byOrderExpectation :: Ex.ByOrder,+ byOrderParser :: StateT (Int, [Yaml.YamlValue]) (ReaderT Yaml.AnchorMap (ExceptT ByOrderErr (Either Err.ErrAtPath))) a+ }+ deriving (Functor)++instance Applicative ByOrder where+ pure =+ ByOrder Ex.AnyByOrder . pure+ (<*>) (ByOrder le lp) (ByOrder re rp) =+ ByOrder+ (Ex.BothByOrder le re)+ (lp <*> rp)++instance Selective ByOrder where+ select (ByOrder le lp) (ByOrder re rp) =+ ByOrder+ (Ex.BothByOrder le re)+ (select lp rp)++fetchByOrder :: Value a -> ByOrder a+fetchByOrder value =+ ByOrder+ (Ex.FetchByOrder (valueExpectation value))+ parser+ where+ parser =+ do+ (!offset, list) <- get+ case list of+ h : t ->+ do+ put (succ offset, t)+ anchorMap <- ask+ lift $ lift $ lift $ first (Err.atSegment (showAsText offset)) $ valueParser value h anchorMap+ _ ->+ throwError $ NotEnoughElementsByOrderErr offset
+ library/YamlUnscrambler/AsciiAtto.hs view
@@ -0,0 +1,23 @@+module YamlUnscrambler.AsciiAtto+where++import YamlUnscrambler.Prelude+import YamlUnscrambler.Model+import Data.Attoparsec.ByteString.Char8+import Attoparsec.Time.ByteString+++integralScalar :: (Integral a, Bits a) => Signed -> NumeralSystem -> Parser a+integralScalar (Signed isSigned) numeralSystem =+ if isSigned+ then+ signed numeralParser+ else+ numeralParser+ where+ numeralParser =+ case numeralSystem of+ DecimalNumeralSystem ->+ decimal+ HexadecimalNumeralSystem ->+ hexadecimal
+ library/YamlUnscrambler/CompactErrRendering.hs view
@@ -0,0 +1,128 @@+module YamlUnscrambler.CompactErrRendering+(+ renderErrAtPath,+)+where++import YamlUnscrambler.Prelude hiding (intercalate)+import YamlUnscrambler.Model+import Text.Builder+import qualified YamlUnscrambler.Err as Err+import qualified YamlUnscrambler.Expectations as Ex+import qualified Data.Text.Encoding as Text+import qualified Data.Text as Text+++renderErrAtPath :: Err.ErrAtPath -> Text+renderErrAtPath =+ run . errAtPath++path a =+ "/" <> intercalate "/" (fmap text a)++errAtPath (Err.ErrAtPath a b) =+ "Error at path " <> path a <> ". " <> reason b++reason =+ \ case+ Err.KeyErr a b c ->+ text c <> ". On input: " <> string (show b) <> ". " <>+ "Expecting: " <> stringExpectation a+ Err.NoneOfMappingKeysFoundErr a b c d ->+ "None of keys found " <> caseSensitively b <> ": " <> string (show d) <> ". " <>+ "Keys available: " <> string (show c)+ Err.NoneOfSequenceKeysFoundErr a b ->+ "None of indices found: " <> string (show b)+ Err.ScalarErr a b c d e ->+ foldMap (\ a -> text a <> ". ") (mfilter (not . Text.null) e) <>+ "Expecting one of the following formats: " <>+ intercalate ", " (fmap scalarExpectation a) <>+ foldMap (\ a -> ". Got input: " <> string (show a)) (Text.decodeUtf8' b)+ Err.UnexpectedScalarErr a ->+ "Unexpected scalar value"+ Err.UnexpectedMappingErr a ->+ "Unexpected mapping value"+ Err.UnexpectedSequenceErr a ->+ "Unexpected sequence value"+ Err.UnknownAnchorErr a ->+ "Unknown anchor: " <> text a+ Err.NotEnoughElementsErr a b ->+ "Not enough elements: " <> decimal b <> ". " <>+ "Expecting: " <> byOrderExpectation a++scalarExpectation =+ \ case+ Ex.StringScalar a ->+ stringExpectation a+ Ex.NullScalar ->+ "null"+ Ex.BoolScalar ->+ "boolean"+ Ex.ScientificScalar ->+ "scientific"+ Ex.DoubleScalar ->+ "double"+ Ex.RationalScalar a ->+ "rational of maximum length of " <> maxInputSize a <> " chars"+ Ex.BoundedIntegerScalar a b ->+ signed a <> " " <> numeralSystem b+ Ex.UnboundedIntegerScalar a b c ->+ signed b <> " " <> numeralSystem c <> " of maximum length of " <> maxInputSize a <> " chars"+ Ex.Iso8601TimestampScalar ->+ "timestamp in ISO-8601"+ Ex.Iso8601DayScalar ->+ "date in ISO-8601"+ Ex.Iso8601TimeScalar ->+ "time in ISO-8601"+ Ex.UuidScalar ->+ "UUID"+ Ex.Base64BinaryScalar ->+ "binary data in Base-64"++stringExpectation =+ \ case+ Ex.AnyString ->+ "any string"+ Ex.OneOfString a b ->+ "one of " <> string (show b) <> "(" <> caseSensitive a <> ")"+ Ex.FormattedString a ->+ text a++byOrderExpectation =+ decimal . count 0+ where+ count !a =+ \ case+ Ex.AnyByOrder ->+ a+ Ex.BothByOrder b c ->+ countBoth a b c+ Ex.FetchByOrder _ ->+ succ a+ countBoth a b c =+ case b of+ Ex.BothByOrder d e ->+ countBoth a d (Ex.BothByOrder e c)+ Ex.AnyByOrder ->+ count a c+ Ex.FetchByOrder _ ->+ count (succ a) c++caseSensitive (CaseSensitive a) =+ "case-" <> bool "insensitive" "sensitive" a++caseSensitively (CaseSensitive a) =+ "case-" <> bool "insensitively" "sensitively" a++signed (Signed a) =+ bool "unsigned" "signed" a++numeralSystem =+ \ case+ DecimalNumeralSystem ->+ "decimal"+ HexadecimalNumeralSystem ->+ "hexadecimal"++maxInputSize (MaxInputSize a) =+ decimal a
+ library/YamlUnscrambler/Err.hs view
@@ -0,0 +1,78 @@+module YamlUnscrambler.Err+(+ ErrAtPath(..),+ Err(..),+ atSegment,+ -- *+ MaxInputSize(..),+ Signed(..),+ NumeralSystem(..),+ CaseSensitive(..),+ -- * Reexports from \"libyaml\"+ Libyaml.Tag(..),+ Libyaml.Style(..),+)+where++import YamlUnscrambler.Prelude hiding (String)+import YamlUnscrambler.Model+import qualified YamlUnscrambler.Expectations as Ex+import qualified Text.Libyaml as Libyaml+import qualified Text.Builder as TextBuilder+++data ErrAtPath =+ ErrAtPath [Text] Err++data Err =+ KeyErr+ Ex.String+ {-^ Key expectation. -}+ Text+ {-^ Key input. -}+ Text+ {-^ String parsing error. -}+ |+ NoneOfMappingKeysFoundErr+ (Ex.ByKey Text)+ CaseSensitive+ [Text]+ {-^ Available keys. -}+ [Text]+ {-^ Keys looked up. -}+ |+ NoneOfSequenceKeysFoundErr+ (Ex.ByKey Int)+ [Int]+ |+ ScalarErr+ [Ex.Scalar]+ {-^ Expected formats. -}+ ByteString+ {-^ Input. -}+ Libyaml.Tag+ {-^ Tag. -}+ Libyaml.Style+ {-^ Style. -}+ (Maybe Text)+ {-^ Last error. -}+ |+ UnexpectedScalarErr+ Ex.Value+ |+ UnexpectedMappingErr+ Ex.Value+ |+ UnexpectedSequenceErr+ Ex.Value+ |+ UnknownAnchorErr+ Text+ |+ NotEnoughElementsErr+ Ex.ByOrder+ Int++atSegment :: Text -> ErrAtPath -> ErrAtPath+atSegment seg (ErrAtPath path err) =+ ErrAtPath (seg : path) err
+ library/YamlUnscrambler/Expectations.hs view
@@ -0,0 +1,115 @@+module YamlUnscrambler.Expectations+(+ Value(..),+ Scalar(..),+ Mapping(..),+ Sequence(..),+ String(..),+ ByKey(..),+ ByOrder(..),+ -- *+ MaxInputSize(..),+ Signed(..),+ NumeralSystem(..),+ CaseSensitive(..),+)+where++import YamlUnscrambler.Prelude hiding (String)+import YamlUnscrambler.Model+import qualified YamlUnscrambler.Util.Maybe as Maybe+++-- *+-------------------------++data Value =+ Value+ [Scalar]+ (Maybe Mapping)+ (Maybe Sequence)++data Scalar =+ StringScalar String+ |+ NullScalar+ |+ BoolScalar+ |+ ScientificScalar+ |+ DoubleScalar+ |+ RationalScalar MaxInputSize+ |+ BoundedIntegerScalar Signed NumeralSystem+ |+ UnboundedIntegerScalar MaxInputSize Signed NumeralSystem+ |+ Iso8601TimestampScalar+ |+ Iso8601DayScalar+ |+ Iso8601TimeScalar+ |+ UuidScalar+ |+ Base64BinaryScalar++data Mapping =+ MonomorphicMapping String Value+ |+ ByKeyMapping CaseSensitive (ByKey Text)++data Sequence =+ MonomorphicSequence Value+ |+ ByOrderSequence ByOrder+ |+ ByKeySequence (ByKey Int)++-- *+-------------------------++data String =+ {-| Any string as it is. -}+ AnyString+ |+ {-| One of options. Suitable for enumerations. -}+ OneOfString CaseSensitive [Text] {-^ Options. -}+ |+ {-| Must conform to a textually described format. -}+ FormattedString Text {-^ Description of the format. -}++data ByKey key =+ AnyByKey+ |+ NoByKey+ |+ EitherByKey (ByKey key) (ByKey key)+ |+ BothByKey (ByKey key) (ByKey key)+ |+ LookupByKey [key] {-^ Keys to lookup. -} Value++data ByOrder =+ AnyByOrder+ |+ BothByOrder ByOrder ByOrder+ |+ FetchByOrder Value+++-- *+-------------------------++instance Semigroup Value where+ (<>) (Value lScalars lMappings lSequences) (Value rScalars rMappings rSequences) =+ Value+ (lScalars <> rScalars)+ (Maybe.firstNonEmpty lMappings rMappings)+ (Maybe.firstNonEmpty lSequences rSequences)++instance Monoid Value where+ mempty =+ Value [] Nothing Nothing
+ library/YamlUnscrambler/Model.hs view
@@ -0,0 +1,24 @@+module YamlUnscrambler.Model+where++import YamlUnscrambler.Prelude+++{-|+Specification of the maximum allowed length for the input.+A safety measure to ensure that the parser doesn't exhaust memory+when parsing to unlimited datatypes.+-}+newtype MaxInputSize =+ MaxInputSize Int++newtype Signed =+ Signed Bool++data NumeralSystem =+ DecimalNumeralSystem+ |+ HexadecimalNumeralSystem++newtype CaseSensitive =+ CaseSensitive Bool
+ library/YamlUnscrambler/Prelude.hs view
@@ -0,0 +1,149 @@+module YamlUnscrambler.Prelude+( + module Exports,+ showAsText,+)+where++-- base+-------------------------+import Control.Applicative as Exports+import Control.Arrow as Exports hiding (first, second)+import Control.Category as Exports+import Control.Concurrent as Exports+import Control.Exception as Exports+import Control.Monad as Exports hiding (fail, mapM_, sequence_, forM_, msum, mapM, sequence, forM)+import Control.Monad.IO.Class as Exports+import Control.Monad.Fail as Exports+import Control.Monad.Fix as Exports hiding (fix)+import Control.Monad.ST as Exports+import Data.Bifunctor as Exports+import Data.Bits as Exports+import Data.Bool as Exports+import Data.Char as Exports+import Data.Coerce as Exports+import Data.Complex as Exports+import Data.Data as Exports+import Data.Dynamic as Exports+import Data.Either as Exports+import Data.Fixed as Exports+import Data.Foldable as Exports hiding (toList)+import Data.Function as Exports hiding (id, (.))+import Data.Functor as Exports+import Data.Functor.Compose as Exports+import Data.Functor.Contravariant as Exports+import Data.Int as Exports+import Data.IORef as Exports+import Data.Ix as Exports+import Data.List as Exports hiding (sortOn, isSubsequenceOf, uncons, concat, foldr, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, find, maximumBy, minimumBy, mapAccumL, mapAccumR, foldl')+import Data.List.NonEmpty as Exports (NonEmpty(..))+import Data.Maybe as Exports+import Data.Monoid as Exports hiding (Alt)+import Data.Ord as Exports+import Data.Proxy as Exports+import Data.Ratio as Exports+import Data.STRef as Exports+import Data.String as Exports+import Data.Traversable as Exports+import Data.Tuple as Exports+import Data.Unique as Exports+import Data.Version as Exports+import Data.Void as Exports+import Data.Word as Exports+import Debug.Trace as Exports+import Foreign.ForeignPtr as Exports+import Foreign.Ptr as Exports+import Foreign.StablePtr as Exports+import Foreign.Storable as Exports+import GHC.Conc as Exports hiding (orElse, withMVar, threadWaitWriteSTM, threadWaitWrite, threadWaitReadSTM, threadWaitRead)+import GHC.Exts as Exports (IsList(..), lazy, inline, sortWith, groupWith)+import GHC.Generics as Exports (Generic)+import GHC.IO.Exception as Exports+import Numeric as Exports+import Prelude as Exports hiding (fail, concat, foldr, mapM_, sequence_, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, mapM, sequence, id, (.))+import System.Environment as Exports+import System.Exit as Exports+import System.IO as Exports (Handle, hClose)+import System.IO.Error as Exports+import System.IO.Unsafe as Exports+import System.Mem as Exports+import System.Mem.StableName as Exports+import System.Timeout as Exports+import Text.ParserCombinators.ReadP as Exports (ReadP, ReadS, readP_to_S, readS_to_P)+import Text.ParserCombinators.ReadPrec as Exports (ReadPrec, readPrec_to_P, readP_to_Prec, readPrec_to_S, readS_to_Prec)+import Text.Printf as Exports (printf, hPrintf)+import Text.Read as Exports (Read(..), readMaybe, readEither)+import Unsafe.Coerce as Exports++-- selective+-------------------------+import Control.Selective as Exports++-- text+-------------------------+import Data.Text as Exports (Text)++-- bytestring+-------------------------+import Data.ByteString as Exports (ByteString)++-- vector+-------------------------+import Data.Vector as Exports (Vector)++-- hashable+-------------------------+import Data.Hashable as Exports (Hashable)++-- containers+-------------------------+import Data.Map.Strict as Exports (Map)++-- unordered-containers+-------------------------+import Data.HashSet as Exports (HashSet)+import Data.HashMap.Strict as Exports (HashMap)++-- uuid+-------------------------+import Data.UUID as Exports (UUID)++-- foldl+-------------------------+import Control.Foldl as Exports (Fold(..))++-- transformers+-------------------------+import Control.Monad.IO.Class as Exports+import Control.Monad.Trans.Class as Exports+import Control.Monad.Trans.Cont as Exports hiding (shift, callCC)+import Control.Monad.Trans.Except as Exports (ExceptT(ExceptT), Except, except, runExcept, runExceptT, mapExcept, mapExceptT, withExcept, withExceptT, throwE, catchE)+import Control.Monad.Trans.Maybe as Exports+import Control.Monad.Trans.Reader as Exports (Reader, runReader, mapReader, withReader, ReaderT(ReaderT), runReaderT, mapReaderT, withReaderT)+import Control.Monad.Trans.State.Strict as Exports (State, runState, evalState, execState, mapState, withState, StateT(StateT), runStateT, evalStateT, execStateT, mapStateT, withStateT)+import Control.Monad.Trans.Writer.Strict as Exports (Writer, runWriter, execWriter, mapWriter, WriterT(..), execWriterT, mapWriterT)++-- mtl+-------------------------+import Control.Monad.Cont.Class as Exports+import Control.Monad.Error.Class as Exports hiding (Error(..))+import Control.Monad.Reader.Class as Exports+import Control.Monad.State.Class as Exports+import Control.Monad.Writer.Class as Exports++-- scientific+-------------------------+import Data.Scientific as Exports (Scientific)++-- time+-------------------------+import Data.Time as Exports+import Data.Time.Clock.System as Exports+import Data.Time.Clock.POSIX as Exports++-- acc+-------------------------+import Acc as Exports (Acc)++showAsText :: Show a => a -> Text+showAsText = show >>> fromString
+ library/YamlUnscrambler/Util/ByteString.hs view
@@ -0,0 +1,19 @@+module YamlUnscrambler.Util.ByteString+where++import YamlUnscrambler.Prelude hiding (map, length)+import Data.ByteString+import qualified YamlUnscrambler.Util.Word8 as Word8+++lowercaseInAscii =+ map Word8.lowercaseInAscii++{-# NOINLINE lowercaseNullInAscii #-}+lowercaseNullInAscii :: ByteString+lowercaseNullInAscii =+ "null"++saysNullInCiAscii :: ByteString -> Bool+saysNullInCiAscii a =+ length a == 4 && lowercaseInAscii a == lowercaseNullInAscii
+ library/YamlUnscrambler/Util/HashMap.hs view
@@ -0,0 +1,10 @@+module YamlUnscrambler.Util.HashMap+where++import YamlUnscrambler.Prelude hiding (lookup)+import Data.HashMap.Strict+++lookupFirst :: (Hashable k, Eq k) => [k] -> HashMap k v -> Maybe (k, v)+lookupFirst keys map =+ getFirst (foldMap (\ k -> First (fmap (k,) (lookup k map))) keys)
+ library/YamlUnscrambler/Util/List.hs view
@@ -0,0 +1,11 @@+module YamlUnscrambler.Util.List+where++import YamlUnscrambler.Prelude hiding (lookup)+++firstNonEmpty :: [a] -> [a] -> [a]+firstNonEmpty a b =+ case a of+ [] -> b+ _ -> a
+ library/YamlUnscrambler/Util/Maybe.hs view
@@ -0,0 +1,11 @@+module YamlUnscrambler.Util.Maybe+where++import YamlUnscrambler.Prelude hiding (lookup)+++firstNonEmpty :: Maybe a -> Maybe a -> Maybe a+firstNonEmpty a b =+ case a of+ Just a -> Just a+ _ -> b
+ library/YamlUnscrambler/Util/Text.hs view
@@ -0,0 +1,18 @@+module YamlUnscrambler.Util.Text+where++import YamlUnscrambler.Prelude+import qualified Data.Attoparsec.Text as Atto+import qualified Attoparsec.Data as Atto+++deshowIfPossible :: Text -> Text+deshowIfPossible a =+ either (const a) id (deshow a)++deshow :: Text -> Either Text Text+deshow =+ first fromString . Atto.parseOnly parser+ where+ parser =+ Atto.show <* Atto.endOfInput
+ library/YamlUnscrambler/Util/Vector.hs view
@@ -0,0 +1,10 @@+module YamlUnscrambler.Util.Vector+where++import YamlUnscrambler.Prelude hiding (lookup)+import Data.Vector+++lookupFirst :: [Int] -> Vector a -> Maybe (Int, a)+lookupFirst keys vector =+ getFirst (foldMap (\ k -> First (fmap (k,) (vector !? k))) keys)
+ library/YamlUnscrambler/Util/Word8.hs view
@@ -0,0 +1,14 @@+module YamlUnscrambler.Util.Word8+where++import YamlUnscrambler.Prelude+++lowercaseInAscii :: Word8 -> Word8+lowercaseInAscii a =+ if+ 65 <= a && a <= 90 ||+ 192 <= a && a <= 214 ||+ 216 <= a && a <= 222+ then a + 32+ else a
+ library/YamlUnscrambler/Util/Yaml.hs view
@@ -0,0 +1,18 @@+module YamlUnscrambler.Util.Yaml+where++import YamlUnscrambler.Prelude+import qualified Conduit+import qualified Data.Yaml as Yaml+import qualified Data.Yaml.Parser as YamlParser+import qualified Data.Text.Encoding as Text+import qualified Text.Libyaml as Libyaml+++parseByteStringToRawDoc :: ByteString -> Either Text YamlParser.RawDoc+parseByteStringToRawDoc input =+ first showAsText $+ unsafePerformIO $+ try @SomeException $+ Conduit.runConduitRes $+ Conduit.fuse (Libyaml.decode input) (YamlParser.sinkRawDoc)
+ test/Main.hs view
@@ -0,0 +1,160 @@+module Main where++import Prelude hiding (assert)+import GHC.Exts (fromList)+import Test.QuickCheck.Instances+import Test.Tasty+import Test.Tasty.Runners+import Test.Tasty.HUnit+import Test.Tasty.QuickCheck+import qualified YamlUnscrambler as U+import qualified Test.QuickCheck as QuickCheck+import qualified Control.Foldl as Fold+import qualified NeatInterpolation as NeatInterpolation+import qualified Data.Text as Text+++main =+ defaultMain $ + testGroup "All tests" [+ testCase "Should fail on sequence when no sequence is specified" $ let+ unscrambler =+ U.value [] (Just mapping) Nothing+ where+ mapping =+ U.foldMapping (,) Fold.list U.textString value+ where+ value =+ U.value [nullScalar, intScalar] Nothing Nothing+ where+ nullScalar =+ U.nullScalar Nothing+ intScalar =+ fmap Just $ U.boundedIntegerScalar @Int (U.Signed True) U.DecimalNumeralSystem+ input =+ [NeatInterpolation.text|+ a: 2+ b: 3+ c:+ - 1+ - 2+ |]+ in case U.parseText unscrambler input of+ Right res ->+ assertFailure (show res)+ Left failure ->+ assertEqual (Text.unpack failure) "Error at path /c. Unexpected sequence value" failure+ ,+ testCase "Domain sum-type regression" $ let+ unscrambler =+ doc+ where+ doc =+ U.mappingValue $+ U.byKeyMapping (U.CaseSensitive True) $+ asum [+ Just <$> U.atByKey "sums" sum,+ pure Nothing+ ]+ sum =+ U.mappingValue $+ U.foldMapping (,) Fold.list U.textString sumVariant+ sumVariant =+ U.mappingValue $+ U.foldMapping (,) Fold.list U.textString params+ params =+ U.value [nullScalar, intScalar] Nothing Nothing+ where+ nullScalar =+ U.nullScalar Nothing+ intScalar =+ fmap Just $ U.boundedIntegerScalar @Int (U.Signed True) U.DecimalNumeralSystem+ input =+ [NeatInterpolation.text|+ sums:+ A:+ a:+ - Int+ - Bool+ b: Char, Double+ |]+ in case U.parseText unscrambler input of+ Right res ->+ assertFailure (show res)+ Left failure ->+ assertEqual "" "Error at path /sums/A/a. Unexpected sequence value" failure+ ,+ testCase "Domain sum-type correct" $ let+ unscrambler =+ doc+ where+ doc =+ U.mappingValue $+ U.byKeyMapping (U.CaseSensitive True) $+ asum [+ Just <$> U.atByKey "sums" sum,+ pure Nothing+ ]+ sum =+ U.mappingValue $+ U.foldMapping (,) Fold.list U.textString sumVariant+ sumVariant =+ U.mappingValue $+ U.foldMapping (,) Fold.list U.textString params+ params =+ U.value [nullScalar, valueScalar] Nothing Nothing+ where+ nullScalar =+ U.nullScalar Nothing+ valueScalar =+ fmap Just $ U.stringScalar U.textString+ input =+ [NeatInterpolation.text|+ sums:+ A:+ a: Text+ b: Int+ |]+ in case U.parseText unscrambler input of+ Right res ->+ assertEqual "" (Just [("A", [("a", Just "Text"), ("b", Just "Int")])]) res+ Left failure ->+ assertFailure (Text.unpack failure)+ ,+ testCase "Scalar errors are readable" $ let+ unscrambler =+ doc+ where+ doc =+ U.mappingValue $+ U.byKeyMapping (U.CaseSensitive True) $+ asum [+ Just <$> U.atByKey "sums" sum,+ pure Nothing+ ]+ sum =+ U.mappingValue $+ U.foldMapping (,) Fold.list U.textString sumVariant+ sumVariant =+ U.mappingValue $+ U.foldMapping (,) Fold.list U.textString params+ params =+ U.value [nullScalar, intScalar] Nothing Nothing+ where+ nullScalar =+ U.nullScalar Nothing+ intScalar =+ fmap Just $ U.boundedIntegerScalar @Int (U.Signed True) U.DecimalNumeralSystem+ input =+ [NeatInterpolation.text|+ sums:+ A:+ a: Int+ b: Char+ |]+ in case U.parseText unscrambler input of+ Right res ->+ assertFailure (show res)+ Left failure ->+ assertEqual "" "Error at path /sums/A/a. Expecting one of the following formats: null, signed decimal. Got input: \"Int\"" failure+ ]
+ yaml-unscrambler.cabal view
@@ -0,0 +1,80 @@+name: yaml-unscrambler+version: 0.1+synopsis: Flexible declarative YAML parsing toolkit+stability: Experimental+homepage: https://github.com/nikita-volkov/yaml-unscrambler+bug-reports: https://github.com/nikita-volkov/yaml-unscrambler/issues+author: Nikita Volkov <nikita.y.volkov@mail.ru>+maintainer: Nikita Volkov <nikita.y.volkov@mail.ru>+copyright: (c) 2020 Nikita Volkov+license: MIT+license-file: LICENSE+build-type: Simple+cabal-version: >=1.10++source-repository head+ type: git+ location: git://github.com/nikita-volkov/yaml-unscrambler.git++library+ hs-source-dirs: library+ default-extensions: BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, InstanceSigs, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, StrictData, TemplateHaskell, TupleSections, TypeApplications, TypeFamilies, TypeOperators, UnboxedTuples, ViewPatterns+ default-language: Haskell2010+ exposed-modules:+ YamlUnscrambler+ YamlUnscrambler.Expectations+ other-modules:+ YamlUnscrambler.AsciiAtto+ YamlUnscrambler.CompactErrRendering+ YamlUnscrambler.Err+ YamlUnscrambler.Model+ YamlUnscrambler.Prelude+ YamlUnscrambler.Util.ByteString+ YamlUnscrambler.Util.Maybe+ YamlUnscrambler.Util.HashMap+ YamlUnscrambler.Util.List+ YamlUnscrambler.Util.Yaml+ YamlUnscrambler.Util.Word8+ YamlUnscrambler.Util.Text+ YamlUnscrambler.Util.Vector+ build-depends:+ acc >=0.1.0.2 && <0.2,+ attoparsec >=0.13 && <0.14,+ attoparsec-data >=1.0.5 && <1.1,+ attoparsec-time >=1.0.1.1 && <1.1,+ base >=4.11 && <5,+ base64 >=0.4.2.2 && <0.5,+ bytestring >=0.10 && <0.11,+ conduit >=1.3.2 && <1.4,+ containers >=0.6.2 && <0.7,+ foldl >=1.4 && <2,+ hashable >=1 && <2,+ libyaml >=0.1.2 && <0.2,+ mtl >=2.2 && <3,+ scientific >=0.3.6.2 && <0.4,+ selective >=0.4 && <0.5,+ text >=1 && <2,+ text-builder >=0.6.6.1 && <0.7,+ time >=1.9 && <2,+ transformers >=0.5 && <0.6,+ unordered-containers >=0.2.10 && <0.3,+ uuid >=1.3 && <2,+ vector >=0.12 && <0.13,+ yaml >=0.11.5 && <0.12++test-suite test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ default-extensions: BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, InstanceSigs, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, StrictData, TemplateHaskell, TupleSections, TypeApplications, TypeFamilies, TypeOperators, UnboxedTuples, ViewPatterns+ default-language: Haskell2010+ main-is: Main.hs+ build-depends:+ foldl >=1.4.9 && <2,+ neat-interpolation >=0.5.1.2 && <0.6,+ QuickCheck >=2.8.1 && <3,+ quickcheck-instances >=0.3.11 && <0.4,+ rerebase >=1.9 && <2,+ tasty >=0.12 && <2,+ tasty-hunit >=0.9 && <0.11,+ tasty-quickcheck >=0.9 && <0.11,+ yaml-unscrambler