module Unison.Hashing.V2.Reference
  ( Reference (..),
    pattern ReferenceDerived,
    ReferenceId (..),
    components,
  )
where

import Data.Text qualified as Text
import Unison.Hash (Hash)
import Unison.Hash qualified as Hash
import Unison.Hashing.V2.Tokenizable (Tokenizable)
import Unison.Hashing.V2.Tokenizable qualified as Hashable
import Unison.Prelude

-- | Either a builtin or a user defined (hashed) top-level declaration.
--
-- Used for both terms and types. Doesn't distinguish between them.
--
-- Other used defined things like local variables don't get @Reference@s.
data Reference
  = ReferenceBuiltin Text.Text
  | -- `Derived` can be part of a strongly connected component.
    -- The `Pos` refers to a particular element of the component
    -- and the `Size` is the number of elements in the component.
    -- Using an ugly name so no one tempted to use this
    ReferenceDerivedId ReferenceId
  deriving stock (Reference -> Reference -> Bool
(Reference -> Reference -> Bool)
-> (Reference -> Reference -> Bool) -> Eq Reference
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Reference -> Reference -> Bool
== :: Reference -> Reference -> Bool
$c/= :: Reference -> Reference -> Bool
/= :: Reference -> Reference -> Bool
Eq, Eq Reference
Eq Reference =>
(Reference -> Reference -> Ordering)
-> (Reference -> Reference -> Bool)
-> (Reference -> Reference -> Bool)
-> (Reference -> Reference -> Bool)
-> (Reference -> Reference -> Bool)
-> (Reference -> Reference -> Reference)
-> (Reference -> Reference -> Reference)
-> Ord Reference
Reference -> Reference -> Bool
Reference -> Reference -> Ordering
Reference -> Reference -> Reference
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 :: Reference -> Reference -> Ordering
compare :: Reference -> Reference -> Ordering
$c< :: Reference -> Reference -> Bool
< :: Reference -> Reference -> Bool
$c<= :: Reference -> Reference -> Bool
<= :: Reference -> Reference -> Bool
$c> :: Reference -> Reference -> Bool
> :: Reference -> Reference -> Bool
$c>= :: Reference -> Reference -> Bool
>= :: Reference -> Reference -> Bool
$cmax :: Reference -> Reference -> Reference
max :: Reference -> Reference -> Reference
$cmin :: Reference -> Reference -> Reference
min :: Reference -> Reference -> Reference
Ord, Int -> Reference -> ShowS
[Reference] -> ShowS
Reference -> String
(Int -> Reference -> ShowS)
-> (Reference -> String)
-> ([Reference] -> ShowS)
-> Show Reference
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Reference -> ShowS
showsPrec :: Int -> Reference -> ShowS
$cshow :: Reference -> String
show :: Reference -> String
$cshowList :: [Reference] -> ShowS
showList :: [Reference] -> ShowS
Show)

type Pos = Word64

pattern ReferenceDerived :: Hash -> Pos -> Reference
pattern $mReferenceDerived :: forall {r}. Reference -> (Hash -> Pos -> r) -> ((# #) -> r) -> r
$bReferenceDerived :: Hash -> Pos -> Reference
ReferenceDerived h i = ReferenceDerivedId (ReferenceId h i)

{-# COMPLETE ReferenceBuiltin, ReferenceDerived #-}

-- | @Pos@ is a position into a cycle of size @Size@, as cycles are hashed together.
data ReferenceId
  = ReferenceId Hash Pos
  deriving stock (ReferenceId -> ReferenceId -> Bool
(ReferenceId -> ReferenceId -> Bool)
-> (ReferenceId -> ReferenceId -> Bool) -> Eq ReferenceId
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ReferenceId -> ReferenceId -> Bool
== :: ReferenceId -> ReferenceId -> Bool
$c/= :: ReferenceId -> ReferenceId -> Bool
/= :: ReferenceId -> ReferenceId -> Bool
Eq, Eq ReferenceId
Eq ReferenceId =>
(ReferenceId -> ReferenceId -> Ordering)
-> (ReferenceId -> ReferenceId -> Bool)
-> (ReferenceId -> ReferenceId -> Bool)
-> (ReferenceId -> ReferenceId -> Bool)
-> (ReferenceId -> ReferenceId -> Bool)
-> (ReferenceId -> ReferenceId -> ReferenceId)
-> (ReferenceId -> ReferenceId -> ReferenceId)
-> Ord ReferenceId
ReferenceId -> ReferenceId -> Bool
ReferenceId -> ReferenceId -> Ordering
ReferenceId -> ReferenceId -> ReferenceId
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 :: ReferenceId -> ReferenceId -> Ordering
compare :: ReferenceId -> ReferenceId -> Ordering
$c< :: ReferenceId -> ReferenceId -> Bool
< :: ReferenceId -> ReferenceId -> Bool
$c<= :: ReferenceId -> ReferenceId -> Bool
<= :: ReferenceId -> ReferenceId -> Bool
$c> :: ReferenceId -> ReferenceId -> Bool
> :: ReferenceId -> ReferenceId -> Bool
$c>= :: ReferenceId -> ReferenceId -> Bool
>= :: ReferenceId -> ReferenceId -> Bool
$cmax :: ReferenceId -> ReferenceId -> ReferenceId
max :: ReferenceId -> ReferenceId -> ReferenceId
$cmin :: ReferenceId -> ReferenceId -> ReferenceId
min :: ReferenceId -> ReferenceId -> ReferenceId
Ord, Int -> ReferenceId -> ShowS
[ReferenceId] -> ShowS
ReferenceId -> String
(Int -> ReferenceId -> ShowS)
-> (ReferenceId -> String)
-> ([ReferenceId] -> ShowS)
-> Show ReferenceId
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ReferenceId -> ShowS
showsPrec :: Int -> ReferenceId -> ShowS
$cshow :: ReferenceId -> String
show :: ReferenceId -> String
$cshowList :: [ReferenceId] -> ShowS
showList :: [ReferenceId] -> ShowS
Show)

component :: Hash -> [k] -> [(k, ReferenceId)]
component :: forall k. Hash -> [k] -> [(k, ReferenceId)]
component Hash
h [k]
ks =
  [(k
k, (Hash -> Pos -> ReferenceId
ReferenceId Hash
h Pos
i)) | (k
k, Pos
i) <- [k]
ks [k] -> [Pos] -> [(k, Pos)]
forall a b. [a] -> [b] -> [(a, b)]
`zip` [Pos
0 ..]]

components :: [(Hash, [k])] -> [(k, ReferenceId)]
components :: forall k. [(Hash, [k])] -> [(k, ReferenceId)]
components [(Hash, [k])]
sccs = (Hash -> [k] -> [(k, ReferenceId)])
-> (Hash, [k]) -> [(k, ReferenceId)]
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Hash -> [k] -> [(k, ReferenceId)]
forall k. Hash -> [k] -> [(k, ReferenceId)]
component ((Hash, [k]) -> [(k, ReferenceId)])
-> [(Hash, [k])] -> [(k, ReferenceId)]
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< [(Hash, [k])]
sccs

instance Tokenizable Reference where
  tokens :: Reference -> [Token]
tokens (ReferenceBuiltin Text
txt) = [Word8 -> Token
Hashable.Tag Word8
0, Text -> Token
Hashable.Text Text
txt]
  tokens (ReferenceDerivedId (ReferenceId Hash
h Pos
i)) = [Word8 -> Token
Hashable.Tag Word8
1, ByteString -> Token
Hashable.Bytes (Hash -> ByteString
Hash.toByteString Hash
h), Pos -> Token
Hashable.Nat Pos
i]