packages feed

invertible-grammar (empty) → 0.1.0

raw patch · 11 files changed

+1327/−0 lines, 11 filesdep +basedep +bifunctorsdep +containerssetup-changed

Dependencies added: base, bifunctors, containers, mtl, prettyprinter, profunctors, semigroups, tagged, template-haskell, text, transformers

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Eugene Smolanka, Sergey Vinokurov.++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of multiple nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,7 @@++invertible-grammar+==================++Framework to build invertible parsing combinator libraries.++See [`sexp-grammar`](http://github.com/esmolanka/sexp-grammar).
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ invertible-grammar.cabal view
@@ -0,0 +1,49 @@+name:                invertible-grammar+version:             0.1.0+license:             BSD3+license-file:        LICENSE+author:              Eugene Smolanka, Sergey Vinokurov+maintainer:          Eugene Smolanka <esmolanka@gmail.com>+homepage:            https://github.com/esmolanka/sexp-grammar+category:            Language+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10+synopsis:+  Invertible parsing combinators framework+description:+  Grammar combinator framework to build invertible parsing libraries+  for concrete syntax.+tested-with:   GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.3++source-repository head+  type: git+  location: https://github.com/esmolanka/invertible-grammar++library+  hs-source-dirs:      src+  default-language:    Haskell2010+  ghc-options:         -Wall -fno-warn-name-shadowing -fno-warn-unused-do-bind+  exposed-modules:+    Data.InvertibleGrammar+    Data.InvertibleGrammar.Base+    Data.InvertibleGrammar.Combinators+    Data.InvertibleGrammar.Generic+    Data.InvertibleGrammar.TH++  other-modules:+    Control.Monad.ContextError+    Data.InvertibleGrammar.Monad++  build-depends:+      base              >=4.7   && <5.0+    , bifunctors        >=4.2   && <5.6+    , containers        >=0.5.5 && <0.6+    , mtl               >=2.1   && <2.3+    , prettyprinter     >=1     && <1.3+    , profunctors       >=4.4   && <5.3+    , semigroups        >=0.16  && <0.19+    , tagged            >=0.7   && <0.9+    , template-haskell  >=2.9   && <2.14+    , transformers      >=0.3   && <0.6+    , text              >=1.2   && <1.3
+ src/Control/Monad/ContextError.hs view
@@ -0,0 +1,229 @@+{-# LANGUAGE CPP                    #-}+{-# LANGUAGE DeriveFunctor          #-}+{-# LANGUAGE FlexibleInstances      #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses  #-}+{-# LANGUAGE RankNTypes             #-}+{-# LANGUAGE UndecidableInstances   #-}++module Control.Monad.ContextError+  ( ContextErrorT+  , runContextErrorT+  , ContextError+  , runContextError+  , MonadContextError (..)+  ) where++#if MIN_VERSION_mtl(2,2,0)+import Control.Monad.Except+#else+import Control.Monad.Error+#endif++import Control.Applicative+import Control.Monad.Trans.Cont as Cont (ContT, liftLocal)+import Control.Monad.Trans.Identity (IdentityT, mapIdentityT)+import Control.Monad.Trans.Maybe (MaybeT, mapMaybeT)+import Control.Monad.Trans.Reader (ReaderT, mapReaderT)+import qualified Control.Monad.Trans.RWS.Lazy as Lazy (RWST, mapRWST)+import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST, mapRWST)+import qualified Control.Monad.Trans.State.Lazy as Lazy (StateT, mapStateT)+import qualified Control.Monad.Trans.State.Strict as Strict (StateT, mapStateT)+import Control.Monad.Trans.Writer.Lazy as Lazy (WriterT, mapWriterT)+import Control.Monad.Trans.Writer.Strict as Strict (WriterT, mapWriterT)++import Control.Monad.State  (MonadState (..))+import Control.Monad.Reader (MonadReader (..))+import Control.Monad.Writer (MonadWriter (..))++import Data.Functor.Identity+#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup+#endif++----------------------------------------------------------------------+-- Monad++newtype ContextErrorT c e m a =+  ContextErrorT { unContextErrorT :: forall b. c -> (e -> m b) -> (c -> a -> m b) -> m b }++runContextErrorT :: (Monad m) => ContextErrorT c e m a -> c -> m (Either e a)+runContextErrorT k c = unContextErrorT k c (return . Left) (const $ return . Right)++type ContextError c e a = ContextErrorT c e Identity a++runContextError :: ContextError c e a -> c -> Either e a+runContextError k c = runIdentity $ unContextErrorT k c (return . Left) (const $ return . Right)++instance Functor (ContextErrorT c e m) where+  fmap f e = ContextErrorT $ \c err ret -> unContextErrorT e c err (\c' -> ret c' . f)++instance Applicative (ContextErrorT c e m) where+  pure a = ContextErrorT $ \c _ ret -> ret c a+  {-# INLINE pure #-}++  fe <*> ae = ContextErrorT $ \c err ret ->+    unContextErrorT fe c err (\c' f -> unContextErrorT ae c' err (\c'' -> ret c'' . f))+  {-# INLINE (<*>) #-}++instance (Semigroup e) => Alternative (ContextErrorT c e m) where+  -- FIXME: sane 'empty' needed!+  empty = ContextErrorT $ \_ err _ -> err (error "empty ContextErrorT")+  {-# INLINE empty #-}++  ae <|> be = ContextErrorT $ \c err ret ->+    unContextErrorT ae c (\e -> unContextErrorT be c (\e' -> err (e <> e')) ret) ret+  {-# INLINE (<|>) #-}++instance Monad (ContextErrorT c e m) where+  return a = ContextErrorT $ \c _ ret -> ret c a+  {-# INLINE return #-}++  ma >>= fb =+    ContextErrorT $ \c err ret ->+      unContextErrorT ma c err $ \c' a ->+        unContextErrorT (fb a) c' err ret+  {-# INLINE (>>=) #-}++instance (Semigroup e) => MonadPlus (ContextErrorT c e m) where+  mzero = empty+  {-# INLINE mzero #-}++  mplus = (<|>)+  {-# INLINE mplus #-}++instance MonadTrans (ContextErrorT c e) where+  lift act = ContextErrorT $ \c _ ret -> act >>= ret c+  {-# INLINE lift #-}++instance MonadState s m => MonadState s (ContextErrorT c e m) where+  get = lift get+  put = lift . put+  state = lift . state++instance MonadWriter w m => MonadWriter w (ContextErrorT c e m) where+  writer = lift . writer+  tell = lift . tell+  listen m = ContextErrorT $ \c err ret -> do+    (res, w) <- listen (unContextErrorT m c (return . Left) (curry (return . Right)))+    case res of+      Left e -> err e+      Right (c', a) -> ret c' (a, w)+  pass m = ContextErrorT $ \c err ret -> pass $ do+    res <- unContextErrorT m c (return . Left) (curry (return . Right))+    case res of+      Right (c', (a, f)) -> liftM (\b -> (b, f)) $ ret c' a+      Left e -> liftM (\b -> (b, id)) $ err e++instance MonadReader r m => MonadReader r (ContextErrorT c e m) where+  ask = lift ask+  local f m = ContextErrorT $ \c err ret ->+    local f (unContextErrorT m c err ret)+  reader = lift . reader++----------------------------------------------------------------------+-- Monad class stuff++class (Monad m) => MonadContextError c e m | m -> c e where+  throwInContext :: (c -> e) -> m a+  askContext     :: m c+  localContext  :: (c -> c) -> m a -> m a+  modifyContext   :: (c -> c) -> m ()++instance Monad m =>+         MonadContextError c e (ContextErrorT c e m) where+  throwInContext f = ContextErrorT $ \c err _ -> err (f c)+  askContext = ContextErrorT $ \c _ ret -> ret c c+  localContext f m = ContextErrorT $ \c err ret ->+    unContextErrorT m (f c) err (\_ -> ret c)+  modifyContext f = ContextErrorT $ \c _ ret -> ret (f c) ()++instance MonadContextError c e m =>+         MonadContextError c e (ContT r m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = Cont.liftLocal askContext localContext+    modifyContext = lift . modifyContext++#if MIN_VERSION_mtl(2, 2, 0)++instance MonadContextError c e m =>+         MonadContextError c e (ExceptT e m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = mapExceptT . localContext+    modifyContext = lift . modifyContext++#else++instance (Error e', MonadContextError c e m) =>+         MonadContextError c e (ErrorT e' m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = mapErrorT . localContext+    modifyContext = lift . modifyContext++#endif++instance MonadContextError c e m =>+         MonadContextError c e (IdentityT m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = mapIdentityT . localContext+    modifyContext = lift . modifyContext++instance MonadContextError c e m =>+         MonadContextError c e (MaybeT m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = mapMaybeT . localContext+    modifyContext = lift . modifyContext++instance MonadContextError c e m =>+         MonadContextError c e (ReaderT r m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = mapReaderT . localContext+    modifyContext = lift . modifyContext++instance (Monoid w, MonadContextError c e m) =>+         MonadContextError c e (Lazy.WriterT w m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = Lazy.mapWriterT . localContext+    modifyContext = lift . modifyContext++instance (Monoid w, MonadContextError c e m) =>+         MonadContextError c e (Strict.WriterT w m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = Strict.mapWriterT . localContext+    modifyContext = lift . modifyContext++instance MonadContextError c e m =>+         MonadContextError c e (Lazy.StateT s m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = Lazy.mapStateT . localContext+    modifyContext = lift . modifyContext++instance MonadContextError c e m =>+         MonadContextError c e (Strict.StateT s m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = Strict.mapStateT . localContext+    modifyContext = lift . modifyContext++instance (Monoid w, MonadContextError c e m) =>+         MonadContextError c e (Lazy.RWST r w s m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = Lazy.mapRWST . localContext+    modifyContext = lift . modifyContext++instance (Monoid w, MonadContextError c e m) =>+         MonadContextError c e (Strict.RWST r w s m) where+    throwInContext = lift . throwInContext+    askContext = lift askContext+    localContext = Strict.mapRWST . localContext+    modifyContext = lift . modifyContext
+ src/Data/InvertibleGrammar.hs view
@@ -0,0 +1,18 @@+module Data.InvertibleGrammar+  ( -- * Base+    Grammar, (:-), forward, backward+    -- * Combinators+  , module Data.InvertibleGrammar.Combinators+    -- * Running grammars+  , runGrammar+  , runGrammarDoc+  , runGrammarString+  -- ** Error messages+  , ErrorMessage(..)+  , ContextError, Propagation , GrammarError+  , Mismatch, expected, unexpected+  ) where++import Data.InvertibleGrammar.Base+import Data.InvertibleGrammar.Combinators+import Data.InvertibleGrammar.Monad
+ src/Data/InvertibleGrammar/Base.hs view
@@ -0,0 +1,181 @@+{-# LANGUAGE CPP                   #-}+{-# LANGUAGE DeriveFoldable        #-}+{-# LANGUAGE DeriveFunctor         #-}+{-# LANGUAGE DeriveTraversable     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE TypeOperators         #-}++module Data.InvertibleGrammar.Base+  ( Grammar (..)+  , (:-) (..)+  , forward+  , backward+  , GrammarError (..)+  , Mismatch+  , expected+  , unexpected+  ) where++import Prelude hiding ((.), id)+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif+import Control.Category+import Control.Monad+import Data.Text (Text)+import Data.Bifunctor+import Data.Bifoldable+import Data.Bitraversable+#if !MIN_VERSION_base(4,8,0)+import Data.Traversable+import Data.Foldable+#endif+#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup+#endif+import Data.InvertibleGrammar.Monad+import qualified Debug.Trace++-- | \"Cons\" pair of a heterogenous list or a stack with potentially+-- polymophic tail. E.g. @"first" :- 2 :- (3,4) :- t@+--+-- Isomorphic to a tuple with two elments, but is much more+-- convenient for nested pairs.+data h :- t = h :- t deriving (Eq, Show, Functor, Foldable, Traversable)+infixr 5 :-++instance Bifunctor (:-) where+  bimap f g (a :- b) = f a :- g b++instance Bifoldable (:-) where+  bifoldr f g x0 (a :- b) = a `f` (b `g` x0)++instance Bitraversable (:-) where+  bitraverse f g (a :- b) = (:-) <$> f a <*> g b++-- | Representation of an invertible grammar -- a grammar that can be+-- run either "forwards" and "backwards".+--+-- For a grammar @Grammar p a b@, running it forwards will take a+-- value of type @a@ and possibly produce a value of type @b@. Running+-- it backwards will take a value of type @b@ and possibly produce an+-- @a@. If a value cannot be produced, an error message is generated.+--+-- As a common example, running a 'Grammar' forwards corresponds to+-- parsing and running backwards corresponds to prettyprinting.+--+-- That is, the grammar defines a partial isomorphism between two+-- values.+data Grammar p a b where+  -- | Total isomorphism grammar.+  Iso        :: (a -> b) -> (b -> a) -> Grammar p a b++  -- | Partial isomorphism. Use 'Flip' to change the direction of+  -- partiality.+  PartialIso :: (a -> b) -> (b -> Either Mismatch a) -> Grammar p a b++  -- | Flip forward and backward passes of an underlying grammar.+  Flip       :: Grammar p a b -> Grammar p b a++  -- | Grammar composition.+  (:.:)      :: Grammar p b c -> Grammar p a b -> Grammar p a c++  -- | Grammar alternation. Left operand is tried first.+  (:<>:)     :: Grammar p a b -> Grammar p a b -> Grammar p a b++  -- | Application of a grammar on 'Traversable' functor.+  Traverse   :: (Traversable f) => Grammar p a b -> Grammar p (f a) (f b)++  -- | Applicaiton of a grammar on stack head+  -- (first component of ':-').+  OnHead     :: Grammar p a b -> Grammar p (a :- t) (b :- t)++  -- | Applicaiton of a grammar on stack tail+  -- (second component of ':-').+  OnTail     :: Grammar p a b -> Grammar p (h :- a) (h :- b)++  -- | Application of a grammar inside a context of annotation, used+  -- for error messages.+  Annotate   :: Text -> Grammar p a b -> Grammar p a b++  -- | Application of a grammar inside a context of a nested+  -- structure, used for error messages. E.g. JSON arrays.+  Dive       :: Grammar p a b -> Grammar p a b++  -- | Propagate logical position inside a nested+  -- structure. E.g. after each successfully matched element of a JSON+  -- array.+  Step       :: Grammar p a a++  -- | Update the position of grammar monad from value on grammar's+  -- input or output on forward or backward pass, respectively. Used+  -- for error messages.+  Locate     :: Grammar p p p++trace :: String -> a -> a+trace = if False then Debug.Trace.trace else flip const+++instance Category (Grammar p) where+  id                                              = Iso id id++  PartialIso f g        . Iso f' g'               = trace "p/i" $ PartialIso (f . f') (fmap g' . g)+  Iso f g               . PartialIso f' g'        = trace "i/p" $ PartialIso (f . f') (g' . g)++  Flip (PartialIso f g) . Iso f' g'               = trace "fp/i" $ Flip $ PartialIso (g' . f) (g . f')+  Iso f g               . Flip (PartialIso f' g') = trace "i/fp" $ Flip $ PartialIso (f' . g) (fmap f . g')++  PartialIso f g        . (Iso f' g'               :.: h) = trace "p/i2" $ PartialIso (f . f') (fmap g' . g) :.: h+  Iso f g               . (PartialIso f' g'        :.: h) = trace "i/p2" $ PartialIso (f . f') (g' . g) :.: h++  Flip (PartialIso f g) . (Iso f' g'               :.: h) = trace "fp/i2" $ Flip (PartialIso (g' . f) (g . f')) :.: h+  Iso f g               . (Flip (PartialIso f' g') :.: h) = trace "i/fp2" $ Flip (PartialIso (f' . g) (fmap f . g')) :.: h++  Flip g . Flip h                                 = trace "f/f" $ Flip (h . g)+  Iso f g . Iso f' g'                             = trace "i/i" $ Iso (f . f') (g' . g)++  (g :.: h)             . j                       = trace "assoc" $ g :.: (h . j)++  g                     . h                       = g :.: h+++instance Semigroup (Grammar p a b) where+  (<>) = (:<>:)++-- | Run 'Grammar' forwards.+--+-- For @Grammar p a b@, given a value of type @a@ tries to produce a+-- value of type @b@, otherwise reports an error with position of type+-- @p@.+forward :: Grammar p a b -> a -> ContextError (Propagation p) (GrammarError p) b+forward (Iso f _)        = return . f+forward (PartialIso f _) = return . f+forward (Flip g)         = backward g+forward (g :.: f)        = forward g <=< forward f+forward (f :<>: g)       = \x -> forward f x `mplus` forward g x+forward (Traverse g)     = traverse (forward g)+forward (OnHead g)       = \(a :- b) -> (:- b) <$> forward g a+forward (OnTail g)       = \(a :- b) -> (a :-) <$> forward g b+forward (Annotate t g)   = doAnnotate t . forward g+forward (Dive g)         = doDive . forward g+forward Step             = \x -> doStep >> return x+forward Locate           = \x -> doLocate x >> return x++-- | Run 'Grammar' backwards.+--+-- For @Grammar p a b@, given a value of type @b@ tries to produce a+-- value of type @a@, otherwise reports an error with position of type+-- @p@.+backward :: Grammar p a b -> b -> ContextError (Propagation p) (GrammarError p) a+backward (Iso _ g)        = return . g+backward (PartialIso _ g) = either doError return . g+backward (Flip g)         = forward g+backward (g :.: f)        = backward g >=> backward f+backward (f :<>: g)       = \x -> backward f x `mplus` backward g x+backward (Traverse g)     = traverse (backward g)+backward (OnHead g)       = \(a :- b) -> (:- b) <$> backward g a+backward (OnTail g)       = \(a :- b) -> (a :-) <$> backward g b+backward (Annotate t g)   = doAnnotate t . backward g+backward (Dive g)         = doDive . backward g+backward Step             = \x -> doStep >> return x+backward Locate           = \x -> doLocate x >> return x
+ src/Data/InvertibleGrammar/Combinators.hs view
@@ -0,0 +1,209 @@+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators       #-}++module Data.InvertibleGrammar.Combinators+  ( iso+  , osi+  , partialIso+  , partialOsi+  , push+  , pair+  , swap+  , cons+  , nil+  , insert+  , insertMay+  , toDefault+  , coproduct+  , onHead+  , onTail+  , traversed+  , flipped+  , sealed+  , coerced+  , annotated+  ) where++import Control.Category ((>>>))+import Data.Coerce+import Data.Maybe+import Data.Void+import Data.Text (Text)+import Data.InvertibleGrammar.Base++#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup+#endif++-- | Isomorphism on the stack head.+iso :: (a -> b) -> (b -> a) -> Grammar p (a :- t) (b :- t)+iso f' g' = Iso f g+  where+    f (a :- t) = f' a :- t+    g (b :- t) = g' b :- t+++-- | Flipped isomorphism on the stack head.+osi :: (b -> a) -> (a -> b) -> Grammar p (a :- t) (b :- t)+osi f' g' = Iso g f+  where+    f (a :- t) = f' a :- t+    g (b :- t) = g' b :- t++-- | Partial isomorphism (for backward run) on the stack head.+partialIso :: (a -> b) -> (b -> Either Mismatch a) -> Grammar p (a :- t) (b :- t)+partialIso f' g' = PartialIso f g+  where+    f (a :- t) = f' a :- t+    g (b :- t) = (:- t) <$> g' b+++-- | Partial isomorphism (for forward run) on the stack head.+partialOsi :: (a -> Either Mismatch b) -> (b -> a) -> Grammar p (a :- t) (b :- t)+partialOsi g' f' = Flip $ PartialIso f g+  where+    f (a :- t) = f' a :- t+    g (b :- t) = (:- t) <$> g' b+++-- | Push an element to the stack on forward run, check if the element+-- satisfies predicate, otherwise report a mismatch.+push :: a -> (a -> Bool) -> (a -> Mismatch) -> Grammar p t (a :- t)+push a p e = PartialIso f g+  where+    f t = a :- t+    g (a' :- t)+      | p a' = Right t+      | otherwise = Left $ e a'+++-- | 2-tuple grammar. Construct on forward run, deconstruct on+-- backward run.+pair :: Grammar p (b :- a :- t) ((a, b) :- t)+pair = Iso+  (\(b :- a :- t) -> (a, b) :- t)+  (\((a, b) :- t) -> b :- a :- t)+++-- | List cons-cell grammar. Construct on forward run, deconstruct on+-- backward run.+cons :: Grammar p ([a] :- a :- t) ([a] :- t)+cons = PartialIso+  (\(lst :- el :- t) -> (el:lst) :- t)+  (\(lst :- t) ->+      case lst of+        [] -> Left (expected "list element")+        (el:rest) -> Right (rest :- el :- t))+++-- | Empty list grammar. Construct empty list on forward run, check if+-- list is empty on backward run.+nil :: Grammar p t ([a] :- t)+nil = PartialIso+  (\t -> [] :- t)+  (\(lst :- t) ->+      case lst of+        [] -> Right t+        (_el:_rest) -> Left (expected "end of list"))+++-- | Swap two topmost stack elements.+swap :: Grammar p (a :- b :- t) (b :- a :- t)+swap = Iso+  (\(a :- b :- t) -> (b :- a :- t))+  (\(b :- a :- t) -> (a :- b :- t))+++-- | Assoc-list element grammar. Inserts an element (with static key)+-- on forward run, look up an element on backward run.+insert :: (Eq k) => k -> Mismatch -> Grammar p (v :- [(k, v)] :- t) ([(k, v)] :- t)+insert k m = PartialIso+  (\(v :- alist :- t) -> ((k, v) : alist) :- t)+  (\(alist :- t) ->+     case popKey k alist of+       Nothing -> Left m+       Just (v, alist') -> Right (v :- alist' :- t))+++-- | Optional assoc-list element grammar. Like 'insert', but does not+-- report a mismatch on backward run. Instead takes and produces a+-- Maybe-value.+insertMay :: (Eq k) => k -> Grammar p (Maybe v :- [(k, v)] :- t) ([(k, v)] :- t)+insertMay k = PartialIso+  (\(mv :- alist :- t) ->+      case mv of+        Just v -> ((k, v) : alist) :- t+        Nothing -> alist :- t)+  (\(alist :- t) ->+     case popKey k alist of+       Nothing -> Right (Nothing :- alist :- t)+       Just (v, alist') -> Right (Just v :- alist' :- t))+++popKey :: forall k v. Eq k => k -> [(k, v)] -> Maybe (v, [(k, v)])+popKey k' = go []+  where+    go :: [(k, v)] -> [(k, v)] -> Maybe (v, [(k, v)])+    go acc (x@(k, v) : xs)+      | k == k' = Just (v, reverse acc ++ xs)+      | otherwise = go (x:acc) xs+    go _ [] = Nothing+++-- | Default value grammar. Replaces 'Nothing' with a default value on+-- forward run, an replaces a default value with 'Nothing' on backward+-- run.+toDefault :: (Eq a) => a -> Grammar p (Maybe a :- t) (a :- t)+toDefault def = iso+  (fromMaybe def)+  (\val -> if val == def then Nothing else Just val)+++-- | Run a grammar operating on the stack head in a context where+-- there is no stack.+sealed :: Grammar p (a :- Void) (b :- Void) -> Grammar p a b+sealed g =+  Iso (:- error "void") (\(a :- _) -> a) >>>+  g >>>+  Iso (\(a :- _) -> a) (:- error "void")+++-- | Focus a given grammar to the stack head.+onHead :: Grammar p a b -> Grammar p (a :- t) (b :- t)+onHead = OnHead+++-- | Focus a given grammar to the stack tail.+onTail :: Grammar p ta tb -> Grammar p (h :- ta) (h :- tb)+onTail = OnTail+++-- | Traverse a structure with a given grammar.+traversed :: (Traversable f) => Grammar p a b -> Grammar p (f a) (f b)+traversed = Traverse+++-- | Run a grammar with inputs and outputs flipped.+flipped :: Grammar p a b -> Grammar p b a+flipped = Flip+++-- | Run a grammar with an annotation.+annotated :: Text -> Grammar p a b -> Grammar p a b+annotated = Annotate+++-- | Run a grammar with the stack heads coerced to other ('Coercible')+-- types.+coerced+  :: (Coercible a c, Coercible b d) =>+     Grammar p (a :- t) (b :- t')+  -> Grammar p (c :- t) (d :- t')+coerced g = iso coerce coerce >>> g >>> iso coerce coerce+++-- | Join alternative grammars in parallel.+coproduct :: [Grammar p a b] -> Grammar p a b+coproduct = foldl1 (<>)
+ src/Data/InvertibleGrammar/Generic.hs view
@@ -0,0 +1,240 @@+{-# LANGUAGE CPP                    #-}+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE FlexibleContexts       #-}+{-# LANGUAGE FlexibleInstances      #-}+{-# LANGUAGE GADTs                  #-}+{-# LANGUAGE MultiParamTypeClasses  #-}+{-# LANGUAGE OverloadedStrings      #-}+{-# LANGUAGE PolyKinds              #-}+{-# LANGUAGE RankNTypes             #-}+{-# LANGUAGE ScopedTypeVariables    #-}+{-# LANGUAGE TypeFamilies           #-}+{-# LANGUAGE TypeOperators          #-}++-- NB: UndecidableInstances needed for nested type family application. :-/+{-# LANGUAGE UndecidableInstances   #-}++module Data.InvertibleGrammar.Generic+  ( with+  , match+  , Coproduct (..)+  ) where++import Prelude hiding ((.), id)++import Control.Applicative+import Control.Category ((.))++import Data.Functor.Identity+import Data.InvertibleGrammar.Base+import Data.Monoid (First(..))+import Data.Profunctor (Choice(..))+import Data.Profunctor.Unsafe+import Data.Tagged+import Data.Text (pack)+#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup ((<>))+#endif++import GHC.Generics++-- | Provide a data constructor/stack isomorphism to a grammar working on+-- stacks. Works for types with one data constructor. For sum types use 'match'+-- and 'Coproduct'.+with+  :: forall a b s t c d f p.+     ( Generic a+     , MkPrismList (Rep a)+     , MkStackPrism f+     , Rep a ~ M1 D d (M1 C c f)+     , StackPrismLhs f t ~ b+     , Constructor c+     ) =>+     (Grammar p b (a :- t) -> Grammar p s (a :- t))+  -> Grammar p s (a :- t)+with g =+  let PrismList (P prism) = mkRevPrismList+      name = conName (undefined :: m c f e)+  in g (PartialIso+         (fwd prism)+         (maybe (Left $ expected ("constructor " <> pack name)) Right . bkwd prism))++-- | Combine all grammars provided in 'Coproduct' list into a single grammar.+match+  :: ( Generic a+     , MkPrismList (Rep a)+     , Match (Rep a) bs t+     , bs ~ Coll (Rep a) t+     ) =>+     Coproduct p s bs a t+  -> Grammar p s (a :- t)+match = fst . match' mkRevPrismList++-- | Heterogenous list of grammars, each one matches a data constructor of type+-- @a@. 'With' is used to provide a data constructor/stack isomorphism to a+-- grammar working on stacks. 'End' ends the list of matches.+data Coproduct p s bs a t where++  With+    :: (Grammar p b (a :- t) -> Grammar p s (a :- t))+    -> Coproduct p s bs a t+    -> Coproduct p s (b ': bs) a t++  End :: Coproduct p s '[] a t++----------------------------------------------------------------------+-- Machinery++type family (:++) (as :: [k]) (bs :: [k]) :: [k] where+  (:++) (a ': as) bs = a ': (as :++ bs)+  (:++) '[] bs = bs++type family Coll (f :: * -> *) (t :: *) :: [*] where+  Coll (f :+: g)  t = Coll f t :++ Coll g t+  Coll (M1 D c f) t = Coll f t+  Coll (M1 C c f) t = '[StackPrismLhs f t]++type family Trav (t :: * -> *) (l :: [*]) :: [*] where+  Trav (f :+: g) lst = Trav g (Trav f lst)+  Trav (M1 D c f) lst = Trav f lst+  Trav (M1 C c f) (l ': ls) = ls++class Match (f :: * -> *) bs t where+  match' :: PrismList f a+         -> Coproduct p s bs a t+         -> ( Grammar p s (a :- t)+            , Coproduct p s (Trav f bs) a t+            )++instance (Match f bs t, Trav f bs ~ '[]) => Match (M1 D c f) bs t where+  match' (PrismList p) = match' p++instance+  ( Match f bs t+  , Match g (Trav f bs) t+  ) => Match (f :+: g) bs t where+  match' (p :& q) lst =+    let (gp, rest)  = match' p lst+        (qp, rest') = match' q rest+    in (gp <> qp, rest')++instance (StackPrismLhs f t ~ b, Constructor c) => Match (M1 C c f) (b ': bs) t where+  match' (P prism) (With g rest) =+    let name = conName (undefined :: m c f e)+        p = fwd prism+        q = maybe (Left $ expected ("constructor " <> pack name)) Right . bkwd prism+    in (g $ PartialIso p q, rest)++-- NB. The following machinery is heavily based on+-- https://github.com/MedeaMelana/stack-prism/blob/master/Data/StackPrism/Generic.hs++-- | Derive a list of stack prisms. For more information on the shape of a+-- 'PrismList', please see the documentation below.+mkRevPrismList :: (Generic a, MkPrismList (Rep a)) => StackPrisms a+mkRevPrismList = mkPrismList' to (Just . from)++type StackPrism a b = forall p f. (Choice p, Applicative f) => p a (f a) -> p b (f b)++-- | Construct a prism.+stackPrism :: (a -> b) -> (b -> Maybe a) -> StackPrism a b+stackPrism f g = dimap (\b -> maybe (Left b) Right (g b)) (either pure (fmap f)) . right'++-- | Apply a prism in forward direction.+fwd :: StackPrism a b -> a -> b+fwd l = runIdentity #. unTagged #. l .# Tagged .# Identity++-- | Apply a prism in backward direction.+bkwd :: StackPrism a b -> b -> Maybe a+bkwd l = getFirst #. getConst #. l (Const #. First #. Just)++-- | Convenient shorthand for a 'PrismList' indexed by a type and its generic+-- representation.+type StackPrisms a = PrismList (Rep a) a++-- | A data family that is indexed on the building blocks from representation+-- types from @GHC.Generics@. It builds up to a list of prisms, one for each+-- constructor in the generic representation. The list is wrapped in the unary+-- constructor @PrismList@. Within that constructor, the prisms are separated by+-- the right-associative binary infix constructor @:&@. Finally, the individual+-- prisms are wrapped in the unary constructor @P@.+--+-- As an example, here is how to define the prisms @nil@ and @cons@ for @[a]@,+-- which is an instance of @Generic@:+--+-- > nil  :: StackPrism              t  ([a] :- t)+-- > cons :: StackPrism (a :- [a] :- t) ([a] :- t)+-- > PrismList (P nil :& P cons) = mkPrismList :: StackPrisms [a]+data family PrismList (f :: * -> *) (a :: *)++class MkPrismList (f :: * -> *) where+  mkPrismList' :: (f p -> a) -> (a -> Maybe (f q)) -> PrismList f a++data instance PrismList (M1 D c f) a = PrismList (PrismList f a)++instance MkPrismList f => MkPrismList (M1 D c f) where+  mkPrismList' f' g' = PrismList (mkPrismList' (f' . M1) (fmap unM1 . g'))++infixr :&+data instance PrismList (f :+: g) a = PrismList f a :& PrismList g a++instance (MkPrismList f, MkPrismList g) => MkPrismList (f :+: g) where+  mkPrismList' f' g' = f f' g' :& g f' g'+    where+      f :: forall a p q. ((f :+: g) p -> a) -> (a -> Maybe ((f :+: g) q)) -> PrismList f a+      f _f' _g' = mkPrismList' (\fp -> _f' (L1 fp)) (matchL _g')+      g :: forall a p q. ((f :+: g) p -> a) -> (a -> Maybe ((f :+: g) q)) -> PrismList g a+      g _f' _g' = mkPrismList' (\gp -> _f' (R1 gp)) (matchR _g')++      matchL :: (a -> Maybe ((f :+: g) q)) -> a -> Maybe (f q)+      matchL _g' a = case _g' a of+        Just (L1 f'') -> Just f''+        _ -> Nothing++      matchR :: (a -> Maybe ((f :+: g) q)) -> a -> Maybe (g q)+      matchR _g' a = case _g' a of+        Just (R1 g'') -> Just g''+        _ -> Nothing++data instance PrismList (M1 C c f) a = P (forall t. StackPrism (StackPrismLhs f t) (a :- t))++instance MkStackPrism f => MkPrismList (M1 C c f) where+  mkPrismList' f' g' = P (stackPrism (f f') (g g'))+    where+      f :: forall a p t. (M1 C c f p -> a) -> StackPrismLhs f t -> a :- t+      f _f' lhs = mapHead (_f' . M1) (mkR lhs)+      g :: forall a p t. (a -> Maybe (M1 C c f p)) -> (a :- t) -> Maybe (StackPrismLhs f t)+      g _g' (a :- t) = fmap (mkL . (:- t) . unM1) (_g' a)++-- Deriving types and conversions for single constructors++type family StackPrismLhs (f :: * -> *) (t :: *) :: *++class MkStackPrism (f :: * -> *) where+  mkR :: forall p t. StackPrismLhs f t -> (f p :- t)+  mkL :: forall p t. (f p :- t) -> StackPrismLhs f t++type instance StackPrismLhs U1 t = t+instance MkStackPrism U1 where+  mkR t         = U1 :- t+  mkL (U1 :- t) = t++type instance StackPrismLhs (K1 i a) t = a :- t+instance MkStackPrism (K1 i a) where+  mkR (h :- t) = K1 h :- t+  mkL (K1 h :- t) = h :- t++type instance StackPrismLhs (M1 i c f) t = StackPrismLhs f t+instance MkStackPrism f => MkStackPrism (M1 i c f) where+  mkR = mapHead M1 . mkR+  mkL = mkL . mapHead unM1++type instance StackPrismLhs (f :*: g) t = StackPrismLhs g (StackPrismLhs f t)+instance (MkStackPrism f, MkStackPrism g) => MkStackPrism (f :*: g) where+  mkR t = (hg :*: hf) :- tg+    where+      hf :- tf = mkR t+      hg :- tg = mkR tf+  mkL ((hf :*: hg) :- t) = mkL (hg :- mkL (hf :- t))++mapHead :: (a -> b) -> (a :- t) -> (b :- t)+mapHead f (h :- t) = f h :- t
+ src/Data/InvertibleGrammar/Monad.hs view
@@ -0,0 +1,205 @@+{-# LANGUAGE CPP               #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns      #-}++module Data.InvertibleGrammar.Monad+  ( module Control.Monad.ContextError+  , runGrammar+  , runGrammarDoc+  , runGrammarString+  , ErrorMessage (..)+  , doAnnotate+  , doDive+  , doStep+  , doLocate+  , doError+  , Propagation+  , GrammarError (..)+  , Mismatch+  , expected+  , unexpected+  ) where++import Control.Arrow (left)+import Control.Applicative+import Control.Monad.ContextError++import Data.Maybe+import Data.Semigroup as Semi+import Data.Set (Set)+import qualified Data.Set as S+import Data.Text (Text)+import GHC.Generics++import Data.Text.Prettyprint.Doc+  ( Doc, Pretty, pretty, vsep, hsep, line, indent, fillSep, punctuate+  , comma, colon, (<+>), layoutSmart, PageWidth(..), LayoutOptions(..)+  )++#if MIN_VERSION_prettyprinter(1,2,0)+import Data.Text.Prettyprint.Doc.Render.String+#else+import Data.Text.Prettyprint.Doc (SimpleDocStream)+import Data.Text.Prettyprint.Doc.Render.ShowS++renderString :: SimpleDocStream ann -> String+renderString stream = renderShowS stream ""+#endif+++initPropagation :: p -> Propagation p+initPropagation = Propagation [0] []++data Propagation p = Propagation+  { pProp :: [Int]+  , pAnns :: [Text]+  , pPos  :: p+  } deriving (Show)++instance Eq (Propagation p) where+  Propagation xs _ _ == Propagation ys _ _ = xs == ys+  {-# INLINE (==) #-}++instance Ord (Propagation p) where+  compare (Propagation as _ _) (Propagation bs _ _) =+    reverse as `compare` reverse bs+  {-# INLINE compare #-}++-- | Data type to encode mismatches during parsing or generation, kept+-- abstract. Use 'expected' and 'unexpected' constructors to build a+-- mismatch report.+data Mismatch = Mismatch+  { mismatchExpected :: Set Text+  , mismatchGot :: Maybe Text+  } deriving (Show, Eq)++-- | Construct a mismatch report with specified expectation. Can be+-- appended to other expectations and 'unexpected' reports to clarify+-- a mismatch.+expected :: Text -> Mismatch+expected a = Mismatch (S.singleton a) Nothing++-- | Construct a mismatch report with information what occurred during+-- the processing but was not expected.+unexpected :: Text -> Mismatch+unexpected a = Mismatch S.empty (Just a)++instance Semigroup Mismatch where+  m <> m' =+    Mismatch+      (mismatchExpected m Semi.<> mismatchExpected m')+      (mismatchGot m <|> mismatchGot m')+  {-# INLINE (<>) #-}++instance Monoid Mismatch where+  mempty = Mismatch mempty mempty+  {-# INLINE mempty #-}+  mappend = (<>)+  {-# INLINE mappend #-}++-- | Run a 'forward' or 'backward' pass of a 'Grammar'.+runGrammar :: p -> ContextError (Propagation p) (GrammarError p) a -> Either (ErrorMessage p) a+runGrammar initPos m =+  case runContextError m (initPropagation initPos) of+    Left (GrammarError p mismatch) ->+      Left $ ErrorMessage+        (pPos p)+        (reverse (pAnns p))+        (mismatchExpected mismatch)+        (mismatchGot mismatch)+    Right a ->+      Right a++-- | Run a 'forward' or 'backward' pass of a 'Grammar', report errors+-- as pretty printed 'Doc' message.+runGrammarDoc :: (Pretty p) => p -> ContextError (Propagation p) (GrammarError p) a -> Either (Doc ann) a+runGrammarDoc initPos m =+  left (ppError pretty) $+    runGrammar initPos m++-- | Run a 'forward' or 'backward' pass of a 'Grammar', report errors+-- as 'String' message.+runGrammarString :: (Show p) => p -> ContextError (Propagation p) (GrammarError p) a -> Either String a+runGrammarString initPos m =+  left (renderString . layoutSmart (LayoutOptions (AvailablePerLine 79 0.75)) . ppError (pretty . show)) $+    runGrammar initPos m++-- | 'Grammar' run error messages type.+data ErrorMessage p = ErrorMessage+  { emPosition :: p+  , emAnnotations :: [Text]+  , emExpected :: Set Text+  , emGot :: Maybe Text+  } deriving (Eq, Ord, Generic)++instance (Pretty p) => Pretty (ErrorMessage p) where+  pretty = ppError pretty++ppMismatch :: Set Text -> Maybe Text -> Doc ann+ppMismatch (S.toList -> []) Nothing =+  "Unknown mismatch occurred"+ppMismatch (S.toList -> []) unexpected =+  "Unexpected:" <+> pretty unexpected+ppMismatch (S.toList -> expected) Nothing =+  "Expected:" <+> fillSep (punctuate comma $ map pretty expected)+ppMismatch (S.toList -> expected) (Just got) =+  vsep+  [ "Expected:" <+> fillSep (punctuate comma $ map pretty expected)+  , "But got: " <+> pretty got+  ]++ppError :: (p -> Doc ann) -> ErrorMessage p -> Doc ann+ppError ppPosition (ErrorMessage pos annotations expected got) =+  vsep $ catMaybes+    [ Just $ ppPosition pos `mappend` ":" <+> "mismatch:"+    , if null annotations+      then Nothing+      else Just $ indent 2 $ "In" <+> hsep (punctuate (comma <> line <> "in") $ map pretty annotations) <> colon+    , Just $ indent 4 $ ppMismatch expected got+    ]++data GrammarError p = GrammarError (Propagation p) Mismatch+  deriving (Show)++instance Semigroup (GrammarError p) where+  GrammarError pos m <> GrammarError pos' m'+    | pos > pos' = GrammarError pos m+    | pos < pos' = GrammarError pos' m'+    | otherwise  = GrammarError pos (m <> m')+  {-# INLINE (<>) #-}++doAnnotate :: MonadContextError (Propagation p) e m => Text -> m a -> m a+doAnnotate ann =+  localContext $ \propagation ->+    propagation { pAnns = ann : pAnns propagation }+{-# INLINE doAnnotate #-}++doDive :: MonadContextError (Propagation p) e m => m a -> m a+doDive =+  localContext $ \propagation ->+    propagation { pProp = 0 : pProp propagation }+{-# INLINE doDive #-}++doStep :: MonadContextError (Propagation p) e m => m ()+doStep =+  modifyContext $ \propagation ->+    propagation+      { pProp = case pProp propagation of+          (x : xs) -> succ x : xs+          [] -> [0]+      }+{-# INLINE doStep #-}++doLocate :: MonadContextError (Propagation p) e m => p -> m ()+doLocate pos =+  modifyContext $ \propagation ->+    propagation { pPos = pos }+{-# INLINE doLocate #-}++doError :: MonadContextError (Propagation p) (GrammarError p) m => Mismatch -> m a+doError mismatch =+  throwInContext $ \ctx ->+    GrammarError ctx mismatch+{-# INLINE doError #-}
+ src/Data/InvertibleGrammar/TH.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE CPP             #-}+{-# LANGUAGE LambdaCase      #-}+{-# LANGUAGE TemplateHaskell #-}++module Data.InvertibleGrammar.TH where++#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif+import Data.Foldable (toList)+import Data.InvertibleGrammar.Base+import Data.Maybe+import Data.Text (pack)+import Language.Haskell.TH as TH+import Data.Set (Set)+import qualified Data.Set as S+#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup ((<>))+#endif+++{- | Build a prism and the corresponding grammar that will match on the+     given constructor and convert it to reverse sequence of :- stacks.++     E.g. consider a data type:++     > data FooBar a b c = Foo a b c | Bar++     For constructor Foo++     > fooGrammar = $(grammarFor 'Foo)++     will expand into++     > fooGrammar = PartialIso+     >   (\(c :- b :- a :- t) -> Foo a b c :- t)+     >   (\case { Foo a b c :- t -> Just $ c :- b :- a :- t; _ -> Nothing })++     Note the order of elements on the stack:++     > ghci> :t fooGrammar+     > fooGrammar :: Grammar p (c :- (b :- (a :- t))) (FooBar a b c :- t)+-}++grammarFor :: Name -> ExpQ+grammarFor constructorName = do+#if defined(__GLASGOW_HASKELL__)+# if __GLASGOW_HASKELL__ <= 710+  DataConI realConstructorName _typ parentName _fixity <- reify constructorName+# else+  DataConI realConstructorName _typ parentName <- reify constructorName+# endif+#endif+  TyConI dataDef <- reify parentName++  let Just (single, constructorInfo) = do+        (single, allConstr) <- constructors dataDef+        constr <- findConstructor realConstructorName allConstr+        return (single, constr)++  let ts = fieldTypes constructorInfo+  vs <- mapM (const $ newName "x") ts+  t <- newName "t"++  let matchStack []      = varP t+      matchStack (_v:vs) = [p| $(varP _v) :- $_vs' |]+        where+          _vs' = matchStack vs+      fPat  = matchStack vs+      buildConstructor = foldr (\v acc -> appE acc (varE v)) (conE realConstructorName) vs+      fBody = [e| $buildConstructor :- $(varE t) |]+      fFunc = lamE [fPat] fBody++  let gPat  = [p| $_matchConsructor :- $(varP t) |]+        where+          _matchConsructor = conP realConstructorName (map varP (reverse vs))+      gBody = foldr (\v acc -> [e| $(varE v) :- $acc |]) (varE t) vs+      gFunc = lamCaseE $ catMaybes+        [ Just $ TH.match gPat (normalB [e| Right ($gBody) |]) []+        , if single+          then Nothing+          else Just $ TH.match wildP (normalB [e| Left (expected $ "constructor " <> pack ( $(stringE (show constructorName))) ) |]) []+        ]++  [e| PartialIso $fFunc $gFunc |]+++{- | Build prisms and corresponding grammars for all data constructors of given+     type. Expects grammars to zip built ones with.++     > $(match ''Maybe)++     Will expand into a lambda:++     > (\nothingG justG -> ($(grammarFor 'Nothing) . nothingG) <>+     >                     ($(grammarFor 'Just)    . justG))+-}+match :: Name -> ExpQ+match tyName = do+  names  <- concatMap (toList . constructorNames) <$> (extractConstructors =<< reify tyName)+  argTys <- mapM (\_ -> newName "a") names+  let grammars = map (\(con, arg) -> [e| $(varE arg) $(grammarFor con) |]) (zip names argTys)+  lamE (map varP argTys) (foldr1 (\e1 e2 -> [e| $e1 <> $e2 |]) grammars)+  where+    extractConstructors :: Info -> Q [Con]+    extractConstructors (TyConI dataDef) =+      case constructors dataDef of+        Just (_, cs) -> pure cs+        Nothing      -> fail $ "Data type " ++ show tyName ++ " defines no constructors"+    extractConstructors _ =+      fail $ "Data definition expected for name " ++ show tyName++----------------------------------------------------------------------+-- Utils++constructors :: Dec -> Maybe (Bool, [Con])+#if defined(__GLASGOW_HASKELL__)+# if __GLASGOW_HASKELL__ <= 710+constructors (DataD _ _ _ cs _)     = Just (length cs == 1, cs)+constructors (NewtypeD _ _ _ c _)   = Just (True, [c])+# else+constructors (DataD _ _ _ _ cs _)   = Just (length cs == 1, cs)+constructors (NewtypeD _ _ _ _ c _) = Just (True, [c])+# endif+#endif+constructors _                      = Nothing++findConstructor :: Name -> [Con] -> Maybe Con+findConstructor _    [] = Nothing+findConstructor name (c:cs)+  | name `S.member` constructorNames c = Just c+  | otherwise                          = findConstructor name cs++constructorNames :: Con -> Set Name+constructorNames = \case+  NormalC name _   -> S.singleton name+  RecC name _      -> S.singleton name+  InfixC _ name _  -> S.singleton name+  ForallC _ _ con' -> constructorNames con'+#if MIN_VERSION_template_haskell(2, 11, 0)+  GadtC cs _ _     -> S.fromList cs+  RecGadtC cs _ _  -> S.fromList cs+#endif++fieldTypes :: Con -> [Type]+fieldTypes = \case+  NormalC _ fieldTypes  -> map extractType fieldTypes+  RecC _ fieldTypes     -> map extractType' fieldTypes+  InfixC (_,a) _b (_,b) -> [a, b]+  ForallC _ _ con'      -> fieldTypes con'+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 800+  GadtC _ fs _          -> map extractType fs+  RecGadtC _ fs _       -> map extractType' fs+#endif+  where+    extractType (_, t) = t+    extractType' (_, _, t) = t