forked from purescript/purescript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExterns.hs
More file actions
238 lines (218 loc) · 9.88 KB
/
Externs.hs
File metadata and controls
238 lines (218 loc) · 9.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
{-# LANGUAGE TemplateHaskell #-}
-- |
-- This module generates code for \"externs\" files, i.e. files containing only
-- foreign import declarations.
--
module Language.PureScript.Externs
( ExternsFile(..)
, ExternsImport(..)
, ExternsFixity(..)
, ExternsTypeFixity(..)
, ExternsDeclaration(..)
, moduleToExternsFile
, applyExternsFileToEnvironment
) where
import Prelude.Compat
import Data.Aeson.TH
import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
import Data.List (foldl', find)
import Data.Foldable (fold)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Version (showVersion)
import qualified Data.Map as M
import qualified Data.Set as S
import Language.PureScript.AST
import Language.PureScript.Crash
import Language.PureScript.Environment
import Language.PureScript.Kinds
import Language.PureScript.Names
import Language.PureScript.TypeClassDictionaries
import Language.PureScript.Types
import Paths_purescript as Paths
-- | The data which will be serialized to an externs file
data ExternsFile = ExternsFile
{
-- | The externs version
efVersion :: Text
-- | Module name
, efModuleName :: ModuleName
-- | List of module exports
, efExports :: [DeclarationRef]
-- | List of module imports
, efImports :: [ExternsImport]
-- | List of operators and their fixities
, efFixities :: [ExternsFixity]
-- | List of type operators and their fixities
, efTypeFixities :: [ExternsTypeFixity]
-- | List of type and value declaration
, efDeclarations :: [ExternsDeclaration]
} deriving (Show)
-- | A module import in an externs file
data ExternsImport = ExternsImport
{
-- | The imported module
eiModule :: ModuleName
-- | The import type: regular, qualified or hiding
, eiImportType :: ImportDeclarationType
-- | The imported-as name, for qualified imports
, eiImportedAs :: Maybe ModuleName
} deriving (Show)
-- | A fixity declaration in an externs file
data ExternsFixity = ExternsFixity
{
-- | The associativity of the operator
efAssociativity :: Associativity
-- | The precedence level of the operator
, efPrecedence :: Precedence
-- | The operator symbol
, efOperator :: OpName 'ValueOpName
-- | The value the operator is an alias for
, efAlias :: Qualified (Either Ident (ProperName 'ConstructorName))
} deriving (Show)
-- | A type fixity declaration in an externs file
data ExternsTypeFixity = ExternsTypeFixity
{
-- | The associativity of the operator
efTypeAssociativity :: Associativity
-- | The precedence level of the operator
, efTypePrecedence :: Precedence
-- | The operator symbol
, efTypeOperator :: OpName 'TypeOpName
-- | The value the operator is an alias for
, efTypeAlias :: Qualified (ProperName 'TypeName)
} deriving (Show)
-- | A type or value declaration appearing in an externs file
data ExternsDeclaration =
-- | A type declaration
EDType
{ edTypeName :: ProperName 'TypeName
, edTypeKind :: Kind
, edTypeDeclarationKind :: TypeKind
}
-- | A type synonym
| EDTypeSynonym
{ edTypeSynonymName :: ProperName 'TypeName
, edTypeSynonymArguments :: [(Text, Maybe Kind)]
, edTypeSynonymType :: Type
}
-- | A data construtor
| EDDataConstructor
{ edDataCtorName :: ProperName 'ConstructorName
, edDataCtorOrigin :: DataDeclType
, edDataCtorTypeCtor :: ProperName 'TypeName
, edDataCtorType :: Type
, edDataCtorFields :: [Ident]
}
-- | A value declaration
| EDValue
{ edValueName :: Ident
, edValueType :: Type
}
-- | A type class declaration
| EDClass
{ edClassName :: ProperName 'ClassName
, edClassTypeArguments :: [(Text, Maybe Kind)]
, edClassMembers :: [(Ident, Type)]
, edClassConstraints :: [Constraint]
, edFunctionalDependencies :: [FunctionalDependency]
}
-- | An instance declaration
| EDInstance
{ edInstanceClassName :: Qualified (ProperName 'ClassName)
, edInstanceName :: Ident
, edInstanceTypes :: [Type]
, edInstanceConstraints :: Maybe [Constraint]
}
-- | A kind declaration
| EDKind
{ edKindName :: ProperName 'KindName
}
deriving Show
-- | Convert an externs file back into a module
applyExternsFileToEnvironment :: ExternsFile -> Environment -> Environment
applyExternsFileToEnvironment ExternsFile{..} = flip (foldl' applyDecl) efDeclarations
where
applyDecl :: Environment -> ExternsDeclaration -> Environment
applyDecl env (EDType pn kind tyKind) = env { types = M.insert (qual pn) (kind, tyKind) (types env) }
applyDecl env (EDTypeSynonym pn args ty) = env { typeSynonyms = M.insert (qual pn) (args, ty) (typeSynonyms env) }
applyDecl env (EDDataConstructor pn dTy tNm ty nms) = env { dataConstructors = M.insert (qual pn) (dTy, tNm, ty, nms) (dataConstructors env) }
applyDecl env (EDValue ident ty) = env { names = M.insert (Qualified (Just efModuleName) ident) (ty, External, Defined) (names env) }
applyDecl env (EDClass pn args members cs deps) = env { typeClasses = M.insert (qual pn) (makeTypeClassData args members cs deps) (typeClasses env) }
applyDecl env (EDKind pn) = env { kinds = S.insert (qual pn) (kinds env) }
applyDecl env (EDInstance className ident tys cs) = env { typeClassDictionaries = updateMap (updateMap (M.insert (qual ident) dict) className) (Just efModuleName) (typeClassDictionaries env) }
where
dict :: NamedDict
dict = TypeClassDictionaryInScope (qual ident) [] className tys cs
updateMap :: (Ord k, Monoid a) => (a -> a) -> k -> M.Map k a -> M.Map k a
updateMap f = M.alter (Just . f . fold)
qual :: a -> Qualified a
qual = Qualified (Just efModuleName)
-- | Generate an externs file for all declarations in a module
moduleToExternsFile :: Module -> Environment -> ExternsFile
moduleToExternsFile (Module _ _ _ _ Nothing) _ = internalError "moduleToExternsFile: module exports were not elaborated"
moduleToExternsFile (Module _ _ mn ds (Just exps)) env = ExternsFile{..}
where
efVersion = T.pack (showVersion Paths.version)
efModuleName = mn
efExports = exps
efImports = mapMaybe importDecl ds
efFixities = mapMaybe fixityDecl ds
efTypeFixities = mapMaybe typeFixityDecl ds
efDeclarations = concatMap toExternsDeclaration efExports
fixityDecl :: Declaration -> Maybe ExternsFixity
fixityDecl (ValueFixityDeclaration (Fixity assoc prec) name op) =
fmap (const (ExternsFixity assoc prec op name)) (find (findOp getValueOpRef op) exps)
fixityDecl (PositionedDeclaration _ _ d) = fixityDecl d
fixityDecl _ = Nothing
typeFixityDecl :: Declaration -> Maybe ExternsTypeFixity
typeFixityDecl (TypeFixityDeclaration (Fixity assoc prec) name op) =
fmap (const (ExternsTypeFixity assoc prec op name)) (find (findOp getTypeOpRef op) exps)
typeFixityDecl (PositionedDeclaration _ _ d) = typeFixityDecl d
typeFixityDecl _ = Nothing
findOp :: (DeclarationRef -> Maybe (OpName a)) -> OpName a -> DeclarationRef -> Bool
findOp g op = maybe False (== op) . g
importDecl :: Declaration -> Maybe ExternsImport
importDecl (ImportDeclaration m mt qmn) = Just (ExternsImport m mt qmn)
importDecl (PositionedDeclaration _ _ d) = importDecl d
importDecl _ = Nothing
toExternsDeclaration :: DeclarationRef -> [ExternsDeclaration]
toExternsDeclaration (PositionedDeclarationRef _ _ r) = toExternsDeclaration r
toExternsDeclaration (TypeRef pn dctors) =
case Qualified (Just mn) pn `M.lookup` types env of
Nothing -> internalError "toExternsDeclaration: no kind in toExternsDeclaration"
Just (kind, TypeSynonym)
| Just (args, synTy) <- Qualified (Just mn) pn `M.lookup` typeSynonyms env -> [ EDType pn kind TypeSynonym, EDTypeSynonym pn args synTy ]
Just (kind, ExternData) -> [ EDType pn kind ExternData ]
Just (kind, tk@(DataType _ tys)) ->
EDType pn kind tk : [ EDDataConstructor dctor dty pn ty args
| dctor <- fromMaybe (map fst tys) dctors
, (dty, _, ty, args) <- maybeToList (Qualified (Just mn) dctor `M.lookup` dataConstructors env)
]
_ -> internalError "toExternsDeclaration: Invalid input"
toExternsDeclaration (ValueRef ident)
| Just (ty, _, _) <- Qualified (Just mn) ident `M.lookup` names env
= [ EDValue ident ty ]
toExternsDeclaration (TypeClassRef className)
| Just TypeClassData{..} <- Qualified (Just mn) className `M.lookup` typeClasses env
, Just (kind, TypeSynonym) <- Qualified (Just mn) (coerceProperName className) `M.lookup` types env
, Just (_, synTy) <- Qualified (Just mn) (coerceProperName className) `M.lookup` typeSynonyms env
= [ EDType (coerceProperName className) kind TypeSynonym
, EDTypeSynonym (coerceProperName className) typeClassArguments synTy
, EDClass className typeClassArguments typeClassMembers typeClassSuperclasses typeClassDependencies
]
toExternsDeclaration (TypeInstanceRef ident)
= [ EDInstance tcdClassName ident tcdInstanceTypes tcdDependencies
| m1 <- maybeToList (M.lookup (Just mn) (typeClassDictionaries env))
, m2 <- M.elems m1
, TypeClassDictionaryInScope{..} <- maybeToList (M.lookup (Qualified (Just mn) ident) m2)
]
toExternsDeclaration (KindRef pn)
| Qualified (Just mn) pn `S.member` kinds env
= [ EDKind pn ]
toExternsDeclaration _ = []
$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''ExternsImport)
$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''ExternsFixity)
$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''ExternsTypeFixity)
$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''ExternsDeclaration)
$(deriveJSON (defaultOptions { sumEncoding = ObjectWithSingleField }) ''ExternsFile)