{-# LANGUAGE DeriveGeneric #-}
module Language.PureScript.Environment where
import Prelude.Compat
import Protolude (ordNub)
import GHC.Generics (Generic)
import Control.DeepSeq (NFData)
import Data.Aeson ((.=), (.:))
import qualified Data.Aeson as A
import qualified Data.Map as M
import qualified Data.Set as S
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Tree (Tree, rootLabel)
import qualified Data.Graph as G
import Data.Foldable (toList, fold)
import Language.PureScript.Crash
import Language.PureScript.Kinds
import Language.PureScript.Names
import Language.PureScript.TypeClassDictionaries
import Language.PureScript.Types
import qualified Language.PureScript.Constants as C
data Environment = Environment
{ names :: M.Map (Qualified Ident) (Type, NameKind, NameVisibility)
, types :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
, dataConstructors :: M.Map (Qualified (ProperName 'ConstructorName)) (DataDeclType, ProperName 'TypeName, Type, [Ident])
, typeSynonyms :: M.Map (Qualified (ProperName 'TypeName)) ([(Text, Maybe Kind)], Type)
, typeClassDictionaries :: M.Map (Maybe ModuleName) (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) NamedDict))
, typeClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
, kinds :: S.Set (Qualified (ProperName 'KindName))
} deriving (Show, Generic)
instance NFData Environment
data TypeClassData = TypeClassData
{ typeClassArguments :: [(Text, Maybe Kind)]
, typeClassMembers :: [(Ident, Type)]
, typeClassSuperclasses :: [Constraint]
, typeClassDependencies :: [FunctionalDependency]
, typeClassDeterminedArguments :: S.Set Int
, typeClassCoveringSets :: S.Set (S.Set Int)
} deriving (Show, Generic)
instance NFData TypeClassData
data FunctionalDependency = FunctionalDependency
{ fdDeterminers :: [Int]
, fdDetermined :: [Int]
} deriving (Show, Generic)
instance NFData FunctionalDependency
instance A.FromJSON FunctionalDependency where
parseJSON = A.withObject "FunctionalDependency" $ \o ->
FunctionalDependency
<$> o .: "determiners"
<*> o .: "determined"
instance A.ToJSON FunctionalDependency where
toJSON FunctionalDependency{..} =
A.object [ "determiners" .= fdDeterminers
, "determined" .= fdDetermined
]
initEnvironment :: Environment
initEnvironment = Environment M.empty allPrimTypes M.empty M.empty M.empty allPrimClasses allPrimKinds
makeTypeClassData
:: [(Text, Maybe Kind)]
-> [(Ident, Type)]
-> [Constraint]
-> [FunctionalDependency]
-> TypeClassData
makeTypeClassData args m s deps = TypeClassData args m s deps determinedArgs coveringSets
where
argumentIndicies = [0 .. length args - 1]
identities = (\i -> (i, [i])) <$> argumentIndicies
contributingDeps = M.fromListWith (++) $ identities ++ do
fd <- deps
src <- fdDeterminers fd
(src, fdDetermined fd) : map (, []) (fdDetermined fd)
(depGraph, fromVertex, fromKey) = G.graphFromEdges ((\(n, v) -> (n, n, ordNub v)) <$> M.toList contributingDeps)
isFunDepDetermined :: Int -> Bool
isFunDepDetermined arg = case fromKey arg of
Nothing -> internalError "Unknown argument index in makeTypeClassData"
Just v -> let contributesToVar = G.reachable (G.transposeG depGraph) v
varContributesTo = G.reachable depGraph v
in any (\r -> not (r `elem` varContributesTo)) contributesToVar
determinedArgs :: S.Set Int
determinedArgs = S.fromList $ filter isFunDepDetermined argumentIndicies
argFromVertex :: G.Vertex -> Int
argFromVertex index = let (_, arg, _) = fromVertex index in arg
isVertexDetermined :: G.Vertex -> Bool
isVertexDetermined = isFunDepDetermined . argFromVertex
sccNonDetermined :: Tree G.Vertex -> Maybe [Int]
sccNonDetermined tree
| isVertexDetermined (rootLabel tree) = Nothing
| otherwise = Just (argFromVertex <$> toList tree)
coveringSets :: S.Set (S.Set Int)
coveringSets = let funDepSets = sequence (mapMaybe sccNonDetermined (G.scc depGraph))
in S.fromList (S.fromList <$> funDepSets)
data NameVisibility
= Undefined
| Defined
deriving (Show, Eq, Generic)
instance NFData NameVisibility
data NameKind
= Private
| Public
| External
deriving (Show, Eq, Generic)
instance NFData NameKind
data TypeKind
= DataType [(Text, Maybe Kind)] [(ProperName 'ConstructorName, [Type])]
| TypeSynonym
| ExternData
| LocalTypeVariable
| ScopedTypeVar
deriving (Show, Eq, Generic)
instance NFData TypeKind
instance A.ToJSON TypeKind where
toJSON (DataType args ctors) =
A.object [ T.pack "DataType" .= A.object ["args" .= args, "ctors" .= ctors] ]
toJSON TypeSynonym = A.toJSON (T.pack "TypeSynonym")
toJSON ExternData = A.toJSON (T.pack "ExternData")
toJSON LocalTypeVariable = A.toJSON (T.pack "LocalTypeVariable")
toJSON ScopedTypeVar = A.toJSON (T.pack "ScopedTypeVar")
instance A.FromJSON TypeKind where
parseJSON (A.Object o) = do
args <- o .: "DataType"
A.withObject "args" (\o1 ->
DataType <$> o1 .: "args"
<*> o1 .: "ctors") args
parseJSON (A.String s) =
case s of
"TypeSynonym" -> pure TypeSynonym
"ExternData" -> pure ExternData
"LocalTypeVariable" -> pure LocalTypeVariable
"ScopedTypeVar" -> pure ScopedTypeVar
_ -> fail "Unknown TypeKind"
parseJSON _ = fail "Invalid TypeKind"
data DataDeclType
= Data
| Newtype
deriving (Show, Eq, Ord, Generic)
instance NFData DataDeclType
showDataDeclType :: DataDeclType -> Text
showDataDeclType Data = "data"
showDataDeclType Newtype = "newtype"
instance A.ToJSON DataDeclType where
toJSON = A.toJSON . showDataDeclType
instance A.FromJSON DataDeclType where
parseJSON = A.withText "DataDeclType" $ \str ->
case str of
"data" -> return Data
"newtype" -> return Newtype
other -> fail $ "invalid type: '" ++ T.unpack other ++ "'"
primName :: Text -> Qualified (ProperName a)
primName = Qualified (Just $ ModuleName [ProperName C.prim]) . ProperName
primSubName :: Text -> Text -> Qualified (ProperName a)
primSubName sub =
Qualified (Just $ ModuleName [ProperName C.prim, ProperName sub]) . ProperName
primKind :: Text -> Kind
primKind = NamedKind . primName
primSubKind :: Text -> Text -> Kind
primSubKind sub = NamedKind . primSubName sub
kindType :: Kind
kindType = primKind C.typ
kindConstraint :: Kind
kindConstraint = kindType
(-:>) :: Kind -> Kind -> Kind
(-:>) = FunKind
infixr 4 -:>
kindSymbol :: Kind
kindSymbol = primKind C.symbol
kindDoc :: Kind
kindDoc = primSubKind C.typeError C.doc
kindOrdering :: Kind
kindOrdering = primSubKind C.moduleOrdering C.kindOrdering
kindRowList :: Kind
kindRowList = primSubKind C.moduleRowList C.kindRowList
primTy :: Text -> Type
primTy = TypeConstructor . primName
tyFunction :: Type
tyFunction = primTy "Function"
tyString :: Type
tyString = primTy "String"
tyChar :: Type
tyChar = primTy "Char"
tyNumber :: Type
tyNumber = primTy "Number"
tyInt :: Type
tyInt = primTy "Int"
tyBoolean :: Type
tyBoolean = primTy "Boolean"
tyArray :: Type
tyArray = primTy "Array"
tyRecord :: Type
tyRecord = primTy "Record"
isObject :: Type -> Bool
isObject = isTypeOrApplied tyRecord
isFunction :: Type -> Bool
isFunction = isTypeOrApplied tyFunction
isTypeOrApplied :: Type -> Type -> Bool
isTypeOrApplied t1 (TypeApp t2 _) = t1 == t2
isTypeOrApplied t1 t2 = t1 == t2
function :: Type -> Type -> Type
function t1 = TypeApp (TypeApp tyFunction t1)
primKinds :: S.Set (Qualified (ProperName 'KindName))
primKinds = S.fromList
[ primName C.typ
, primName C.symbol
]
primOrderingKinds :: S.Set (Qualified (ProperName 'KindName))
primOrderingKinds = S.fromList
[ primSubName C.moduleOrdering C.kindOrdering
]
primRowListKinds :: S.Set (Qualified (ProperName 'KindName))
primRowListKinds = S.fromList
[ primSubName C.moduleRowList C.kindRowList
]
primTypeErrorKinds :: S.Set (Qualified (ProperName 'KindName))
primTypeErrorKinds = S.fromList
[ primSubName C.typeError C.doc
]
allPrimKinds :: S.Set (Qualified (ProperName 'KindName))
allPrimKinds = fold
[ primKinds
, primOrderingKinds
, primRowListKinds
, primTypeErrorKinds
]
primTypes :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
primTypes = M.fromList
[ (primName "Function", (kindType -:> kindType -:> kindType, ExternData))
, (primName "Array", (kindType -:> kindType, ExternData))
, (primName "Record", (Row kindType -:> kindType, ExternData))
, (primName "String", (kindType, ExternData))
, (primName "Char", (kindType, ExternData))
, (primName "Number", (kindType, ExternData))
, (primName "Int", (kindType, ExternData))
, (primName "Boolean", (kindType, ExternData))
, (primName "Partial", (kindConstraint, ExternData))
]
allPrimTypes :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
allPrimTypes = M.unions
[ primTypes
, primOrderingTypes
, primRowTypes
, primRowListTypes
, primSymbolTypes
, primTypeErrorTypes
]
primOrderingTypes :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
primOrderingTypes =
M.fromList
[ (primSubName C.moduleOrdering "LT", (kindOrdering, ExternData))
, (primSubName C.moduleOrdering "EQ", (kindOrdering, ExternData))
, (primSubName C.moduleOrdering "GT", (kindOrdering, ExternData))
]
primRowTypes :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
primRowTypes =
M.fromList
[ (primSubName C.moduleRow "Union", (Row kindType -:> Row kindType -:> Row kindType -:> kindConstraint, ExternData))
, (primSubName C.moduleRow "Nub", (Row kindType -:> Row kindType -:> kindConstraint, ExternData))
, (primSubName C.moduleRow "Lacks", (kindSymbol -:> Row kindType -:> kindConstraint, ExternData))
, (primSubName C.moduleRow "Cons", (kindSymbol -:> kindType -:> Row kindType -:> Row kindType -:> kindConstraint, ExternData))
]
primRowListTypes :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
primRowListTypes =
M.fromList
[ (primSubName C.moduleRowList "Cons", (kindSymbol -:> kindType -:> kindRowList -:> kindRowList, ExternData))
, (primSubName C.moduleRowList "Nil", (kindRowList, ExternData))
, (primSubName C.moduleRowList "RowToList", (Row kindType -:> kindRowList -:> kindConstraint, ExternData))
]
primSymbolTypes :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
primSymbolTypes =
M.fromList
[ (primSubName C.moduleSymbol "Append", (kindSymbol -:> kindSymbol -:> kindSymbol -:> kindConstraint, ExternData))
, (primSubName C.moduleSymbol "Compare", (kindSymbol -:> kindSymbol -:> kindOrdering -:> kindConstraint, ExternData))
, (primSubName C.moduleSymbol "Cons", (kindSymbol -:> kindSymbol -:> kindSymbol -:> kindConstraint, ExternData))
]
primTypeErrorTypes :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
primTypeErrorTypes =
M.fromList
[ (primSubName C.typeError "Fail", (kindDoc -:> kindConstraint, ExternData))
, (primSubName C.typeError "Warn", (kindDoc -:> kindConstraint, ExternData))
, (primSubName C.typeError "Text", (kindSymbol -:> kindDoc, ExternData))
, (primSubName C.typeError "Quote", (kindType -:> kindDoc, ExternData))
, (primSubName C.typeError "Beside", (kindDoc -:> kindDoc -:> kindDoc, ExternData))
, (primSubName C.typeError "Above", (kindDoc -:> kindDoc -:> kindDoc, ExternData))
]
primClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
primClasses =
M.fromList
[ (primName "Partial", (makeTypeClassData [] [] [] []))
]
allPrimClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
allPrimClasses = M.unions
[ primClasses
, primRowClasses
, primRowListClasses
, primSymbolClasses
, primTypeErrorClasses
]
primRowClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
primRowClasses =
M.fromList
[ (primSubName C.moduleRow "Union", makeTypeClassData
[ ("left", Just (Row kindType))
, ("right", Just (Row kindType))
, ("union", Just (Row kindType))
] [] []
[ FunctionalDependency [0, 1] [2]
, FunctionalDependency [1, 2] [0]
, FunctionalDependency [2, 0] [1]
])
, (primSubName C.moduleRow "Nub", makeTypeClassData
[ ("original", Just (Row kindType))
, ("nubbed", Just (Row kindType))
] [] []
[ FunctionalDependency [0] [1]
])
, (primSubName C.moduleRow "Lacks", makeTypeClassData
[ ("label", Just kindSymbol)
, ("row", Just (Row kindType))
] [] [] [])
, (primSubName C.moduleRow "Cons", makeTypeClassData
[ ("label", Just kindSymbol)
, ("a", Just kindType)
, ("tail", Just (Row kindType))
, ("row", Just (Row kindType))
] [] []
[ FunctionalDependency [0, 1, 2] [3]
, FunctionalDependency [0, 3] [1, 2]
])
]
primRowListClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
primRowListClasses =
M.fromList
[ (primSubName C.moduleRowList "RowToList", makeTypeClassData
[ ("row", Just (Row kindType))
, ("list", Just kindRowList)
] [] []
[ FunctionalDependency [0] [1]
])
]
primSymbolClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
primSymbolClasses =
M.fromList
[ (primSubName C.moduleSymbol "Append", makeTypeClassData
[ ("left", Just kindSymbol)
, ("right", Just kindSymbol)
, ("appended", Just kindSymbol)
] [] []
[ FunctionalDependency [0, 1] [2]
, FunctionalDependency [1, 2] [0]
, FunctionalDependency [2, 0] [1]
])
, (primSubName C.moduleSymbol "Compare", makeTypeClassData
[ ("left", Just kindSymbol)
, ("right", Just kindSymbol)
, ("ordering", Just kindOrdering)
] [] []
[ FunctionalDependency [0, 1] [2]
])
, (primSubName C.moduleSymbol "Cons", makeTypeClassData
[ ("head", Just kindSymbol)
, ("tail", Just kindSymbol)
, ("symbol", Just kindSymbol)
] [] []
[ FunctionalDependency [0, 1] [2]
, FunctionalDependency [2] [0, 1]
])
]
primTypeErrorClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
primTypeErrorClasses =
M.fromList
[ (primSubName C.typeError "Fail", makeTypeClassData
[("message", Just kindDoc)] [] [] [])
, (primSubName C.typeError "Warn", makeTypeClassData
[("message", Just kindDoc)] [] [] [])
]
lookupConstructor :: Environment -> Qualified (ProperName 'ConstructorName) -> (DataDeclType, ProperName 'TypeName, Type, [Ident])
lookupConstructor env ctor =
fromMaybe (internalError "Data constructor not found") $ ctor `M.lookup` dataConstructors env
isNewtypeConstructor :: Environment -> Qualified (ProperName 'ConstructorName) -> Bool
isNewtypeConstructor e ctor = case lookupConstructor e ctor of
(Newtype, _, _, _) -> True
(Data, _, _, _) -> False
lookupValue :: Environment -> Qualified Ident -> Maybe (Type, NameKind, NameVisibility)
lookupValue env ident = ident `M.lookup` names env