packages feed

hedn 0.1.9.1 → 0.2.0.0

raw patch · 30 files changed

+2109/−1738 lines, 30 filesdep +deriving-compatdep +hedgehogdep +megaparsecdep −HUnitdep −QuickCheckdep −attoparsecdep ~basedep ~containersdep ~deepseqPVP ok

version bump matches the API change (PVP)

Dependencies added: deriving-compat, hedgehog, megaparsec, parser-combinators, prettyprinter, uuid

Dependencies removed: HUnit, QuickCheck, attoparsec, base-compat, bytestring, hspec, hspec-contrib, mtl, stringsearch, time-locale-compat, utf8-string

Dependency ranges changed: base, containers, deepseq, hedn, scientific, template-haskell, text, time, vector

API changes (from Hackage documentation)

- Data.EDN: (.!=) :: Parser (Maybe a) -> a -> Parser a
- Data.EDN: (.:) :: (Show k, ToEDN k, FromEDN a) => EDNMap -> k -> Parser a
- Data.EDN: (.:?) :: (ToEDN k, FromEDN a) => EDNMap -> k -> Parser (Maybe a)
- Data.EDN: (.=) :: ToEDN a => ByteString -> a -> Pair
- Data.EDN: bool :: Bool -> TaggedValue
- Data.EDN: char :: Char -> TaggedValue
- Data.EDN: decode :: FromEDN a => ByteString -> Maybe a
- Data.EDN: encode :: ToEDN a => a -> ByteString
- Data.EDN: false :: TaggedValue
- Data.EDN: floating :: Double -> TaggedValue
- Data.EDN: fromEDNv :: FromEDN a => Value -> Result a
- Data.EDN: getTag :: TaggedValue -> (ByteString, ByteString)
- Data.EDN: integer :: Integer -> TaggedValue
- Data.EDN: keyword :: ByteString -> TaggedValue
- Data.EDN: makeList :: [TaggedValue] -> Value
- Data.EDN: makeMap :: [Pair] -> Value
- Data.EDN: makeSet :: [TaggedValue] -> Value
- Data.EDN: makeVec :: [TaggedValue] -> Value
- Data.EDN: nil :: TaggedValue
- Data.EDN: notag :: a -> Tagged a
- Data.EDN: parseMaybe :: ByteString -> Maybe TaggedValue
- Data.EDN: setTag :: ByteString -> ByteString -> Tagged a -> Tagged a
- Data.EDN: string :: Text -> TaggedValue
- Data.EDN: symbol :: ByteString -> TaggedValue
- Data.EDN: symbolNS :: ByteString -> ByteString -> TaggedValue
- Data.EDN: tag :: ByteString -> ByteString -> a -> Tagged a
- Data.EDN: true :: TaggedValue
- Data.EDN: type Pair = (Value, TaggedValue)
- Data.EDN.Encode: encode :: ToEDN a => a -> ByteString
- Data.EDN.Encode: fromTagged :: TaggedValue -> Builder
- Data.EDN.Encode: fromValue :: Value -> Builder
- Data.EDN.Parser: parseBS :: ByteString -> Result TaggedValue
- Data.EDN.Parser: parseBSL :: ByteString -> Result TaggedValue
- Data.EDN.Parser: parseEither :: ByteString -> Either String TaggedValue
- Data.EDN.Parser: parseMaybe :: ByteString -> Maybe TaggedValue
- Data.EDN.Parser: parseS :: String -> Result TaggedValue
- Data.EDN.Parser: parseT :: Text -> Result TaggedValue
- Data.EDN.Parser: parseTL :: Text -> Result TaggedValue
- Data.EDN.Parser: parseTagged :: Parser TaggedValue
- Data.EDN.Parser: parseValue :: Parser Value
- Data.EDN.Types: Boolean :: !Bool -> Value
- Data.EDN.Types: Character :: !Char -> Value
- Data.EDN.Types: Floating :: !Double -> Value
- Data.EDN.Types: Integer :: !Integer -> Value
- Data.EDN.Types: Keyword :: !ByteString -> Value
- Data.EDN.Types: List :: EDNList -> Value
- Data.EDN.Types: Map :: !EDNMap -> Value
- Data.EDN.Types: Nil :: Value
- Data.EDN.Types: NoTag :: !a -> Tagged a
- Data.EDN.Types: Set :: !EDNSet -> Value
- Data.EDN.Types: String :: !Text -> Value
- Data.EDN.Types: Symbol :: !ByteString -> !ByteString -> Value
- Data.EDN.Types: Tagged :: !a -> !ByteString -> !ByteString -> Tagged a
- Data.EDN.Types: Vec :: !EDNVec -> Value
- Data.EDN.Types: bool :: Bool -> TaggedValue
- Data.EDN.Types: char :: Char -> TaggedValue
- Data.EDN.Types: data Tagged a
- Data.EDN.Types: data Value
- Data.EDN.Types: false :: TaggedValue
- Data.EDN.Types: floating :: Double -> TaggedValue
- Data.EDN.Types: getTag :: TaggedValue -> (ByteString, ByteString)
- Data.EDN.Types: instance Control.DeepSeq.NFData Data.EDN.Types.Value
- Data.EDN.Types: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Data.EDN.Types.Tagged a)
- Data.EDN.Types: instance Data.String.IsString (Data.EDN.Types.Tagged Data.EDN.Types.Value)
- Data.EDN.Types: instance Data.String.IsString Data.EDN.Types.Value
- Data.EDN.Types: instance GHC.Base.Functor Data.EDN.Types.Tagged
- Data.EDN.Types: instance GHC.Classes.Eq Data.EDN.Types.Value
- Data.EDN.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.EDN.Types.Tagged a)
- Data.EDN.Types: instance GHC.Classes.Ord Data.EDN.Types.Value
- Data.EDN.Types: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.EDN.Types.Tagged a)
- Data.EDN.Types: instance GHC.Show.Show Data.EDN.Types.Value
- Data.EDN.Types: instance GHC.Show.Show a => GHC.Show.Show (Data.EDN.Types.Tagged a)
- Data.EDN.Types: integer :: Integer -> TaggedValue
- Data.EDN.Types: keyword :: ByteString -> TaggedValue
- Data.EDN.Types: makeList :: [TaggedValue] -> Value
- Data.EDN.Types: makeMap :: [Pair] -> Value
- Data.EDN.Types: makeSet :: [TaggedValue] -> Value
- Data.EDN.Types: makeVec :: [TaggedValue] -> Value
- Data.EDN.Types: nil :: TaggedValue
- Data.EDN.Types: notag :: a -> Tagged a
- Data.EDN.Types: setTag :: ByteString -> ByteString -> Tagged a -> Tagged a
- Data.EDN.Types: string :: Text -> TaggedValue
- Data.EDN.Types: stripTag :: Tagged a -> a
- Data.EDN.Types: symbol :: ByteString -> TaggedValue
- Data.EDN.Types: symbolNS :: ByteString -> ByteString -> TaggedValue
- Data.EDN.Types: tag :: ByteString -> ByteString -> a -> Tagged a
- Data.EDN.Types: true :: TaggedValue
- Data.EDN.Types: type EDNList = [TaggedValue] " An EDN list (head:tail container)."
- Data.EDN.Types: type EDNMap = Map Value TaggedValue " An EDN map (key/value container). Keys are untagged and values are tagged."
- Data.EDN.Types: type EDNSet = Set TaggedValue " An EDN set (unordered container of unique values)."
- Data.EDN.Types: type EDNVec = Vector TaggedValue " An EDN vector (position-indexed container) of EDN values."
- Data.EDN.Types: type Pair = (Value, TaggedValue)
- Data.EDN.Types: type TaggedValue = Tagged Value
- Data.EDN.Types.Class: (.!=) :: Parser (Maybe a) -> a -> Parser a
- Data.EDN.Types.Class: (.:) :: (Show k, ToEDN k, FromEDN a) => EDNMap -> k -> Parser a
- Data.EDN.Types.Class: (.:?) :: (ToEDN k, FromEDN a) => EDNMap -> k -> Parser (Maybe a)
- Data.EDN.Types.Class: (.=) :: ToEDN a => ByteString -> a -> Pair
- Data.EDN.Types.Class: Error :: String -> Result a
- Data.EDN.Types.Class: Success :: a -> Result a
- Data.EDN.Types.Class: class FromEDN a
- Data.EDN.Types.Class: class ToEDN a
- Data.EDN.Types.Class: data Parser a
- Data.EDN.Types.Class: data Result a
- Data.EDN.Types.Class: decode :: FromEDN a => ByteString -> Maybe a
- Data.EDN.Types.Class: eitherDecode :: FromEDN a => ByteString -> Either String a
- Data.EDN.Types.Class: fromEDN :: FromEDN a => TaggedValue -> Result a
- Data.EDN.Types.Class: fromEDNv :: FromEDN a => Value -> Result a
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.FromEDN a, Data.EDN.Types.Class.FromEDN b) => Data.EDN.Types.Class.FromEDN (Data.Either.Either a b)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.FromEDN a, Data.EDN.Types.Class.FromEDN b) => Data.EDN.Types.Class.FromEDN (a, b)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.FromEDN a, Data.EDN.Types.Class.FromEDN b, Data.EDN.Types.Class.FromEDN c) => Data.EDN.Types.Class.FromEDN (a, b, c)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.FromEDN a, Data.EDN.Types.Class.FromEDN b, Data.EDN.Types.Class.FromEDN c, Data.EDN.Types.Class.FromEDN d) => Data.EDN.Types.Class.FromEDN (a, b, c, d)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.FromEDN a, Data.EDN.Types.Class.FromEDN b, Data.EDN.Types.Class.FromEDN c, Data.EDN.Types.Class.FromEDN d, Data.EDN.Types.Class.FromEDN e) => Data.EDN.Types.Class.FromEDN (a, b, c, d, e)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.FromEDN a, Data.EDN.Types.Class.FromEDN b, Data.EDN.Types.Class.FromEDN c, Data.EDN.Types.Class.FromEDN d, Data.EDN.Types.Class.FromEDN e, Data.EDN.Types.Class.FromEDN f) => Data.EDN.Types.Class.FromEDN (a, b, c, d, e, f)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.FromEDN a, Data.EDN.Types.Class.FromEDN b, Data.EDN.Types.Class.FromEDN c, Data.EDN.Types.Class.FromEDN d, Data.EDN.Types.Class.FromEDN e, Data.EDN.Types.Class.FromEDN f, Data.EDN.Types.Class.FromEDN g) => Data.EDN.Types.Class.FromEDN (a, b, c, d, e, f, g)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.FromEDN a, Data.EDN.Types.Class.FromEDN b, Data.EDN.Types.Class.FromEDN c, Data.EDN.Types.Class.FromEDN d, Data.EDN.Types.Class.FromEDN e, Data.EDN.Types.Class.FromEDN f, Data.EDN.Types.Class.FromEDN g, Data.EDN.Types.Class.FromEDN h) => Data.EDN.Types.Class.FromEDN (a, b, c, d, e, f, g, h)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.ToEDN a, Data.EDN.Types.Class.ToEDN b) => Data.EDN.Types.Class.ToEDN (Data.Either.Either a b)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.ToEDN a, Data.EDN.Types.Class.ToEDN b) => Data.EDN.Types.Class.ToEDN (Data.Map.Internal.Map a b)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.ToEDN a, Data.EDN.Types.Class.ToEDN b) => Data.EDN.Types.Class.ToEDN (a, b)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.ToEDN a, Data.EDN.Types.Class.ToEDN b, Data.EDN.Types.Class.ToEDN c) => Data.EDN.Types.Class.ToEDN (a, b, c)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.ToEDN a, Data.EDN.Types.Class.ToEDN b, Data.EDN.Types.Class.ToEDN c, Data.EDN.Types.Class.ToEDN d) => Data.EDN.Types.Class.ToEDN (a, b, c, d)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.ToEDN a, Data.EDN.Types.Class.ToEDN b, Data.EDN.Types.Class.ToEDN c, Data.EDN.Types.Class.ToEDN d, Data.EDN.Types.Class.ToEDN e) => Data.EDN.Types.Class.ToEDN (a, b, c, d, e)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.ToEDN a, Data.EDN.Types.Class.ToEDN b, Data.EDN.Types.Class.ToEDN c, Data.EDN.Types.Class.ToEDN d, Data.EDN.Types.Class.ToEDN e, Data.EDN.Types.Class.ToEDN f) => Data.EDN.Types.Class.ToEDN (a, b, c, d, e, f)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.ToEDN a, Data.EDN.Types.Class.ToEDN b, Data.EDN.Types.Class.ToEDN c, Data.EDN.Types.Class.ToEDN d, Data.EDN.Types.Class.ToEDN e, Data.EDN.Types.Class.ToEDN f, Data.EDN.Types.Class.ToEDN g) => Data.EDN.Types.Class.ToEDN (a, b, c, d, e, f, g)
- Data.EDN.Types.Class: instance (Data.EDN.Types.Class.ToEDN a, Data.EDN.Types.Class.ToEDN b, Data.EDN.Types.Class.ToEDN c, Data.EDN.Types.Class.ToEDN d, Data.EDN.Types.Class.ToEDN e, Data.EDN.Types.Class.ToEDN f, Data.EDN.Types.Class.ToEDN g, Data.EDN.Types.Class.ToEDN h) => Data.EDN.Types.Class.ToEDN (a, b, c, d, e, f, g, h)
- Data.EDN.Types.Class: instance (GHC.Classes.Ord a, Data.EDN.Types.Class.FromEDN a) => Data.EDN.Types.Class.FromEDN (Data.Set.Internal.Set a)
- Data.EDN.Types.Class: instance (GHC.Classes.Ord a, Data.EDN.Types.Class.FromEDN a, Data.EDN.Types.Class.FromEDN b) => Data.EDN.Types.Class.FromEDN (Data.Map.Internal.Map a b)
- Data.EDN.Types.Class: instance (GHC.Classes.Ord a, Data.EDN.Types.Class.ToEDN a) => Data.EDN.Types.Class.ToEDN (Data.Set.Internal.Set a)
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN ()
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN Data.ByteString.Internal.ByteString
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN Data.ByteString.Lazy.Internal.ByteString
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN Data.EDN.Types.TaggedValue
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN Data.EDN.Types.Value
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN Data.Text.Internal.Lazy.Text
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN Data.Text.Internal.Text
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN Data.Time.Clock.Internal.UTCTime.UTCTime
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN GHC.Integer.Type.Integer
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN GHC.Types.Bool
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN GHC.Types.Char
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN GHC.Types.Double
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN GHC.Types.Int
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN [GHC.Types.Char]
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN a => Data.EDN.Types.Class.FromEDN (Data.EDN.Types.Tagged a)
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN a => Data.EDN.Types.Class.FromEDN (Data.Vector.Vector a)
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN a => Data.EDN.Types.Class.FromEDN (GHC.Maybe.Maybe a)
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.FromEDN a => Data.EDN.Types.Class.FromEDN [a]
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN ()
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN Data.ByteString.Internal.ByteString
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN Data.ByteString.Lazy.Internal.ByteString
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN Data.EDN.Types.TaggedValue
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN Data.EDN.Types.Value
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN Data.Text.Internal.Lazy.Text
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN Data.Text.Internal.Text
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN Data.Time.Clock.Internal.UTCTime.UTCTime
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN GHC.Integer.Type.Integer
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN GHC.Types.Bool
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN GHC.Types.Char
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN GHC.Types.Double
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN GHC.Types.Int
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN [GHC.Types.Char]
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN a => Data.EDN.Types.Class.ToEDN (Data.EDN.Types.Tagged a)
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN a => Data.EDN.Types.Class.ToEDN (Data.Vector.Vector a)
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN a => Data.EDN.Types.Class.ToEDN (GHC.Maybe.Maybe a)
- Data.EDN.Types.Class: instance Data.EDN.Types.Class.ToEDN a => Data.EDN.Types.Class.ToEDN [a]
- Data.EDN.Types.Class: parse :: (a -> Parser b) -> a -> Result b
- Data.EDN.Types.Class: parseEDN :: FromEDN a => TaggedValue -> Parser a
- Data.EDN.Types.Class: parseEDNv :: FromEDN a => Value -> Parser a
- Data.EDN.Types.Class: parseEither :: (a -> Parser b) -> a -> Either String b
- Data.EDN.Types.Class: parseMaybe :: (a -> Parser b) -> a -> Maybe b
- Data.EDN.Types.Class: toEDN :: ToEDN a => a -> TaggedValue
- Data.EDN.Types.Class: toEDNv :: ToEDN a => a -> Value
- Data.EDN.Types.Class: typeMismatch :: String -> Value -> Parser a
+ Data.EDN: decodeText :: (FromEDN a, Monad m) => String -> Text -> m a
+ Data.EDN: encodeText :: ToEDN a => a -> Text
+ Data.EDN: mapGetKeyword :: FromEDN a => Text -> EDNMap -> Parser a
+ Data.EDN: mapGetString :: FromEDN a => Text -> EDNMap -> Parser a
+ Data.EDN: mapGetSymbol :: FromEDN a => Text -> EDNMap -> Parser a
+ Data.EDN: mapGetSymbolNS :: FromEDN a => Text -> Text -> EDNMap -> Parser a
+ Data.EDN: mkList :: Foldable f => f TaggedValue -> Value
+ Data.EDN: mkMap :: Foldable f => f (TaggedValue, TaggedValue) -> Value
+ Data.EDN: mkSet :: Foldable f => f TaggedValue -> Value
+ Data.EDN: mkVec :: Foldable f => f TaggedValue -> Value
+ Data.EDN: parseEDN :: FromEDN a => TaggedValue -> Parser a
+ Data.EDN: parseEDNv :: FromEDN a => Value -> Parser a
+ Data.EDN: parseText :: Monad m => String -> Text -> m TaggedValue
+ Data.EDN: renderText :: TaggedValue -> Text
+ Data.EDN: toEDNtagged :: ToEDN a => Text -> Text -> a -> TaggedValue
+ Data.EDN: toEDNv :: ToEDN a => a -> Value
+ Data.EDN: type EDNList = [TaggedValue]
+ Data.EDN: type EDNMap = Map TaggedValue TaggedValue
+ Data.EDN: type EDNSet = Set TaggedValue
+ Data.EDN: type EDNVec = Vector TaggedValue
+ Data.EDN: unexpected :: Value -> Label -> Parser a
+ Data.EDN: vecGet :: FromEDN a => Int -> EDNVec -> Parser a
+ Data.EDN: withBoolean :: (Bool -> Parser a) -> Value -> Parser a
+ Data.EDN: withCharacter :: (Char -> Parser a) -> Value -> Parser a
+ Data.EDN: withFloating :: (Double -> Parser a) -> Value -> Parser a
+ Data.EDN: withFractional :: Fractional f => (f -> Parser a) -> Value -> Parser a
+ Data.EDN: withInteger :: (Int -> Parser a) -> Value -> Parser a
+ Data.EDN: withIntegral :: Integral i => (i -> Parser a) -> Value -> Parser a
+ Data.EDN: withKeyword :: (Text -> Parser a) -> Value -> Parser a
+ Data.EDN: withList :: (EDNList -> Parser a) -> Value -> Parser a
+ Data.EDN: withMap :: (EDNMap -> Parser a) -> Value -> Parser a
+ Data.EDN: withNil :: Parser a -> Value -> Parser a
+ Data.EDN: withNoTag :: (Value -> Parser a) -> TaggedValue -> Parser a
+ Data.EDN: withSet :: (EDNSet -> Parser a) -> Value -> Parser a
+ Data.EDN: withString :: (Text -> Parser a) -> Value -> Parser a
+ Data.EDN: withSymbol :: (Text -> Text -> Parser a) -> Value -> Parser a
+ Data.EDN: withTagged :: Text -> Text -> (Value -> Parser a) -> TaggedValue -> Parser a
+ Data.EDN: withTextual :: (Text -> Parser a) -> Value -> Parser a
+ Data.EDN: withVec :: (EDNVec -> Parser a) -> Value -> Parser a
+ Data.EDN.AST.Lexer: dropWS :: Parser ()
+ Data.EDN.AST.Lexer: floating :: Parser Double
+ Data.EDN.AST.Lexer: hexadecimal :: Parser Int
+ Data.EDN.AST.Lexer: integer :: Parser Int
+ Data.EDN.AST.Lexer: lexeme :: Parser a -> Parser a
+ Data.EDN.AST.Lexer: symbol :: Text -> Parser Text
+ Data.EDN.AST.Parser: digitChars :: [Char]
+ Data.EDN.AST.Parser: keywordChars :: [Char]
+ Data.EDN.AST.Parser: keywordInitialChars :: [Char]
+ Data.EDN.AST.Parser: lowerChars :: [Char]
+ Data.EDN.AST.Parser: miscChars :: [Char]
+ Data.EDN.AST.Parser: parseBool :: Parser Value
+ Data.EDN.AST.Parser: parseCollections :: Parser Value
+ Data.EDN.AST.Parser: parseDiscard :: Parser ()
+ Data.EDN.AST.Parser: parseDoc :: Parser TaggedValue
+ Data.EDN.AST.Parser: parseKeyword :: Parser Value
+ Data.EDN.AST.Parser: parseNil :: Parser Value
+ Data.EDN.AST.Parser: parseNumber :: Parser Value
+ Data.EDN.AST.Parser: parseSymbol :: Parser Value
+ Data.EDN.AST.Parser: parseTagged :: Parser TaggedValue
+ Data.EDN.AST.Parser: parseText :: Monad m => String -> Text -> m TaggedValue
+ Data.EDN.AST.Parser: parseValue :: Parser Value
+ Data.EDN.AST.Parser: symbolChars :: [Char]
+ Data.EDN.AST.Parser: symbolInitialChars :: [Char]
+ Data.EDN.AST.Parser: tagChars :: [Char]
+ Data.EDN.AST.Parser: upperChars :: [Char]
+ Data.EDN.AST.Printer: prettyTaggedValue :: TaggedValue -> Doc a
+ Data.EDN.AST.Printer: prettyValue :: Value -> Doc a
+ Data.EDN.AST.Printer: renderText :: TaggedValue -> Text
+ Data.EDN.AST.Types: type Parser = Parsec Void Text
+ Data.EDN.AST.Types.Tagged: NoTag :: !a -> Tagged tag a
+ Data.EDN.AST.Types.Tagged: Tagged :: !tag -> !tag -> !a -> Tagged tag a
+ Data.EDN.AST.Types.Tagged: data Tagged tag a
+ Data.EDN.AST.Types.Tagged: instance (Data.Data.Data tag, Data.Data.Data a) => Data.Data.Data (Data.EDN.AST.Types.Tagged.Tagged tag a)
+ Data.EDN.AST.Types.Tagged: instance (GHC.Classes.Eq tag, GHC.Classes.Eq a) => GHC.Classes.Eq (Data.EDN.AST.Types.Tagged.Tagged tag a)
+ Data.EDN.AST.Types.Tagged: instance (GHC.Classes.Ord tag, GHC.Classes.Ord a) => GHC.Classes.Ord (Data.EDN.AST.Types.Tagged.Tagged tag a)
+ Data.EDN.AST.Types.Tagged: instance (GHC.Show.Show tag, GHC.Show.Show a) => GHC.Show.Show (Data.EDN.AST.Types.Tagged.Tagged tag a)
+ Data.EDN.AST.Types.Tagged: instance GHC.Base.Functor (Data.EDN.AST.Types.Tagged.Tagged tag)
+ Data.EDN.AST.Types.Tagged: stripTag :: Tagged tag a -> a
+ Data.EDN.AST.Types.Value: Boolean :: !Bool -> Value
+ Data.EDN.AST.Types.Value: Character :: !Char -> Value
+ Data.EDN.AST.Types.Value: Floating :: !Double -> Value
+ Data.EDN.AST.Types.Value: Integer :: !Int -> Value
+ Data.EDN.AST.Types.Value: Keyword :: !Text -> Value
+ Data.EDN.AST.Types.Value: List :: !EDNList -> Value
+ Data.EDN.AST.Types.Value: Map :: !EDNMap -> Value
+ Data.EDN.AST.Types.Value: Nil :: Value
+ Data.EDN.AST.Types.Value: Set :: !EDNSet -> Value
+ Data.EDN.AST.Types.Value: String :: !Text -> Value
+ Data.EDN.AST.Types.Value: Symbol :: !Text -> !Text -> Value
+ Data.EDN.AST.Types.Value: Vec :: !EDNVec -> Value
+ Data.EDN.AST.Types.Value: data Value
+ Data.EDN.AST.Types.Value: instance Data.Data.Data Data.EDN.AST.Types.Value.Value
+ Data.EDN.AST.Types.Value: instance GHC.Classes.Eq Data.EDN.AST.Types.Value.Value
+ Data.EDN.AST.Types.Value: instance GHC.Classes.Ord Data.EDN.AST.Types.Value.Value
+ Data.EDN.AST.Types.Value: instance GHC.Show.Show Data.EDN.AST.Types.Value.Value
+ Data.EDN.AST.Types.Value: mkList :: Foldable f => f TaggedValue -> Value
+ Data.EDN.AST.Types.Value: mkMap :: Foldable f => f (TaggedValue, TaggedValue) -> Value
+ Data.EDN.AST.Types.Value: mkSet :: Foldable f => f TaggedValue -> Value
+ Data.EDN.AST.Types.Value: mkVec :: Foldable f => f TaggedValue -> Value
+ Data.EDN.AST.Types.Value: type EDNList = [TaggedValue]
+ Data.EDN.AST.Types.Value: type EDNMap = Map TaggedValue TaggedValue
+ Data.EDN.AST.Types.Value: type EDNSet = Set TaggedValue
+ Data.EDN.AST.Types.Value: type EDNVec = Vector TaggedValue
+ Data.EDN.AST.Types.Value: type TaggedValue = Tagged Text Value
+ Data.EDN.Class: class FromEDN a
+ Data.EDN.Class: class ToEDN a
+ Data.EDN.Class: fromEDN :: (FromEDN a, Monad m) => TaggedValue -> m a
+ Data.EDN.Class: instance (Data.EDN.Class.FromEDN a, Data.EDN.Class.FromEDN b) => Data.EDN.Class.FromEDN (a, b)
+ Data.EDN.Class: instance (Data.EDN.Class.FromEDN a, Data.EDN.Class.FromEDN b, Data.EDN.Class.FromEDN c) => Data.EDN.Class.FromEDN (a, b, c)
+ Data.EDN.Class: instance (Data.EDN.Class.FromEDN a, Data.EDN.Class.FromEDN b, Data.EDN.Class.FromEDN c, Data.EDN.Class.FromEDN d) => Data.EDN.Class.FromEDN (a, b, c, d)
+ Data.EDN.Class: instance (Data.EDN.Class.FromEDN a, GHC.Classes.Ord a) => Data.EDN.Class.FromEDN (Data.Set.Internal.Set a)
+ Data.EDN.Class: instance (Data.EDN.Class.FromEDN k, Data.EDN.Class.FromEDN v, GHC.Classes.Ord k) => Data.EDN.Class.FromEDN (Data.Map.Internal.Map k v)
+ Data.EDN.Class: instance (Data.EDN.Class.ToEDN a, Data.EDN.Class.ToEDN b) => Data.EDN.Class.ToEDN (a, b)
+ Data.EDN.Class: instance (Data.EDN.Class.ToEDN a, Data.EDN.Class.ToEDN b, Data.EDN.Class.ToEDN c) => Data.EDN.Class.ToEDN (a, b, c)
+ Data.EDN.Class: instance (Data.EDN.Class.ToEDN a, Data.EDN.Class.ToEDN b, Data.EDN.Class.ToEDN c, Data.EDN.Class.ToEDN d) => Data.EDN.Class.ToEDN (a, b, c, d)
+ Data.EDN.Class: instance (Data.EDN.Class.ToEDN k, Data.EDN.Class.ToEDN v) => Data.EDN.Class.ToEDN (Data.Map.Internal.Map k v)
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN ()
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN Data.EDN.AST.Types.Value.TaggedValue
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN Data.EDN.AST.Types.Value.Value
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN Data.Text.Internal.Lazy.Text
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN Data.Text.Internal.Text
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN Data.Time.Clock.Internal.UTCTime.UTCTime
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN Data.UUID.Types.Internal.UUID
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN Data.Void.Void
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN GHC.Types.Bool
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN GHC.Types.Char
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN GHC.Types.Double
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN GHC.Types.Int
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN a => Data.EDN.Class.FromEDN (Data.Vector.Vector a)
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN a => Data.EDN.Class.FromEDN (GHC.Maybe.Maybe a)
+ Data.EDN.Class: instance Data.EDN.Class.FromEDN a => Data.EDN.Class.FromEDN [a]
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN ()
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN Data.EDN.AST.Types.Value.TaggedValue
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN Data.EDN.AST.Types.Value.Value
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN Data.Text.Internal.Lazy.Text
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN Data.Text.Internal.Text
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN Data.Time.Clock.Internal.UTCTime.UTCTime
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN Data.UUID.Types.Internal.UUID
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN Data.Void.Void
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN GHC.Types.Bool
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN GHC.Types.Char
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN GHC.Types.Double
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN GHC.Types.Int
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN a => Data.EDN.Class.ToEDN (Data.Set.Internal.Set a)
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN a => Data.EDN.Class.ToEDN (Data.Vector.Vector a)
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN a => Data.EDN.Class.ToEDN (GHC.Maybe.Maybe a)
+ Data.EDN.Class: instance Data.EDN.Class.ToEDN a => Data.EDN.Class.ToEDN [a]
+ Data.EDN.Class: mapGetKeyword :: FromEDN a => Text -> EDNMap -> Parser a
+ Data.EDN.Class: mapGetP :: Monad m => TaggedValue -> (TaggedValue -> m a) -> EDNMap -> m a
+ Data.EDN.Class: mapGetString :: FromEDN a => Text -> EDNMap -> Parser a
+ Data.EDN.Class: mapGetSymbol :: FromEDN a => Text -> EDNMap -> Parser a
+ Data.EDN.Class: mapGetSymbolNS :: FromEDN a => Text -> Text -> EDNMap -> Parser a
+ Data.EDN.Class: parseEDN :: FromEDN a => TaggedValue -> Parser a
+ Data.EDN.Class: parseEDNv :: FromEDN a => Value -> Parser a
+ Data.EDN.Class: toEDN :: ToEDN a => a -> TaggedValue
+ Data.EDN.Class: toEDNtagged :: ToEDN a => Text -> Text -> a -> TaggedValue
+ Data.EDN.Class: toEDNv :: ToEDN a => a -> Value
+ Data.EDN.Class: type Expected = [Label]
+ Data.EDN.Class: type Label = String
+ Data.EDN.Class: unexpected :: Value -> Label -> Parser a
+ Data.EDN.Class: vecGet :: FromEDN a => Int -> EDNVec -> Parser a
+ Data.EDN.Class: withBoolean :: (Bool -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withCharacter :: (Char -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withFloating :: (Double -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withFractional :: Fractional f => (f -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withInteger :: (Int -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withIntegral :: Integral i => (i -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withKeyword :: (Text -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withList :: (EDNList -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withMap :: (EDNMap -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withNil :: Parser a -> Value -> Parser a
+ Data.EDN.Class: withNoTag :: (Value -> Parser a) -> TaggedValue -> Parser a
+ Data.EDN.Class: withSet :: (EDNSet -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withString :: (Text -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withSymbol :: (Text -> Text -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withTagged :: Text -> Text -> (Value -> Parser a) -> TaggedValue -> Parser a
+ Data.EDN.Class: withTextual :: (Text -> Parser a) -> Value -> Parser a
+ Data.EDN.Class: withVec :: (EDNVec -> Parser a) -> Value -> Parser a
+ Data.EDN.Class.Parser: Parser :: (forall f r. Failure f r -> Success a f r -> f r) -> Parser a
+ Data.EDN.Class.Parser: [runParser] :: Parser a -> forall f r. Failure f r -> Success a f r -> f r
+ Data.EDN.Class.Parser: instance Control.Monad.Fail.MonadFail Data.EDN.Class.Parser.Parser
+ Data.EDN.Class.Parser: instance GHC.Base.Alternative Data.EDN.Class.Parser.Parser
+ Data.EDN.Class.Parser: instance GHC.Base.Applicative Data.EDN.Class.Parser.Parser
+ Data.EDN.Class.Parser: instance GHC.Base.Functor Data.EDN.Class.Parser.Parser
+ Data.EDN.Class.Parser: instance GHC.Base.Monad Data.EDN.Class.Parser.Parser
+ Data.EDN.Class.Parser: instance GHC.Base.MonadPlus Data.EDN.Class.Parser.Parser
+ Data.EDN.Class.Parser: instance GHC.Base.Monoid (Data.EDN.Class.Parser.Parser a)
+ Data.EDN.Class.Parser: instance GHC.Base.Semigroup (Data.EDN.Class.Parser.Parser a)
+ Data.EDN.Class.Parser: newtype Parser a
+ Data.EDN.Class.Parser: parseEither :: (a -> Parser b) -> a -> Either String b
+ Data.EDN.Class.Parser: parseM :: Monad m => (a -> Parser b) -> a -> m b
+ Data.EDN.Class.Parser: type Expected = [Label]
+ Data.EDN.Class.Parser: type Failure f r = Expected -> String -> f r
+ Data.EDN.Class.Parser: type Label = String
+ Data.EDN.Class.Parser: type Success a f r = a -> f r
+ Data.EDN.QQ: edn :: QuasiQuoter
+ Data.EDN.QQ: ednList :: QuasiQuoter
+ Data.EDN.QQ: ednMap :: QuasiQuoter
+ Data.EDN.QQ: ednSet :: QuasiQuoter
+ Data.EDN.QQ: ednVec :: QuasiQuoter
+ Data.EDN.QQ: fromEDN :: forall a. (Lift a, FromEDN a) => QuasiQuoter
+ Data.EDN.QQ: instance Data.Data.Data a => Language.Haskell.TH.Syntax.Lift (Data.EDN.AST.Types.Tagged.Tagged Data.Text.Internal.Text a)
+ Data.EDN.QQ: instance Language.Haskell.TH.Syntax.Lift Data.EDN.AST.Types.Value.Value
- Data.EDN: Integer :: !Integer -> Value
+ Data.EDN: Integer :: !Int -> Value
- Data.EDN: Keyword :: !ByteString -> Value
+ Data.EDN: Keyword :: !Text -> Value
- Data.EDN: List :: EDNList -> Value
+ Data.EDN: List :: !EDNList -> Value
- Data.EDN: NoTag :: !a -> Tagged a
+ Data.EDN: NoTag :: !a -> Tagged tag a
- Data.EDN: Symbol :: !ByteString -> !ByteString -> Value
+ Data.EDN: Symbol :: !Text -> !Text -> Value
- Data.EDN: Tagged :: !a -> !ByteString -> !ByteString -> Tagged a
+ Data.EDN: Tagged :: !tag -> !tag -> !a -> Tagged tag a
- Data.EDN: data Tagged a
+ Data.EDN: data Tagged tag a
- Data.EDN: fromEDN :: FromEDN a => TaggedValue -> Result a
+ Data.EDN: fromEDN :: (FromEDN a, Monad m) => TaggedValue -> m a
- Data.EDN: stripTag :: Tagged a -> a
+ Data.EDN: stripTag :: Tagged tag a -> a
- Data.EDN: type TaggedValue = Tagged Value
+ Data.EDN: type TaggedValue = Tagged Text Value

Files

CHANGELOG.md view
@@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [0.2.0.0] - 2019-01-18++### Changed++- Complete rewrite in Megaparsec.+- Text types changed to, well, `Text`+- Document  with `prettyprinter`+- Test suite changed to Hedgehog+- Code repository moved to GitLab++### Removed++- GHC < 8.0 (Stackage LTS < 7) aren't supported due to Semigroup-Monoid changes.++## [0.1.9.1] - 2018-12-03++### Changed++- GHC 8.4 compatibility fixes+ ## [0.1.9.0] - 2018-04-05  ### Fixed@@ -31,7 +51,9 @@  - Use utf8-string parsing for unicode literals. -[0.1.9.0]: https://bitbucket.org/dpwiz/hedn/commits/tag/0.1.9.0-[0.1.8.2]: https://bitbucket.org/dpwiz/hedn/commits/tag/0.1.8.2-[0.1.8.1]: https://bitbucket.org/dpwiz/hedn/commits/tag/0.1.8.1-[0.1.8.0]: https://bitbucket.org/dpwiz/hedn/commits/tag/0.1.8.0+[0.2.0.0]: https://gitlab.com/dpwiz/hedn/tree/0.2.0.0+[0.1.9.1]: https://gitlab.com/dpwiz/hedn/tree/0.1.9.1+[0.1.9.0]: https://gitlab.com/dpwiz/hedn/tree/0.1.9.0+[0.1.8.2]: https://gitlab.com/dpwiz/hedn/tree/0.1.8.2+[0.1.8.1]: https://gitlab.com/dpwiz/hedn/tree/0.1.8.1+[0.1.8.0]: https://gitlab.com/dpwiz/hedn/tree/0.1.8.0
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2012, Alexander Bondarenko+Copyright (c) 2019, Alexander Bondarenko  All rights reserved. 
+ README.md view
@@ -0,0 +1,184 @@+# Haskell EDN++An EDN parsing and encoding library inspired by `aeson`.++Based on [spec] and [hints] published on GitHub.++Hackage: https://hackage.haskell.org/package/hedn++Stackage: https://www.stackage.org/package/hedn++[spec]: https://github.com/edn-format/edn+[hints]: https://github.com/wagjo/serialization-formats++# Examples++## AST++```clojure+(#haskell/edn example/kitchensink ; tagged symbol+ nil ; ugh..+ \space ; character+ "dynamic \"typing\"" ; string+ {:numbers ; keyword+  [42 ; integer+   42.0 ; floating+  ] ; Vector+  :bools+  #{true, false} ; Set (with optional commas)+ } ; Map+) ; List+```++```haskell+import qualified Data.EDN as EDN+import qualified Data.Text.IO as Text++main = do+  edn <- Text.readFile "example.edn"+  value <- EDN.parseText "example.edn" edn+  print value+```++```haskell+NoTag+  (List+     [ Tagged "haskell" "edn" (Symbol "example" "kitchensink")+     , NoTag Nil+     , NoTag (Character ' ')+     , NoTag (String "dynamic \"typing\"")+     , NoTag+         (Map+            (fromList+               [ ( NoTag (Keyword "bools")+                 , NoTag+                     (Set (fromList [ NoTag (Boolean False) , NoTag (Boolean True) ]))+                 )+               , ( NoTag (Keyword "numbers")+                 , NoTag (Vec [ NoTag (Integer 42) , NoTag (Floating 42.0) ])+                 )+               ]))+     ])+```++## Conversion++More examples in `tests/Data/EDN/Class/Test.hs`.++```haskell+data Person = Person+  { personFirst :: Text+  , personLast  :: Text+  } deriving (Eq, Show)++instance ToEDN Person where+  toEDN Person{..} =+    toEDNtagged "myapp" "Person" $ Map.fromList+      [ (EDN.Keyword "first", toEDN personFirst)+      , (EDN.Keyword "last", toEDN personLast)+      ]++instance FromEDN Person where+  parseEDN = \case+    EDN.Tagged "myapp" "Person" v -> Person+      <$> EDN.mapGetKeyword "first" v+      <*> EDN.mapGetKeyword "last" v+    _ ->+      fail "myapp/Person expected"+```++```haskell+import qualified Data.EDN as EDN+import qualified Data.Text.IO as Text++main = Text.putStrLn $+  encodeText (Person "Fred" "Mertz")+```++```clojure+#myapp/Person {:first "Fred" :last "Mertz"}+```++## Quasiquoter++Embed EDN AST literals with `edn`:++```haskell+fredEDN = [edn|+  #myapp/Person {:first "Fred" :last "Mertz"}+|]+```++Additionally there are QQs for untagged collections.+They simply wrap the block in appropriate symbols.++* Lists, `()`+    ```haskell+    EDN.List (items :: [TaggedValue]) =+      [ednList|+        this is a list of symbols, commas are whitespace+      |]+    ```+* Vectors, `[]`+    ```haskell+    EDN.Vec (items :: Vector TaggedValue) =+      [ednVec|+        42 is #the/answer true+      |]+    ```+* Sets, `#{}`+    ```haskell+    EDN.Set (items :: Set TaggedValue) =+      [ednSet|+        badger badger badger badger+        badger badger badger badger+        mushroom mushroom+      |]+    ```+* Maps, `{}`+    ```haskell+    EDN.Map (dict :: Map TaggedValue TaggedValue) =+      [ednMap|+        :keywords "as you like it"+        or/symbols "I won't judge"+        #uuid "d748ab62-9cb1-41fb-b8dc-e23f3ffc5f9b"+        (tagged values, collections, anything goes)+      |]+    ```++### EDN as DSL syntax++You can add quasiquoters for your values by specializing `fromEDN` QuasiQuoter.++> Usual TH shenanigans apply.+> Define things in a separate module.+> Either `Data.Data.Data` or `Language.Haskell.TH.Syntax.Lift` instance required to convert values to Haskell AST.++```haskell+module My.Data.QQ (myData) where++import Data.EDN.QQ (fromEDN)+import My.Data.Types (MyData)++myData :: QuasiQuoter+myData = fromEDN @MyData+```++```haskell+module Main where++import My.Data.QQ (myData)++main :: IO ()+main = cobc+  [myData|+    {:identification-division+        [#program/id hello]+     :procedure-division+        ((display "Hello, world!"+          end-display)+         stop-run+        )+    }+  |]+```
+ example.edn view
@@ -0,0 +1,12 @@+(#haskell/edn example/kitchensink ; tagged symbol+ nil ; ugh..+ \space ; character+ "dynamic \"typing\"" ; string+ {:numbers ; keyword+  [42 ; integer+   42.0 ; floating+  ] ; Vector+  :bools+  #{true false} ; Set+ } ; Map+) ; List
hedn.cabal view
@@ -1,78 +1,89 @@-name:                hedn-version:             0.1.9.1-synopsis:            EDN parsing and encoding-homepage:            https://bitbucket.org/dpwiz/hedn-license:             BSD3-license-file:        LICENSE-author:              Alexander Bondarenko-maintainer:          aenor.realm@gmail.com-copyright:           (c) 2012 Alexander Bondarenko-category:            Data-build-type:          Simple-cabal-version:       >=1.8-tested-with:-  GHC==7.4.2,-  GHC==7.6.3,-  GHC==7.8.4,-  GHC==7.10.3,-  GHC==8.0.1,-  GHC==8.2.2,-  GHC==8.4.4+cabal-version: 1.12 -description:-    A EDN parsing and encoding library inspired by Data.Aeson.-    .-    Based on specs published at <https://github.com/edn-format/edn>.+-- This file has been generated from package.yaml by hpack version 0.31.1.+--+-- see: https://github.com/sol/hpack+--+-- hash: ca74a2494ad480664d2ec10fd783cd811fee1231966abb8f2415d6c92159be1a +name:           hedn+version:        0.2.0.0+synopsis:       EDN parsing and encoding+description:    A EDN parsing and encoding library.+                .+                Based on "specs" published at <https://github.com/edn-format/edn>.+category:       Data+author:         Alexander Bondarenko+maintainer:     aenor.realm@gmail.com+copyright:      (c) 2019 Alexander Bondarenko+license:        BSD3+license-file:   LICENSE+tested-with:    GHC==8.0.1, GHC==8.2.2, GHC==8.4.4, GHC==8.6.3+build-type:     Simple extra-source-files:-  CHANGELOG.md-  tests/Main.hs-  tests/Data/EDN/Test/*.hs-  tests/Data/EDN/Test/QuickCheck/TH.hs+    CHANGELOG.md+    LICENSE+    README.md+    example.edn +source-repository head+  type: git+  location: https://gitlab.com/dpwiz/hedn+ library   exposed-modules:-      Data.EDN,-      Data.EDN.Types,-      Data.EDN.Types.Class,-      Data.EDN.Parser,-      Data.EDN.Encode-+      Data.EDN+      Data.EDN.AST.Lexer+      Data.EDN.AST.Parser+      Data.EDN.AST.Printer+      Data.EDN.AST.Types+      Data.EDN.AST.Types.Tagged+      Data.EDN.AST.Types.Value+      Data.EDN.Class+      Data.EDN.Class.Parser+      Data.EDN.QQ   other-modules:-      Data.Parser--  hs-source-dirs: src/+      Paths_hedn+  hs-source-dirs:+      lib+  ghc-options: -Wall   build-depends:-      base >=4.5 && <5,-      base-compat >=0.6.0,-      attoparsec,-      text,-      bytestring >=0.10,-      scientific,-      utf8-string,-      containers,-      vector,-      stringsearch,-      mtl,-      deepseq,-      time,-      time-locale-compat--  ghc-options: -Wall -fno-warn-unused-do-bind+      base >=4.9 && <4.13+    , containers >=0.5.7 && <0.7+    , deepseq >=1.4 && <2+    , deriving-compat >=0.3.6 && <0.6+    , megaparsec >=7.0 && <8+    , parser-combinators >=1.0 && <2+    , prettyprinter >=1.2 && <2+    , scientific >=0.3 && <0.4+    , template-haskell >=2.11 && <3+    , text >=1.2 && <2+    , time >=1.6 && <2+    , uuid >=1.3 && <2+    , vector >=0.11 && <1+  default-language: Haskell2010 -Test-Suite tests-  type:              exitcode-stdio-1.0-  main-is:           Main.hs-  hs-source-dirs:    tests/+test-suite edn-test+  type: exitcode-stdio-1.0+  main-is: Main.hs   other-modules:-    Data.EDN.Test.QuickCheck-    Data.EDN.Test.QuickCheck.TH-    Data.EDN.Test.Unit--  build-depends:     base, hedn, bytestring, text, containers, vector, template-haskell, time,-                     QuickCheck >= 2.5.1, hspec >= 1.5.1, HUnit >= 1.2.5, hspec-contrib-  ghc-options:       -Wall--source-repository head-  type:     git-  location: https://bitbucket.org/dpwiz/hedn+      Data.EDN.AST.Gen+      Data.EDN.AST.Test+      Data.EDN.Class.Gen+      Data.EDN.Class.Test+      Data.EDN.QQ.Test+      Paths_hedn+  hs-source-dirs:+      tests+  ghc-options: -Wall+  build-depends:+      base >=4.9 && <4.13+    , containers >=0.5.7 && <0.7+    , hedgehog >=0.6 && <0.7+    , hedn >=0.2 && <1+    , megaparsec >=7.0 && <8+    , text >=1.2 && <2+    , time >=1.6 && <2+    , uuid >=1.3 && <2+    , vector >=0.11 && <1+  default-language: Haskell2010
+ lib/Data/EDN.hs view
@@ -0,0 +1,70 @@+module Data.EDN+  ( decodeText+  , encodeText++  -- * EDN AST+  , parseText+  , renderText+  , TaggedValue+  , Tagged(..)+  , stripTag+  , Value(..)+  , EDNList, EDNVec, EDNMap, EDNSet+  , mkList, mkVec, mkMap, mkSet++  -- * Encoding to EDN AST+  , ToEDN(..)+  , toEDNtagged++  -- * Decoding EDN AST+  , FromEDN(..)+  , fromEDN+  -- ** AST-parsing wrappers+  , withTagged+  , withNoTag+  , withNil+  , withBoolean+  , withString+  , withCharacter+  , withSymbol+  , withKeyword+  , withTextual+  , withInteger+  , withIntegral+  , withFloating+  , withFractional+  , withList+  , withVec+  , withMap+  , withSet+  , unexpected+  -- ** Additional helpers+  , vecGet+  , mapGetKeyword+  , mapGetString+  , mapGetSymbol+  , mapGetSymbolNS+) where++import Data.Text (Text)++import Data.EDN.AST.Parser (parseText)+import Data.EDN.AST.Printer (renderText)+import Data.EDN.AST.Types.Tagged+import Data.EDN.AST.Types.Value+import Data.EDN.Class++-- | Convert value to AST using 'ToEDN' instance and render it.+encodeText :: ToEDN a => a -> Text+encodeText =+  renderText . toEDN++-- | Decode EDN document into AST and parse value using its 'FromEDN' instance.+decodeText+  :: (FromEDN a, Monad m)+  => String -- ^ Source name, for megaparsec error reports+            -- e.g. @/path/to/file.edn@ or @<stdin>@+  -> Text   -- ^ EDN document body+  -> m a+decodeText sourceName source =+  parseText sourceName source >>= fromEDN
+ lib/Data/EDN/AST/Lexer.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.EDN.AST.Lexer+  ( dropWS+  , lexeme+  , symbol+  , integer+  , hexadecimal+  , floating+  ) where++import Data.Text (Text)++import qualified Control.Monad.Combinators as P+import qualified Text.Megaparsec as P+import qualified Text.Megaparsec.Char as P+import qualified Text.Megaparsec.Char.Lexer as L++import Data.EDN.AST.Types (Parser)++dropWS :: Parser ()+dropWS = P.skipMany $ P.choice+  [ P.hidden P.space1+  , P.hidden $ () <$ P.char ','+  , P.hidden $ L.skipLineComment ";"+  ]++-- | Whitespace will be consumed after every lexeme automatically, but not before it.+lexeme :: Parser a -> Parser a+lexeme = L.lexeme dropWS++symbol :: Text -> Parser Text+symbol = L.symbol dropWS++integer :: Parser Int+integer = lexeme $ L.signed (pure ()) L.decimal++hexadecimal :: Parser Int+hexadecimal = lexeme L.hexadecimal++floating :: Parser Double+floating = lexeme $ L.signed (pure ()) L.float
+ lib/Data/EDN/AST/Parser.hs view
@@ -0,0 +1,249 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.EDN.AST.Parser+  ( parseText+   -- * EDN document+  , parseDoc+    -- * Single value+  , parseTagged+  , parseValue+    -- * Primitive parsers+  , parseDiscard+  , parseNil+  , parseBool+  , parseNumber+  , parseKeyword+  , parseSymbol+  , parseCollections+    -- * Character classes+  , tagChars+  , keywordInitialChars+  , keywordChars+  , symbolInitialChars+  , symbolChars+    -- ** Basic characters+  , digitChars+  , lowerChars+  , upperChars+  , miscChars+  ) where++import Control.Applicative ((<|>))+import Data.Char (chr)+import Data.Text (Text)++import qualified Data.Text as Text+import qualified Text.Megaparsec as P+import qualified Text.Megaparsec.Char as P++import Data.EDN.AST.Types (Parser, Tagged(..), TaggedValue, Value(..))++import qualified Data.EDN.AST.Lexer as L+import qualified Data.EDN.AST.Types as EDN++parseText+  :: Monad m+  => String -- ^ Source name, for megaparsec error reports+            -- e.g. @/path/to/file.edn@ or @<stdin>@+  -> Text   -- ^ EDN document body+  -> m TaggedValue+parseText sourceName source =+  case P.parse parseDoc sourceName source of+    Right tv ->+      pure tv+    Left err ->+      fail (P.errorBundlePretty err)++parseDoc :: Parser TaggedValue+parseDoc = do+  L.dropWS+  parseTagged <* P.eof++parseTagged :: Parser TaggedValue+parseTagged = P.choice+  [ P.try withNS+  , P.try withoutNS+  , withoutTag+  ]+  where+    withNS = Tagged+      <$> (P.char '#' *> parseIdent)+      <*> (P.char '/' *> parseIdent)+      <*> (L.dropWS *> parseValue)++    withoutNS = Tagged+      <$> pure mempty+      <*> (P.char '#' *> parseIdent)+      <*> (L.dropWS *> parseValue)++    withoutTag = NoTag+      <$> parseValue++    parseIdent :: Parser Text+    parseIdent =+      P.takeWhile1P (Just "tag") (`elem` tagChars)++parseValue :: Parser Value+parseValue = do+  P.skipMany parseDiscard+  P.choice+    [ parseNil+    , parseBool+    , P.try parseNumber+    , parseSymbol+    , parseCharacter+    , parseString+    , P.try parseKeyword+    , parseCollections+    ]++parseDiscard :: Parser ()+parseDiscard = do+  _ <- P.try $ L.symbol "#_"+  () <$ parseValue++parseCollections :: Parser Value+parseCollections = L.lexeme . P.try $ do+  start <- P.oneOf ['#', '{', '[', '(']+  case start of+    '#' -> do+      _ <- P.char '{'+      EDN.mkSet <$> parseItemsTill '}' parseTagged+    '{' -> do+      let pairs = (,) <$> parseTagged <*> parseTagged+      EDN.mkMap <$> parseItemsTill '}' pairs+    '[' ->+      EDN.mkVec <$> parseItemsTill ']' parseTagged+    '(' ->+      EDN.mkList <$> parseItemsTill ')' parseTagged+    _ ->+      error "assert: start is one of the collection openers"+  where+    parseItemsTill end p = do+      L.dropWS+      P.manyTill (L.dropWS *> p) (L.dropWS *> P.char end)++parseNil :: Parser Value+parseNil = Nil <$ L.symbol "nil"++parseBool :: Parser Value+parseBool = P.choice+  [ Boolean True  <$ L.symbol "true"+  , Boolean False <$ L.symbol "false"+  ]++parseSymbol :: Parser Value+parseSymbol = P.label "symbol" . L.lexeme $ do+  initial <- P.eitherP (P.char '/') (P.oneOf symbolInitialChars)+  (ns, name) <- case initial of+    Left _slash ->+      pure ("", "/")+    Right char ->+      P.try (withNS char) <|> withoutNS char+  pure $ Symbol ns name+  where+    withNS :: Char -> Parser (Text, Text)+    withNS nsInitial = do+      ns <- P.takeWhileP (Just "symbol namespace") (`elem` symbolChars)+      _ <- P.char '/'+      nameInitial <- P.oneOf symbolInitialChars+      name <- P.takeWhileP (Just "symbol name") (`elem` symbolChars)+      pure (Text.cons nsInitial ns, Text.cons nameInitial name)++    withoutNS :: Char -> Parser (Text, Text)+    withoutNS nameInitial = do+      name <- P.takeWhileP (Just "symbol name") (`elem` symbolChars)+      pure ("", Text.cons nameInitial name)++parseCharacter :: Parser Value+parseCharacter = do+  _ <- P.char '\\'+  fmap Character (P.try unicode <|> named <|> L.lexeme P.printChar)+  where+    unicode = P.label "hex-encoded unicode character" $ do+      _ <- P.char 'u'+      fmap chr L.hexadecimal++    named = P.label "whitespace character name" $ P.choice+      [ '\n' <$ L.symbol "newline"+      , '\r' <$ L.symbol "return"+      , ' '  <$ L.symbol "space"+      , '\t' <$ L.symbol "tab"+      ]++parseString :: Parser Value+parseString = L.lexeme $+  P.between (P.char '"') (P.char '"') $+    String . Text.pack <$> P.many (escaped <|> plain)+  where+    escaped :: Parser Char+    escaped = do+      _ <- P.char '\\'+      c <- P.anySingle+      pure $ case c of+        'n' -> '\n'+        't' -> '\t'+        'r' -> '\r'+        -- 'u' -> error "TODO: unicode escapes \u1234"+        _   -> c+    plain = P.anySingleBut '"'++parseNumber :: Parser Value+parseNumber = P.try parseFloating <|> parseInteger+  where+    parseFloating = Floating <$> L.floating+    parseInteger = Integer <$> L.integer++parseKeyword :: Parser Value+parseKeyword = P.label "keyword" . L.lexeme $ do+  c <- P.char ':' *> P.satisfy (`elem` keywordInitialChars)+  cs <- P.takeWhileP Nothing (`elem` keywordChars)+  pure $ Keyword (Text.cons c cs)++tagChars :: [Char]+tagChars = mconcat+  [ lowerChars+  , upperChars+  , digitChars+  , "-"+  ]++keywordInitialChars :: [Char]+keywordInitialChars = mconcat+  [ lowerChars+  , upperChars+  , miscChars+  ]++keywordChars :: [Char]+keywordChars = mconcat+  [ keywordInitialChars+  , digitChars+  , "/#:"+  ]++symbolInitialChars :: [Char]+symbolInitialChars = mconcat+  [ lowerChars+  , upperChars+  , miscChars+  ]++symbolChars :: [Char]+symbolChars = mconcat+  [ symbolInitialChars+  , digitChars+  , "#:"+  ]++digitChars :: [Char]+digitChars = ['0' .. '9']++upperChars :: [Char]+upperChars = ['A' .. 'Z']++lowerChars :: [Char]+lowerChars = ['a' .. 'z']++miscChars :: [Char]+miscChars = ".*<>!?$%&=+_-"
+ lib/Data/EDN/AST/Printer.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++module Data.EDN.AST.Printer+  ( renderText+  , prettyTaggedValue+  , prettyValue+  ) where++import Data.Char (ord)+import Data.Foldable (toList)+import Data.Semigroup ((<>))+import Data.Text (Text)+import Data.Text.Prettyprint.Doc (Doc, pretty, (<+>))+import Text.Printf (printf)++import qualified Data.Map as Map+import qualified Data.Text as Text+import qualified Data.Text.Prettyprint.Doc as PP+import qualified Data.Text.Prettyprint.Doc.Render.Text as PP++import qualified Data.EDN.AST.Types as EDN++-- | Render EDN document to 'Text'+renderText :: EDN.TaggedValue -> Text+renderText =+  PP.renderStrict . PP.layoutPretty options . prettyTaggedValue+  where+    options = PP.defaultLayoutOptions++-- | Prepare 'EDN.TaggedValue'+prettyTaggedValue :: EDN.TaggedValue -> Doc a+prettyTaggedValue = \case+  EDN.Tagged "" tag value ->+    "#" <> pretty tag <+> prettyValue value+  EDN.Tagged ns tag value ->+    "#" <> pretty ns <> "/" <> pretty tag <+> prettyValue value+  EDN.NoTag value ->+    prettyValue value++-- | Prepare 'EDN.Value'+prettyValue :: EDN.Value -> Doc a+prettyValue = PP.fuse PP.Shallow . \case+  EDN.Nil ->+    "nil"+  EDN.Boolean bool ->+    if bool then "true" else "false"+  EDN.Character char ->+    mappend "\\" $ case char of+      '\n' -> "newline"+      '\t' -> "tab"+      '\r' -> "return"+      ' '  -> "space"+      _ ->+        case show char of+          '\'' : '\\' : _ ->+            pretty (printf "u%04X" (ord char) :: [Char])+          _ ->+            pretty char+  EDN.Symbol "" name ->+    pretty name+  EDN.Symbol ns name ->+    pretty ns <> "/" <> pretty name+  EDN.Keyword ident ->+    ":" <> pretty ident+  EDN.Integer int ->+    pretty int+  EDN.Floating double ->+    pretty double+  EDN.String str ->+    PP.enclose "\"" "\"" . pretty $ escapeText str+  EDN.List items ->+    PP.parens . PP.hsep $+      map prettyTaggedValue items+  EDN.Vec items ->+    PP.brackets . PP.hsep $+      map prettyTaggedValue (toList items)+  EDN.Set items ->+    mappend "#" . PP.braces . PP.hsep $+      map prettyTaggedValue (toList items)+  EDN.Map pairs ->+    PP.braces . PP.hsep $+      [ prettyTaggedValue k <+> prettyTaggedValue v+      | (k, v) <- Map.toList pairs+      ]++escapeText :: Text -> Text+escapeText = Text.concatMap escape+  where+    escape = \case+      '\n' -> "\\n"+      '\r' -> "\\r"+      '\t' -> "\\t"+      '"'  -> "\\\""+      c    -> Text.singleton c
+ lib/Data/EDN/AST/Types.hs view
@@ -0,0 +1,14 @@+module Data.EDN.AST.Types+  ( Parser+  , module Data.EDN.AST.Types.Value+  , module Data.EDN.AST.Types.Tagged+  ) where++import Data.Text (Text)+import Data.Void (Void)+import Text.Megaparsec (Parsec)++import Data.EDN.AST.Types.Tagged+import Data.EDN.AST.Types.Value++type Parser = Parsec Void Text
+ lib/Data/EDN/AST/Types/Tagged.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}++module Data.EDN.AST.Types.Tagged+  ( Tagged(..)+  , stripTag+  ) where++import Data.Data (Data)++data Tagged tag a+  = Tagged !tag !tag !a+  | NoTag !a+  deriving (Eq, Ord, Show, Data, Functor)++stripTag :: Tagged tag a -> a+stripTag (NoTag value) = value+stripTag (Tagged _ns _tag value) = value
+ lib/Data/EDN/AST/Types/Value.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Data.EDN.AST.Types.Value+  ( TaggedValue+  , Value(..)+  , EDNList+  , EDNVec+  , EDNMap+  , EDNSet+  , mkList+  , mkVec+  , mkMap+  , mkSet+  ) where++import Data.Data (Data)+import Data.Foldable (toList)+import Data.Text (Text)++import qualified Data.Vector as V+import qualified Data.Map.Strict as M+import qualified Data.Set as S++import Data.EDN.AST.Types.Tagged (Tagged)++type TaggedValue = Tagged Text Value++data Value+  = Nil+  | Boolean   !Bool+  | String    !Text+  | Character !Char+  | Symbol    !Text !Text+  | Keyword   !Text+  | Integer   !Int+  | Floating  !Double+  | List      !EDNList+  | Vec       !EDNVec+  | Map       !EDNMap+  | Set       !EDNSet+  deriving (Eq, Ord, Show, Data)++type EDNList = [TaggedValue]+type EDNVec  = V.Vector TaggedValue+type EDNMap  = M.Map TaggedValue TaggedValue+type EDNSet  = S.Set TaggedValue++mkList :: Foldable f => f TaggedValue -> Value+mkList = List . toList++mkVec :: Foldable f => f TaggedValue -> Value+mkVec = Vec . V.fromList . toList++mkMap :: Foldable f => f (TaggedValue, TaggedValue) -> Value+mkMap = Map . M.fromList . toList++mkSet :: Foldable f => f TaggedValue -> Value+mkSet = Set . S.fromList . toList
+ lib/Data/EDN/Class.hs view
@@ -0,0 +1,495 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeSynonymInstances #-}++module Data.EDN.Class+  ( ToEDN(..)+  , toEDNtagged+  , FromEDN(..)+  , fromEDN+  , withTagged+  , withNoTag+  , withNil+  , withBoolean+  , withString+  , withCharacter+  , withSymbol+  , withKeyword+  , withTextual+  , withInteger+  , withIntegral+  , withFloating+  , withFractional+  , withList+  , withVec+  , withMap+  , withSet+  , unexpected+  , DP.Expected+  , DP.Label+  , vecGet+  , mapGetP+  , mapGetKeyword+  , mapGetString+  , mapGetSymbol+  , mapGetSymbolNS+  ) where++import Control.Applicative ((<|>))+import Data.Map (Map)+import Data.Set (Set)+import Data.Vector (Vector)+import Data.Void (Void, absurd)+import Data.Semigroup ((<>))+import Data.Text (Text)+import Data.Time (UTCTime, defaultTimeLocale, formatTime, parseTimeM)+import Data.UUID (UUID)++import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Data.Text as Text+import qualified Data.Text.Lazy as LText+import qualified Data.UUID as UUID+import qualified Data.Vector as Vector++import Data.EDN.AST.Printer (renderText)++import qualified Data.EDN.AST.Types as EDN+import qualified Data.EDN.Class.Parser as DP++-- * Encoding++-- | A type that can be converted to EDN AST.+class ToEDN a where+  {-# MINIMAL toEDN | toEDNv #-}++  toEDN :: a -> EDN.TaggedValue+  toEDN = EDN.NoTag . toEDNv+  {-# INLINE toEDN #-}++  toEDNv :: a -> EDN.Value+  toEDNv = EDN.stripTag . toEDN+  {-# INLINE toEDNv #-}++toEDNtagged :: ToEDN a => Text -> Text -> a -> EDN.TaggedValue+toEDNtagged tagNS tag = EDN.Tagged tagNS tag . toEDNv++instance ToEDN EDN.TaggedValue where+  toEDN = id++instance ToEDN EDN.Value where+  toEDNv = id++instance ToEDN Void where+  toEDNv = absurd++instance ToEDN () where+  toEDN () = EDN.NoTag EDN.Nil++instance ToEDN Bool where+  toEDNv = EDN.Boolean++instance ToEDN Text where+  toEDNv = EDN.String++instance ToEDN LText.Text where+  toEDNv = EDN.String . LText.toStrict++instance ToEDN Char where+  toEDNv = EDN.Character++instance ToEDN Int where+  toEDNv = EDN.Integer++instance ToEDN Double where+  toEDNv = EDN.Floating++instance ToEDN a => ToEDN (Maybe a) where+  toEDN Nothing = EDN.NoTag EDN.Nil+  toEDN (Just a) = toEDN a++instance ToEDN a => ToEDN [a] where+  toEDNv = EDN.List . map toEDN++instance ToEDN a => ToEDN (Vector a) where+  toEDNv = EDN.Vec . fmap toEDN++instance ToEDN a => ToEDN (Set a) where+  toEDNv = EDN.Set . Set.fromList . map toEDN . Set.toList++instance (ToEDN k, ToEDN v) => ToEDN (Map k v) where+  toEDNv+    = EDN.Map+    . Map.fromList+    . map (\(k, v) -> (toEDN k, toEDN v))+    . Map.toList++instance (ToEDN a, ToEDN b) => ToEDN (a, b) where+  toEDNv (a, b) = EDN.Vec $ Vector.fromList+    [ toEDN a+    , toEDN b+    ]++instance (ToEDN a, ToEDN b, ToEDN c) => ToEDN (a, b, c) where+  toEDNv (a, b, c) = EDN.Vec $ Vector.fromList+    [ toEDN a+    , toEDN b+    , toEDN c+    ]++instance (ToEDN a, ToEDN b, ToEDN c, ToEDN d) => ToEDN (a, b, c, d) where+  toEDNv (a, b, c, d) = EDN.Vec $ Vector.fromList+    [ toEDN a+    , toEDN b+    , toEDN c+    , toEDN d+    ]++instance ToEDN UTCTime where+  toEDN+    = toEDNtagged "" "inst"+    . Text.pack . formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S%Q%EZ"++instance ToEDN UUID where+  toEDN = toEDNtagged "" "uuid" . UUID.toText++-- * Decoding++withTagged+  :: Text+  -> Text+  -> (EDN.Value -> DP.Parser a)+  -> EDN.TaggedValue+  -> DP.Parser a+withTagged tagNS tag p tv =+  case tv of+    EDN.Tagged tagNS' tag' v+      | tagNS' == tagNS && tag == tag' ->+          p v+      | otherwise ->+          fail . Text.unpack $ mconcat+            [ "unexpected tag. "+            , "expecting: #"+            , nsToText tagNS' tag'+            , "; got: #"+            , nsToText tagNS tag+            ]+    _ ->+      fail "expected tagged value"++withNoTag :: (EDN.Value -> DP.Parser a) -> EDN.TaggedValue -> DP.Parser a+withNoTag p tv =+  case tv of+    EDN.NoTag v ->+      p v+    EDN.Tagged tagNS tag _v ->+      fail $ "no tag expected, got #" <> Text.unpack (nsToText tagNS tag)++withNil :: DP.Parser a -> EDN.Value -> DP.Parser a+withNil p = \case+  EDN.Nil ->+    p+  got ->+    got `unexpected` "nil"++withBoolean :: (Bool -> DP.Parser a) -> EDN.Value -> DP.Parser a+withBoolean p = \case+  EDN.Boolean b ->+    p b+  got ->+    got `unexpected` "boolean"++withString :: (Text -> DP.Parser a) -> EDN.Value -> DP.Parser a+withString p = \case+  EDN.String t ->+    p t+  got ->+    got `unexpected` "string"++withCharacter :: (Char -> DP.Parser a) -> EDN.Value -> DP.Parser a+withCharacter p = \case+  EDN.Character c ->+    p c+  got ->+    got `unexpected` "char"++withSymbol :: (Text -> Text -> DP.Parser a) -> EDN.Value -> DP.Parser a+withSymbol p = \case+  EDN.Symbol ns n ->+    p ns n+  got ->+    got `unexpected` "symbol"++withKeyword :: (Text -> DP.Parser a) -> EDN.Value -> DP.Parser a+withKeyword p = \case+  EDN.Keyword t ->+    p t+  got ->+    got `unexpected` "keyword"++withTextual :: (Text -> DP.Parser a) -> EDN.Value -> DP.Parser a+withTextual p tv =+  withString p tv <|>+  withCharacter (p . Text.singleton) tv <|>+  withKeyword p tv <|>+  withSymbol (\ns n -> p $ nsToText ns n) tv++withInteger :: (Int -> DP.Parser a) -> EDN.Value -> DP.Parser a+withInteger p = \case+  EDN.Integer i ->+    p i+  got ->+    got `unexpected` "integer"++withIntegral :: Integral i => (i -> DP.Parser a) -> EDN.Value -> DP.Parser a+withIntegral p = \case+  EDN.Integer i ->+    p (fromIntegral i)+  got ->+    got `unexpected` "integer"++withFloating :: (Double -> DP.Parser a) -> EDN.Value -> DP.Parser a+withFloating p = \case+  EDN.Floating d ->+    p d+  got ->+    got `unexpected` "double"++withFractional :: Fractional f => (f -> DP.Parser a) -> EDN.Value -> DP.Parser a+withFractional p = \case+  EDN.Floating d ->+    p (realToFrac d)+  got ->+    got `unexpected` "double"++withList :: (EDN.EDNList -> DP.Parser a) -> EDN.Value -> DP.Parser a+withList p = \case+  EDN.List tvs ->+    p tvs+  got ->+    got `unexpected` "list"++withVec :: (EDN.EDNVec -> DP.Parser a) -> EDN.Value -> DP.Parser a+withVec p = \case+  EDN.Vec v ->+    p v+  got ->+    got `unexpected` "vector"++withMap :: (EDN.EDNMap -> DP.Parser a) -> EDN.Value -> DP.Parser a+withMap p = \case+  EDN.Map m ->+    p m+  got ->+    got `unexpected` "map"++withSet :: (EDN.EDNSet -> DP.Parser a) -> EDN.Value -> DP.Parser a+withSet p = \case+  EDN.Set m ->+    p m+  got ->+    got `unexpected` "set"++-- | Report an decoding error due to unexpected AST node given.+-- The 'DP.Parser' combines and reports alternatives expected.+unexpected :: EDN.Value -> DP.Label -> DP.Parser a+unexpected value label = DP.Parser $ \kf _ks ->+  kf (pure label) $ "Unexpected " <> label'+  where+    label' = case value of+      EDN.Nil         -> "nil"+      EDN.Boolean{}   -> "boolean"+      EDN.String{}    -> "string"+      EDN.Character{} -> "character"+      EDN.Symbol{}    -> "symbol"+      EDN.Keyword{}   -> "keyword"+      EDN.Integer{}   -> "integer"+      EDN.Floating{}  -> "floating"+      EDN.List{}      -> "list"+      EDN.Vec{}       -> "vector"+      EDN.Map{}       -> "map"+      EDN.Set{}       -> "set"++-- | A type that can be converted from EDN, with a possibility of failure.+--+-- When writing an instance, use 'unexpected' or 'fail' to make a+-- conversion fail, e.g. if an 'Map.Map' is missing a required key, or+-- the value is of the wrong type.+class FromEDN a where+  {-# MINIMAL parseEDN | parseEDNv #-}++  parseEDN :: EDN.TaggedValue -> DP.Parser a+  parseEDN = parseEDNv . EDN.stripTag+  {-# INLINE parseEDN #-}++  parseEDNv :: EDN.Value -> DP.Parser a+  parseEDNv = parseEDN . EDN.NoTag+  {-# INLINE parseEDNv #-}++-- | Apply appropriate parsers for a value to decode AST.+fromEDN :: (FromEDN a, Monad m) => EDN.TaggedValue -> m a+fromEDN = DP.parseM parseEDN++instance FromEDN EDN.TaggedValue where+  parseEDN = pure++instance FromEDN EDN.Value where+  parseEDNv = pure++instance FromEDN Void where+  parseEDN = fail "absurd"++instance FromEDN () where+  parseEDNv = withNil $ pure ()++instance FromEDN Bool where+  parseEDNv = withBoolean pure++instance FromEDN Text where+  parseEDNv = withTextual pure++instance FromEDN LText.Text where+  parseEDNv = withTextual (pure . LText.fromStrict)++instance FromEDN Char where+  parseEDNv = withCharacter pure++instance FromEDN Int where+  parseEDNv = withInteger pure++instance FromEDN Double where+  parseEDNv = withFloating pure++instance FromEDN a => FromEDN (Maybe a) where+  parseEDN = \case+    EDN.NoTag      EDN.Nil -> pure Nothing+    EDN.Tagged _ _ EDN.Nil -> pure Nothing+    tv                     -> Just <$> parseEDN tv++  parseEDNv = \case+    EDN.Nil -> pure Nothing+    v       -> Just <$> parseEDNv v++instance FromEDN a => FromEDN [a] where+  parseEDNv = withList (traverse parseEDN)++instance FromEDN a => FromEDN (Vector a) where+  parseEDNv = withVec (traverse parseEDN)++-- | Get ix-th element of 'EDN.EDNVec' or fail with appropriate message.+vecGet+  :: FromEDN a+  => Int         -- ^ Element index+  -> EDN.EDNVec  -- ^ 'Vector.Vector' of EDN values+  -> DP.Parser a+vecGet ix v =+  case v Vector.!? ix of+    Nothing ->+      fail $ unwords+        [ "expected vector with at least"+        , show (succ ix)+        , "elements"+        ]+    Just x ->+      parseEDN x++instance (FromEDN a, Ord a) => FromEDN (Set a) where+  parseEDNv = withSet $ \s ->+    Set.fromList <$> traverse parseEDN (Set.toList s)++instance (FromEDN k, FromEDN v, Ord k) => FromEDN (Map k v) where+  parseEDNv = withMap $ \m ->+    Map.fromList <$> traverse parsePair (Map.toList m)+    where+      parsePair (k, v) = (,) <$> parseEDN k <*> parseEDN v++-- | Get a value from 'EDN.EDNMap' and apply a parser to it+mapGetP+  :: Monad m+  => EDN.TaggedValue          -- ^ Map key+  -> (EDN.TaggedValue -> m a) -- ^ Parser to apply to a value+  -> EDN.EDNMap               -- ^ Map with EDN keys and values+  -> m a+mapGetP key inner m =+  case Map.lookup key m of+    Just tv ->+      inner tv+    Nothing ->+      fail . Text.unpack $ "key not found: " <> renderText key++-- | Get a value from 'EDN.EDNMap' for a 'EDN.Keyword' key.+mapGetKeyword :: FromEDN a => Text -> EDN.EDNMap -> DP.Parser a+mapGetKeyword key = mapGetP (EDN.NoTag $ EDN.Keyword key) parseEDN++-- | Get a value from 'EDN.EDNMap' for a 'EDN.String' key.+mapGetString :: FromEDN a => Text -> EDN.EDNMap -> DP.Parser a+mapGetString key = mapGetP (EDN.NoTag $ EDN.String key) parseEDN++-- | Get a value from 'EDN.EDNMap' for a 'EDN.Symbol' (empty namespace) key.+mapGetSymbol :: FromEDN a => Text -> EDN.EDNMap -> DP.Parser a+mapGetSymbol = mapGetSymbolNS ""++-- | Get a value from 'EDN.EDNMap' for a 'EDN.Symbol' (empty namespace) key.+mapGetSymbolNS+  :: FromEDN a+  => Text        -- ^ Symbol namespace+  -> Text        -- ^ Symbol name+  -> EDN.EDNMap+  -> DP.Parser a+mapGetSymbolNS ns name = mapGetP (EDN.NoTag $ EDN.Symbol ns name) parseEDN++instance (FromEDN a, FromEDN b) => FromEDN (a, b) where+  parseEDNv = withVec $ \case+    [a, b] ->+      (,) <$> parseEDN a <*> parseEDN b+    _ ->+      fail "vector of size 2 expected"++instance (FromEDN a, FromEDN b, FromEDN c) => FromEDN (a, b, c) where+  parseEDNv = withVec $ \case+    [a, b, c] ->+      (,,) <$> parseEDN a <*> parseEDN b <*> parseEDN c+    _ ->+      fail "vector of size 3 expected"++instance (FromEDN a, FromEDN b, FromEDN c, FromEDN d) => FromEDN (a, b, c, d) where+  parseEDNv = withVec $ \case+    [a, b, c, d] ->+      (,,,) <$> parseEDN a <*> parseEDN b <*> parseEDN c <*> parseEDN d+    _ ->+      fail "vector of size 3 expected"++instance FromEDN UTCTime where+  parseEDN tv = parseTaggedUTCTime tv <|> parseUntaggedUTCTime tv+    where+      parseTaggedUTCTime =+        withTagged "" "inst" $ withString parseUTCTime++      parseUntaggedUTCTime =+        withNoTag $ withString parseUTCTime++      parseUTCTime =+        parseTimeM False defaultTimeLocale "%Y-%m-%dT%H:%M:%S%Q%Z" . Text.unpack++instance FromEDN UUID where+  parseEDN tv = parseTaggedUUID tv <|> parseUntaggedUUID tv+    where+      parseTaggedUUID = withTagged "" "uuid" $ withString parseUUID++      parseUntaggedUUID = withNoTag $ withString parseUUID++      parseUUID t =+        case UUID.fromText t of+          Nothing ->+            fail "invalid UUID string"+          Just uuid ->+            pure uuid++nsToText+  :: Text -- ^ Namespace+  -> Text -- ^ Name+  -> Text -- ^ Resulting text+nsToText "" n = n+nsToText ns n = ns <> "/" <> n
+ lib/Data/EDN/Class/Parser.hs view
@@ -0,0 +1,134 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE RankNTypes #-}++-- | Generic continuation-based parser++module Data.EDN.Class.Parser+  ( parseEither+  , parseM+  , Parser(..)+  , Success+  , Failure+  , Expected+  , Label+  ) where++import Control.Applicative (Alternative(..))+import Control.Monad (MonadPlus(..))+import Data.Semigroup (Semigroup(..))++import qualified Control.Monad.Fail as Fail+import qualified Data.List as List++-- | Run a 'Parser' reporting to arbitrary 'Monad' with 'fail'.+parseM :: Monad m => (a -> Parser b) -> a -> m b+parseM p v = runParser (p v) (unexpected fail) pure++-- | Run a 'Parser' reporting to an 'Either'.+parseEither :: (a -> Parser b) -> a -> Either String b+parseEither p v = runParser (p v) (unexpected Left) Right++-- | Helper to convert expected labels to a failure value.+unexpected+  :: (String -> a) -- ^ Smart-constructor for failure value+  -> Expected      -- ^ Accumulated expected labels+  -> String        -- ^ Failure message+  -> a+unexpected failWith [] err =+  failWith err+unexpected failWith es err =+  failWith $ unlines+    [ err+    , "expected"+    , "  " <> List.intercalate ", " es+    ]++-- | A continuation-based parser type.+newtype Parser a = Parser+  { runParser+    :: forall f r.+       Failure f r+    -> Success a f r+    -> f r+  }++-- | Megaparsec-style collection of elements expected by combined parser alternatives.+type Expected = [Label]++-- | Single element expected by a parser. 'String' because 'Control.Monad.Fail.MonadFail' method.+type Label = String++-- | Failure continuation.+type Failure f r = Expected -> String -> f r++-- | Success continuation.+type Success a f r = a -> f r++instance Functor Parser where+  fmap f p =+    Parser $ \kf ks ->+      runParser p kf $ \a ->+        ks (f a)+  {-# INLINE fmap #-}++instance Applicative Parser where+  pure a =+    Parser $ \_kf ks ->+      ks a+  {-# INLINE pure #-}++  d <*> e =+    d >>= \b -> fmap b e+  {-# INLINE (<*>) #-}++instance Alternative Parser where+  empty =+    Parser $ \kf _ks ->+      kf mempty "empty"+  {-# INLINE empty #-}++  a <|> b =+    Parser $ \kf ks ->+      let+       kf' expected _ = runParser b (kf . mappend expected) ks+      in+       runParser a kf' ks+  {-# INLINE (<|>) #-}++instance Fail.MonadFail Parser where+  fail msg =+    Parser $ \kf _ks ->+      kf mempty msg+  {-# INLINE fail #-}++instance Monad Parser where+  m >>= g =+    Parser $ \kf ks ->+      let+        ks' a = runParser (g a) kf ks+      in+        runParser m kf ks'+  {-# INLINE (>>=) #-}++  return = pure+  {-# INLINE return #-}++  fail = Fail.fail+  {-# INLINE fail #-}++instance MonadPlus Parser where+  mzero = fail "mzero"+  {-# INLINE mzero #-}++  mplus a b = a <|> b+  {-# INLINE mplus #-}++instance Semigroup (Parser a) where+  (<>) = mplus++instance Monoid (Parser a) where+  mempty  = fail "mempty"+  {-# INLINE mempty #-}++  mappend = (<>)+  {-# INLINE mappend #-}
+ lib/Data/EDN/QQ.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}++-- XXX: Lift instances. Don't want to pollute main module with TH shenanigans.+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Data.EDN.QQ+  ( edn+  , ednList+  , ednMap+  , ednVec+  , ednSet+  , fromEDN+  ) where++import Data.Data (Data)+import Data.Semigroup ((<>))+import Data.Text (Text)+import Data.Typeable (cast)+import Language.Haskell.TH.Quote (QuasiQuoter(..))+import Language.Haskell.TH.Syntax (Exp(..), Lift(..), Q, dataToExpQ, loc_filename, qLocation)++import qualified Data.Text as Text+import qualified Data.Vector as Vector++import Data.EDN (FromEDN, parseText, decodeText)+import Data.EDN.AST.Types (Tagged(..), Value(..))++-- | Quasiquoter for 'Data.EDN.TaggedValue'.+--+-- @+-- Tagged "foo" "bar" Nil === [edn| #foo/bar nil |]+-- @+edn :: QuasiQuoter+edn = ednQQ $ \str -> do+  src <- fmap loc_filename qLocation+  tv <- parseText src (Text.pack str)+  lift tv++-- | Quasiquoter for untagged 'Value' wrapped in a List.+--+-- @+-- [ednList| #foo/bar nil |]+-- ===+-- List [ Tagged "foo" "bar" Nil ]+-- @+ednList :: QuasiQuoter+ednList = ednQQ $ \str -> do+  src <- fmap loc_filename qLocation+  NoTag tv <- parseText src $ "(" <> Text.pack str <> ")"+  lift tv++-- | Quasiquoter for untagged 'Value' wrapped in a Vec.+--+-- @+-- [ednVec| #foo/bar nil |]+-- ===+-- Vec [ Tagged "foo" "bar" Nil ]+-- @+ednVec :: QuasiQuoter+ednVec = ednQQ $ \str -> do+  src <- fmap loc_filename qLocation+  NoTag tv <- parseText src $ "[" <> Text.pack str <> "]"+  lift tv++-- | Quasiquoter for untagged 'Value' wrapped in a Set.+--+-- @+-- [ednList| #foo/bar nil |]+-- ===+-- List [ Tagged "foo" "bar" Nil ]+-- @+ednSet :: QuasiQuoter+ednSet = ednQQ $ \str -> do+  src <- fmap loc_filename qLocation+  NoTag tv <- parseText src $ "#{" <> Text.pack str <> "}"+  lift tv++-- | Quasiquoter for untagged 'Value' wrapped in a Map.+--+-- @+-- [ednMap| :key value |]+-- ===+-- Map [ (NoTag (Keyword "key"), NoTag (Symbol "" "value")) ]+-- @+ednMap :: QuasiQuoter+ednMap = ednQQ $ \str -> do+  src <- fmap loc_filename qLocation+  NoTag tv <- parseText src $ "{" <> Text.pack str <> "}"+  lift tv++-- | Specializable QuasiQuoter for compile-time decoding.+--+-- > ednPerson = fromEDN @Person+--+-- And in another module (a TH restriction):+--+-- > theFred = [ednPerson| #myapp/Person { :first "Fred" } |]+fromEDN :: forall a. (Lift a, FromEDN a) => QuasiQuoter+fromEDN = ednQQ $ \str -> do+  src <- fmap loc_filename qLocation+  val <- decodeText src (Text.pack str) :: Q a+  lift val++ednQQ :: (String -> Q Exp) -> QuasiQuoter+ednQQ qexp = QuasiQuoter+  { quoteExp  = qexp+  , quotePat  = error "EDN unavailable in patterns"+  , quoteType = error "EDN unavailable in types"+  , quoteDec  = error "EDN unavailable in declarations"+  }++-- XXX: Workaround for Text.pack not present in the same module with Text constructors.+-- See https://stackoverflow.com/a/38182444+liftData' :: Data a => a -> Q Exp+liftData' = dataToExpQ $ fmap liftText . cast++liftText :: Text.Text -> Q Exp+liftText txt = AppE (VarE 'Text.pack) <$> lift (Text.unpack txt)++-- XXX: Workaround for undefined toConstr in Data instance for Vector.+instance Data a => Lift (Tagged Text a) where+  lift = \case+    NoTag val -> do+      val' <- liftData' val+      pure $ ConE 'NoTag `AppE` val'+    Tagged tagNS tag val -> do+      tagNS' <- liftText tagNS+      tag' <- liftText tag+      val' <- liftData' val+      pure $ ConE 'Tagged `AppE` tagNS' `AppE` tag' `AppE` val'++instance Lift Value where+  lift = \case+    Vec items ->+      AppE (ConE 'Vec) <$> liftVector items+    val ->+      liftData' val++liftVector :: Lift a => Vector.Vector a -> Q Exp+liftVector vec =+  AppE (VarE 'Vector.fromList) <$> lift (Vector.toList vec)
− src/Data/EDN.hs
@@ -1,34 +0,0 @@-module Data.EDN (-    -- * Encoding and decoding-    decode, encode,--    -- * Core EDN types-    Value(..), TaggedValue, Tagged(..),--    -- * Type conversion-    ToEDN, FromEDN, toEDN, fromEDN, fromEDNv, (.:), (.:?), (.!=),--    -- * Constructors-    tag, notag,--    -- ** Basic values-    nil,-    bool, true, false,-    char, string,-    symbol, symbolNS, keyword,-    integer, floating,--    -- ** Containers-    makeList, makeVec, makeSet, makeMap, Pair, (.=),--    -- * Tag manipulation-    setTag, getTag, stripTag,--    -- * Parsing-    parseMaybe-) where--import Data.EDN.Types-import Data.EDN.Types.Class (decode, FromEDN, ToEDN, toEDN, fromEDN, fromEDNv, (.:), (.:?), (.!=), (.=))-import Data.EDN.Encode (encode)-import Data.EDN.Parser (parseMaybe)
− src/Data/EDN/Encode.hs
@@ -1,93 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Data.EDN.Encode (fromValue, fromTagged, encode) where--import Data.Monoid ((<>))-import qualified Data.Text as T-import Data.Text.Lazy.Builder-import Data.Text.Lazy.Builder.Int (decimal)-import Data.Text.Lazy.Builder.RealFloat (realFloat)-import Data.Text.Lazy.Encoding (encodeUtf8, decodeUtf8)-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy as L--import qualified Data.Vector as V-import qualified Data.Map as M-import qualified Data.Set as S--import qualified Data.EDN.Types as E-import Data.EDN.Types.Class (ToEDN, toEDN)---- | Encode a Tagged EDN value to a 'Builder'.-fromTagged :: E.TaggedValue -> Builder-fromTagged (E.NoTag v) = fromValue v-fromTagged (E.Tagged v "" t) = singleton '#' <> string t <> singleton ' ' <> fromValue v-fromTagged (E.Tagged v ns t) = singleton '#' <> string ns <> singleton '/' <> string t <> singleton ' ' <> fromValue v---- | Encode a raw EDN value to a 'Builder'.-fromValue :: E.Value -> Builder--fromValue E.Nil = "nil"--fromValue (E.Boolean b) = if b then "true" else "false"--fromValue (E.String t) = singleton '"' <> quote t <> singleton '"'--fromValue (E.Character c) = singleton '\\' <> quoteChar c--fromValue (E.Symbol "" v) = string v-fromValue (E.Symbol ns v) = string ns <> singleton '/' <> string v--fromValue (E.Keyword kw) = singleton ':' <> string kw--fromValue (E.Integer i) = decimal i--fromValue (E.Floating f) = realFloat f--fromValue (E.List xs) = singleton '(' <> fromList xs <> singleton ')'--fromValue (E.Vec xs) = singleton '[' <> fromList (V.toList xs) <> singleton ']'--fromValue (E.Set xs) = "#{" <> fromList (S.toList xs) <> singleton '}'--fromValue (E.Map as) = singleton '{' <> fromAssoc (M.assocs as) <> singleton '}'--string :: BS.ByteString -> Builder-string s = fromLazyText . decodeUtf8 . L.fromChunks $ [s]--quote :: T.Text -> Builder-quote q = case T.uncons t of-    Nothing -> fromText h-    Just (c, t') -> fromText h <> escape c <> quote t'-    where-        (h, t) = T.break isEscape q-        isEscape c = c == '\"' || c == '\\' || c < '\x20'-        escape '\"' = "\\\""-        escape '\\' = "\\\\"-        escape '\n' = "\\n"-        escape '\r' = "\\r"-        escape '\t' = "\\t"-        escape c = singleton c--quoteChar :: Char -> Builder-quoteChar c = case c of-    '\n' -> string "newline"-    '\r' -> string "return"-    '\t' -> string "tab"-    ' '  -> string "space"-    _    -> singleton c--fromList :: [E.TaggedValue] -> Builder-fromList [] = ""-fromList [x] = fromTagged x-fromList (x:xs) = fromTagged x <> singleton ' ' <> fromList xs--fromAssoc :: [(E.Value, E.TaggedValue)] -> Builder-fromAssoc [] = ""-fromAssoc [(k, v)] = fromValue k <> singleton ' ' <> fromTagged v-fromAssoc ((k, v):as) = fromValue k <> singleton ' ' <> fromTagged v <> singleton ' ' <> fromAssoc as---- | Serialize a value as a lazy 'L.ByteString'.-encode :: ToEDN a => a -> L.ByteString-encode = encodeUtf8 . toLazyText . fromTagged . toEDN-{-# INLINE encode #-}
− src/Data/EDN/Parser.hs
@@ -1,281 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- | Parse an UTF-8 encoded EDN string into a haskell representation of EDN objects.--- Use 'Data.EDN.decode' to get actual types.--module Data.EDN.Parser (-    -- * Data parsers-    parseMaybe, parseEither, parseBSL, parseBS, parseT, parseTL, parseS,-    -- * Attoparsec implementation-    parseValue, parseTagged-) where--import Prelude ()-import Prelude.Compat hiding (String, takeWhile)--import           Control.Applicative              ((<|>))-import           Control.Monad                    (void)-import           Data.Attoparsec.ByteString.Char8 as A-import           Data.Attoparsec.Combinator       ()-import qualified Data.Attoparsec.Lazy             as AL-import qualified Data.ByteString.Char8            as BS-import qualified Data.ByteString.Lazy.Char8       as BSL-import           Data.ByteString.Search           (replace)-import qualified Data.ByteString.UTF8             as UTF8-import           Data.Functor                     (($>))-import           Data.Maybe                       (fromJust)-import           Data.Scientific                  as Sci-import qualified Data.Text                        as T-import qualified Data.Text.Encoding               as TE-import qualified Data.Text.Lazy                   as TL-import qualified Data.Text.Lazy.Encoding          as TLE--import           Data.EDN.Types                   (Tagged (..), TaggedValue,-                                                   Value (..), makeMap, makeSet,-                                                   makeVec)-import qualified Prelude                          as P--isSpaceOrComma :: Char -> Bool-isSpaceOrComma ' '  = True-isSpaceOrComma '\r' = True-isSpaceOrComma '\n' = True-isSpaceOrComma '\t' = True-isSpaceOrComma ','  = True-isSpaceOrComma _    = False--spaceOrComma :: Parser Char-spaceOrComma = satisfy isSpaceOrComma <?> "space/comma"--skipSoC :: Parser ()-skipSoC = skipWhile isSpaceOrComma--parseNil :: Parser Value-parseNil = do-    skipSoC-    string "nil" $> Nil--parseBool :: Parser Value-parseBool = do-    skipSoC-    choice [ string "true"  *> pure (Boolean True)-           , string "false" *> pure (Boolean False)-           ]--parseString :: Parser Value-parseString = do-    skipSoC-    void $ char '"'--    x <- A.scan False $ \s c -> if s-                                then Just False-                                else if c == '"'-                                     then Nothing-                                     else Just (c == '\\')-    void $ char '"'--    let prepare = if '\\' `BS.elem` x-                  then rep "\\\"" "\""-                       . rep "\\\\" "\\"-                       . rep "\\n" "\n"-                       . rep "\\r" "\r"-                       . rep "\\t" "\t"-                  else id--    return $! String $ TE.decodeUtf8 $ prepare x--    where-        rep f t s = BS.concat . BSL.toChunks-                    $! replace (BS.pack f) (BS.pack t) s--parseCharacter :: Parser Value-parseCharacter = do-    skipSoC-    char '\\' *> (simple <|> anyCharUtf8)--    where-        simple :: Parser Value-        simple = do-            x <- string "newline"-             <|> string "return"-             <|> string "space"-             <|> string "tab"-             <|> string "\\"--            return . Character $! case x of-              "newline" -> '\n'-              "return" -> '\r'-              "space" -> ' '-              "tab" -> '\t'-              "\\" -> '\\'-              _ -> error ("EDN.parseCharacter: impossible - simple" ++ show x)--        anyCharUtf8 :: Parser Value-        anyCharUtf8 = do-            bs <- scan BS.empty go-            case UTF8.decode bs of-                Just (c, _) -> return $! Character c-                Nothing     -> error $ "EDN.parseCharacter: bad utf8 data? " ++ show bs--        go :: BS.ByteString -> Char -> Maybe BS.ByteString-        go s c-            | BS.null s = Just (BS.singleton c)-            | otherwise = case UTF8.decode s of-                              Nothing      -> Just (s `BS.snoc` c)-                              Just (uc, _) -> if uc == UTF8.replacement_char-                                                  then Just (s `BS.snoc` c)-                                                  else Nothing--parseSymbol :: Parser Value-parseSymbol = do-    skipSoC-    c <- satisfy (inClass "a-zA-Z.*/<>!?$%&=+_-")-    (ns, val) <- withNS c <|> withoutNS c-    return $! Symbol ns val-    where-        withNS c = do-            ns <- takeWhile (inClass "a-zA-Z0-9#:.*<>!?$%&=+_-")-            vc <- char '/' *> satisfy (inClass "a-zA-Z.*/<>!?$%&=+_-")-            val <- takeWhile1 (inClass "a-zA-Z0-9#:.*<>!?$%&=+_-")-            return (c `BS.cons` ns, vc `BS.cons` val)--        withoutNS c = do-            val <- takeWhile (inClass "a-zA-Z0-9#:.*<>!?$%&=+_-")-            return ("", c `BS.cons` val)--parseKeyword :: Parser Value-parseKeyword = do-    skipSoC-    c <- char ':' *> satisfy (inClass "a-zA-Z.*/<>!?$%&=+_-")-    x <-           takeWhile (inClass "a-zA-Z0-9#:.*/<>!?$%&=+_-")-    return $! Keyword (c `BS.cons` x)--parseNumber :: Parser Value-parseNumber = do-    skipSoC-    n <- A.scientific-    return $!-        if Sci.isInteger n-        then Integer  (fromIntegral (fromJust (Sci.toBoundedInteger n) :: P.Int))-        else Floating (Sci.toRealFloat n)---parseColl :: Parser t1       -- opening bracket-          -> Parser t2       -- closing bracket-          -> Parser a        -- item parser-          -> ([a] -> Value)  -- Value constructor-          -> Parser Value-parseColl openingBr closingBr item construct = do-    skipSoC-    _ <- openingBr-    A.skipSpace-    vs <- item `sepBy` spaceOrComma-    A.skipSpace-    _<- closingBr-    return $! construct vs--parseList :: Parser Value-parseList =-    parseColl (char '(') (char ')') parseTagged List--parseVector :: Parser Value-parseVector =-    parseColl (char '[') (char ']') parseTagged makeVec--parseSet :: Parser Value-parseSet =-    parseColl (char '#' *> char '{') (char '}') parseTagged makeSet--parseMap :: Parser Value-parseMap =-    parseColl (char '{') (char '}') parseAssoc makeMap-    where-        parseAssoc = do-            key <- parseValue-            val <- parseTagged-            return (key, val)---skipComment :: Parser ()-skipComment = skipSoC *> char ';' *> skipWhile (/= '\n')--parseDiscard :: Parser ()-parseDiscard = do-    skipSoC-    void $ string "#_"-    void parseValue---- | Parse a \"raw\" EDN value into a 'Value'.-parseValue :: Parser Value-parseValue = do-    skipSoC-    skipMany skipComment-    skipMany parseDiscard--    parseSet <|> parseMap-             <|> parseVector <|> parseList-             <|> parseNil <|> parseBool-             <|> parseNumber-             <|> parseKeyword <|> parseSymbol-             <|> parseCharacter-             <|> parseString---- | Parse a probably tagged EDN value into a 'TaggedValue'.-parseTagged :: Parser TaggedValue-parseTagged = do-    skipSoC-    withNS <|> withoutNS <|> noTag-    where-        withNS = do-            ns <- char '#' *> parseIdent-            tag <- char '/' *> parseIdent-            value <- parseValue-            return $! Tagged value ns tag--        withoutNS = do-            tag <- char '#' *> parseIdent-            value <- parseValue-            return $! Tagged value "" tag--        parseIdent = takeWhile1 (inClass "a-zA-Z0-9-")--        noTag = do-            value <- parseValue-            return $! NoTag value--{- | Parse a lazy 'BSL.ByteString' into a 'TaggedValue'.-If fails due to incomplete or invalid input, 'Nothing' is returned.--}-parseMaybe :: BSL.ByteString -> Maybe TaggedValue-parseMaybe = AL.maybeResult . parseBSL--{- | Parse a lazy 'BSL.ByteString' into a 'TaggedValue'.-If fails due to incomplete or invalid input,-'Left' is returned with the error message.--}-parseEither :: BSL.ByteString -> Either P.String TaggedValue-parseEither = AL.eitherResult . parseBSL---- | Parse a lazy 'BSL.ByteString'.-parseBSL :: BSL.ByteString -> AL.Result TaggedValue-parseBSL = AL.parse parseTagged-{-# INLINE parseBSL #-}---- | Parse a strict 'BS.ByteString', but without continutations.-parseBS :: BS.ByteString -> AL.Result TaggedValue-parseBS s = parseBSL . BSL.fromChunks $ [s]-{-# INLINE parseBS #-}---- | Parse a lazy 'TL.Text'.-parseTL :: TL.Text -> AL.Result TaggedValue-parseTL = parseBSL . TLE.encodeUtf8-{-# INLINE parseTL #-}---- | Parse a strict 'T.Text'.-parseT :: T.Text -> AL.Result TaggedValue-parseT = parseBS . TE.encodeUtf8-{-# INLINE parseT #-}---- | Parse a string AKA '[Char]'. Not really useful other than for debugging purposes.-parseS :: P.String -> AL.Result TaggedValue-parseS = parseBSL . BSL.pack-{-# INLINE parseS #-}
− src/Data/EDN/Types.hs
@@ -1,200 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}--module Data.EDN.Types (-    -- * Types-    Value(..), Tagged(..), TaggedValue,--    -- ** Internal containers-    EDNList, EDNVec, EDNSet, EDNMap, Pair,--    -- * Tag manipulation-    setTag, getTag, stripTag,--    -- * Constructors-    tag, notag,--    -- ** Trivial values-    nil,-    bool, true, false,-    char, string,-    symbol, symbolNS, keyword,-    integer, floating,--    -- ** Containers-    makeList, makeVec, makeSet, makeMap-) where--import Data.String (IsString(..))-import Control.DeepSeq (NFData(..))-import Data.Text (Text)-import qualified Data.Text as T-import Data.ByteString.Char8 (ByteString)-import qualified Data.ByteString.Char8 as BS-import qualified Data.Vector as V-import qualified Data.Map as M-import qualified Data.Set as S---- | Abstract namespaced tag.-data Tagged a = NoTag !a-              | Tagged !a !ByteString !ByteString-              deriving (Eq, Ord, Show)--instance Functor Tagged where-    fmap f (NoTag v) = NoTag (f v)-    fmap f (Tagged v ns t) = Tagged (f v) ns t--instance NFData a => NFData (Tagged a) where-    rnf (NoTag v) = rnf v-    rnf (Tagged v ns t) = rnf v `seq` rnf ns `seq` rnf t--type TaggedValue = Tagged Value--type EDNList = [TaggedValue] -- ^ An EDN list (head:tail container).-type EDNVec = V.Vector TaggedValue -- ^ An EDN vector (position-indexed container) of EDN values.-type EDNMap = M.Map Value TaggedValue -- ^ An EDN map (key/value container). Keys are untagged and values are tagged.-type EDNSet = S.Set TaggedValue -- ^ An EDN set (unordered container of unique values).---- | A \"raw\" EDN value represented as a Haskell value.-data Value = Nil-           | Boolean !Bool-           | String !Text-           | Character !Char-           | Symbol !ByteString !ByteString-           | Keyword !ByteString-           | Integer !Integer-           | Floating !Double-           | List EDNList-           | Vec !EDNVec-           | Map !EDNMap-           | Set !EDNSet-           deriving (Eq, Ord, Show)---- | Strings starting with \":\" will become keywords.-instance IsString Value where-  fromString (':':s) = Keyword . BS.pack $ s-  fromString s = String . T.pack $ s-  {-# INLINE fromString #-}---- | Strings will become an tagless EDN strings.-instance IsString (Tagged Value) where-  fromString = string . T.pack-  {-# INLINE fromString #-}--instance NFData Value where-  rnf (Map m) = rnf m-  rnf (Set s) = rnf s-  rnf (Vec v) = V.foldl' (\x y -> rnf y `seq` x) () v-  rnf (List l) = rnf l-  rnf (Floating f) = rnf f-  rnf (Integer i) = rnf i-  rnf (Symbol ns s) = rnf ns `seq` rnf s-  rnf (Keyword kw) = rnf kw-  rnf (String t) = rnf t-  rnf (Character c) = rnf c-  rnf (Boolean b) = rnf b-  rnf Nil = ()---- | Basic EDN nil.-nil :: TaggedValue-nil = NoTag Nil-{-# INLINE nil #-}---- | Basic EDN boolean.-bool :: Bool -> TaggedValue-bool = NoTag . Boolean-{-# INLINE bool #-}---- | Const EDN True.-true :: TaggedValue-true = bool True-{-# INLINE true #-}---- | Const EDN False.-false :: TaggedValue-false = bool False-{-# INLINE false #-}---- | Basic EDN character.-char :: Char -> TaggedValue-char = NoTag . Character-{-# INLINE char #-}---- | Basic EDN string.-string :: Text -> TaggedValue-string = NoTag . String-{-# INLINE string #-}---- | A namespaced symbol.-symbolNS :: ByteString -> ByteString -> TaggedValue-symbolNS ns value = NoTag $ Symbol ns value-{-# INLINE symbolNS #-}---- | \"Bare\" symbol.-symbol :: ByteString -> TaggedValue-symbol = symbolNS BS.empty-{-# INLINE symbol #-}---- | Basic EDN keyword.-keyword :: ByteString -> TaggedValue-keyword = NoTag . Keyword-{-# INLINE keyword #-}---- | Basic EDN integer.-integer :: Integer -> TaggedValue-integer = NoTag . Integer-{-# INLINE integer #-}---- | Basic EDN fp number.-floating :: Double -> TaggedValue-floating = NoTag . Floating-{-# INLINE floating #-}---- | Attach a namespaced tag to a value.-tag :: ByteString -> ByteString -> a -> Tagged a-tag ns t value = Tagged value ns t-{-# INLINE tag #-}---- | Wrap a value into tagless container.-notag :: a -> Tagged a-notag = NoTag-{-# INLINE notag #-}---- | Replace a tag on a 'Tagged' value.-setTag :: ByteString -> ByteString -> Tagged a -> Tagged a-setTag ns t (NoTag v) = tag ns t v-setTag ns t (Tagged v _ _) = tag ns t v-{-# INLINE setTag #-}---- | Extract namespace and tag from a tagged container. Will be a pair of 'BS.empty' for tagless containers.-getTag :: TaggedValue -> (ByteString, ByteString)-getTag (NoTag _) = (BS.empty, BS.empty)-getTag (Tagged _ ns t) = (ns, t)---- | Extract bare value from a tagged or tagless container.-stripTag :: Tagged a -> a-stripTag (NoTag v) = v-stripTag (Tagged v _ _) = v-{-# INLINE stripTag #-}---- | Create an EDN 'List' from a 'Value' list wrapping them into empty tags.-makeList :: [TaggedValue] -> Value-makeList = List-{-# INLINE makeList #-}---- | Create an EDN 'Vector' from a 'TaggedValue' list.-makeVec :: [TaggedValue] -> Value-makeVec = Vec . V.fromList-{-# INLINE makeVec #-}---- | Create an EDN 'Set' from a 'TaggedValue' list.-makeSet :: [TaggedValue] -> Value-makeSet = Set . S.fromList-{-# INLINE makeSet #-}---- | A key\/value pair for a EDN Map-type Pair = (Value, TaggedValue)---- | Create an EDN 'Map' from a assoc list with untagged keys and tagged values.-makeMap :: [Pair] -> Value-makeMap = Map . M.fromList-{-# INLINE makeMap #-}
− src/Data/EDN/Types/Class.hs
@@ -1,539 +0,0 @@-{-# LANGUAGE CPP                 #-}-{-# LANGUAGE FlexibleInstances   #-}-{-# LANGUAGE IncoherentInstances #-}-{-# LANGUAGE OverloadedStrings   #-}-module Data.EDN.Types.Class (-    -- * Type conversion-    ToEDN(..), FromEDN(..), fromEDN, fromEDNv,--    -- * EDN value decoding-    decode, eitherDecode, DP.parse, DP.parseEither, DP.parseMaybe, DP.Parser, DP.Result(..),--    -- * Convenience functions-    (.=), (.:), (.:?), (.!=), typeMismatch-) where--import Prelude ()-import Prelude.Compat--import           Control.Monad              (liftM2)-import qualified Data.ByteString.Char8      as BS-import qualified Data.ByteString.Lazy.Char8 as BSL-import qualified Data.Map                   as M-import           Data.Maybe                 (fromMaybe)-import           Data.Monoid                (First (..))-import qualified Data.Set                   as S-import qualified Data.Text                  as T-import qualified Data.Text.Encoding         as TE-import qualified Data.Text.Lazy             as TL-import qualified Data.Text.Lazy.Encoding    as TLE-import           Data.Time.Clock            (UTCTime)-#if MIN_VERSION_time(1,5,0)-import           Data.Time.Format           (formatTime, parseTimeM)-#else-import           Data.Time.Format           (formatTime, parseTime)-#endif-import           Data.Time.Locale.Compat    (defaultTimeLocale)-import qualified Data.Vector                as V--import qualified Data.EDN.Parser            as P-import qualified Data.EDN.Types             as E-import           Data.Parser                (Parser, Result)-import qualified Data.Parser                as DP---- | A type that can be converted to JSON.-class ToEDN a where-    {-# MINIMAL toEDN | toEDNv #-}--    toEDN :: a -> E.TaggedValue-    toEDN = E.notag . toEDNv-    {-# INLINE toEDN #-}--    toEDNv :: a -> E.Value-    toEDNv = E.stripTag . toEDN-    {-# INLINE toEDNv #-}---- | A type that can be converted from EDN, with a possibility of failure.------ When writing an instance, use 'empty', 'mzero', or 'fail' to make a--- conversion fail, e.g. if an 'M.Map' is missing a required key, or--- the value is of the wrong type.-class FromEDN a where-    {-# MINIMAL parseEDN | parseEDNv #-}--    parseEDN :: E.TaggedValue -> Parser a-    parseEDN = parseEDNv . E.stripTag-    {-# INLINE parseEDN #-}--    parseEDNv :: E.Value -> Parser a-    parseEDNv = parseEDN . E.notag-    {-# INLINE parseEDNv #-}--instance (ToEDN a) => ToEDN (Maybe a) where-    toEDN (Just a) = toEDN a-    toEDN Nothing = E.nil-    {-# INLINE toEDN #-}--instance (FromEDN a) => (FromEDN (Maybe a)) where-    parseEDN (E.NoTag E.Nil) = pure Nothing-    parseEDN a = Just <$> parseEDN a-    {-# INLINE parseEDN #-}--instance (ToEDN a, ToEDN b) => ToEDN (Either a b) where-    toEDN (Left a) = E.tag "either" "left" $ toEDNv a-    toEDN (Right b) = E.tag "either" "right" $ toEDNv b-    {-# INLINE toEDN #-}--instance (FromEDN a, FromEDN b) => FromEDN (Either a b) where-    parseEDN (E.Tagged v "either" "left") = Left <$> parseEDNv v-    parseEDN (E.Tagged v "either" "right") = Right <$> parseEDNv v-    parseEDN  E.Tagged{} = fail "incorrect tag"-    parseEDN (E.NoTag _) = fail "no tag"-    {-# INLINE parseEDN #-}--instance (ToEDN a) => ToEDN (E.Tagged a) where-    toEDN (E.Tagged v ns t) = E.setTag ns t $ toEDN v-    toEDN (E.NoTag v) = toEDN v-    {-# INLINE toEDN #-}--instance (FromEDN a) => FromEDN (E.Tagged a) where-   parseEDN (E.Tagged v ns t) = E.tag ns t <$> parseEDNv v-   parseEDN (E.NoTag v) = E.notag <$> parseEDNv v-   {-# INLINE parseEDN #-}--instance ToEDN Bool where-    toEDN = E.bool-    {-# INLINE toEDN #-}--instance FromEDN Bool where-    parseEDNv (E.Boolean b) = pure b-    parseEDNv v             = typeMismatch "Boolean" v-    {-# INLINE parseEDNv #-}--instance ToEDN () where-    toEDNv _ = E.List []-    {-# INLINE toEDNv #-}--instance FromEDN () where-    parseEDNv (E.List l) | null l = pure ()-    parseEDNv v = typeMismatch "()" v-    {-# INLINE parseEDNv #-}--instance ToEDN [Char] where-    toEDNv = E.String . T.pack-    {-# INLINE toEDNv #-}--instance FromEDN [Char] where-    parseEDNv (E.String t) = pure $ T.unpack t-    parseEDNv (E.Symbol "" s) = pure $ BS.unpack s-    parseEDNv (E.Symbol ns s) = pure . BS.unpack $ BS.concat [ns, "/", s]-    parseEDNv (E.Keyword k) = pure . BS.unpack $ BS.cons ':' k-    parseEDNv v = typeMismatch "String/Symbol/Keyword" v-    {-# INLINE parseEDNv #-}--instance ToEDN T.Text where-    toEDNv = E.String-    {-# INLINE toEDNv #-}--instance FromEDN T.Text where-    parseEDNv (E.String t) = pure t-    parseEDNv v = typeMismatch "String" v-    {-# INLINE parseEDNv #-}--instance ToEDN TL.Text where-    toEDNv = E.String . TL.toStrict-    {-# INLINE toEDNv #-}--instance FromEDN TL.Text where-    parseEDNv (E.String t) = pure $ TL.fromStrict t-    parseEDNv v = typeMismatch "String" v-    {-# INLINE parseEDNv #-}--instance ToEDN BS.ByteString where-    toEDNv = E.String . TE.decodeUtf8-    {-# INLINE toEDNv #-}--instance FromEDN BS.ByteString where-    parseEDNv (E.String t) = pure $ TE.encodeUtf8 t-    parseEDNv v = typeMismatch "String" v-    {-# INLINE parseEDNv #-}--instance ToEDN BSL.ByteString where-    toEDNv = E.String . TL.toStrict . TLE.decodeUtf8-    {-# INLINE toEDNv #-}--instance FromEDN BSL.ByteString where-    parseEDNv (E.String t) = pure . TLE.encodeUtf8 . TL.fromStrict $ t-    parseEDNv v = typeMismatch "String" v-    {-# INLINE parseEDNv #-}--instance ToEDN Char where-    toEDNv = E.Character-    {-# INLINE toEDNv #-}--instance FromEDN Char where-    parseEDNv (E.Character c) = pure c-    parseEDNv v = typeMismatch "Character" v-    {-# INLINE parseEDNv #-}--instance ToEDN Double where-    toEDNv = E.Floating-    {-# INLINE toEDNv #-}--instance FromEDN Double where-    parseEDNv (E.Floating d) = pure d-    parseEDNv v = typeMismatch "Floating" v-    {-# INLINE parseEDNv #-}--instance ToEDN Integer where-    toEDNv = E.Integer-    {-# INLINE toEDNv #-}--instance FromEDN Integer where-    parseEDNv (E.Integer i) = pure i-    parseEDNv v = typeMismatch "Integer" v-    {-# INLINE parseEDNv #-}--instance ToEDN Int where-    toEDNv = E.Integer . fromIntegral-    {-# INLINE toEDNv #-}--instance FromEDN Int where-    parseEDNv (E.Integer i) = return (fromIntegral i)-    parseEDNv v = typeMismatch "Int" v---showRFC3339 :: UTCTime -> String-showRFC3339 time =-    concat [fm "%FT%T." time-           ,  take 3 $ fm "%-q" time-           ,  "+00:00"]-  where-    fm = formatTime defaultTimeLocale--instance ToEDN UTCTime where-    toEDN time = E.Tagged (E.String . T.pack $ showRFC3339 time)-                          ""-                          "inst"-    {-# INLINE toEDN #-}--instance FromEDN UTCTime where-    parseEDN val@(E.Tagged (E.String ts) "" "inst") = do-        let result = getFirst . mconcat $ map (First . parseTime') validRFC3339-        case result of-          Just time -> return time-          Nothing -> typeMismatch "UTCTime" $ E.stripTag val-      where-        tsStr = T.unpack ts-#if MIN_VERSION_time(1,5,0)-        parseTime' fmt = parseTimeM True defaultTimeLocale fmt tsStr-#else-        parseTime' fmt = parseTime defaultTimeLocale fmt tsStr-#endif-        validRFC3339 = [ "%FT%T%Q%z"-                       , "%FT%T%QZ"-                       , "%FT%T%z"-                       , "%FT%TZ" ]-    parseEDN v = typeMismatch "UTCTime" $ E.stripTag v-    {-# INLINE parseEDN #-}---instance ToEDN a => ToEDN [a] where-    toEDNv = E.List . map toEDN-    {-# INLINE toEDNv #-}--instance FromEDN a => FromEDN [a] where-    parseEDNv (E.Vec vs) = V.toList <$> V.mapM parseEDN vs-    parseEDNv (E.List vs) = mapM parseEDN vs-    parseEDNv v = typeMismatch "List" v-    {-# INLINE parseEDNv #-}--instance ToEDN a => ToEDN (V.Vector a) where-    toEDNv = E.Vec . V.map toEDN-    {-# INLINE toEDNv #-}--instance FromEDN a => FromEDN (V.Vector a) where-    parseEDNv (E.Vec as) = V.mapM parseEDN as-    parseEDNv v = typeMismatch "Vec" v-    {-# INLINE parseEDNv #-}--instance (Ord a, ToEDN a) => ToEDN (S.Set a) where-    toEDNv = E.Set . S.map toEDN-    {-# INLINE toEDNv #-}--instance (Ord a, FromEDN a) => FromEDN (S.Set a) where-    parseEDNv (E.Set s) = mapMset parseEDN s-    parseEDNv v = typeMismatch "Set" v-    {-# INLINE parseEDNv #-}--instance (ToEDN a, ToEDN b) => ToEDN (M.Map a b) where-    toEDNv m = E.Map $! M.fromList [(toEDNv k, toEDN v) | (k, v) <- M.assocs m]-    {-# INLINE toEDNv #-}--instance (Ord a, FromEDN a, FromEDN b) => FromEDN (M.Map a b) where-    parseEDNv (E.Map m) = mapMmap parseEDNv parseEDN m-    parseEDNv v = typeMismatch "Map" v-    {-# INLINE parseEDNv #-}--instance (ToEDN a, ToEDN b) => ToEDN (a, b) where-    toEDNv (a, b) = E.Vec $! V.fromList [toEDN a, toEDN b]-    {-# INLINE toEDNv #-}--instance (FromEDN a, FromEDN b) => FromEDN (a, b) where-    parseEDNv v@(E.Vec vec)-      | V.length vec == 2 = (,) <$> parseEDN (vec V.! 0)-                                <*> parseEDN (vec V.! 1)-      | otherwise = typeMismatch "(a, b)" v-    parseEDNv v = typeMismatch "(a, b)" v-    {-# INLINE parseEDNv #-}--instance (ToEDN a, ToEDN b, ToEDN c) => ToEDN (a, b, c) where-    toEDNv (a, b, c) = E.Vec $! V.fromList [toEDN a, toEDN b, toEDN c]-    {-# INLINE toEDNv #-}--instance (FromEDN a, FromEDN b, FromEDN c) => FromEDN (a, b, c) where-    parseEDNv v@(E.Vec vec)-      | V.length vec == 3 = (,,) <$> parseEDN (vec V.! 0)-                                 <*> parseEDN (vec V.! 1)-                                 <*> parseEDN (vec V.! 2)-      | otherwise = typeMismatch "(a, b, c)" v-    parseEDNv v = typeMismatch "(a, b, c)" v-    {-# INLINE parseEDNv #-}--instance (ToEDN a, ToEDN b, ToEDN c, ToEDN d) => ToEDN (a, b, c, d) where-    toEDNv (a, b, c, d) = E.Vec $! V.fromList [toEDN a, toEDN b, toEDN c, toEDN d]-    {-# INLINE toEDNv #-}--instance (FromEDN a, FromEDN b, FromEDN c, FromEDN d) => FromEDN (a, b, c, d) where-    parseEDNv v@(E.Vec vec)-      | V.length vec == 4 = (,,,) <$> parseEDN (vec V.! 0)-                                  <*> parseEDN (vec V.! 1)-                                  <*> parseEDN (vec V.! 2)-                                  <*> parseEDN (vec V.! 3)-      | otherwise = typeMismatch "(a, b, c, d)" v-    parseEDNv v = typeMismatch "(a, b, c, d)" v-    {-# INLINE parseEDNv #-}--instance (ToEDN a, ToEDN b, ToEDN c, ToEDN d, ToEDN e) => ToEDN (a, b, c, d, e) where-    toEDNv (a, b, c, d, e) = E.Vec $! V.fromList [-                               toEDN a-                             , toEDN b-                             , toEDN c-                             , toEDN d-                             , toEDN e ]-    {-# INLINE toEDNv #-}--instance (FromEDN a, FromEDN b, FromEDN c, FromEDN d, FromEDN e)-    => FromEDN (a, b, c, d, e) where-    parseEDNv v@(E.Vec vec)-      | V.length vec == 5 = (,,,,) <$> parseEDN (vec V.! 0)-                                   <*> parseEDN (vec V.! 1)-                                   <*> parseEDN (vec V.! 2)-                                   <*> parseEDN (vec V.! 3)-                                   <*> parseEDN (vec V.! 4)-      | otherwise = typeMismatch "(a, b, c, d, e)" v-    parseEDNv v = typeMismatch "(a, b, c, d, e)" v-    {-# INLINE parseEDNv #-}--instance (ToEDN a, ToEDN b, ToEDN c, ToEDN d, ToEDN e, ToEDN f)-    => ToEDN (a, b, c, d, e, f) where-    toEDNv (a, b, c, d, e, f) = E.Vec $! V.fromList [-                               toEDN a-                             , toEDN b-                             , toEDN c-                             , toEDN d-                             , toEDN e-                             , toEDN f]-    {-# INLINE toEDNv #-}--instance (FromEDN a, FromEDN b, FromEDN c, FromEDN d, FromEDN e, FromEDN f)-    => FromEDN (a, b, c, d, e, f) where-    parseEDNv v@(E.Vec vec)-      | V.length vec == 6 = (,,,,,) <$> parseEDN (vec V.! 0)-                                    <*> parseEDN (vec V.! 1)-                                    <*> parseEDN (vec V.! 2)-                                    <*> parseEDN (vec V.! 3)-                                    <*> parseEDN (vec V.! 4)-                                    <*> parseEDN (vec V.! 5)-      | otherwise = typeMismatch "(a, b, c, d, e, f)" v-    parseEDNv v = typeMismatch "(a, b, c, d, e, f)" v-    {-# INLINE parseEDNv #-}--instance (ToEDN a, ToEDN b, ToEDN c, ToEDN d, ToEDN e, ToEDN f, ToEDN g)-    => ToEDN (a, b, c, d, e, f, g) where-    toEDNv (a, b, c, d, e, f, g) =-        E.Vec $! V.fromList [ toEDN a-                            , toEDN b-                            , toEDN c-                            , toEDN d-                            , toEDN e-                            , toEDN f-                            , toEDN g]-    {-# INLINE toEDNv #-}--instance (FromEDN a, FromEDN b, FromEDN c, FromEDN d, FromEDN e, FromEDN f, FromEDN g)-    => FromEDN (a, b, c, d, e, f, g) where-    parseEDNv v@(E.Vec vec)-      | V.length vec == 7 = (,,,,,,) <$> parseEDN (vec V.! 0)-                                     <*> parseEDN (vec V.! 1)-                                     <*> parseEDN (vec V.! 2)-                                     <*> parseEDN (vec V.! 3)-                                     <*> parseEDN (vec V.! 4)-                                     <*> parseEDN (vec V.! 5)-                                     <*> parseEDN (vec V.! 6)-      | otherwise = typeMismatch "(a, b, c, d, e, f, g)" v-    parseEDNv v = typeMismatch "(a, b, c, d, e, f, g)" v-    {-# INLINE parseEDNv #-}--instance (ToEDN a, ToEDN b, ToEDN c, ToEDN d, ToEDN e, ToEDN f, ToEDN g, ToEDN h)-    => ToEDN (a, b, c, d, e, f, g, h) where-    toEDNv (a, b, c, d, e, f, g, h) =-        E.Vec $! V.fromList [ toEDN a-                            , toEDN b-                            , toEDN c-                            , toEDN d-                            , toEDN e-                            , toEDN f-                            , toEDN g-                            , toEDN h]-    {-# INLINE toEDNv #-}--instance (FromEDN a, FromEDN b, FromEDN c, FromEDN d,-          FromEDN e, FromEDN f, FromEDN g, FromEDN h)-    => FromEDN (a, b, c, d, e, f, g, h) where-    parseEDNv v@(E.Vec vec)-      | V.length vec == 8 = (,,,,,,,) <$> parseEDN (vec V.! 0)-                                      <*> parseEDN (vec V.! 1)-                                      <*> parseEDN (vec V.! 2)-                                      <*> parseEDN (vec V.! 3)-                                      <*> parseEDN (vec V.! 4)-                                      <*> parseEDN (vec V.! 5)-                                      <*> parseEDN (vec V.! 6)-                                      <*> parseEDN (vec V.! 7)-      | otherwise = typeMismatch "(a, b, c, d, e, f, g, h)" v-    parseEDNv v = typeMismatch "(a, b, c, d, e, f, g, h)" v-    {-# INLINE parseEDNv #-}---instance ToEDN E.Value where-    toEDNv = id--instance FromEDN E.Value where-    parseEDNv = pure--instance ToEDN E.TaggedValue where-    toEDN = id--instance FromEDN E.TaggedValue where-    parseEDN = pure---- | Convert a value from 'E.TaggedValue', failing if the types do not match.-fromEDN :: FromEDN a => E.TaggedValue -> Result a-fromEDN = DP.parse parseEDN-{-# INLINE fromEDN #-}---- | Convert a value from 'E.Value', failing if the types do not match.-fromEDNv :: FromEDN a => E.Value -> Result a-fromEDNv = DP.parse parseEDNv-{-# INLINE fromEDNv #-}---- | Deserializes a EDN value from a lazy 'BSL.ByteString'.--- If this fails to to incomplete or invalid input, 'Nothing' is returned.-decode :: FromEDN a => BSL.ByteString -> Maybe a-decode s = case P.parseMaybe s of-    Just tv -> DP.parseMaybe parseEDN tv-    Nothing -> Nothing---- | Deserializes a EDN value from a lazy 'BSL.ByteString'.--- If this fails to to incomplete or invalid input, 'Left' is returned--- with an error message.-eitherDecode :: FromEDN a => BSL.ByteString -> Either String a-eitherDecode s = case P.parseEither s of-   Right tv -> DP.parseEither parseEDN tv-   Left e -> Left e---- | Construct a 'Pair' from a key (as EDN keyword) and a value.-(.=) :: ToEDN a => BS.ByteString -> a -> E.Pair-name .= value = (E.Keyword name, toEDN value)-{-# INLINE (.=) #-}---- | Retrieve the value associated with the given key of an 'E.EDNMap'.--- The result is 'empty' if the key is not present or the value cannot--- be converted to the desired type.------ This accessor is appropriate if the key and value /must/ be present--- in an object for it to be valid. If the key and value are--- optional, use '(.:?)' instead.-(.:) :: (Show k, ToEDN k, FromEDN a) => E.EDNMap -> k -> Parser a-emap .: key = case M.lookup (toEDNv key) emap of-                  Nothing -> fail $ "key " ++ show key ++ " not present"-                  Just v -> parseEDN v-{-# INLINE (.:) #-}---- | Retrieve the value associated with the given key of an 'E.EDNMap'.--- The result is 'Nothing' if the key is not present, or 'empty' if--- the value cannot be converted to the desired type.------ This accessor is most useful if the key and value can be absent--- from an object without affecting its validity.  If the key and--- value are mandatory, use '(.:)' instead.-(.:?) :: (ToEDN k, FromEDN a) => E.EDNMap -> k -> Parser (Maybe a)-emap .:? key = case M.lookup (toEDNv key) emap of-                   Nothing -> pure Nothing-                   Just v -> parseEDN v-{-# INLINE (.:?) #-}---- | Helper for use in combination with '.:?' to provide default--- values for optional JSON object fields.------ This combinator is most useful if the key and value can be absent--- from an object without affecting its validity and we know a default--- value to assign in that case.  If the key and value are mandatory,--- use '(.:)' instead.------ Example usage:------ @ v1 <- o '.:?' \"opt_field_with_dfl\" .!= \"default_val\"--- v2 <- o '.:'  \"mandatory_field\"--- v3 <- o '.:?' \"opt_field2\"--- @-(.!=) :: Parser (Maybe a) -> a -> Parser a-pmval .!= val = fromMaybe val <$> pmval-{-# INLINE (.!=) #-}---- | Fail parsing due to a type mismatch, with a descriptive message.-typeMismatch :: String -- ^ The name of the type you are trying to parse.-             -> E.Value -- ^ The actual value encountered.-             -> Parser a-typeMismatch expected actual =-    fail $ "when expecting a " ++ expected ++ ", encountered " ++ name ++-           " instead"-  where-    name = case actual of-        E.Nil           -> "Nil"-        E.Boolean   _   -> "Boolean"-        E.String    _   -> "String"-        E.Character _   -> "Character"-        E.Symbol    _ _ -> "Symbol"-        E.Keyword   _   -> "Keyword"-        E.Integer   _   -> "Integer"-        E.Floating  _   -> "Floating"-        E.List      _   -> "List"-        E.Vec       _   -> "Vec"-        E.Map       _   -> "Map"-        E.Set       _   -> "Set"--mapMset :: (Applicative m, Monad m, Ord b)-        => (a -> m b)-        -> S.Set a-        -> m (S.Set b)-mapMset f s = S.fromList <$> traverse f (S.toList s)-{-# INLINE mapMset #-}--mapMmap :: (Ord a2, Monad m)-        => (a1 -> m a2)-        -> (b1 -> m b2)-        -> M.Map a1 b1-        -> m (M.Map a2 b2)-mapMmap kf vf = fmap M.fromList . mapM (\(k, v) -> liftM2 (,) (kf k) (vf v)) . M.assocs-{-# INLINE mapMmap #-}
− src/Data/Parser.hs
@@ -1,139 +0,0 @@-{-# LANGUAGE Rank2Types, DeriveDataTypeable #-}---- | Generic continuation-based parser type.--module Data.Parser (-    Parser, Result(..),-    parse, parseMaybe, parseEither-) where--import Prelude ()-import Prelude.Compat--import Control.Applicative-import Control.DeepSeq (NFData(..))-import Control.Monad.State.Strict-import Data.Semigroup (Semigroup, (<>))-import Data.Typeable (Typeable)---- | The result of running a 'Parser'.-data Result a = Error String-              | Success a-                deriving (Eq, Show, Typeable)--instance (NFData a) => NFData (Result a) where-    rnf (Success a) = rnf a-    rnf (Error err) = rnf err--instance Functor Result where-    fmap f (Success a) = Success (f a)-    fmap _ (Error err) = Error err-    {-# INLINE fmap #-}--instance Monad Result where-    return = Success-    {-# INLINE return #-}-    Success a >>= k = k a-    Error err >>= _ = Error err-    {-# INLINE (>>=) #-}--instance Applicative Result where-    pure  = return-    {-# INLINE pure #-}-    (<*>) = ap-    {-# INLINE (<*>) #-}--instance MonadPlus Result where-    mzero = fail "mzero"-    {-# INLINE mzero #-}-    mplus a@(Success _) _ = a-    mplus _ b             = b-    {-# INLINE mplus #-}--instance Alternative Result where-    empty = mzero-    {-# INLINE empty #-}-    (<|>) = mplus-    {-# INLINE (<|>) #-}--instance Semigroup (Result a) where-    (<>) = mplus--instance Monoid (Result a) where-    mempty  = fail "mempty"-    {-# INLINE mempty #-}-    mappend = (<>)-    {-# INLINE mappend #-}---- | Failure continuation.-type Failure f r   = String -> f r--- | Success continuation.-type Success a f r = a -> f r---- | A continuation-based parser type.-newtype Parser a = Parser {-      runParser :: forall f r.-                   Failure f r-                -> Success a f r-                -> f r-    }--instance Monad Parser where-    m >>= g = Parser $ \kf ks -> let ks' a = runParser (g a) kf ks-                                 in runParser m kf ks'-    {-# INLINE (>>=) #-}-    return a = Parser $ \_kf ks -> ks a-    {-# INLINE return #-}-    fail msg = Parser $ \kf _ks -> kf msg-    {-# INLINE fail #-}--instance Functor Parser where-    fmap f m = Parser $ \kf ks -> let ks' a = ks (f a)-                                  in runParser m kf ks'-    {-# INLINE fmap #-}--instance Applicative Parser where-    pure  = return-    {-# INLINE pure #-}-    (<*>) = apP-    {-# INLINE (<*>) #-}--instance Alternative Parser where-    empty = fail "empty"-    {-# INLINE empty #-}-    (<|>) = mplus-    {-# INLINE (<|>) #-}--instance MonadPlus Parser where-    mzero = fail "mzero"-    {-# INLINE mzero #-}-    mplus a b = Parser $ \kf ks -> let kf' _ = runParser b kf ks-                                   in runParser a kf' ks-    {-# INLINE mplus #-}--instance Semigroup (Parser a) where-    (<>) = mplus--instance Monoid (Parser a) where-    mempty  = fail "mempty"-    {-# INLINE mempty #-}-    mappend = (<>)-    {-# INLINE mappend #-}--apP :: Parser (a -> b) -> Parser a -> Parser b-apP d e = do-  b <- d-  b <$> e-{-# INLINE apP #-}---- | Run a 'Parser'.-parse :: (a -> Parser b) -> a -> Result b-parse m v = runParser (m v) Error Success---- | Run a 'Parser' with a 'Maybe' result type.-parseMaybe :: (a -> Parser b) -> a -> Maybe b-parseMaybe m v = runParser (m v) (const Nothing) Just---- | Run a 'Parser' with an 'Either' result type.-parseEither :: (a -> Parser b) -> a -> Either String b-parseEither m v = runParser (m v) Left Right
+ tests/Data/EDN/AST/Gen.hs view
@@ -0,0 +1,159 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.EDN.AST.Gen+  ( module Data.EDN.AST.Gen+  ) where++import Data.Text (Text)+import Data.Function (on)+import Data.List (nubBy)+import Hedgehog (Gen)++import qualified Data.Text as Text+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range++import qualified Data.EDN.AST.Types as EDN+import qualified Data.EDN.AST.Parser as EDN++genTaggedValue :: Gen EDN.TaggedValue+genTaggedValue = genTagged genValue++genTagged :: Gen a -> Gen (EDN.Tagged Text a)+genTagged gen = Gen.choice+  [ withoutTag+  , withNS+  , withoutNS+  ]+  where+    withNS = EDN.Tagged+      <$> genTagIdent+      <*> genTagIdent+      <*> gen++    -- XXX: the spec says only some raw names are valid+    withoutNS = EDN.Tagged+      <$> pure ""+      <*> genTagIdent+      <*> gen+    +    withoutTag = EDN.NoTag <$> gen++genTagIdent :: Gen Text+genTagIdent = genText EDN.tagChars++genValue :: Gen EDN.Value+genValue = Gen.recursive Gen.choice atomic collections++atomic :: [Gen EDN.Value]+atomic =+  [ genNil+  , genBool+  , genString+  , genChar+  , genInteger+  , genFloating+  , genKeyword+  , genSymbol+  ]++collections :: [Gen EDN.Value]+collections =+  [ genSet+  , genMap+  , genVector+  , genList+  ]++genBool :: Gen EDN.Value+genBool = EDN.Boolean+  <$> Gen.element [True, False]++genNil :: Gen EDN.Value+genNil = pure EDN.Nil++genString :: Gen EDN.Value+genString = EDN.String+  <$> Gen.text (Range.exponential 4 16) Gen.unicode++genChar :: Gen EDN.Value+genChar = EDN.Character+  <$> Gen.unicode++genInteger :: Gen EDN.Value+genInteger = (EDN.Integer . fromIntegral)+  <$> Gen.int64 Range.exponentialBounded++genFloating :: Gen EDN.Value+genFloating = EDN.Floating+  <$> Gen.double (Range.exponentialFloat (-1e-308) 1e308) -- XXX: 🤷++genKeyword :: Gen EDN.Value+genKeyword = EDN.Keyword+  <$> genKeywordName++genKeywordName :: Gen Text+genKeywordName = genTextInitial EDN.keywordInitialChars EDN.keywordChars++genSymbol :: Gen EDN.Value+genSymbol = EDN.Symbol+  <$> genSymbolName+  <*> genSymbolName++genSymbolName :: Gen Text+genSymbolName = genTextInitial EDN.symbolInitialChars EDN.symbolChars++genSet :: Gen EDN.Value+genSet = EDN.mkSet+  <$> genNubList (0, 16) 0 id genTaggedValue++genMap :: Gen EDN.Value+genMap = do+  keys <- genNubList (0, 16) 0 id genMapKey+  values <- Gen.list (Range.singleton $ length keys) genTaggedValue+  pure . EDN.mkMap $ zip keys values++genMapKey :: Gen EDN.TaggedValue+genMapKey = genTagged $ Gen.choice atomic++genVector :: Gen EDN.Value+genVector = EDN.mkVec+  <$> Gen.list (Range.exponential 0 16) genTaggedValue++genList :: Gen EDN.Value+genList = EDN.mkList+  <$> Gen.list (Range.exponential 0 16) genTaggedValue++-- * Helpers++genText :: [Char] -> Gen Text+genText = Gen.text (Range.exponential 4 16) . Gen.element++genTextInitial :: [Char] -> [Char] -> Gen Text+genTextInitial initial chars = Gen.choice+  [ genSingleton+  , genLong'ish+  ]+  where+    genSingleton = Text.singleton+      <$> Gen.element initial++    genLong'ish = Text.cons+      <$> Gen.element initial+      <*> genText chars++genNubList+  :: Eq key+  => (Int, Int)+  -> Int+  -> (a -> key)+  -> Gen a+  -> Gen [a]+genNubList (from, to) atLeast key gen = do+  items <- Gen.list (Range.exponential from to) gen+  let unique = nubBy ((==) `on` key) items+  if length unique < atLeast+    then+      Gen.discard+    else+      pure $ take to unique
+ tests/Data/EDN/AST/Test.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}++module Data.EDN.AST.Test+  ( tests+  ) where++import Hedgehog+  ( Group, Property+  , discover, forAll, property, tripping, withTests+  , (===)+  )++import Data.Text (Text)++import qualified Data.EDN.AST.Types as EDN+import qualified Data.EDN.AST.Gen as EDN+import qualified Data.EDN.AST.Parser as EDN+import qualified Data.EDN.AST.Printer as EDN++tests :: Group+tests = $$(discover)++type ASTParser = Text -> Either String EDN.TaggedValue++prop_generated :: Property+prop_generated = property $ do+  tv <- forAll EDN.genTaggedValue+  tripping tv EDN.renderText (EDN.parseText "<generated>" :: ASTParser)++prop_regr_empty_containers :: Property+prop_regr_empty_containers = withTests 1 . property $ do+  emptyList <- EDN.parseText "<regr_empty_containers>" "( )"+  emptyList === EDN.NoTag (EDN.List mempty)++  emptyVec <- EDN.parseText "<regr_empty_containers>" "[ ]"+  emptyVec === EDN.NoTag (EDN.Vec mempty)++  emptySet <- EDN.parseText "<regr_empty_containers>" "#{ }"+  emptySet === EDN.NoTag (EDN.Set mempty)++  emptyMap <- EDN.parseText "<regr_empty_containers>" "{ }"+  emptyMap === EDN.NoTag (EDN.Map mempty)
+ tests/Data/EDN/Class/Gen.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE OverloadedStrings #-}++-- TODO: generate values for types with EDN instances++module Data.EDN.Class.Gen+  (+  ) where
+ tests/Data/EDN/Class/Test.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}++module Data.EDN.Class.Test+  ( tests+  ) where++import Hedgehog+  ( Group, Property+  , discover, property, withTests+  , tripping+  , (===)+  )+import Data.Text (Text)+import Data.Time (UTCTime, defaultTimeLocale, parseTimeOrError)+import Data.UUID (UUID)+import Data.Void (Void)++import qualified Data.Vector as Vector+import qualified Data.Set as Set+import qualified Data.Map as Map++import Data.EDN.Class (ToEDN(..), FromEDN(..), toEDNtagged, fromEDN)++import qualified Data.EDN as EDN++tests :: Group+tests = $$(discover)++type Example = Property++codecExample :: forall a. (ToEDN a, FromEDN a, Eq a, Show a) => Text -> a -> Example+codecExample encoded value = withTests 1 . property $ do+  tripping value toEDN (fromEDN :: EDN.TaggedValue -> Either String a)+  EDN.renderText (toEDN value) === encoded++-- * Atomic types++-- ** The Singleton Value++prop_nil_unit :: Example+prop_nil_unit = codecExample "nil" ()++-- ** Bools++prop_bool_true :: Example+prop_bool_true = codecExample "true" True++prop_bool_false :: Example+prop_bool_false = codecExample "false" False++-- ** Strings++prop_text :: Example+prop_text = codecExample+  "\"stringly \\\"typed\\\" languages\""+  ("stringly \"typed\" languages" :: Text)++-- ** Characters++prop_char_unicode_hex :: Example+prop_char_unicode_hex = codecExample "\\uA650" 'Ꙑ'++prop_char_named_WS :: Example+prop_char_named_WS = codecExample "\\space" ' '++prop_char_basic :: Example+prop_char_basic = codecExample "\\n" 'n'++-- ** Numeric++prop_int :: Example+prop_int = codecExample "42" (42 :: Int)++prop_double :: Example+prop_double = codecExample "42.0" (42.0 :: Double)++-- * Containers++items :: [EDN.Value]+items =+  [ toEDNv (1 :: Int)+  , toEDNv True+  , EDN.Symbol "sort-of" "hetero"+  ]++prop_nil_maybe :: Example+prop_nil_maybe = codecExample "nil" (Nothing :: Maybe Void)++prop_list_empty :: Example+prop_list_empty = codecExample "()" ([] :: [Void])++prop_list :: Example+prop_list = codecExample "(1 true sort-of/hetero)" items++prop_vector_empty :: Example+prop_vector_empty = codecExample "[]" (mempty :: Vector.Vector Void)++prop_vector :: Example+prop_vector = codecExample "[1 true sort-of/hetero]" $ Vector.fromList items++prop_set_empty :: Example+prop_set_empty = codecExample "#{}" (mempty :: Set.Set Void)++prop_set :: Example+prop_set = codecExample "#{true sort-of/hetero 1}" $ Set.fromList items++prop_map_empty :: Example+prop_map_empty = codecExample "{}" (mempty :: Map.Map () ())++prop_map :: Example+prop_map = codecExample "{the/question \\? :the-answer 42.0}" $ Map.fromList+  [ ( EDN.Keyword "the-answer"+    , EDN.Floating 42.0+    )+  , ( EDN.Symbol "the" "question"+    , EDN.Character '?'+    )+  ]++-- ** Tuples++prop_tuple_2 :: Example+prop_tuple_2 = codecExample "[true nil]" (True, ())++prop_tuple_3 :: Example+prop_tuple_3 = codecExample "[true nil \\space]" (True, (), ' ')++prop_tuple_4 :: Example+prop_tuple_4 = codecExample "[true nil \\space ()]" (True, (), ' ', [] :: [Void])++-- * Tagged++prop_tagged_utctime :: Example+prop_tagged_utctime = codecExample+  "#inst \"1985-04-12T23:20:50.52Z\""+  (parseTimeOrError+    False+    defaultTimeLocale+    "%Y-%m-%dT%H:%M:%S%Q%Z"+    "1985-04-12T23:20:50.52Z"+    :: UTCTime+  )++prop_tagged_uuid :: Example+prop_tagged_uuid = codecExample+  "#uuid \"f81d4fae-7dec-11d0-a765-00a0c91e6bf6\""+  (read "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" :: UUID)++-- ** User type++data Person = Person+  { personFirst :: Text+  , personLast  :: Text+  } deriving (Eq, Show)++instance ToEDN Person where+  toEDN Person{..} =+    toEDNtagged "myapp" "Person" $ Map.fromList+      [ (EDN.Keyword "first", toEDN personFirst)+      , (EDN.Keyword "last", toEDN personLast)+      ]++instance FromEDN Person where+  parseEDN =+    EDN.withTagged "myapp" "Person" . EDN.withMap $ \m -> Person+      <$> EDN.mapGetKeyword "first" m+      <*> EDN.mapGetKeyword "last" m++prop_tagged_person :: Example+prop_tagged_person = codecExample+  "#myapp/Person {:first \"Fred\" :last \"Mertz\"}"+  (Person "Fred" "Mertz")++-- * Expected stuff++prop_unexpected :: Property+prop_unexpected = property $ do+  uuid <- EDN.decodeText "Test.hs:184" "#uuid \"f81d4fae-7dec-11d0-a765-00a0c91e6bf6\""+  uuid === (read "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" :: UUID)
+ tests/Data/EDN/QQ/Test.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Data.EDN.QQ.Test+  ( tests+  ) where++import Hedgehog (Group, Property, discover, property, withTests, (===))++import qualified Data.EDN as EDN+import qualified Data.EDN.QQ as QQ++prop_tagged_value :: Property+prop_tagged_value = withTests 1 . property $+  EDN.Tagged "test" "tagged" EDN.Nil === [QQ.edn| #test/tagged nil |]++prop_list :: Property+prop_list = withTests 1 . property $ do+  EDN.mkList items === [QQ.ednList|+    false,+    42+    #my/tagged symbol+  |]++prop_vector :: Property+prop_vector = withTests 1 . property $ do+  EDN.mkVec items === [QQ.ednVec|+    false,+    42+    #my/tagged symbol+  |]++prop_set :: Property+prop_set = withTests 1 . property $ do+  EDN.mkSet items === [QQ.ednSet|+    false,+    42+    #my/tagged symbol+  |]++prop_map :: Property+prop_map = withTests 1 . property $ do+  EDN.mkMap pairs === [QQ.ednMap|+    this/is false,+    :the-answer 42+    #my/tagged key value+  |]+  where+    pairs =+      [ ( EDN.NoTag $ EDN.Symbol "this" "is"+        , EDN.NoTag $ EDN.Boolean False)+      , ( EDN.NoTag $ EDN.Keyword "the-answer"+        , EDN.NoTag $ EDN.Integer 42+        )+      , ( EDN.Tagged "my" "tagged" $ EDN.Symbol "" "key"+        , EDN.NoTag $ EDN.Symbol "" "value"+        )+      ]++items :: [EDN.TaggedValue]+items =+  [ EDN.NoTag $ EDN.Boolean False+  , EDN.NoTag $ EDN.Integer 42+  , EDN.Tagged "my" "tagged" $ EDN.Symbol "" "symbol"+  ]++-- XXX: type application must be in some other module.+-- prop_from_value :: Property+-- prop_from_value = withTests 1 . property $ do+--   () === [fromEDN @()| nil |]++tests :: Group+tests = $$(discover)
− tests/Data/EDN/Test/QuickCheck.hs
@@ -1,74 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Data.EDN.Test.QuickCheck where--import           Data.EDN.Test.QuickCheck.TH-import           Data.Time                   (UTCTime)--import qualified Data.Time.Calendar          as Calendar-import           Data.Time.Clock             (UTCTime (..), UniversalTime (..),-                                              secondsToDiffTime)-import           Data.Time.LocalTime         (localTimeToUT1, localTimeToUTC,-                                              ut1ToLocalTime, utc,-                                              utcToLocalTime)--import qualified Test.QuickCheck             as QC---- * UTCTime Arbitrary instance --------------------------------------------------fromGregorian :: Integer -> Int -> Int -> Int -> Int -> Int -> UTCTime-fromGregorian year month day hours minutes seconds =-    UTCTime day' (secondsToDiffTime . fromIntegral $ seconds')-  where-    day'     = Calendar.fromGregorian year month day-    seconds' = 3600 * hours + 60 * minutes + seconds--fromUniversalTime :: UniversalTime -> UTCTime-fromUniversalTime = localTimeToUTC utc . ut1ToLocalTime 0--toUniversalTime :: UTCTime -> UniversalTime-toUniversalTime = localTimeToUT1 0 . utcToLocalTime utc--fromGregorian' :: Integer -> Int -> Int -> UTCTime-fromGregorian' y m d = fromGregorian y m d 0 0 0--startOfTime :: UTCTime-startOfTime = fromGregorian' 1970 1 1--fromMJD :: Rational -> UTCTime-fromMJD = fromUniversalTime . ModJulianDate--fromMJD' :: Float -> UTCTime-fromMJD' = fromMJD . realToFrac--startOfTimeMJD :: Rational-startOfTimeMJD = toMJD startOfTime--toMJD :: UTCTime -> Rational-toMJD = getModJulianDate . toUniversalTime--instance QC.Arbitrary UTCTime where-    -- coarbitrary = undefined-    arbitrary = do-      offset <- QC.choose (0, 20000) :: QC.Gen Float-      return . fromMJD' $ offset + fromRational startOfTimeMJD------------------------------------------------------------------------------------type MUTCTime = Maybe UTCTime-type Tuple  = (Int, Int)-type Triple = (Int, String, UTCTime)-type Tuple4 = (Int, String, [Maybe UTCTime], [Maybe Bool])-type Tuple5 = (Int, [String], [Maybe UTCTime], [Maybe Bool], (Bool, Bool))--$(mkSerializeSpecs [-                   -- ''Char    -- encode/decode are not isomorphic with Char-                   --  ''Double -- encode/decode are not isomorphic with Double-                   -- ''String-                     ''UTCTime-                   , ''MUTCTime-                   -- , ''Tuple-                   -- , ''Triple-                   -- , ''Tuple4-                   -- , ''Tuple5-                   ])
− tests/Data/EDN/Test/QuickCheck/TH.hs
@@ -1,60 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-module Data.EDN.Test.QuickCheck.TH where--import           Control.Applicative   ((<$>), (<*>))-import           Language.Haskell.TH-import           Test.Hspec            (Spec, describe)-import           Test.Hspec.QuickCheck (prop)----import qualified Data.EDN              as E------------------------------------------------------------------------------------serializeProperty :: (Eq a, E.ToEDN a, E.FromEDN a) => a -> Bool-serializeProperty object =-  case decoded of-    Just result -> result == object-    Nothing -> False-  where-      encoded = E.encode object-      decoded = E.decode encoded--getTestPropertyName :: Name -> Name-getTestPropertyName qualTypeName =-    mkName $ "test" ++ nameBase qualTypeName ++ "Serialization"--mkEDNSerializeSpecGroup :: [Name] -> Q [Dec]-mkEDNSerializeSpecGroup names = do-    let fnName = mkName "ednSerializationProperties"-    specType <- [t| Spec |]-    describeFn <- [| describe |]-    return [ SigD fnName specType-           , ValD (VarP fnName)-                  (NormalB (AppE (AppE describeFn-                                      (LitE (StringL "EDN.Serialization")))-                                 (DoE (map (NoBindS . VarE . getTestPropertyName)-                                           names)))) []]--mkSerializeSpec :: Name -> Q [Dec]-mkSerializeSpec qualTypeName = do-    let typeName = nameBase qualTypeName-        fnName = getTestPropertyName qualTypeName-    propFn <- [| prop |]-    serializePropertyFn <- [| serializeProperty |]-    boolType <- [t| Bool |]-    specType <- [t| Spec |]-    return [ SigD fnName specType-           , ValD (VarP fnName)-                    (NormalB (AppE-                      (AppE propFn-                        (LitE (StringL $ typeName ++ " serialization/deserialization")))-                      (SigE serializePropertyFn-                        (AppT (AppT ArrowT (ConT qualTypeName))-                              boolType)))) []]--mkSerializeSpecs :: [Name] -> Q [Dec]-mkSerializeSpecs types =-    (++) <$> (concat `fmap` mapM mkSerializeSpec types)-         <*> mkEDNSerializeSpecGroup types
− tests/Data/EDN/Test/Unit.hs
@@ -1,238 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ExistentialQuantification #-}-module Data.EDN.Test.Unit (tests) where--import Test.HUnit--import qualified Data.ByteString.Lazy.Char8 as BSL-import qualified Data.ByteString.Char8 as BS-import qualified Data.Text.Lazy as TL-import qualified Data.Text as T--import qualified Data.Vector as V-import qualified Data.Set as S-import qualified Data.Map as M--import Data.EDN (encode, decode, ToEDN, toEDN, FromEDN, fromEDN)-import Data.EDN.Types as E-import Data.EDN.Types.Class (Result(..), (.=))-import Data.EDN.Parser (parseMaybe)--tests :: Test-tests = TestList [ TestLabel "BSL -> TV parser" $ TestList $ map makeParserCase parserCases-                 , TestLabel "TV -> BSL encoder" $ TestList $ map makeEncodeCase encodeCases-                 , TestLabel "ToEDN conversion" $ TestList $ map makeToEDNcase toEDNcases-                 , TestLabel "FromEDN converter/decoder" $ TestList $ map makeFromEDNcase fromEDNcases-                 , TestLabel "decoder fail" (TestCase (assertEqual "bad unicode" Nothing (parseMaybe "№")))-                 , TestLabel "'Tagged a' conversion" $ TestList taggedConversion-                 ]--makeParserCase :: (BSL.ByteString, E.TaggedValue) -> Test-makeParserCase (i, o) = TestCase (assertEqual (BSL.unpack i) (Just o) (parseMaybe i))--makeEncodeCase :: (E.TaggedValue, BSL.ByteString) -> Test-makeEncodeCase (i, o) = TestCase (assertEqual (BSL.unpack o) o (encode i))--parserCases :: [(BSL.ByteString, E.TaggedValue)]-parserCases = [ ("nil", E.nil)--              , ("true", E.true)-              , ("false", E.false)--              , ("\"a nice string\"", "a nice string")-              , ("\"split\\second \\t\\rai\\n\"", "split\\second \t\rai\n")-              , ("\"test \\\"sausage\\\" shmest\"", "test \"sausage\" shmest")-              , ("\"\"", "")--              , ("\\c", E.char 'c')-              , ("\\\\", E.char '\\')-              , ("\\newline", E.char '\n')-              , ("\\space", E.char ' ')-              , ("\\tab", E.char '\t')--              , ("justasymbol", E.symbol "justasymbol")-              , ("with#stuff:inside", E.symbol "with#stuff:inside")-              , ("my-namespace/foo", E.symbolNS "my-namespace" "foo")-              , ("/", E.symbol "/")--              , (":fred", E.keyword "fred")-              , (":my/fred", E.keyword "my/fred")--              , ("42", E.integer 42)-              , ("-1", E.integer (-1))--              , ("100.50", E.floating 100.5)-              , ("-3.14", E.floating (-3.14))-               -- ...and many other strange stuff...--              , ("(a b 42)", sampleList)-              , ("()", E.notag $ E.makeList [])--              , ("[a b 42]", sampleVec)-              , ("[]", E.notag $ E.makeVec [])--              , ("{:a 1, \"foo\" :bar, [1 2 3] four}", sampleMap)-              , ("{}", E.notag $ E.makeMap [])--              , ("#{a b [1 2 3]}", sampleSet)-              , ("#{}", E.notag $ E.makeSet [])--              , ("[a b #_foo 42]", sampleDiscard)-              , ("(1 2 ;more to go!\n 3 4)", sampleComment)--              , ("#myapp/Person {:first \"Fred\" :last \"Mertz\"}", E.tag "myapp" "Person" sampleTaggedMap)-              , ("{:first \"Fred\" :last \"Mertz\"}", E.notag sampleTaggedMap)-              ]--encodeCases :: [(E.TaggedValue, BSL.ByteString)]-encodeCases = [ (E.nil, "nil")--              , (E.true, "true")-              , (E.false, "false")--              , (E.string "a nice string", "\"a nice string\"")-              , ("overloaded", "\"overloaded\"")-              , ("escape \rou\te \\ \"\north\"", "\"escape \\rou\\te \\\\ \\\"\\north\\\"\"")--              , (E.char 'c', "\\c")-              , (E.char '\\', "\\\\")-              , (E.char '\n', "\\newline")-              , (E.char '\r', "\\return")-              , (E.char '\t', "\\tab")-              , (E.char ' ', "\\space")--              , (E.symbol "justasymbol", "justasymbol")-              , (E.symbol "with#stuff:inside", "with#stuff:inside")-              , (E.symbolNS "whatever" "whenever", "whatever/whenever")-              , (E.symbol "/", "/")--              , (E.keyword "fred", ":fred")-              , (E.keyword "my/fred", ":my/fred")-              , (E.notag ":overloaded", ":overloaded") -- IsString kw transform only for untagged values--              , (E.integer 42, "42")-              , (E.integer (-1), "-1")--              , (E.floating 100.50, "100.5")-              , (E.floating (-3.14), "-3.14")--              , (sampleList, "(a b 42)")-              , (E.notag $ E.makeList [], "()")--              , (sampleVec, "[a b 42]")-              , (E.notag $ E.makeVec [], "[]")--              , (sampleMap, "{\"foo\" :bar :a 1 [1 2 3] four}") -- Order not guaranteed-              , (E.notag $ E.makeMap [], "{}")--              , (E.tag "myapp" "Person" sampleTaggedMap, "#myapp/Person {:first \"Fred\" :last \"Mertz\"}")-              ]--sampleList :: E.TaggedValue-sampleList = E.notag $ E.makeList [E.symbol "a", E.symbol "b", E.integer 42]--sampleVec :: E.TaggedValue-sampleVec = E.notag $ E.makeVec [E.symbol "a", E.symbol "b", E.integer 42]--sampleMap :: E.TaggedValue-sampleMap = E.notag $ E.makeMap [ (":a",                                              E.integer 1)-                                , ("foo",                                             E.keyword "bar")-                                , (E.makeVec [E.integer 1, E.integer 2, E.integer 3], E.symbol "four")-                                ]--sampleSet :: E.TaggedValue-sampleSet = E.notag $ E.makeSet [ E.symbol "a"-                                , E.symbol "b"-                                , E.notag $ E.makeVec [E.integer 1, E.integer 2, E.integer 3]-                                ]--sampleDiscard :: E.TaggedValue-sampleDiscard = E.notag $ E.makeVec [E.symbol "a", E.symbol "b", E.integer 42]--sampleComment :: E.TaggedValue-sampleComment = E.notag $ E.makeList [E.integer 1, E.integer 2, E.integer 3, E.integer 4]--sampleTaggedMap :: E.Value-sampleTaggedMap = E.makeMap [ "first" .= E.string "Fred", "last" .= E.string "Mertz" ]--data ToEDNCase = forall a. ToEDN a => ToEDNCase !a !E.TaggedValue--makeToEDNcase :: ToEDNCase -> Test-makeToEDNcase (ToEDNCase i o) = TestCase (assertEqual (show o) o (toEDN i))--toEDNcases :: [ToEDNCase]-toEDNcases = [ ToEDNCase (Nothing :: Maybe Bool) E.nil--             , ToEDNCase (Left "hi" :: Either String ()) $ E.tag "either" "left" "hi"-             , ToEDNCase (Right "z" :: Either () String) $ E.tag "either" "right" "z"-             , ToEDNCase (Just '!') (E.char '!')--             , ToEDNCase True E.true-             , ToEDNCase False E.false--             , ToEDNCase ("test" :: String) (E.string "test")-             , ToEDNCase ("test" :: BSL.ByteString) "test"-             , ToEDNCase ("test" :: BS.ByteString) "test"-             , ToEDNCase ("test" :: TL.Text) "test"-             , ToEDNCase ("test" :: T.Text) "test"--             , ToEDNCase 'c' $ E.char 'c'-             , ToEDNCase '\n' $ E.char '\n'-             , ToEDNCase '\\' $ E.char '\\'--             , ToEDNCase (3.14 :: Double) $ E.floating 3.14-             , ToEDNCase (42 :: Integer) $ E.integer 42--             , ToEDNCase () (E.notag $ E.makeList [])-             , ToEDNCase ([] :: [()]) (E.notag $ E.makeList [])-             , ToEDNCase (["yo", "wassup"] :: [String]) (E.notag $ E.makeList ["yo", "wassup"])-             , ToEDNCase [E.string "i", "am", E.symbolNS "boxed" "container"] (E.notag $ E.makeList [E.string "i", "am", E.symbolNS "boxed" "container"])--             , ToEDNCase (V.fromList ['o', 'm', 'g']) (E.notag $ E.makeVec [E.char 'o', E.char 'm', E.char 'g'])--             , ToEDNCase (S.fromList ['o', 'm', 'g']) (E.notag $ E.makeSet [E.char 'o', E.char 'm', E.char 'g'])--             , ToEDNCase (M.fromList [("test", "shmest"), ("foo", "bar")] :: M.Map String String) (E.notag $ E.makeMap [("test", "shmest"), ("foo", "bar")])-             , ToEDNCase (M.fromList [(":test", "shmest"), (":foo", "bar")] :: EDNMap) (E.notag $ E.makeMap ["test" .= E.string "shmest", "foo" .= E.string "bar"])-             ]--data FromEDNCase = forall a. (FromEDN a, Show a, Eq a) => FromEDNCase !BSL.ByteString !a--makeFromEDNcase :: FromEDNCase -> Test-makeFromEDNcase (FromEDNCase i o) = TestCase $ assertEqual (BSL.unpack i) (Just o) (decode i)--fromEDNcases :: [FromEDNCase]-fromEDNcases = [ FromEDNCase "nil" (Nothing :: Maybe Bool)--               , FromEDNCase "false" False-               , FromEDNCase "true" True--               , FromEDNCase "#either/left hi" (Left "hi" :: Either String ())-               , FromEDNCase "#either/right z" (Right "z" :: Either () String)-               , FromEDNCase "\"ok fine\"" (Just "ok fine" :: Maybe T.Text)-               , FromEDNCase "nil" (Nothing :: Maybe BSL.ByteString)--               , FromEDNCase "\\newline" '\n'-               , FromEDNCase "\\z" 'z'--               , FromEDNCase "42" (42 :: Integer)-               , FromEDNCase "-3.14" (-3.14 :: Double)--               , FromEDNCase "([1 2] [3 4])" ([(1,2), (3,4)]:: [(Int,Int)])-               , FromEDNCase "((1 2) (3 4))" ([[1,2], [3,4]]:: [[Int]])-               , FromEDNCase "[[1 2] [3 4]]" ([(1,2), (3,4)]:: [(Int, Int)])-               , FromEDNCase "[[1 2] [3 4]]" ([[1,2], [3,4]]:: [[Int]])-               , FromEDNCase "(hello world)" (["hello", "world"] :: [String])--               , FromEDNCase "[3 2 1 \"Kaboom!\"]" $ E.makeVec [E.integer 3, E.integer 2, E.integer 1, "Kaboom!"]-               , FromEDNCase "#{a/bag :of \"bugs\"}" $ E.makeSet [E.symbolNS "a" "bag", E.keyword "of", E.string "bugs"]-               , FromEDNCase "{json: \"like\", but/not: \"really\"}" (M.fromList [("json:", "like"), ("but/not:", "really")] :: M.Map String String)-               , FromEDNCase "{ json: \"like\", but/not: \"really\" }" (M.fromList [("json:", "like"), ("but/not:", "really")] :: M.Map String String)-               ]--taggedConversion :: [Test]-taggedConversion = [ TestCase (assertEqual "toEDN tagged" (E.tag "wo" "ot" (E.Boolean False)) (toEDN $ E.tag "wo" "ot" False))-                   , TestCase (assertEqual "toEDN notag" E.false (toEDN $ E.notag False))-                   , TestCase (assertEqual "fromEDN tagged" (Success $ E.tag "wo" "ot" False) (fromEDN $ E.tag "wo" "ot" (E.Boolean False)))-                   , TestCase (assertEqual "fromEDN notag" (Success $ E.notag False) (fromEDN E.false))-                   ]
tests/Main.hs view
@@ -1,11 +1,23 @@ module Main where-import           Test.Hspec.Contrib.HUnit (fromHUnitTest)-import           Test.Hspec.Runner        (hspec) -import           Data.EDN.Test.QuickCheck (ednSerializationProperties)-import           Data.EDN.Test.Unit       (tests)+import Control.Monad (unless)+import Hedgehog (checkParallel)+-- import Hedgehog (checkSequential)+import System.Exit (exitFailure) +import qualified Data.EDN.AST.Test as AST+import qualified Data.EDN.Class.Test as Class+import qualified Data.EDN.QQ.Test as QQ+ main :: IO ()-main = hspec $ do-  fromHUnitTest tests-  ednSerializationProperties+main = do+  putStrLn ""+  ok <- tests+  unless ok exitFailure++tests :: IO Bool+tests = fmap and . sequence $ map checkParallel+  [ AST.tests+  , Class.tests+  , QQ.tests+  ]