{-# LANGUAGE DataKinds, TypeFamilies, PolyKinds, DeriveDataTypeable,
             StandaloneDeriving, FlexibleInstances, ConstraintKinds #-}
module Data.Singletons.Syntax where
import Prelude hiding ( exp )
import Data.Kind (Constraint, Type)
import Language.Haskell.TH.Syntax hiding (Type)
import Language.Haskell.TH.Desugar
import Data.Map.Strict ( Map )
import qualified Data.Map.Strict as Map
import Data.Set ( Set )
import Data.Semigroup (Semigroup(..))
type VarPromotions = [(Name, Name)] 
data PromDPatInfos = PromDPatInfos
  { prom_dpat_vars    :: VarPromotions
      
  , prom_dpat_sig_kvs :: Set Name
      
      
  }
instance Semigroup PromDPatInfos where
  PromDPatInfos vars1 sig_kvs1 <> PromDPatInfos vars2 sig_kvs2
    = PromDPatInfos (vars1 <> vars2) (sig_kvs1 <> sig_kvs2)
instance Monoid PromDPatInfos where
  mempty = PromDPatInfos mempty mempty
type SingDSigPaInfos = [(DExp, DType)]
data DataDecl = DataDecl Name [DTyVarBndr] [DCon]
data TySynDecl = TySynDecl Name [DTyVarBndr]
type OpenTypeFamilyDecl = TypeFamilyDecl 'Open
type ClosedTypeFamilyDecl = TypeFamilyDecl 'Closed
newtype TypeFamilyDecl (info :: FamilyInfo)
  = TypeFamilyDecl { getTypeFamilyDecl :: DTypeFamilyHead }
data FamilyInfo = Open | Closed
data ClassDecl ann = ClassDecl { cd_cxt  :: DCxt
                               , cd_name :: Name
                               , cd_tvbs :: [DTyVarBndr]
                               , cd_fds  :: [FunDep]
                               , cd_lde  :: LetDecEnv ann
                               }
data InstDecl  ann = InstDecl { id_cxt     :: DCxt
                              , id_name    :: Name
                              , id_arg_tys :: [DType]
                              , id_sigs    :: Map Name DType
                              , id_meths   :: [(Name, LetDecRHS ann)] }
type UClassDecl = ClassDecl Unannotated
type UInstDecl  = InstDecl Unannotated
type AClassDecl = ClassDecl Annotated
type AInstDecl  = InstDecl Annotated
data ADExp = ADVarE Name
           | ADConE Name
           | ADLitE Lit
           | ADAppE ADExp ADExp
           | ADLamE [Name]         
                    DType          
                    [Name] ADExp
           | ADCaseE ADExp [ADMatch] DType
               
           | ADLetE ALetDecEnv ADExp
           | ADSigE DType          
                    ADExp DType
data ADPat = ADLitPa Lit
           | ADVarPa Name
           | ADConPa Name [ADPat]
           | ADTildePa ADPat
           | ADBangPa ADPat
           | ADSigPa DType 
                           
                     ADPat DType
           | ADWildPa
data ADMatch = ADMatch VarPromotions ADPat ADExp
data ADClause = ADClause VarPromotions
                         [ADPat] ADExp
data AnnotationFlag = Annotated | Unannotated
type Annotated   = 'Annotated
type Unannotated = 'Unannotated
type family IfAnn (ann :: AnnotationFlag) (yes :: k) (no :: k) :: k where
  IfAnn Annotated   yes no = yes
  IfAnn Unannotated yes no = no
data family LetDecRHS :: AnnotationFlag -> Type
data instance LetDecRHS Annotated
  = AFunction DType  
    Int    
    [ADClause]
  | AValue DType 
    Int   
    ADExp
data instance LetDecRHS Unannotated = UFunction [DClause]
                                    | UValue DExp
type ALetDecRHS = LetDecRHS Annotated
type ULetDecRHS = LetDecRHS Unannotated
data LetDecEnv ann = LetDecEnv
                   { lde_defns :: Map Name (LetDecRHS ann)
                   , lde_types :: Map Name DType   
                   , lde_infix :: Map Name Fixity  
                   , lde_proms :: IfAnn ann (Map Name DType) () 
                   , lde_bound_kvs :: IfAnn ann (Map Name (Set Name)) ()
                     
                     
                     
                   }
type ALetDecEnv = LetDecEnv Annotated
type ULetDecEnv = LetDecEnv Unannotated
instance Semigroup ULetDecEnv where
  LetDecEnv defns1 types1 infx1 _ _ <> LetDecEnv defns2 types2 infx2 _ _ =
    LetDecEnv (defns1 <> defns2) (types1 <> types2) (infx1 <> infx2) () ()
instance Monoid ULetDecEnv where
  mempty = LetDecEnv Map.empty Map.empty Map.empty () ()
valueBinding :: Name -> ULetDecRHS -> ULetDecEnv
valueBinding n v = emptyLetDecEnv { lde_defns = Map.singleton n v }
typeBinding :: Name -> DType -> ULetDecEnv
typeBinding n t = emptyLetDecEnv { lde_types = Map.singleton n t }
infixDecl :: Fixity -> Name -> ULetDecEnv
infixDecl f n = emptyLetDecEnv { lde_infix = Map.singleton n f }
emptyLetDecEnv :: ULetDecEnv
emptyLetDecEnv = mempty
buildLetDecEnv :: Quasi q => [DLetDec] -> q ULetDecEnv
buildLetDecEnv = go emptyLetDecEnv
  where
    go acc [] = return acc
    go acc (DFunD name clauses : rest) =
      go (valueBinding name (UFunction clauses) <> acc) rest
    go acc (DValD (DVarPa name) exp : rest) =
      go (valueBinding name (UValue exp) <> acc) rest
    go acc (dec@(DValD {}) : rest) = do
      flattened <- flattenDValD dec
      go acc (flattened ++ rest)
    go acc (DSigD name ty : rest) =
      go (typeBinding name ty <> acc) rest
    go acc (DInfixD f n : rest) =
      go (infixDecl f n <> acc) rest
    go acc (DPragmaD{} : rest) = go acc rest
data DerivedDecl (cls :: Type -> Constraint) = DerivedDecl
  { ded_mb_cxt :: Maybe DCxt
  , ded_type   :: DType
  , ded_decl   :: DataDecl
  }
type DerivedEqDecl   = DerivedDecl Eq
type DerivedShowDecl = DerivedDecl Show