{-# LANGUAGE DeriveLift #-}

-- | This module provides facilities for generating unique names.
module Futhark.FreshNames
  ( VNameSource,
    blankNameSource,
    newNameSource,
    newName,
  )
where

import Language.Futhark.Core
import Language.Haskell.TH.Syntax (Lift)

-- | A name source is conceptually an infinite sequence of names with
-- no repeating entries.  In practice, when asked for a name, the name
-- source will return the name along with a new name source, which
-- should then be used in place of the original.
--
-- The 'Ord' instance is based on how many names have been extracted
-- from the name source.
newtype VNameSource = VNameSource Int
  deriving (VNameSource -> Q Exp
VNameSource -> Q (TExp VNameSource)
(VNameSource -> Q Exp)
-> (VNameSource -> Q (TExp VNameSource)) -> Lift VNameSource
forall t. (t -> Q Exp) -> (t -> Q (TExp t)) -> Lift t
liftTyped :: VNameSource -> Q (TExp VNameSource)
$cliftTyped :: VNameSource -> Q (TExp VNameSource)
lift :: VNameSource -> Q Exp
$clift :: VNameSource -> Q Exp
Lift, VNameSource -> VNameSource -> Bool
(VNameSource -> VNameSource -> Bool)
-> (VNameSource -> VNameSource -> Bool) -> Eq VNameSource
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: VNameSource -> VNameSource -> Bool
$c/= :: VNameSource -> VNameSource -> Bool
== :: VNameSource -> VNameSource -> Bool
$c== :: VNameSource -> VNameSource -> Bool
Eq, Eq VNameSource
Eq VNameSource
-> (VNameSource -> VNameSource -> Ordering)
-> (VNameSource -> VNameSource -> Bool)
-> (VNameSource -> VNameSource -> Bool)
-> (VNameSource -> VNameSource -> Bool)
-> (VNameSource -> VNameSource -> Bool)
-> (VNameSource -> VNameSource -> VNameSource)
-> (VNameSource -> VNameSource -> VNameSource)
-> Ord VNameSource
VNameSource -> VNameSource -> Bool
VNameSource -> VNameSource -> Ordering
VNameSource -> VNameSource -> VNameSource
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
min :: VNameSource -> VNameSource -> VNameSource
$cmin :: VNameSource -> VNameSource -> VNameSource
max :: VNameSource -> VNameSource -> VNameSource
$cmax :: VNameSource -> VNameSource -> VNameSource
>= :: VNameSource -> VNameSource -> Bool
$c>= :: VNameSource -> VNameSource -> Bool
> :: VNameSource -> VNameSource -> Bool
$c> :: VNameSource -> VNameSource -> Bool
<= :: VNameSource -> VNameSource -> Bool
$c<= :: VNameSource -> VNameSource -> Bool
< :: VNameSource -> VNameSource -> Bool
$c< :: VNameSource -> VNameSource -> Bool
compare :: VNameSource -> VNameSource -> Ordering
$ccompare :: VNameSource -> VNameSource -> Ordering
$cp1Ord :: Eq VNameSource
Ord)

instance Semigroup VNameSource where
  VNameSource Int
x <> :: VNameSource -> VNameSource -> VNameSource
<> VNameSource Int
y = Int -> VNameSource
VNameSource (Int
x Int -> Int -> Int
forall a. Ord a => a -> a -> a
`max` Int
y)

instance Monoid VNameSource where
  mempty :: VNameSource
mempty = VNameSource
blankNameSource

-- | Produce a fresh name, using the given name as a template.
newName :: VNameSource -> VName -> (VName, VNameSource)
newName :: VNameSource -> VName -> (VName, VNameSource)
newName (VNameSource Int
i) VName
k = Int
i' Int -> (VName, VNameSource) -> (VName, VNameSource)
`seq` (Name -> Int -> VName
VName (VName -> Name
baseName VName
k) Int
i, Int -> VNameSource
VNameSource Int
i')
  where
    i' :: Int
i' = Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1

-- | A blank name source.
blankNameSource :: VNameSource
blankNameSource :: VNameSource
blankNameSource = Int -> VNameSource
newNameSource Int
0

-- | A new name source that starts counting from the given number.
newNameSource :: Int -> VNameSource
newNameSource :: Int -> VNameSource
newNameSource = Int -> VNameSource
VNameSource