1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
7 , Descriptor(Descriptor)
14 , (==>), float, char, string, bool
27import Control.Monad.State.Strict (StateT, liftIO)
28import qualified Control.Monad.State.Strict as State
29import qualified Data.Char as Char
30import qualified Data.Map.Strict as Map
31import Data.Map.Strict ((!))
32import Data.Monoid ((<>))
33import qualified Data.Text as Text
34import Data.Text (Text)
35import Data.Word (Word32)
37import qualified AST.Module.Name as ModuleName
38import qualified AST.Type as T
39import qualified Elm.Name as N
40import qualified Type.UnionFind as UF
52 = App1 ModuleName.Canonical N.Name [Variable]
53 | Fun1 Variable Variable
55 | Record1 (Map.Map Text Variable) Variable
57 | Tuple1 Variable Variable [Variable]
62 | AliasN ModuleName.Canonical N.Name [(Text, Type)] Type
64 | AppN ModuleName.Canonical N.Name [Type]
67 | RecordN (Map.Map Text Type) Type
69 | TupleN Type Type [Type]
81 , _copy :: Maybe Variable
86 = FlexVar (Maybe Text)
87 | FlexSuper T.Super (Maybe Text)
89 | RigidSuper T.Super Text
91 | Alias ModuleName.Canonical N.Name [(Text,Variable)] Variable
95makeDescriptor :: Content -> Descriptor
96makeDescriptor content =
97 Descriptor content noRank noMark Nothing
118newtype Mark = Mark Word32
132getVarNamesMark :: Mark
137{-# INLINE nextMark #-}
138nextMark :: Mark -> Mark
139nextMark (Mark mark) =
151(==>) :: Type -> Type -> Type
156{-# NOINLINE float #-}
159 AppN ModuleName.basics "Float" []
165 AppN ModuleName.basics "Char" []
168{-# NOINLINE string #-}
171 AppN ModuleName.basics "String" []
177 AppN ModuleName.basics "Bool" []
181-- MAKE FLEX VARIABLES
184mkFlexVar :: IO Variable
186 UF.fresh flexVarDescriptor
189{-# NOINLINE flexVarDescriptor #-}
190flexVarDescriptor :: Descriptor
192 makeDescriptor unnamedFlexVar
195{-# NOINLINE unnamedFlexVar #-}
196unnamedFlexVar :: Content
205mkFlexNumber :: IO Variable
207 UF.fresh flexNumberDescriptor
210{-# NOINLINE flexNumberDescriptor #-}
211flexNumberDescriptor :: Descriptor
212flexNumberDescriptor =
213 makeDescriptor (unnamedFlexSuper T.Number)
216unnamedFlexSuper :: T.Super -> Content
217unnamedFlexSuper super =
218 FlexSuper super Nothing
222-- MAKE NAMED VARIABLES
225nameToFlex :: N.Name -> IO Variable
227 UF.fresh $ makeDescriptor $
228 maybe FlexVar FlexSuper (toSuper name) (Just name)
231nameToRigid :: N.Name -> IO Variable
233 UF.fresh $ makeDescriptor $
234 maybe RigidVar RigidSuper (toSuper name) name
237toSuper :: N.Name -> Maybe T.Super
239 if Text.isPrefixOf "number" name then
242 else if Text.isPrefixOf "comparable" name then
245 else if Text.isPrefixOf "appendable" name then
248 else if Text.isPrefixOf "compappend" name then
259-- TODO should the freeFlexVars be ranked a certain way?
260-- How does this interact with `TS.flatten` exactly?
261fromFlexSrcType :: T.Canonical -> IO ( Type, Map.Map N.Name Variable )
262fromFlexSrcType srcType =
263 do let freeVars = gatherFreeVars srcType Map.empty
264 freeFlexVars <- Map.traverseWithKey (\name () -> nameToFlex name) freeVars
265 tipe <- fromSrcType (Map.map VarN freeFlexVars) srcType
266 return ( tipe, freeFlexVars )
269fromRigidSrcType :: T.Canonical -> IO ( Type, Map.Map N.Name Variable )
270fromRigidSrcType srcType =
271 do let freeVars = gatherFreeVars srcType Map.empty
272 freeRigidVars <- Map.traverseWithKey (\name () -> nameToRigid name) freeVars
273 tipe <- fromSrcType (Map.map VarN freeRigidVars) srcType
274 return ( tipe, freeRigidVars )
277gatherFreeVars :: T.Canonical -> Map.Map N.Name () -> Map.Map N.Name ()
278gatherFreeVars tipe dict =
280 T.Lambda arg result ->
281 gatherFreeVars result (gatherFreeVars arg dict)
284 Map.insert name () dict
287 foldr gatherFreeVars dict args
289 T.Aliased _ _ args _ ->
290 foldr gatherFreeVars dict (map snd args)
293 gatherFreeVars a $ gatherFreeVars b $
294 foldr gatherFreeVars dict cs
299 T.Record fields maybeExt ->
302 Map.foldr gatherFreeVars dict fields
305 Map.foldr gatherFreeVars (gatherFreeVars ext dict) fields
308fromSrcType :: Map.Map N.Name Type -> T.Canonical -> IO Type
309fromSrcType freeVars sourceType =
311 T.Lambda arg result ->
313 <$> fromSrcType freeVars arg
314 <*> fromSrcType freeVars result
317 return (freeVars ! name)
319 T.Type home name args ->
320 AppN home name <$> traverse (fromSrcType freeVars) args
322 T.Aliased home name args aliasedType ->
323 do targs <- traverse (traverse (fromSrcType freeVars)) args
324 AliasN home name targs <$>
327 fromSrcType freeVars realType
330 fromSrcType (Map.fromList targs) realType
334 <$> fromSrcType freeVars a
335 <*> fromSrcType freeVars b
336 <*> traverse (fromSrcType freeVars) cs
341 T.Record fields maybeExt ->
343 <$> traverse (fromSrcType freeVars) fields
350 fromSrcType freeVars ext
357-- TODO: Attach resulting type to the descriptor so that you
358-- never have to do extra work, particularly nice for aliased types
359toSrcType :: Variable -> IO T.Canonical
361 do takenNames <- State.execStateT (getVarNames variable) Map.empty
362 State.evalStateT (variableToSrcType variable) (makeNameState takenNames)
365variableToSrcType :: Variable -> StateT NameState IO T.Canonical
366variableToSrcType variable =
367 do descriptor <- liftIO $ UF.descriptor variable
368 let mark = _mark descriptor
369 if mark == occursMark
374 do liftIO $ UF.modifyDescriptor variable (\desc -> desc { _mark = occursMark })
375 srcType <- contentToSrcType variable (_content descriptor)
376 liftIO $ UF.modifyDescriptor variable (\desc -> desc { _mark = mark })
380contentToSrcType :: Variable -> Content -> StateT NameState IO T.Canonical
381contentToSrcType variable content =
392 do name <- getFreshVarName
393 liftIO $ UF.modifyDescriptor variable (\desc -> desc { _content = FlexVar (Just name) })
396 FlexSuper super maybeName ->
402 do name <- getFreshSuperName super
403 liftIO $ UF.modifyDescriptor variable (\desc -> desc { _content = FlexSuper super (Just name) })
412 Alias home name args realVariable ->
413 do srcArgs <- traverse (traverse variableToSrcType) args
414 srcType <- variableToSrcType realVariable
415 return (T.Aliased home name srcArgs (T.Filled srcType))
421termToSrcType :: FlatType -> StateT NameState IO T.Canonical
424 App1 home name args ->
425 T.Type home name <$> traverse variableToSrcType args
429 <$> variableToSrcType a
430 <*> variableToSrcType b
433 return $ T.Record Map.empty Nothing
435 Record1 fields extension ->
436 do srcFields <- traverse variableToSrcType fields
437 srcExt <- T.iteratedDealias <$> variableToSrcType extension
440 T.Record subFields subExt ->
441 T.Record (Map.union subFields srcFields) subExt
444 T.Record srcFields (Just srcExt)
447 error "Used toSrcType on a type that is not well-formed"
454 <$> variableToSrcType a
455 <*> variableToSrcType b
456 <*> traverse variableToSrcType cs
460-- MANAGE FRESH VARIABLE NAMES
465 { _taken :: TakenNames
468 , _comparables :: Int
469 , _appendables :: Int
470 , _compAppends :: Int
474type TakenNames = Map.Map Text Variable
477makeNameState :: TakenNames -> NameState
479 NameState taken 0 0 0 0 0
482getFreshVarName :: (Monad m) => StateT NameState m Text
484 do index <- State.gets _normals
485 taken <- State.gets _taken
486 let (uniqueName, newIndex) = getFreshNormal index taken
487 State.modify (\state -> state { _normals = newIndex })
491getFreshSuperName :: (Monad m) => T.Super -> StateT NameState m Text
492getFreshSuperName super =
495 getFreshSuper "number" _numbers (\index state -> state { _numbers = index })
498 getFreshSuper "comparable" _comparables (\index state -> state { _comparables = index })
501 getFreshSuper "appendable" _appendables (\index state -> state { _appendables = index })
504 getFreshSuper "compappend" _compAppends (\index state -> state { _compAppends = index })
507getFreshNormal :: Int -> TakenNames -> (Text, Int)
508getFreshNormal index taken =
514 Char.chr (97 + letter)
517 Text.pack (if postfix <= 0 then [character] else character : show postfix)
519 if Map.member name taken then
520 getFreshNormal (index + 1) taken
529 -> (NameState -> Int)
530 -> (Int -> NameState -> NameState)
531 -> StateT NameState m Text
532getFreshSuper name getter setter =
533 do index <- State.gets getter
534 taken <- State.gets _taken
535 let (uniqueName, newIndex) = getFreshSuperHelp name index taken
536 State.modify (setter newIndex)
540getFreshSuperHelp :: Text -> Int -> TakenNames -> (Text, Int)
541getFreshSuperHelp name index taken =
544 if index <= 0 then name else name <> Text.pack (show index)
546 if Map.member newName taken then
547 getFreshSuperHelp name (index + 1) taken
554-- GET ALL VARIABLE NAMES
557getVarNames :: Variable -> StateT TakenNames IO ()
559 do (Descriptor content rank mark copy) <- liftIO $ UF.descriptor var
560 if mark == getVarNamesMark then return () else
561 do liftIO $ UF.setDescriptor var (Descriptor content rank getVarNamesMark copy)
572 registerName name var (FlexVar . Just)
574 FlexSuper super maybeName ->
580 registerName name var (FlexSuper super . Just)
583 registerName name var RigidVar
585 RigidSuper super name ->
586 registerName name var (RigidSuper super)
588 Alias _ _ args realVar ->
589 do mapM_ (getVarNames . snd) args
592 Structure flatType ->
595 mapM_ getVarNames args
604 Record1 fields extension ->
605 do mapM_ getVarNames fields
606 getVarNames extension
618-- REGISTER NAME / RENAME DUPLICATES
621registerName :: Text -> Variable -> (Text -> Content) -> StateT TakenNames IO ()
622registerName givenName var makeContent =
623 if Text.null givenName then
626 do takenNames <- State.get
627 State.put =<< liftIO (checkName 0 givenName var makeContent takenNames)
630checkName :: Int -> Text -> Variable -> (Text -> Content) -> TakenNames -> IO TakenNames
631checkName index givenName var makeContent takenNames =
636 else if Char.isDigit (Text.last givenName) then
637 givenName <> Text.pack ('_' : show index)
639 givenName <> Text.pack (show index)
641 case Map.lookup indexedName takenNames of
643 do if indexedName == givenName then return () else
644 UF.modifyDescriptor var $ \(Descriptor _ rank mark copy) ->
645 Descriptor (makeContent indexedName) rank mark copy
646 return $ Map.insert indexedName var takenNames
649 do same <- UF.equivalent var otherVar
651 then return takenNames
652 else checkName (index + 1) givenName var makeContent takenNames
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list