    {-|
      Copyright   :  (C) 2012-2016, University of Twente,
                         2017-2018, Google Inc.
                              2021, QBayLogic B.V.
      License     :  BSD2 (see the file LICENSE)
      Maintainer  :  QBayLogic B.V. <devops@qbaylogic.com>

      Variables in CoreHW
    -}

    {-# LANGUAGE DeriveAnyClass #-}
    {-# LANGUAGE GADTs #-}
    {-# LANGUAGE NamedFieldPuns #-}
    {-# LANGUAGE RankNTypes #-}

    module Clash.Core.Var
      ( Var (..)
      , IdScope (..)
      , Id
      , TyVar
      , mkId
      , mkLocalId
      , mkGlobalId
      , mkTyVar
      , setIdScope
      , modifyVarName
      , isGlobalId
      , isLocalId
      )
    where

    import Control.DeepSeq                  (NFData (..))
    import Data.Binary                      (Binary)
    import Data.Function                    (on)
    import Data.Hashable                    (Hashable(hashWithSalt))
    import GHC.Generics                     (Generic)
    import Clash.Core.Name                  (Name (..))
    import {-# SOURCE #-} Clash.Core.Term   (Term, TmName)
    import {-# SOURCE #-} Clash.Core.Type   (Kind, Type, TyName)
    import Clash.Unique

    -- | Variables in CoreHW
    data Var a
      -- | Constructor for type variables
      = TyVar
      { forall a. Var a -> Name a
    varName :: !(Name a)
      , forall a. Var a -> Unique
    varUniq :: {-# UNPACK #-} !Unique
      -- ^ Invariant: forall x . varUniq x ~ nameUniq (varName x)
      , forall a. Var a -> Kind
    varType :: Kind
      }
      -- | Constructor for term variables
      | Id
      { varName :: !(Name a)
      , varUniq :: {-# UNPACK #-} !Unique
      -- ^ Invariant: forall x . varUniq x ~ nameUniq (varName x)
      , varType :: Type
      , forall a. Var a -> IdScope
    idScope :: IdScope
      }
      deriving (Int -> Var a -> ShowS
    [Var a] -> ShowS
    Var a -> String
    (Int -> Var a -> ShowS)
    -> (Var a -> String) -> ([Var a] -> ShowS) -> Show (Var a)
    forall a. Int -> Var a -> ShowS
    forall a. [Var a] -> ShowS
    forall a. Var a -> String
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: forall a. Int -> Var a -> ShowS
    showsPrec :: Int -> Var a -> ShowS
    $cshow :: forall a. Var a -> String
    show :: Var a -> String
    $cshowList :: forall a. [Var a] -> ShowS
    showList :: [Var a] -> ShowS
    Show,(forall x. Var a -> Rep (Var a) x)
    -> (forall x. Rep (Var a) x -> Var a) -> Generic (Var a)
    forall x. Rep (Var a) x -> Var a
    forall x. Var a -> Rep (Var a) x
    forall a.
    (forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
    forall a x. Rep (Var a) x -> Var a
    forall a x. Var a -> Rep (Var a) x
    $cfrom :: forall a x. Var a -> Rep (Var a) x
    from :: forall x. Var a -> Rep (Var a) x
    $cto :: forall a x. Rep (Var a) x -> Var a
    to :: forall x. Rep (Var a) x -> Var a
    Generic,Var a -> ()
    (Var a -> ()) -> NFData (Var a)
    forall a. Var a -> ()
    forall a. (a -> ()) -> NFData a
    $crnf :: forall a. Var a -> ()
    rnf :: Var a -> ()
    NFData,Get (Var a)
    [Var a] -> Put
    Var a -> Put
    (Var a -> Put) -> Get (Var a) -> ([Var a] -> Put) -> Binary (Var a)
    forall a. Get (Var a)
    forall a. [Var a] -> Put
    forall a. Var a -> Put
    forall t. (t -> Put) -> Get t -> ([t] -> Put) -> Binary t
    $cput :: forall a. Var a -> Put
    put :: Var a -> Put
    $cget :: forall a. Get (Var a)
    get :: Get (Var a)
    $cputList :: forall a. [Var a] -> Put
    putList :: [Var a] -> Put
    Binary)

    -- | Gets a _key_ in the DBMS sense: a value that uniquely identifies a
    -- Var. In case of a "Var" that is its unique and (if applicable) scope
    varKey :: Var a -> (Unique, Maybe IdScope)
    varKey :: forall a. Var a -> (Unique, Maybe IdScope)
    varKey TyVar{Unique
    varUniq :: forall a. Var a -> Unique
    varUniq :: Unique
    varUniq} = (Unique
    varUniq, Maybe IdScope
    forall a. Maybe a
    Nothing)
    varKey Id{Unique
    varUniq :: forall a. Var a -> Unique
    varUniq :: Unique
    varUniq,IdScope
    idScope :: forall a. Var a -> IdScope
    idScope :: IdScope
    idScope} = (Unique
    varUniq, IdScope -> Maybe IdScope
    forall a. a -> Maybe a
    Just IdScope
    idScope)

    instance Hashable (Var a) where
      hashWithSalt :: Int -> Var a -> Int
    hashWithSalt Int
    salt Var a
    a = Int -> (Unique, Maybe IdScope) -> Int
    forall a. Hashable a => Int -> a -> Int
    hashWithSalt Int
    salt (Var a -> (Unique, Maybe IdScope)
    forall a. Var a -> (Unique, Maybe IdScope)
    varKey Var a
    a)

    instance Eq (Var a) where
      == :: Var a -> Var a -> Bool
    (==) = (Unique, Maybe IdScope) -> (Unique, Maybe IdScope) -> Bool
    forall a. Eq a => a -> a -> Bool
    (==) ((Unique, Maybe IdScope) -> (Unique, Maybe IdScope) -> Bool)
    -> (Var a -> (Unique, Maybe IdScope)) -> Var a -> Var a -> Bool
    forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
    `on` Var a -> (Unique, Maybe IdScope)
    forall a. Var a -> (Unique, Maybe IdScope)
    varKey
      /= :: Var a -> Var a -> Bool
    (/=) = (Unique, Maybe IdScope) -> (Unique, Maybe IdScope) -> Bool
    forall a. Eq a => a -> a -> Bool
    (/=) ((Unique, Maybe IdScope) -> (Unique, Maybe IdScope) -> Bool)
    -> (Var a -> (Unique, Maybe IdScope)) -> Var a -> Var a -> Bool
    forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
    `on` Var a -> (Unique, Maybe IdScope)
    forall a. Var a -> (Unique, Maybe IdScope)
    varKey

    instance Ord (Var a) where
      compare :: Var a -> Var a -> Ordering
    compare = (Unique, Maybe IdScope) -> (Unique, Maybe IdScope) -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare ((Unique, Maybe IdScope) -> (Unique, Maybe IdScope) -> Ordering)
    -> (Var a -> (Unique, Maybe IdScope)) -> Var a -> Var a -> Ordering
    forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
    `on` Var a -> (Unique, Maybe IdScope)
    forall a. Var a -> (Unique, Maybe IdScope)
    varKey

    instance Uniquable (Var a) where
      getUnique :: Var a -> Unique
    getUnique = Var a -> Unique
    forall a. Var a -> Unique
    varUniq
      setUnique :: Var a -> Unique -> Var a
    setUnique Var a
    var Unique
    u = Var a
    var {varUniq=u, varName=(varName var){nameUniq=u}}

    data IdScope = GlobalId | LocalId
      deriving (Int -> IdScope -> ShowS
    [IdScope] -> ShowS
    IdScope -> String
    (Int -> IdScope -> ShowS)
    -> (IdScope -> String) -> ([IdScope] -> ShowS) -> Show IdScope
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> IdScope -> ShowS
    showsPrec :: Int -> IdScope -> ShowS
    $cshow :: IdScope -> String
    show :: IdScope -> String
    $cshowList :: [IdScope] -> ShowS
    showList :: [IdScope] -> ShowS
    Show,(forall x. IdScope -> Rep IdScope x)
    -> (forall x. Rep IdScope x -> IdScope) -> Generic IdScope
    forall x. Rep IdScope x -> IdScope
    forall x. IdScope -> Rep IdScope x
    forall a.
    (forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
    $cfrom :: forall x. IdScope -> Rep IdScope x
    from :: forall x. IdScope -> Rep IdScope x
    $cto :: forall x. Rep IdScope x -> IdScope
    to :: forall x. Rep IdScope x -> IdScope
    Generic,IdScope -> ()
    (IdScope -> ()) -> NFData IdScope
    forall a. (a -> ()) -> NFData a
    $crnf :: IdScope -> ()
    rnf :: IdScope -> ()
    NFData,Eq IdScope
    Eq IdScope =>
    (Int -> IdScope -> Int) -> (IdScope -> Int) -> Hashable IdScope
    Int -> IdScope -> Int
    IdScope -> Int
    forall a. Eq a => (Int -> a -> Int) -> (a -> Int) -> Hashable a
    $chashWithSalt :: Int -> IdScope -> Int
    hashWithSalt :: Int -> IdScope -> Int
    $chash :: IdScope -> Int
    hash :: IdScope -> Int
    Hashable,Get IdScope
    [IdScope] -> Put
    IdScope -> Put
    (IdScope -> Put)
    -> Get IdScope -> ([IdScope] -> Put) -> Binary IdScope
    forall t. (t -> Put) -> Get t -> ([t] -> Put) -> Binary t
    $cput :: IdScope -> Put
    put :: IdScope -> Put
    $cget :: Get IdScope
    get :: Get IdScope
    $cputList :: [IdScope] -> Put
    putList :: [IdScope] -> Put
    Binary,IdScope -> IdScope -> Bool
    (IdScope -> IdScope -> Bool)
    -> (IdScope -> IdScope -> Bool) -> Eq IdScope
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: IdScope -> IdScope -> Bool
    == :: IdScope -> IdScope -> Bool
    $c/= :: IdScope -> IdScope -> Bool
    /= :: IdScope -> IdScope -> Bool
    Eq,Eq IdScope
    Eq IdScope =>
    (IdScope -> IdScope -> Ordering)
    -> (IdScope -> IdScope -> Bool)
    -> (IdScope -> IdScope -> Bool)
    -> (IdScope -> IdScope -> Bool)
    -> (IdScope -> IdScope -> Bool)
    -> (IdScope -> IdScope -> IdScope)
    -> (IdScope -> IdScope -> IdScope)
    -> Ord IdScope
    IdScope -> IdScope -> Bool
    IdScope -> IdScope -> Ordering
    IdScope -> IdScope -> IdScope
    forall a.
    Eq a =>
    (a -> a -> Ordering)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> a)
    -> (a -> a -> a)
    -> Ord a
    $ccompare :: IdScope -> IdScope -> Ordering
    compare :: IdScope -> IdScope -> Ordering
    $c< :: IdScope -> IdScope -> Bool
    < :: IdScope -> IdScope -> Bool
    $c<= :: IdScope -> IdScope -> Bool
    <= :: IdScope -> IdScope -> Bool
    $c> :: IdScope -> IdScope -> Bool
    > :: IdScope -> IdScope -> Bool
    $c>= :: IdScope -> IdScope -> Bool
    >= :: IdScope -> IdScope -> Bool
    $cmax :: IdScope -> IdScope -> IdScope
    max :: IdScope -> IdScope -> IdScope
    $cmin :: IdScope -> IdScope -> IdScope
    min :: IdScope -> IdScope -> IdScope
    Ord)

    -- | Term variable
    type Id    = Var Term
    -- | Type variable
    type TyVar = Var Type

    -- | Change the name of a variable
    modifyVarName ::
      (Name a -> Name a)
      -> Var a
      -> Var a
    modifyVarName :: forall a. (Name a -> Name a) -> Var a -> Var a
    modifyVarName Name a -> Name a
    f (TyVar Name a
    n Unique
    _ Kind
    k) =
      let n' :: Name a
    n' = Name a -> Name a
    f Name a
    n
      in  Name a -> Unique -> Kind -> Var a
    forall a. Name a -> Unique -> Kind -> Var a
    TyVar Name a
    n' (Name a -> Unique
    forall a. Name a -> Unique
    nameUniq Name a
    n') Kind
    k
    modifyVarName Name a -> Name a
    f (Id Name a
    n Unique
    _ Kind
    t IdScope
    s) =
      let n' :: Name a
    n' = Name a -> Name a
    f Name a
    n
      in  Name a -> Unique -> Kind -> IdScope -> Var a
    forall a. Name a -> Unique -> Kind -> IdScope -> Var a
    Id Name a
    n' (Name a -> Unique
    forall a. Name a -> Unique
    nameUniq Name a
    n') Kind
    t IdScope
    s

    -- | Make a type variable
    mkTyVar
      :: Kind
      -> TyName
      -> TyVar
    mkTyVar :: Kind -> TyName -> TyVar
    mkTyVar Kind
    tyKind TyName
    tyName = TyName -> Unique -> Kind -> TyVar
    forall a. Name a -> Unique -> Kind -> Var a
    TyVar TyName
    tyName (TyName -> Unique
    forall a. Name a -> Unique
    nameUniq TyName
    tyName) Kind
    tyKind

    -- | Make a term variable
    mkId
      :: Type
      -> IdScope
      -> TmName
      -> Id
    mkId :: Kind -> IdScope -> TmName -> Id
    mkId Kind
    tmType IdScope
    scope TmName
    tmName = TmName -> Unique -> Kind -> IdScope -> Id
    forall a. Name a -> Unique -> Kind -> IdScope -> Var a
    Id TmName
    tmName (TmName -> Unique
    forall a. Name a -> Unique
    nameUniq TmName
    tmName) Kind
    tmType IdScope
    scope

    mkLocalId
      :: Type
      -> TmName
      -> Id
    mkLocalId :: Kind -> TmName -> Id
    mkLocalId Kind
    tmType TmName
    tmName = TmName -> Unique -> Kind -> IdScope -> Id
    forall a. Name a -> Unique -> Kind -> IdScope -> Var a
    Id TmName
    tmName (TmName -> Unique
    forall a. Name a -> Unique
    nameUniq TmName
    tmName) Kind
    tmType IdScope
    LocalId

    mkGlobalId
      :: Type
      -> TmName
      -> Id
    mkGlobalId :: Kind -> TmName -> Id
    mkGlobalId Kind
    tmType TmName
    tmName = TmName -> Unique -> Kind -> IdScope -> Id
    forall a. Name a -> Unique -> Kind -> IdScope -> Var a
    Id TmName
    tmName (TmName -> Unique
    forall a. Name a -> Unique
    nameUniq TmName
    tmName) Kind
    tmType IdScope
    GlobalId

    isGlobalId
      :: Var a
      -> Bool
    isGlobalId :: forall a. Var a -> Bool
    isGlobalId (Id {idScope :: forall a. Var a -> IdScope
    idScope = IdScope
    GlobalId}) = Bool
    True
    isGlobalId Var a
    _ = Bool
    False

    isLocalId
      :: Var a
      -> Bool
    isLocalId :: forall a. Var a -> Bool
    isLocalId (Id {idScope :: forall a. Var a -> IdScope
    idScope = IdScope
    LocalId}) = Bool
    True
    isLocalId Var a
    _  = Bool
    False

    setIdScope
      :: IdScope
      -> Var a
      -> Var a
    setIdScope :: forall a. IdScope -> Var a -> Var a
    setIdScope IdScope
    s (Id Name a
    nm Unique
    u Kind
    t IdScope
    _) = Name a -> Unique -> Kind -> IdScope -> Var a
    forall a. Name a -> Unique -> Kind -> IdScope -> Var a
    Id Name a
    nm Unique
    u Kind
    t IdScope
    s
    setIdScope IdScope
    _ Var a
    v = Var a
    v