{-# LANGUAGE FlexibleContexts, FlexibleInstances, GADTs #-}
module Construct
(
Format, parse, serialize,
(Construct.<$), (Construct.*>), (Construct.<*), (Construct.<|>), (<+>), (<?>),
empty, optional, optionWithDefault, pair, deppair, many, some, sepBy, count,
mfix, record,
mapSerialized, mapMaybeSerialized, mapValue, mapMaybeValue,
satisfy, value, padded, padded1,
literal, byte, char,
cereal, cereal',
Construct.take, Construct.takeWhile, Construct.takeWhile1, Construct.takeCharsWhile, Construct.takeCharsWhile1,
testParse, testSerialize
) where
import qualified Control.Applicative as Applicative
import qualified Control.Monad.Fix as Monad.Fix
import Control.Applicative (Applicative, Alternative)
import Control.Monad.Fix (MonadFix)
import Data.Functor ((<$>), void)
import qualified Data.Functor.Const as Functor
import qualified Data.List as List
import Data.List.NonEmpty (nonEmpty)
import Data.Maybe (fromMaybe)
import Data.Semigroup (Semigroup, (<>), sconcat)
import Data.Word (Word8)
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import qualified Data.Monoid.Factorial as Factorial
import qualified Data.Monoid.Textual as Textual
import qualified Data.Monoid.Null as Null
import Data.Monoid.Factorial (FactorialMonoid)
import Data.Monoid.Textual (TextualMonoid)
import Data.String (IsString, fromString)
import qualified Text.Parser.Combinators as Parser
import qualified Text.Parser.Char as Parser.Char
import qualified Text.ParserCombinators.Incremental as Incremental
import Text.ParserCombinators.Incremental.Symmetric (Symmetric)
import Data.Serialize (Serialize, Result(Done, Fail, Partial), Get, Putter, runGetPartial, runPut)
import qualified Data.Serialize as Serialize
import qualified Rank2
import qualified Construct.Classes as Input
import Construct.Classes (AlternativeFail(failure), InputParsing (ParserInput), InputCharParsing, InputMappableParsing,
FixTraversable, Error, errorString, expectedName)
import Construct.Internal
import Prelude hiding (pred, take, takeWhile)
literal :: (Functor m, InputParsing m, Applicative n, ParserInput m ~ s) => s -> Format m n s ()
literal s = Format{
parse = void (Input.string s),
serialize = const (pure s)
}
padded :: (Monad m, Functor n, InputParsing m, ParserInput m ~ s, FactorialMonoid s) =>
s -> Format m n s s -> Format m n s s
padded template format = Format{
parse = parse format >>= parsePadding,
serialize = (padRight <$>) . serialize format
}
where padRight s = s <> Factorial.drop (Factorial.length s) template
parsePadding s = if Null.null padding then pure s else s Applicative.<$ Input.string padding
where padding = Factorial.drop (Factorial.length s) template
padded1 :: (Monad m, Monad n, InputParsing m, ParserInput m ~ s, FactorialMonoid s, Show s, AlternativeFail n) =>
s -> Format m n s s -> Format m n s s
padded1 template format = Format{
parse = parse format >>= parsePadding,
serialize = \a-> serialize format a >>= padRight
}
where padRight s = if Null.null padding then expectedName ("padded1 " ++ show template) (failure $ show s)
else pure (s <> padding)
where padding = Factorial.drop (Factorial.length s) template
parsePadding s = if Null.null padding
then Parser.unexpected (show s) Parser.<?> ("padded1 " ++ show template)
else s Applicative.<$ Input.string padding
where padding = Factorial.drop (Factorial.length s) template
take :: (InputParsing m, ParserInput m ~ s, FactorialMonoid s, Show s, AlternativeFail n) => Int -> Format m n s s
take n = Format{
parse = Input.take n,
serialize = \s-> if Factorial.length s == n then pure s
else expectedName ("a value of length " ++ show n) (failure $ show s)
}
takeWhile :: (InputParsing m, ParserInput m ~ s, FactorialMonoid s, Show s, AlternativeFail n) =>
(s -> Bool) -> Format m n s s
takeWhile pred = Format{
parse = Input.takeWhile pred,
serialize = \s-> if Null.null (Factorial.dropWhile pred s) then pure s
else expectedName "takeWhile" (failure $ show s)
}
takeWhile1 :: (InputParsing m, ParserInput m ~ s, FactorialMonoid s, Show s, AlternativeFail n) =>
(s -> Bool) -> Format m n s s
takeWhile1 pred = Format{
parse = Input.takeWhile1 pred,
serialize = \s-> if not (Null.null s) && Null.null (Factorial.dropWhile pred s) then pure s
else expectedName "takeWhile1" (failure $ show s)
}
takeCharsWhile :: (InputCharParsing m, ParserInput m ~ s, TextualMonoid s, Show s, AlternativeFail n) =>
(Char -> Bool) -> Format m n s s
takeCharsWhile pred = Format{
parse = Input.takeCharsWhile pred,
serialize = \s-> if Null.null (Textual.dropWhile_ False pred s) then pure s
else expectedName "takeCharsWhile" (failure $ show s)
}
takeCharsWhile1 :: (InputCharParsing m, ParserInput m ~ s, TextualMonoid s, Show s, AlternativeFail n) =>
(Char -> Bool) -> Format m n s s
takeCharsWhile1 pred = Format{
parse = Input.takeCharsWhile1 pred,
serialize = \s-> if not (Null.null s) && Null.null (Textual.dropWhile_ False pred s) then pure s
else expectedName "takeCharsWhile1" (failure $ show s)
}
value :: (Eq a, Show a, Parser.Parsing m, Monad m, Alternative n) => Format m n s a -> a -> Format m n s ()
value f v = Format{
parse = void (parse f >>= \x-> if x == v then pure x else Parser.unexpected (show x)),
serialize = \()-> serialize f v
}
satisfy :: (Parser.Parsing m, Monad m, AlternativeFail n, Show a) => (a -> Bool) -> Format m n s a -> Format m n s a
satisfy predicate f = Format{
parse = parse f >>= \v-> if predicate v then pure v else Parser.unexpected (show v),
serialize = \v-> if predicate v then serialize f v else expectedName "satisfy" (failure $ show v)
}
mapSerialized :: (Monoid s, Monoid t, InputParsing (m s), InputParsing (m t),
s ~ ParserInput (m s), t ~ ParserInput (m t), InputMappableParsing m, Functor n) =>
(s -> t) -> (t -> s) -> Format (m s) n s a -> Format (m t) n t a
mapSerialized f f' format = Format{
parse = Input.mapParserInput f f' (parse format),
serialize = (f <$>) . serialize format}
mapMaybeSerialized :: (Monoid s, Monoid t, InputParsing (m s), InputParsing (m t),
s ~ ParserInput (m s), t ~ ParserInput (m t), InputMappableParsing m, Functor n) =>
(s -> Maybe t) -> (t -> Maybe s) -> Format (m s) n s a -> Format (m t) n t a
mapMaybeSerialized f f' format = Format{
parse = Input.mapMaybeParserInput f f' (parse format),
serialize = (fromMaybe (error "Partial serialization") . f <$>) . serialize format}
mapValue :: Functor m => (a -> b) -> (b -> a) -> Format m n s a -> Format m n s b
mapValue f f' format = Format{
parse = f <$> parse format,
serialize = serialize format . f'}
mapMaybeValue :: (Monad m, Parser.Parsing m, Show a, Show b, AlternativeFail n) =>
(a -> Maybe b) -> (b -> Maybe a) -> Format m n s a -> Format m n s b
mapMaybeValue f f' format = Format{
parse = parse format >>= \v-> maybe (Parser.unexpected $ show v) pure (f v),
serialize = \v-> maybe (expectedName "mapMaybeValue" (failure $ show v)) (serialize format) (f' v)}
byte :: (InputParsing m, ParserInput m ~ ByteString, Applicative n) => Format m n ByteString Word8
byte = Format{
parse = ByteString.head <$> Input.anyToken,
serialize = pure . ByteString.singleton}
char :: (Parser.Char.CharParsing m, ParserInput m ~ s, IsString s, Applicative n) => Format m n s Char
char = Format{
parse = Parser.Char.anyChar,
serialize = pure . fromString . (:[])}
cereal :: (Serialize a, Monad m, InputParsing m, ParserInput m ~ ByteString, Applicative n) => Format m n ByteString a
cereal = cereal' Serialize.get Serialize.put
cereal' :: (Monad m, InputParsing m, ParserInput m ~ ByteString, Applicative n) =>
Get a -> Putter a -> Format m n ByteString a
cereal' get put = Format p (pure . runPut . put)
where p = go (runGetPartial get mempty)
where go (Fail msg _) = fail msg
go (Done r _) = pure r
go (Partial cont) = Input.anyToken >>= go . cont
count :: (Applicative m, AlternativeFail n, Show a, Monoid s) => Int -> Format m n s a -> Format m n s [a]
count n item = Format{
parse = Parser.count (fromIntegral n) (parse item),
serialize = \as-> if length as == n then mconcat <$> traverse (serialize item) as
else expectedName ("a list of length " ++ show n) (failure $ show as)}
record :: (Rank2.Apply g, Rank2.Traversable g, FixTraversable m, Monoid (n s), Applicative o, Foldable o) =>
g (Format m n s) -> Format m n s (g o)
record formats = Format{
parse = Input.fixSequence (parse Rank2.<$> formats),
serialize = Rank2.foldMap Functor.getConst . Rank2.liftA2 serializeField formats
}
where serializeField format xs = Functor.Const (foldMap (serialize format) xs)
infixl 3 <|>
infixl 4 <$
infixl 4 <*
infixl 4 *>
(<$) :: (Eq a, Show a, Functor m, AlternativeFail n) => a -> Format m n s () -> Format m n s a
a <$ f = Format{
parse = a Applicative.<$ parse f,
serialize = \b-> if a == b then serialize f () else expectedName (show a) (failure $ show b)}
(*>) :: (Applicative m, Semigroup (n s)) => Format m n s () -> Format m n s a -> Format m n s a
f1 *> f2 = Format{
parse = parse f1 Applicative.*> parse f2,
serialize = \a-> serialize f1 () <> serialize f2 a}
(<*) :: (Applicative m, Semigroup (n s)) => Format m n s a -> Format m n s () -> Format m n s a
f1 <* f2 = Format{
parse = parse f1 Applicative.<* parse f2,
serialize = \a-> serialize f1 a <> serialize f2 ()}
(<|>) :: (Alternative m, Alternative n) => Format m n s a -> Format m n s a -> Format m n s a
f1 <|> f2 = Format{
parse = parse f1 Applicative.<|> parse f2,
serialize = \a-> serialize f1 a Applicative.<|> serialize f2 a}
(<+>) :: Alternative m => Format m n s a -> Format m n s b -> Format m n s (Either a b)
f1 <+> f2 = Format{
parse = Left <$> parse f1 Applicative.<|> Right <$> parse f2,
serialize = either (serialize f1) (serialize f2)}
optional :: (Alternative m, Alternative n, Monoid (n s)) => Format m n s a -> Format m n s (Maybe a)
optional f = Format{
parse = Applicative.optional (parse f),
serialize = maybe mempty (serialize f)}
optionWithDefault :: (Alternative m, Alternative n, Monoid (n s)) =>
Format m n s () -> Format m n s a -> Format m n s (Maybe a)
optionWithDefault d f = Format{
parse = Just <$> parse f Applicative.<|> Nothing Applicative.<$ parse d,
serialize = maybe (serialize d ()) (serialize f)}
many :: (Alternative m, Monoid (n s)) => Format m n s a -> Format m n s [a]
many f = Format{
parse = Applicative.many (parse f),
serialize = foldMap (serialize f)}
some :: (Alternative m, AlternativeFail n, Semigroup (n s)) => Format m n s a -> Format m n s [a]
some f = Format{
parse = Applicative.some (parse f),
serialize = maybe (failure "[]") sconcat . nonEmpty . map (serialize f)}
sepBy :: (Alternative m, Applicative n, Monoid s) => Format m n s a -> Format m n s () -> Format m n s [a]
sepBy format separator = Format{
parse = Parser.sepBy (parse format) (parse separator),
serialize = \xs-> mconcat <$> sequenceA (List.intersperse (serialize separator ()) $ serialize format <$> xs)}
pair :: (Applicative m, Semigroup (n s)) => Format m n s a -> Format m n s b -> Format m n s (a, b)
pair f g = Format{
parse = (,) <$> parse f <*> parse g,
serialize = \(a, b)-> serialize f a <> serialize g b}
deppair :: (Monad m, Semigroup (n s)) => Format m n s a -> (a -> Format m n s b) -> Format m n s (a, b)
deppair f g = Format{
parse = parse f >>= \a-> parse (g a) >>= \b-> return (a, b),
serialize = \(a, b)-> serialize f a <> serialize (g a) b}
empty :: (Alternative m, Alternative n) => Format m n s a
empty = Format{
parse = Applicative.empty,
serialize = const Applicative.empty}
infixr 0 <?>
(<?>) :: (Parser.Parsing m, AlternativeFail n) => Format m n s a -> String -> Format m n s a
f <?> name = Format{
parse = parse f Parser.<?> name,
serialize = expectedName name . serialize f}
mfix :: MonadFix m => (a -> Format m n s a) -> Format m n s a
mfix f = Format{
parse = Monad.Fix.mfix (parse . f),
serialize = \a-> serialize (f a) a}
testParse :: Monoid s => Format (Incremental.Parser Symmetric s) (Either Error) s a -> s -> Either String [(a, s)]
testParse format input = fst <$> Incremental.inspect (Incremental.feedEof $ Incremental.feed input $ parse format)
testSerialize :: Format (Incremental.Parser Symmetric s) (Either Error) s a -> a -> Either String s
testSerialize format = either (Left . errorString) Right . serialize format