#ifndef MIN_VERSION_GLASGOW_HASKELL
#define MIN_VERSION_GLASGOW_HASKELL(a,b,c,d) 0
#endif
  
#if MIN_VERSION_GLASGOW_HASKELL(7,10,0,0)
#else
#endif
module Language.Syntactic.Syntax
    ( 
      AST (..)
    , ASTF
    , ASTFull (..)
    , Full (..)
    , (:->) (..)
    , SigRep (..)
    , Signature (..)
    , DenResult
    , Symbol (..)
    , size
      
    , SmartFun
    , SmartSig
    , SmartSym
    , smartSym'
      
    , (:+:) (..)
    , Project (..)
    , (:<:) (..)
    , smartSym
    , smartSymTyped
    , Empty
      
    , E (..)
    , liftE
    , liftE2
    , EF (..)
    , liftEF
    , liftEF2
      
    , Typed (..)
    , injT
    , castExpr
      
    , NFData1 (..)
    , symType
    , prjP
    ) where
import Control.DeepSeq
import Data.Typeable
#if MIN_VERSION_GLASGOW_HASKELL(7,10,0,0)
#else
import Data.Foldable (Foldable)
import Data.Proxy  
import Data.Traversable (Traversable)
#endif
data AST sym sig
  where
    Sym  :: sym sig -> AST sym sig
    (:$) :: AST sym (a :-> sig) -> AST sym (Full a) -> AST sym sig
infixl 1 :$
type ASTF sym a = AST sym (Full a)
newtype ASTFull sym a = ASTFull {unASTFull :: ASTF sym a}
instance Functor sym => Functor (AST sym)
  where
    fmap f (Sym s)  = Sym (fmap f s)
    fmap f (s :$ a) = fmap (fmap f) s :$ a
newtype Full a = Full { result :: a }
  deriving (Eq, Show, Typeable, Functor)
newtype a :-> sig = Partial (a -> sig)
  deriving (Typeable, Functor)
infixr :->
data SigRep sig
  where
    SigFull :: SigRep (Full a)
    SigMore :: SigRep sig -> SigRep (a :-> sig)
class Signature sig
  where
    signature :: SigRep sig
instance Signature (Full a)
  where
    signature = SigFull
instance Signature sig => Signature (a :-> sig)
  where
    signature = SigMore signature
type family   DenResult sig
type instance DenResult (Full a)    = a
type instance DenResult (a :-> sig) = DenResult sig
class Symbol sym
  where
    
    symSig :: sym sig -> SigRep sig
instance NFData1 sym => NFData (AST sym sig)
  where
    rnf (Sym s)  = liftRnf (`seq` ()) s
    rnf (s :$ a) = rnf s `seq` rnf a
size :: AST sym sig -> Int
size (Sym _)  = 1
size (s :$ a) = size s + size a
type family   SmartFun (sym :: * -> *) sig
type instance SmartFun sym (Full a)    = ASTF sym a
type instance SmartFun sym (a :-> sig) = ASTF sym a -> SmartFun sym sig
type family   SmartSig f
type instance SmartSig (AST sym sig)     = sig
type instance SmartSig (ASTF sym a -> f) = a :-> SmartSig f
type family   SmartSym f :: * -> *
type instance SmartSym (AST sym sig) = sym
type instance SmartSym (a -> f)      = SmartSym f
smartSym' :: forall sig f sym
    .  ( Signature sig
       , f   ~ SmartFun sym sig
       , sig ~ SmartSig f
       , sym ~ SmartSym f
       )
    => sym sig -> f
smartSym' s = go (signature :: SigRep sig) (Sym s)
  where
    go :: forall sig . SigRep sig -> AST sym sig -> SmartFun sym sig
    go SigFull s       = s
    go (SigMore sig) s = \a -> go sig (s :$ a)
data (sym1 :+: sym2) sig
  where
    InjL :: sym1 a -> (sym1 :+: sym2) a
    InjR :: sym2 a -> (sym1 :+: sym2) a
  deriving (Functor, Foldable, Traversable)
infixr :+:
instance (Symbol sym1, Symbol sym2) => Symbol (sym1 :+: sym2)
  where
    symSig (InjL s) = symSig s
    symSig (InjR s) = symSig s
instance (NFData1 sym1, NFData1 sym2) => NFData1 (sym1 :+: sym2)
  where
    liftRnf r (InjL s) = liftRnf r s
    liftRnf r (InjR s) = liftRnf r s
class Project sub sup
  where
    
    prj :: sup a -> Maybe (sub a)
instance  Project sub sup => Project sub (AST sup)
  where
    prj (Sym s) = prj s
    prj _       = Nothing
instance  Project sym sym
  where
    prj = Just
instance  Project sym1 (sym1 :+: sym2)
  where
    prj (InjL a) = Just a
    prj _        = Nothing
instance  Project sym1 sym3 => Project sym1 (sym2 :+: sym3)
  where
    prj (InjR a) = prj a
    prj _        = Nothing
instance Project sub sup
  where
    prj _ = Nothing
class Project sub sup => sub :<: sup
  where
    
    inj :: sub a -> sup a
instance  (sub :<: sup) => (sub :<: AST sup)
  where
    inj = Sym . inj
instance  (sym :<: sym)
  where
    inj = id
instance  (sym1 :<: (sym1 :+: sym2))
  where
    inj = InjL
instance  (sym1 :<: sym3) => (sym1 :<: (sym2 :+: sym3))
  where
    inj = InjR . inj
smartSym
    :: ( Signature sig
       , f   ~ SmartFun sup sig
       , sig ~ SmartSig f
       , sup ~ SmartSym f
       , sub :<: sup
       )
    => sub sig -> f
smartSym = smartSym' . inj
smartSymTyped
    :: ( Signature sig
       , f         ~ SmartFun (Typed sup) sig
       , sig       ~ SmartSig f
       , Typed sup ~ SmartSym f
       , sub :<: sup
       , Typeable (DenResult sig)
       )
    => sub sig -> f
smartSymTyped = smartSym' . Typed . inj
data Empty :: * -> *
data E e
  where
    E :: e a -> E e
liftE :: (forall a . e a -> b) -> E e -> b
liftE f (E a) = f a
liftE2 :: (forall a b . e a -> e b -> c) -> E e -> E e -> c
liftE2 f (E a) (E b) = f a b
data EF e
  where
    EF :: e (Full a) -> EF e
liftEF :: (forall a . e (Full a) -> b) -> EF e -> b
liftEF f (EF a) = f a
liftEF2 :: (forall a b . e (Full a) -> e (Full b) -> c) -> EF e -> EF e -> c
liftEF2 f (EF a) (EF b) = f a b
data Typed sym sig
  where
    Typed :: Typeable (DenResult sig) => sym sig -> Typed sym sig
instance  Project sub sup => Project sub (Typed sup)
  where
    prj (Typed s) = prj s
injT :: (sub :<: sup, Typeable (DenResult sig)) =>
    sub sig -> AST (Typed sup) sig
injT = Sym . Typed . inj
castExpr :: forall sym a b
    .  ASTF (Typed sym) a  
    -> ASTF (Typed sym) b  
    -> Maybe (ASTF (Typed sym) b)
castExpr a b = cast1 a
  where
    cast1 :: (DenResult sig ~ a) => AST (Typed sym) sig -> Maybe (ASTF (Typed sym) b)
    cast1 (s :$ _) = cast1 s
    cast1 (Sym (Typed _)) = cast2 b
      where
        cast2 :: (DenResult sig ~ b) => AST (Typed sym) sig -> Maybe (ASTF (Typed sym) b)
        cast2 (s :$ _)        = cast2 s
        cast2 (Sym (Typed _)) = gcast a
  
  
  
  
  
  
  
  
  
symType :: Proxy sym -> sym sig -> sym sig
symType _ = id
prjP :: Project sub sup => Proxy sub -> sup sig -> Maybe (sub sig)
prjP _ = prj