{-# LANGUAGE CPP #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE PackageImports, LambdaCase, RecordWildCards #-} {-# LANGUAGE DeriveAnyClass, AutoDeriveTypeable, DeriveDataTypeable, DeriveGeneric #-} {-# LANGUAGE RankNTypes, PolyKinds #-} {-| -} module Prelude.Spiros.GUI where -------------------------------------------------- -------------------------------------------------- #include <sboo-base-feature-macros.h> -------------------------------------------------- import Prelude.Spiros.Reexports import Prelude.Spiros.Utilities import Prelude.Spiros.Classes -------------------------------------------------- -- Imports --------------------------------------- -------------------------------------------------- import Prelude hiding ( (<), (>) -- shadowed , fail -- deprecated ) -------------------------------------------------- import "template-haskell" Language.Haskell.TH.Syntax ( Name(..) , NameFlavour(NameG,NameL) , NameSpace(..) , OccName(..) , ModName(..) , PkgName(..) ) -------------------------------------------------- import "base" GHC.Generics (Generic) import "base" Data.Data (Data) import "base" Data.Typeable ( tyConModule, tyConName, tyConPackage, typeRepTyCon ) -------------------------------------------------- -- Types ----------------------------------------- -------------------------------------------------- {- | A globally unique haskell identifier, for either a value or type, fully-qualified with its module and package. TODO new field: @Version@. -} data GUI = GUI { _guiPackage :: !PkgName , _guiModule :: !ModName , _guiIdentifier :: !OccName , _guiNamespace :: !NameSpace -- , _guiVersion :: !(Maybe Version) } deriving ( Generic, Data , Eq, Ord #if MIN_VERSION_GLASGOW_HASKELL(8,0,0,0) , Show -- « GHC 8 » introduced « instance Show NameSpace ». #endif ) -------------------------------------------------- #if !MIN_VERSION_GLASGOW_HASKELL(8,0,0,0) showsPrec_NameSpace :: Int -> NameSpace -> ShowS showsPrec_NameSpace _ = showString . go where go = \case VarName -> "VarName" DataName -> "DataName" TcClsName -> "TcClsName" -------------------------------------------------- instance Show GUI where showsPrec precedence (GUI p m i n) = showParen (precedence >= 11) (foldr (.) id ss) where ss = [ (showString "GUI {") , (showString "_guiPackage = ") , (showsPrec 0 p) , (showString ", ") , (showString "_guiModule = ") , (showsPrec 0 m) , (showString ", ") , (showString "_guiIdentifier = ") , (showsPrec 0 i) , (showString ", ") , (showString "_guiNamespace = ") , (showsPrec_NameSpace 0 n) , (showString "}") ] #endif -------------------------------------------------- instance NFData GUI where rnf GUI{..} = rnfString (coerce _guiPackage) `seq` rnfString (coerce _guiModule) `seq` rnfString (coerce _guiIdentifier) `seq` rwhnf _guiNamespace where rnfString :: String -> () rnfString = rnf -------------------------------------------------- instance Hashable GUI where -- hashWithSalt s GUI{..} = s `hashWithSalt` ... hashWithSalt s GUI{..} = s `hashStringWithSalt` (coerce _guiPackage) `hashStringWithSalt` (coerce _guiModule) `hashStringWithSalt` (coerce _guiIdentifier) `hashWithSalt` (fromEnumNameSpace _guiNamespace) -- lol -- `hashWithSalt` (fromEnum _guiNamespace) where hashStringWithSalt :: Int -> String -> Int hashStringWithSalt = hashWithSalt fromEnumNameSpace :: NameSpace -> Int fromEnumNameSpace = \case VarName -> 0 DataName -> 1 TcClsName -> 2 -------------------------------------------------- -- {- | An identifer of a haskell /value/, -- fully-qualified with its module and package, -- and tagged with the type of the value. -- @t@ is phantom. -- -} -- data GUI' t = GUI' -- { _guiPackage' :: !PkgName -- , _guiModule' :: !ModName -- , _guiIdentifier' :: !OccName -- -- , _guiVersion :: !(Maybe Version) -- } deriving (Show,Eq,Ord,Data,Generic) -- -- instance NFData (GUI' t) where -- -- instance Hashable (GUI' t) where -------------------------------------------------- -- Functions ------------------------------------- -------------------------------------------------- {-| Return a globally unique identifier from a Template Haskell 'Name', even if it's local (i.e. not global). == Implementation: @TemplateHaskellQuotes@ return only "local names" ('NameL') and "global names" ('NameG', which 'fromGlobalName' validates). * 'NameG' See 'Name': * 'NameS': An unqualified name; dynamically bound * 'NameQ' ModName: A qualified name; dynamically bound * 'NameU' !Int: A unique local name * 'NameL' !Int: Local name bound outside of the TH AST * 'NameG' NameSpace PkgName ModName: Global name bound outside of the TH AST: An original name (occurrences only, not binders) Need the namespace too to be sure which thing we are naming -} unsafeGUI :: Name -> GUI unsafeGUI name = go name where go :: Name -> GUI go = fromGlobalName > maybe (fakeGUI name) id fakeGUI :: Name -> GUI fakeGUI (Name nIdentifier nFlavor) = case nFlavor of NameL i -> localFakeGUI nIdentifier i _ -> defaultFakeGUI nIdentifier localFakeGUI :: (Integral i) => OccName -> i -> GUI localFakeGUI (OccName n) i = GUI fakePkgName fakeModName qualifiedLocalOccName defaultNameSpace where qualifiedLocalOccName = OccName $ n ++ "$" ++ show (toInteger i) defaultFakeGUI :: OccName -> GUI defaultFakeGUI realOccName = GUI fakePkgName fakeModName realOccName defaultNameSpace fakePkgName :: PkgName fakePkgName = PkgName "?" fakeModName :: ModName fakeModName = ModName "?" defaultNameSpace :: NameSpace defaultNameSpace = VarName -------------------------------------------------- {-| if the given identifier is [1] global and [2] a value, then return it as a GUI. e.g. @ > fromGlobalName 'fromGlobalName Just (PkgName "spiros-0.0.1-inplace",ModName "Prelude.Spiros.Exception",OccName "fromGlobalName") @ Implementation Note: 'Name' use is compatible with @template-haskell >=2.11@. -} fromGlobalName :: Name -> Maybe GUI fromGlobalName = \case Name nIdentifier (NameG nNameSpace nPackage nModule) -> Just $ GUI nPackage nModule nIdentifier nNameSpace _ -> Nothing -------------------------------------------------- {-| like 'fromGlobalName', but restricted to /identifiers/ (i.e. not types\/classes, not constructors\/patterns). e.g. -} fromValueName :: Name -> Maybe GUI fromValueName = fromGlobalName >=> go where go gui = gui & \case GUI _ _ _ VarName -> Just gui _ -> Nothing -------------------------------------------------- {- | The globally unique identifier for a type: @(pkg, <https://www.haskell.org/onlinereport/lexemes.html modid>, <https://www.haskell.org/onlinereport/lexemes.html tycon>)@ >>> :set -XPolyKinds >>> import Data.Proxy >>> displayGUI $ fromTypeProxy (Proxy :: Proxy []) "ghc-prim:GHC.Types.(type [])" -} fromTypeProxy :: forall a proxy. ( Typeable a ) => proxy a -> GUI fromTypeProxy = typeRep > typeRepTyCon > go where go t = GUI{..} where _guiPackage = PkgName (tyConPackage t) _guiModule = ModName (tyConModule t) _guiIdentifier = OccName (tyConName t) _guiNamespace = TcClsName -- {-| -- -} -- failure :: Name -> Possibly a -- failure = throwM . userError . showName -------------------------------------------------- {-| >>> displayGUI (GUI (PkgName "package-name") (ModName "Module.SubModule") (OccName "identifierName") VarName) "package-name:Module.SubModule.identifierName" >>> displayGUI (GUI (PkgName "package-name") (ModName "Module.SubModule") (OccName "ConstructorName") DataName) "package-name:Module.SubModule.ConstructorName" >>> displayGUI (GUI (PkgName "package-name") (ModName "Module.SubModule") (OccName "TypeName") TcClsName) "package-name:Module.SubModule.(type TypeName)" -} displayGUI :: GUI -> String displayGUI (GUI (PkgName p) (ModName m) (OccName o) n) = p ++ ":" ++ m ++ "." ++ i where i = displayNamespacePrefix n & \case Nothing -> o -- un-paranthesized Just s -> "(" ++ s ++ o ++ ")" -- paranthesized, for ExplicitNamespace syntax displayNamespacePrefix :: NameSpace -> Maybe String displayNamespacePrefix = \case VarName -> Nothing -- "" DataName -> Nothing -- "" TcClsName -> Just "type " -------------------------------------------------- -- Notes ----------------------------------------- -------------------------------------------------- {-NOTES data Name Name OccName NameFlavour the built-in syntax 'f and ''T can be used to construct names, The expression 'f gives a Name which refers to the value f currently in scope, and ''T gives a Name which refers to the type T currently in scope. These names can never be captured. data NameFlavour NameG NameSpace PkgName ModName ... Global name bound outside of the TH AST: An original name (occurrences only, not binders) Need the namespace too to be sure which thing we are naming data NameSpace VarName ... Variables -}