packages feed

deep-transformations-0.4: src/Transformation/AG/Dimorphic.hs

{-# Language Haskell2010, DefaultSignatures, DeriveDataTypeable,
             FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes,
             ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}

-- | A special case of an attribute grammar where every node has only a single inherited and a single synthesized
-- attribute of the same monoidal type. The synthesized attributes of child nodes are all 'mconcat`ted together.

module Transformation.AG.Dimorphic where

import Data.Data (Data, Typeable)
import Data.Functor.Const (Const(..))
import Data.Kind (Type)
import Data.Semigroup (Semigroup(..))
import Unsafe.Coerce (unsafeCoerce)
import qualified Rank2
import Transformation (Transformation, Domain, Codomain)
import qualified Transformation
import qualified Transformation.Deep as Deep
import qualified Transformation.Full as Full
import qualified Transformation.AG as AG

-- | Wrapper that provides a 'Transformation' instance for any 'Attribution'
newtype T t = T t

-- | Wrapper that provides a default 'AG.Attribution' and (via AG.Knit) 'Transformation' instance for any 'Attribution'
newtype Auto t = Auto t

-- | Node attributes
data Atts a b = Atts{
   -- | inherited
   inh :: a,
   -- | synthesized
   syn :: b}
   deriving (Data, Typeable, Show)

instance (Semigroup a, Semigroup b) => Semigroup (Atts a b) where
   Atts i1 s1 <> Atts i2 s2 = Atts (i1 <> i2) (s1 <> s2)

instance (Monoid a, Monoid b) => Monoid (Atts a b) where
   mappend = (<>)
   mempty = Atts mempty mempty

-- | A node's 'Semantics' maps its inherited attribute to its synthesized attribute.
type Semantics a b = Const (a -> b)

-- | An attribution rule maps a node's inherited attribute and its child nodes' synthesized attributes to the node's
-- synthesized attribute and the children nodes' inherited attributes.
type Rule a b = Atts a b -> Atts a b

-- | Class of transformations that assign the same type of inherited and synthesized attributes to every node.
class Attribution t where
   type Origin t :: Type -> Type
   type Inherited t :: Type
   type Synthesized t :: Type
   -- | Unwrap the value from the original attribution domain
   unwrap :: t -> Origin t x -> x

-- | The core type class for defining the attribute grammar. The instances of this class typically have a form like
--
-- > instance MyAttGrammar `At` MyNode where
-- >   attribution MyAttGrammar{} (Identity MyNode{})
-- >               Atts{inh= fromParent,
-- >                    syn= fromChildren}
-- >             = Atts{syn= toParent,
-- >                    inh= toChildren}
class Attribution t => At t (g :: (Type -> Type) -> (Type -> Type) -> Type) where
   -- | The attribution rule for a given transformation and node.
   attribution :: forall f. Rank2.Functor (g f) => t -> Origin t (g f f) -> Rule (Inherited t) (Synthesized t)

instance {-# overlappable #-} Attribution t => At t g where
   attribution = const (const id)

instance {-# overlappable #-} (Attribution t, p ~ Origin t, a ~ Inherited t, b ~ Synthesized t,
                               q ~ Semantics a b, Rank2.Foldable (g q), Rank2.Functor (g q),
                               Monoid a, Monoid b, Foldable p, At t g) =>
                              T t `Transformation.At` g (Semantics a b) (Semantics a b) where
   T t $ x = knit (attribution t x) (unwrap t x)
   {-# INLINE ($) #-}

instance (Attribution t, At t g, p ~ Origin t, a ~ Inherited t, b ~ Synthesized t, q ~ Semantics a b,
          Monoid a, Monoid b, Foldable p, Functor p,
          Rank2.Foldable (g q), Rank2.Functor (g p), Rank2.Functor (g q), Deep.Functor (T t) g) =>
         Full.Functor (T t) g where
   (<$>) = Full.mapUpDefault

-- | The core function to tie the recursive knot, turning a 'Rule' for a node into its 'Semantics'.
knit :: (Rank2.Foldable (g sem), sem ~ Semantics a b, Monoid a, Monoid b)
     => Rule a b -> g sem sem -> sem (g sem sem)
knit r chSem = Const knitted
   where knitted inherited = synthesized
            where Atts{syn= synthesized, inh= chInh} = r Atts{inh= inherited, syn= chSyn}
                  chSyn = Rank2.foldMap (($ chInh) . getConst) chSem

instance Attribution t => Transformation (T t) where
   type Domain (T t) = Origin t
   type Codomain (T t) = Semantics (Inherited t) (Synthesized t)

instance (Attribution t, Foldable (Origin t)) => AG.Attribution (Auto t) where
   type Origin (Auto t) = Origin t
   unwrap _ = foldr1 const

type instance AG.Atts (AG.Inherited (Auto t)) g = Inherited t
type instance AG.Atts (AG.Synthesized (Auto t)) g = Synthesized t

instance (Attribution t, f ~ Origin t, Foldable f, At t g,
          Rank2.Foldable (g (AG.Semantics (Auto t))), Rank2.Functor (g (AG.Semantics (Auto t))),
          Monoid (Synthesized t)) => Auto t `AG.At` g where
   attribution (Auto t) x (inherited, chSyn) = (AG.Synthesized $ unsafeCoerce $ syn result, unsafeCoerce chInh)
      where result = attribution t x Atts{inh=AG.inh inherited, syn=Rank2.foldMap AG.syn chSyn}
            chInh = uniformInheritance Rank2.<$> foldr const (error "Missing node") x
            uniformInheritance :: forall p a. p a -> AG.Inherited (Auto t) a
            uniformInheritance = const $ AG.Inherited (AG.inh inherited)