packages feed

g2-0.1.0.0: src/G2/Language/Naming.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

module G2.Language.Naming
    ( nameOcc
    , nameModule
    , nameLoc
    , NameGen
    , Named (names, rename, renames)
    , doRename
    , doRenames
    , renameAll
    , nameToStr
    , strToName
    , mkNameGen
    , varIds
    , varNames
    , exprNames
    , typeNames

    , renameExprs
    , renameExpr
    , renameVars

    , freshSeededString
    , freshSeededStrings

    , freshName
    , freshNames
    , freshSeededName
    , freshSeededNames

    , freshId
    , freshSeededId
    , freshIds
    , freshVar

    , childrenNames

    , mapNG
    ) where

import G2.Language.AST
import G2.Language.KnownValues
import G2.Language.Syntax
import G2.Language.TypeEnv

import Data.Data (Data, Typeable)
import Data.Hashable
import qualified Data.HashMap.Lazy as HM
import qualified Data.HashSet as HS
import Data.List
import Data.List.Utils
import qualified Data.Map as M
import qualified Data.Text as T
import Data.Tuple

nameOcc :: Name -> T.Text
nameOcc (Name occ _ _ _) = occ

nameModule :: Name -> Maybe T.Text
nameModule (Name _ mb _ _) = mb

nameLoc :: Name -> Maybe Span
nameLoc (Name _ _ _ s) = s

-- | Allows the creation of fresh `Name`s.
data NameGen = NameGen { max_uniq :: (HM.HashMap (T.Text, Maybe T.Text) Int)
                       , dc_children :: (HM.HashMap Name [Name]) }
                deriving (Show, Eq, Read, Typeable, Data)

-- nameToStr relies on NameCleaner eliminating all '_', to preserve uniqueness
-- | Converts a name to a string, which is useful to interact with solvers.
nameToStr :: Name -> String
nameToStr (Name n (Just m) i _) = T.unpack n ++ "_m_" ++ T.unpack m ++ "_" ++ show i
nameToStr (Name n Nothing i _) = T.unpack n ++ "_n__" ++ show i

-- | Converts a string generated by nameToStr to a name.
-- Loses location information
strToName :: String -> Name
strToName str =
    let
        (n, _:q:_:mi) = breakList (\s -> isPrefixOf "_m_" s || isPrefixOf "_n_" s) str
        (m, _:i) = break ((==) '_') mi
        m' = if q == 'm' then Just m else Nothing
    in
    Name (T.pack n) (fmap T.pack m') (read i :: Int) Nothing

-- | Initializes a `NameGen`.  The returned `NameGen` is guarenteed to not give any `Name`
-- in the given `Named` type.
mkNameGen :: Named n => n -> NameGen
mkNameGen nmd =
    let
        allNames = names nmd
    in
    NameGen {
          max_uniq = HM.fromListWith max $ map (\(Name n m i _) -> ((n, m), i + 1)) allNames
            -- (foldr (\(Name n m i _) hm -> HM.insertWith max (n, m) (i + 1) hm) 
            --     HM.empty allNames
            -- )
            , dc_children = HM.empty
    }

-- | Returns all @Var@ Ids in an ASTContainer
varIds :: (ASTContainer m Expr) => m -> [Id]
varIds = evalASTs varIds'

varIds' :: Expr -> [Id]
varIds' (Var i) = [i]
varIds' _ = []

varNames :: (ASTContainer m Expr) => m -> [Name]
varNames = map idName . varIds

-- Returns every `Name` that appears in an `Expr`, but ignores those only in `Type`s.
exprNames :: (ASTContainer m Expr) => m -> [Name]
exprNames = evalASTs exprTopNames

exprTopNames :: Expr -> [Name]
exprTopNames (Var var) = [idName var]
exprTopNames (Data dc) = dataConName dc
exprTopNames (Lam _ b _) = [idName b]
exprTopNames (Let kvs _) = map (idName . fst) kvs
exprTopNames (Case _ cvar as) = idName cvar :
                                concatMap (\(Alt am _) -> altMatchNames am)
                                          as
exprTopNames (Assume (Just is) _ _) = [funcName is]
exprTopNames (Assert (Just is) _ _) = [funcName is]
exprTopNames _ = []

altMatchNames :: AltMatch -> [Name]
altMatchNames (DataAlt dc i) = dataConName dc ++ (map idName i)
altMatchNames _ = []

dataConName :: DataCon -> [Name]
dataConName (DataCon n _) = [n]

typeNames :: (ASTContainer m Type) => m -> [Name]
typeNames = evalASTs typeTopNames

typeTopNames :: Type -> [Name]
typeTopNames (TyVar i) = [idName i]
typeTopNames (TyCon n _) = [n]
typeTopNames (TyForAll (NamedTyBndr v) _) = [idName v]
typeTopNames _ = []

doRename :: Named a => Name -> NameGen -> a -> (a, NameGen)
doRename n ngen x = (rename n n' x, ngen')
  where (n', ngen') = freshSeededName n ngen

doRenames :: Named a => [Name] -> NameGen -> a -> (a, NameGen)
doRenames ns ng e =
    let
        (ns', ng') = freshSeededNames ns ng
        hm = HM.fromList $ zip ns ns'
    in
    (renames hm e, ng')

renameAll :: (Named a) => a -> NameGen -> (a, NameGen)
renameAll x ng =
    let
        old = nub $ names x
    in
    doRenames old ng x

-- | Types that contain `Name`@s@
class Named a where
    names :: a -> [Name]
    rename :: Name -> Name -> a -> a
    renames :: HM.HashMap Name Name -> a -> a

    renames hm e = HM.foldrWithKey (\k v -> rename k v) e hm

instance Named Name where
    {-# INLINE names #-}
    names n = [n]
    {-# INLINE rename #-}
    rename old (Name nn nm ni _) n@(Name _ _ _ l) = if old == n then Name nn nm ni l else n
    {-# INLINE renames #-}
    renames hm n@(Name _ _ _ l) =
        case HM.lookupDefault n n hm of
            Name n' m' i _ -> Name n' m' i l

instance Named Id where
    {-# INLINE names #-}
    names (Id n t) = n:names t
    {-# INLINE rename #-}
    rename old new (Id n t) = Id (rename old new n) (rename old new t)
    {-# INLINE renames #-}
    renames hm (Id n t) = Id (renames hm n) (renames hm t)

instance Named Expr where
    names = eval go
        where
            go :: Expr -> [Name]
            go (Var i) = names i
            go (Prim _ t) = names t
            go (Data d) = names d
            go (Lam _ i _) = names i
            go (Let b _) = concatMap (names . fst) b
            go (Case _ i a) = names i ++ concatMap (names . altMatch) a
            go (Type t) = names t
            go (Cast _ c) = names c
            go (Coercion c) = names c
            go (Tick t _) = names t
            go (SymGen t) = names t
            go (Assume is _ _) = names is
            go (Assert is _ _) = names is
            go _ = []

    rename old new = modify go
      where
        go :: Expr -> Expr
        go (Var i) = Var (rename old new i)
        go (Data d) = Data (rename old new d)
        go (Lam u i e) = Lam u (rename old new i) e
        go (Let b e) =
            let b' = map (\(n, e') -> (rename old new n, e')) b
            in Let b' e
        go (Case e i a) =
            Case e (rename old new i) (map goAlt a)
        go (Type t) = Type (rename old new t)
        go (Cast e c) = Cast e (rename old new c)
        go (Coercion c) = Coercion (rename old new c)
        go (Tick t e) = Tick (rename old new t) e
        go (SymGen t) = SymGen (rename old new t)
        go (Assume is e e') = Assume (rename old new is) e e'
        go (Assert is e e') = Assert (rename old new is) e e'
        go e = e

        goAlt :: Alt -> Alt
        goAlt (Alt am e) = Alt (rename old new am) e

    renames hm = modify go
        where
            go :: Expr -> Expr
            go (Var i) = Var (renames hm i)
            go (Data d) = Data (renames hm d)
            go (Lam u i e) = Lam u (renames hm i) e
            go (Let b e) = 
                let b' = map (\(n, e') -> (renames hm n, e')) b
                in Let b' e
            go (Case e i a) = Case e (renames hm i) (map goAlt a)
            go (Type t) = Type (renames hm t)
            go (Cast e c) = Cast e (renames hm c)
            go (Coercion c) = Coercion (renames hm c)
            go (Tick t e) = Tick (renames hm t) e
            go (SymGen t) = SymGen (renames hm t)
            go (Assume is e e') = Assume (renames hm is) e e'
            go (Assert is e e') = Assert (renames hm is) e e'
            go e = e

            goAlt :: Alt -> Alt
            goAlt (Alt am e) = Alt (renames hm am) e

renameExprs :: ASTContainer m Expr => [(Name, Name)] -> m -> m
renameExprs n a = foldr (\(old, new) -> renameExpr old new) a n

-- | Rename only the names in an `Expr` that are the `Name` of an `Id`/`Let`/`Data`/`Case` Binding.
-- Does not change Types.
renameExpr :: ASTContainer m Expr => Name -> Name -> m -> m
renameExpr old new = modifyASTs (renameExpr' old new)

renameExpr' :: Name -> Name -> Expr -> Expr
renameExpr' old new (Var i) = Var (renameExprId old new i)
renameExpr' old new (Data d) = Data (renameExprDataCon old new d)
renameExpr' old new (Lam u i e) = Lam u (renameExprId old new i) e
renameExpr' old new (Let b e) = Let (map (\(b', e') -> (renameExprId old new b', e')) b) e
renameExpr' old new (Case e i a) = Case e (renameExprId old new i) $ map (renameExprAlt old new) a
renameExpr' old new (Assume is e e') = Assume (fmap (rename old new) is) e e'
renameExpr' old new (Assert is e e') = Assert (fmap (rename old new) is) e e'
renameExpr' _ _ e = e

-- | Renames only the @Vars@ in an `Expr`.
renameVars :: ASTContainer m Expr => Name -> Name -> m -> m
renameVars old new = modifyASTs (renameVars' old new)

renameVars' :: Name -> Name -> Expr -> Expr
renameVars' old new (Var i) = Var (renameExprId old new i)
renameVars' old new (Lam u i e) = Lam u (renameExprId old new i) e
renameVars' old new (Let b e) = Let (map (\(b', e') -> (renameExprId old new b', e')) b) e
renameVars' old new (Case e i a) = Case e (renameExprId old new i) $ map (renameExprAltIds old new) a
renameVars' old new (Assert is e e') = Assert (fmap (rename old new) is) e e'
renameVars' _ _ e = e


renameExprId :: Name -> Name -> Id -> Id
renameExprId old new (Id n t) = Id (rename old new n) t

renameExprDataCon :: Name -> Name -> DataCon -> DataCon
renameExprDataCon old new (DataCon n t) = DataCon (rename old new n) t

renameExprAlt :: Name -> Name -> Alt -> Alt
renameExprAlt old new (Alt (DataAlt dc is) e) =
    let
        dc' = renameExprDataCon old new dc
        is' = map (renameExprId old new) is
    in
    Alt (DataAlt dc' is') e
renameExprAlt _ _ a = a

renameExprAltIds :: Name -> Name -> Alt -> Alt
renameExprAltIds old new (Alt (DataAlt dc is) e) =
    let
        is' = map (renameExprId old new) is
    in
    Alt (DataAlt dc is') e
renameExprAltIds _ _ a = a


instance Named Type where
    names = eval go
        where
            go (TyVar i) = idNamesInType i
            go (TyCon n _) = [n]
            go (TyForAll b _) = tyBinderNamesInType b
            go _ = []

    rename old new = modify go
      where
        go :: Type -> Type
        go (TyVar i) = TyVar (renameIdInType old new i)
        go (TyCon n ts) = TyCon (rename old new n) ts
        go (TyForAll tb t) = TyForAll (renameTyBinderInType old new tb) t
        go t = t

    renames hm = modify go
      where
        go :: Type -> Type
        go (TyVar i) = TyVar (renamesIdInType hm i)
        go (TyCon n ts) = TyCon (renames hm n) ts
        go (TyForAll tb t) = TyForAll (renamesTyBinderInType hm tb) t
        go t = t

-- We don't want both modify and go to recurse on the Type's in TyBinders or Ids
-- so we introduce functions to collect or rename only the Names directly in those types
tyBinderNamesInType :: TyBinder -> [Name]
tyBinderNamesInType (NamedTyBndr i) = idNamesInType i
tyBinderNamesInType _ = []

idNamesInType :: Id -> [Name]
idNamesInType (Id n _) = [n]

renameTyBinderInType :: Name -> Name -> TyBinder -> TyBinder
renameTyBinderInType old new (NamedTyBndr i) = NamedTyBndr $ renameIdInType old new i
renameTyBinderInType _ _ tyb = tyb

renameIdInType :: Name -> Name -> Id -> Id
renameIdInType old new (Id n t) = Id (rename old new n) t

renamesTyBinderInType :: HM.HashMap Name Name -> TyBinder -> TyBinder
renamesTyBinderInType hm (NamedTyBndr i) = NamedTyBndr $ renamesIdInType hm i
renamesTyBinderInType _ tyb = tyb

renamesIdInType :: HM.HashMap Name Name -> Id -> Id
renamesIdInType hm (Id n t) = Id (renames hm n) t

instance Named Alt where
    {-# INLINE names #-}
    names (Alt am e) = names am ++ names e

    {-# INLINE rename #-}
    rename old new (Alt am e) = Alt (rename old new am) (rename old new e)

    {-# INLINE renames #-}
    renames hm (Alt am e) = Alt (renames hm am) (renames hm e)

instance Named DataCon where
    {-# INLINE names #-}
    names (DataCon n t) = n:names t

    {-# INLINE rename #-}
    rename old new (DataCon n t) =
        DataCon (rename old new n) (rename old new t)

    {-# INLINE renames #-}
    renames hm (DataCon n t) =
        DataCon (renames hm n) (renames hm t)

instance Named AltMatch where
    {-# INLINE names #-}
    names (DataAlt dc i) = names dc ++ names i
    names _ = []

    {-# INLINE rename #-}
    rename old new (DataAlt dc i) =
        DataAlt (rename old new dc) (rename old new i)
    rename _ _ am = am

    {-# INLINE renames #-}
    renames hm (DataAlt dc i) =
        DataAlt (renames hm dc) (renames hm i)
    renames _ am = am

instance Named TyBinder where
    names (AnonTyBndr t) = names t
    names (NamedTyBndr i) = names i

    rename old new (AnonTyBndr t) = AnonTyBndr (rename old new t)
    rename old new (NamedTyBndr i) = NamedTyBndr (rename old new i)

    renames hm (AnonTyBndr t) = AnonTyBndr (renames hm t)
    renames hm (NamedTyBndr i) = NamedTyBndr (renames hm i)

instance Named Coercion where
    names (t1 :~ t2) = names t1 ++ names t2
    rename old new (t1 :~ t2) = rename old new t1 :~ rename old new t2
    renames hm (t1 :~ t2) = renames hm t1 :~ renames hm t2

instance Named Tickish where
    names (Breakpoint _) = []
    names (NamedLoc n) = [n]

    rename _ _ bp@(Breakpoint _) = bp
    rename old new (NamedLoc n) = NamedLoc $ rename old new n

    renames _ bp@(Breakpoint _) = bp
    renames hm (NamedLoc n) = NamedLoc $ renames hm n

instance Named RewriteRule where
    names (RewriteRule { ru_head = h
                       , ru_rough = rs
                       , ru_bndrs = b
                       , ru_args = as
                       , ru_rhs = rhs}) =
        h:names rs ++ names b ++ names as ++ names rhs

    rename old new (RewriteRule { ru_name = n
                                , ru_head = h
                                , ru_rough = rs
                                , ru_bndrs = b
                                , ru_args = as
                                , ru_rhs = rhs}) =
        RewriteRule { ru_name = n
                    , ru_head = rename old new h
                    , ru_rough = rename old new rs
                    , ru_bndrs = rename old new b
                    , ru_args = rename old new as
                    , ru_rhs = rename old new rhs}

    renames hm (RewriteRule { ru_name = n
                            , ru_head = h
                            , ru_rough = rs
                            , ru_bndrs = b
                            , ru_args = as
                            , ru_rhs = rhs}) =
        RewriteRule { ru_name = n
                    , ru_head = renames hm h
                    , ru_rough = renames hm rs
                    , ru_bndrs = renames hm b
                    , ru_args = renames hm as
                    , ru_rhs = renames hm rhs}

instance Named FuncCall where
    names (FuncCall {funcName = n, arguments = as, returns = r}) = n:names as ++ names r
    rename old new (FuncCall {funcName = n, arguments = as, returns = r}) = 
        FuncCall {funcName = rename old new n, arguments = rename old new as, returns = rename old new r}
    renames hm (FuncCall {funcName = n, arguments = as, returns = r} ) =
        FuncCall {funcName = renames hm n, arguments = renames hm as, returns = renames hm r}


instance Named AlgDataTy where
    names (DataTyCon ns dc) = names ns ++ names dc
    names (NewTyCon ns dc rt) = names ns ++ names dc ++ names rt
    names (TypeSynonym is st) = names is ++ names st

    rename old new (DataTyCon n dc) = DataTyCon (rename old new n) (rename old new dc)
    rename old new (NewTyCon n dc rt) = NewTyCon (rename old new n) (rename old new dc) (rename old new rt)
    rename old new (TypeSynonym is st) = (TypeSynonym (rename old new is) (rename old new st))

    renames hm (DataTyCon n dc) = DataTyCon (renames hm n) (renames hm dc)
    renames hm (NewTyCon n dc rt) = NewTyCon (renames hm n) (renames hm dc) (renames hm rt)
    renames hm (TypeSynonym is st) = TypeSynonym (renames hm is) (renames hm st)

instance Named KnownValues where
    names (KnownValues {
              dcInt = dI
            , dcFloat = dF
            , dcDouble = dD
            , dcInteger = dI2
            , dcChar = dcCh

            , tyInt = tI
            , tyFloat = tF
            , tyDouble = tD
            , tyInteger = tI2
            , tyChar = tCh

            , tyBool = tB
            , dcTrue = dcT
            , dcFalse = dcF

            , tyList = tList
            , dcCons = tCons
            , dcEmpty = tEmp

            , eqTC = eqT
            , numTC = numT
            , ordTC = ordT
            , integralTC = integralT

            , eqFunc = eqF
            , neqFunc = neqF

            , plusFunc = plF
            , minusFunc = minusF
            , timesFunc = tmsF
            , divFunc = divF
            , negateFunc = negF
            , modFunc = modF
            , fromIntegerFunc = fromIntegerF
            , toIntegerFunc = toIntegerF

            , geFunc = geF
            , gtFunc = gtF
            , ltFunc = ltF
            , leFunc = leF

            , structEqTC = seT
            , structEqFunc = seF

            , andFunc = andF
            , orFunc = orF

            , patErrorFunc = patE
            }) =
            [dI, dF, dD, dI2, dcCh, tI, tI2, tF, tD, tCh, tB, dcT, dcF, tList, tCons, tEmp
            , eqT, numT, ordT, integralT, eqF, neqF, plF, minusF, tmsF, divF, negF, modF, fromIntegerF, toIntegerF
            , geF, gtF, ltF, leF, seT, seF
            , andF, orF, patE]

    rename old new (KnownValues {
                     dcInt = dI
                   , dcFloat = dF
                   , dcDouble = dD
                   , dcInteger = dI2
                   , dcChar = dcCh

                   , tyInt = tI
                   , tyFloat = tF
                   , tyDouble = tD
                   , tyInteger = tI2
                   , tyChar = tCh

                   , tyBool = tB
                   , dcTrue = dcT
                   , dcFalse = dcF

                   , tyList = tList
                   , dcCons = tCons
                   , dcEmpty = tEmp

                   , eqTC = eqT
                   , numTC = numT
                   , ordTC = ordT
                   , integralTC = integralT

                   , eqFunc = eqF
                   , neqFunc = neqF

                   , plusFunc = plF
                   , minusFunc = minusF
                   , timesFunc = tmsF
                   , divFunc = divF
                   , negateFunc = negF
                   , modFunc = modF
                   , fromIntegerFunc = fromIntegerF
                   , toIntegerFunc = toIntegerF

                   , geFunc = geF
                   , gtFunc = gtF
                   , ltFunc = ltF
                   , leFunc = leF

                   , structEqTC = seT
                   , structEqFunc = seF

                   , andFunc = andF
                   , orFunc = orF

                   , patErrorFunc = patE
                   }) =
                    (KnownValues {
                          dcInt = rename old new dI
                        , dcFloat = rename old new dF
                        , dcDouble = rename old new dD
                        , dcInteger = rename old new dI2
                        , dcChar = rename old new dcCh

                        , tyInt = rename old new tI
                        , tyFloat = rename old new tF
                        , tyDouble = rename old new tD
                        , tyInteger = rename old new tI2
                        , tyChar = rename old new tCh

                        , tyBool = rename old new tB
                        , dcTrue = rename old new dcT
                        , dcFalse = rename old new dcF
                        , tyList = rename old new tList
                        , dcCons = rename old new tCons
                        , dcEmpty = rename old new tEmp

                        , eqTC = rename old new eqT
                        , numTC = rename old new numT
                        , ordTC = rename old new ordT
                        , integralTC = rename old new integralT

                        , eqFunc = rename old new eqF
                        , neqFunc = rename old new neqF

                        , plusFunc = rename old new plF
                        , minusFunc = rename old new minusF
                        , timesFunc = rename old new tmsF
                        , divFunc = rename old new divF
                        , negateFunc = rename old new negF
                        , modFunc = rename old new modF
                        , fromIntegerFunc = rename old new fromIntegerF
                        , toIntegerFunc = rename old new toIntegerF

                        , geFunc = rename old new geF
                        , gtFunc = rename old new gtF
                        , ltFunc = rename old new ltF
                        , leFunc = rename old new leF

                        , structEqTC = rename old new seT
                        , structEqFunc = rename old new seF

                        , andFunc = rename old new andF
                        , orFunc = rename old new orF

                        , patErrorFunc = rename old new patE
                        })

instance Named a => Named [a] where
    {-# INLINE names #-}
    names = foldMap names
    {-# INLINE rename #-}
    rename old new = fmap (rename old new)
    {-# INLINE renames #-}
    renames hm = fmap (renames hm)


instance Named a => Named (Maybe a) where
    {-# INLINE names #-}
    names = foldMap names
    {-# INLINE rename #-}
    rename old new = fmap (rename old new)
    {-# INLINE renames #-}
    renames hm = fmap (renames hm)

instance Named a => Named (M.Map k a) where
    {-# INLINE names #-}
    names = foldMap names
    {-# INLINE rename #-}
    rename old new = fmap (rename old new)
    {-# INLINE renames #-}
    renames hm = fmap (renames hm)

instance Named a => Named (HM.HashMap k a) where
    {-# INLINE names #-}
    names = foldMap names
    {-# INLINE rename #-}
    rename old new = fmap (rename old new)
    {-# INLINE renames #-}
    renames hm = fmap (renames hm)

instance Named () where
    {-# INLINE names #-}
    names _ = []
    {-# INLINE rename #-}
    rename _ _ = id
    {-# INLINE renames #-}
    renames _ = id

instance (Named s, Hashable s, Eq s) => Named (HS.HashSet s) where
    {-# INLINE names #-}
    names = names . HS.toList
    {-# INLINE rename #-}
    rename old new = HS.map (rename old new)
    {-# INLINE renames #-}
    renames hm = HS.map (renames hm)

instance (Named a, Named b) => Named (a, b) where
    names (a, b) = names a ++ names b
    rename old new (a, b) = (rename old new a, rename old new b)
    renames hm (a, b) = (renames hm a, renames hm b)

instance (Named a, Named b, Named c) => Named (a, b, c) where
    names (a, b, c) = names a ++ names b ++ names c
    rename old new (a, b, c) = (rename old new a, rename old new b, rename old new c)
    renames hm (a, b, c) = (renames hm a, renames hm b, renames hm c)

instance (Named a, Named b, Named c, Named d) => Named (a, b, c, d) where
    names (a, b, c, d) = names a ++ names b ++ names c ++ names d
    rename old new (a, b, c, d) = (rename old new a, rename old new b, rename old new c, rename old new d)
    renames hm (a, b, c, d) = (renames hm a, renames hm b, renames hm c, renames hm d)

instance (Named a, Named b, Named c, Named d, Named e) => Named (a, b, c, d, e) where
    names (a, b, c, d, e) = names a ++ names b ++ names c ++ names d ++ names e
    rename old new (a, b, c, d, e) = (rename old new a, rename old new b, rename old new c, rename old new d, rename old new e)
    renames hm (a, b, c, d, e) = (renames hm a, renames hm b, renames hm c, renames hm d, renames hm e)

instance Named Int where
    {-# INLINE names #-}
    names _ = []
    {-# INLINE rename #-}
    rename _ _ = id

instance Named T.Text where
    {-# INLINE names #-}
    names _ = []
    {-# INLINE rename #-}
    rename _ _ = id

freshSeededString :: T.Text -> NameGen -> (Name, NameGen)
freshSeededString t = freshSeededName (Name t Nothing 0 Nothing)

freshSeededStrings :: [T.Text] -> NameGen -> ([Name], NameGen)
freshSeededStrings t = freshSeededNames (map (\t' -> Name t' Nothing 0 Nothing) t)

freshSeededName :: Name -> NameGen -> (Name, NameGen)
freshSeededName (Name n m _ l) (NameGen { max_uniq = hm, dc_children = chm }) =
    (Name n m i' l, NameGen hm' chm)
    where 
        i' = HM.lookupDefault 0 (n, m) hm
        hm' = HM.insert (n, m) (i' + 1) hm

freshSeededNames :: [Name] -> NameGen -> ([Name], NameGen)
freshSeededNames [] r = ([], r)
freshSeededNames (n:ns) r = (n':ns', ngen'') 
  where (n', ngen') = freshSeededName n r
        (ns', ngen'') = freshSeededNames ns ngen'

freshName :: NameGen -> (Name, NameGen)
freshName ngen = freshSeededName seed ngen
  where
    seed = Name "fs?" Nothing 0 Nothing

freshNames :: Int -> NameGen -> ([Name], NameGen)
freshNames i ngen = freshSeededNames (replicate i (Name "fs?" Nothing 0 Nothing)) ngen

freshId :: Type -> NameGen -> (Id, NameGen)
freshId = freshSeededId (Name "fs?" Nothing 0 Nothing)

freshIds :: [Type] -> NameGen -> ([Id], NameGen)
freshIds ts ngen = 
    let
        (ns, ngen') = freshNames (length ts) ngen
    in
    (map (uncurry Id) (zip ns ts), ngen')
    
freshSeededId :: Named a => a -> Type -> NameGen -> (Id, NameGen)
freshSeededId x t ngen =
    let
        (n, ngen') = freshSeededName (head $ names x) ngen
    in
    (Id n t, ngen')

freshVar :: Type -> NameGen -> (Expr, NameGen)
freshVar t ngen =
    let
        (i, ngen') = freshId t ngen
    in
    (Var i, ngen')

-- | Given the name n of a datacon, and some names for it's children,
-- returns new names ns for the children
-- Returns a new NameGen that will always return the same ns for that n
-- If this is called with different length ns's, the shorter will be the prefix
-- of the longer
childrenNames :: Name -> [Name] -> NameGen -> ([Name], NameGen)
childrenNames n ns ng@(NameGen { dc_children = chm }) =
    case HM.lookup n chm of
        Just ens' -> childrenNamesExisting n ns ens' ng
        Nothing -> childrenNamesNew n ns ng-- []

childrenNamesExisting :: Name -> [Name] -> [Name] -> NameGen -> ([Name], NameGen)
childrenNamesExisting n ns ens ng =
    let
        (fns, NameGen hm chm) = freshSeededNames (drop (length ens) ns) ng
        ns' = ens ++ fns

        chm' = HM.insert n ns' chm
    in
    case length ns `compare` length ens of
        LT -> (take (length ns) ens, ng)
        EQ -> (ens, ng)
        GT -> (ns', NameGen hm chm')

childrenNamesNew :: Name -> [Name] -> NameGen -> ([Name], NameGen)
childrenNamesNew n ns ng =
    let
        (fns, NameGen hm chm) = freshSeededNames ns ng
        chm' = HM.insert n fns chm
    in
    (fns, NameGen hm chm')


-- | Allows mapping, while passing a NameGen along
mapNG :: (a -> NameGen -> (b, NameGen)) -> [a] -> NameGen -> ([b], NameGen)
mapNG f xs ng = swap $ mapAccumR (\xs' ng' -> swap $ f ng' xs') ng xs -- mapNG' f (reverse xs) ng []
{-# INLINE mapNG #-}

-- mapNG' :: (a -> NameGen -> (b, NameGen)) -> [a] -> NameGen -> [b] -> ([b], NameGen)
-- mapNG' _ [] ng xs = (xs, ng)
-- mapNG' f (x:xs) ng xs' =
--     let
--         (x', ng') = f x ng
--     in
--     mapNG' f xs ng' (x':xs')