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 Data.Foldable (foldrM)
31import qualified Data.Map.Strict as Map
32import Data.Map.Strict ((!))
33import Data.Monoid ((<>))
34import qualified Data.Text as Text
35import Data.Text (Text)
36import Data.Word (Word32)
38import qualified AST.Module.Name as ModuleName
39import qualified AST.Type as T
40import qualified Elm.Name as N
41import qualified Type.UnionFind as UF
53 = App1 ModuleName.Canonical N.Name [Variable]
54 | Fun1 Variable Variable
56 | Record1 (Map.Map Text Variable) Variable
58 | Tuple1 Variable Variable (Maybe Variable)
63 | AliasN ModuleName.Canonical N.Name [(Text, Type)] Type
65 | AppN ModuleName.Canonical N.Name [Type]
68 | RecordN (Map.Map Text Type) Type
70 | TupleN Type Type (Maybe Type)
82 , _copy :: Maybe Variable
87 = FlexVar (Maybe Text)
88 | FlexSuper T.Super (Maybe Text)
90 | RigidSuper T.Super Text
92 | Alias ModuleName.Canonical N.Name [(Text,Variable)] Variable
96makeDescriptor :: Content -> Descriptor
97makeDescriptor content =
98 Descriptor content noRank noMark Nothing
119newtype Mark = Mark Word32
133getVarNamesMark :: Mark
138{-# INLINE nextMark #-}
139nextMark :: Mark -> Mark
140nextMark (Mark mark) =
152(==>) :: Type -> Type -> Type
157{-# NOINLINE float #-}
160 AppN ModuleName.basics "Float" []
166 AppN ModuleName.basics "Char" []
169{-# NOINLINE string #-}
172 AppN ModuleName.basics "String" []
178 AppN ModuleName.basics "Bool" []
182-- MAKE FLEX VARIABLES
185mkFlexVar :: IO Variable
187 UF.fresh flexVarDescriptor
190{-# NOINLINE flexVarDescriptor #-}
191flexVarDescriptor :: Descriptor
193 makeDescriptor unnamedFlexVar
196{-# NOINLINE unnamedFlexVar #-}
197unnamedFlexVar :: Content
206mkFlexNumber :: IO Variable
208 UF.fresh flexNumberDescriptor
211{-# NOINLINE flexNumberDescriptor #-}
212flexNumberDescriptor :: Descriptor
213flexNumberDescriptor =
214 makeDescriptor (unnamedFlexSuper T.Number)
217unnamedFlexSuper :: T.Super -> Content
218unnamedFlexSuper super =
219 FlexSuper super Nothing
223-- MAKE NAMED VARIABLES
226nameToFlex :: N.Name -> IO Variable
228 UF.fresh $ makeDescriptor $
229 maybe FlexVar FlexSuper (toSuper name) (Just name)
232nameToRigid :: N.Name -> IO Variable
234 UF.fresh $ makeDescriptor $
235 maybe RigidVar RigidSuper (toSuper name) name
238toSuper :: N.Name -> Maybe T.Super
240 if Text.isPrefixOf "number" name then
243 else if Text.isPrefixOf "comparable" name then
246 else if Text.isPrefixOf "appendable" name then
249 else if Text.isPrefixOf "compappend" name then
260-- TODO should the freeFlexVars be ranked a certain way?
261-- How does this interact with `TS.flatten` exactly?
262fromFlexSrcType :: T.Canonical -> IO ( Type, Map.Map N.Name Variable )
263fromFlexSrcType srcType =
264 do let freeVars = gatherFreeVars srcType Map.empty
265 freeFlexVars <- Map.traverseWithKey (\name () -> nameToFlex name) freeVars
266 tipe <- fromSrcType (Map.map VarN freeFlexVars) srcType
267 return ( tipe, freeFlexVars )
270fromRigidSrcType :: T.Canonical -> IO ( Type, Map.Map N.Name Variable )
271fromRigidSrcType srcType =
272 do let freeVars = gatherFreeVars srcType Map.empty
273 freeRigidVars <- Map.traverseWithKey (\name () -> nameToRigid name) freeVars
274 tipe <- fromSrcType (Map.map VarN freeRigidVars) srcType
275 return ( tipe, freeRigidVars )
278gatherFreeVars :: T.Canonical -> Map.Map N.Name () -> Map.Map N.Name ()
279gatherFreeVars tipe dict =
281 T.Lambda arg result ->
282 gatherFreeVars result (gatherFreeVars arg dict)
285 Map.insert name () dict
288 foldr gatherFreeVars dict args
290 T.Aliased _ _ args _ ->
291 foldr gatherFreeVars dict (map snd args)
293 T.Tuple a b maybeC ->
294 gatherFreeVars a $ gatherFreeVars b $
300 gatherFreeVars c dict
305 T.Record fields maybeExt ->
308 Map.foldr gatherFreeVars dict fields
311 Map.foldr gatherFreeVars (gatherFreeVars ext dict) fields
314fromSrcType :: Map.Map N.Name Type -> T.Canonical -> IO Type
315fromSrcType freeVars sourceType =
317 T.Lambda arg result ->
319 <$> fromSrcType freeVars arg
320 <*> fromSrcType freeVars result
323 return (freeVars ! name)
325 T.Type home name args ->
326 AppN home name <$> traverse (fromSrcType freeVars) args
328 T.Aliased home name args aliasedType ->
329 do targs <- traverse (traverse (fromSrcType freeVars)) args
330 AliasN home name targs <$>
333 fromSrcType freeVars realType
336 fromSrcType (Map.fromList targs) realType
338 T.Tuple a b maybeC ->
340 <$> fromSrcType freeVars a
341 <*> fromSrcType freeVars b
342 <*> traverse (fromSrcType freeVars) maybeC
347 T.Record fields maybeExt ->
349 <$> traverse (fromSrcType freeVars) fields
356 fromSrcType freeVars ext
363-- TODO: Attach resulting type to the descriptor so that you
364-- never have to do extra work, particularly nice for aliased types
365toSrcType :: Variable -> IO T.Canonical
367 do takenNames <- getVarNames variable Map.empty
368 State.evalStateT (variableToSrcType variable) (makeNameState takenNames)
371variableToSrcType :: Variable -> StateT NameState IO T.Canonical
372variableToSrcType variable =
373 do descriptor <- liftIO $ UF.descriptor variable
374 let mark = _mark descriptor
375 if mark == occursMark
380 do liftIO $ UF.modifyDescriptor variable (\desc -> desc { _mark = occursMark })
381 srcType <- contentToSrcType variable (_content descriptor)
382 liftIO $ UF.modifyDescriptor variable (\desc -> desc { _mark = mark })
386contentToSrcType :: Variable -> Content -> StateT NameState IO T.Canonical
387contentToSrcType variable content =
398 do name <- getFreshVarName
399 liftIO $ UF.modifyDescriptor variable (\desc -> desc { _content = FlexVar (Just name) })
402 FlexSuper super maybeName ->
408 do name <- getFreshSuperName super
409 liftIO $ UF.modifyDescriptor variable (\desc -> desc { _content = FlexSuper super (Just name) })
418 Alias home name args realVariable ->
419 do srcArgs <- traverse (traverse variableToSrcType) args
420 srcType <- variableToSrcType realVariable
421 return (T.Aliased home name srcArgs (T.Filled srcType))
427termToSrcType :: FlatType -> StateT NameState IO T.Canonical
430 App1 home name args ->
431 T.Type home name <$> traverse variableToSrcType args
435 <$> variableToSrcType a
436 <*> variableToSrcType b
439 return $ T.Record Map.empty Nothing
441 Record1 fields extension ->
442 do srcFields <- traverse variableToSrcType fields
443 srcExt <- T.iteratedDealias <$> variableToSrcType extension
446 T.Record subFields subExt ->
447 T.Record (Map.union subFields srcFields) subExt
450 T.Record srcFields (Just srcExt)
453 error "Used toSrcType on a type that is not well-formed"
460 <$> variableToSrcType a
461 <*> variableToSrcType b
462 <*> traverse variableToSrcType maybeC
466-- MANAGE FRESH VARIABLE NAMES
471 { _taken :: TakenNames
474 , _comparables :: Int
475 , _appendables :: Int
476 , _compAppends :: Int
480type TakenNames = Map.Map Text Variable
483makeNameState :: TakenNames -> NameState
485 NameState taken 0 0 0 0 0
488getFreshVarName :: (Monad m) => StateT NameState m Text
490 do index <- State.gets _normals
491 taken <- State.gets _taken
492 let (uniqueName, newIndex) = getFreshNormal index taken
493 State.modify (\state -> state { _normals = newIndex })
497getFreshSuperName :: (Monad m) => T.Super -> StateT NameState m Text
498getFreshSuperName super =
501 getFreshSuper "number" _numbers (\index state -> state { _numbers = index })
504 getFreshSuper "comparable" _comparables (\index state -> state { _comparables = index })
507 getFreshSuper "appendable" _appendables (\index state -> state { _appendables = index })
510 getFreshSuper "compappend" _compAppends (\index state -> state { _compAppends = index })
513getFreshNormal :: Int -> TakenNames -> (Text, Int)
514getFreshNormal index taken =
520 Char.chr (97 + letter)
523 Text.pack (if postfix <= 0 then [character] else character : show postfix)
525 if Map.member name taken then
526 getFreshNormal (index + 1) taken
535 -> (NameState -> Int)
536 -> (Int -> NameState -> NameState)
537 -> StateT NameState m Text
538getFreshSuper name getter setter =
539 do index <- State.gets getter
540 taken <- State.gets _taken
541 let (uniqueName, newIndex) = getFreshSuperHelp name index taken
542 State.modify (setter newIndex)
546getFreshSuperHelp :: Text -> Int -> TakenNames -> (Text, Int)
547getFreshSuperHelp name index taken =
550 if index <= 0 then name else name <> Text.pack (show index)
552 if Map.member newName taken then
553 getFreshSuperHelp name (index + 1) taken
560-- GET ALL VARIABLE NAMES
563getVarNames :: Variable -> TakenNames -> IO TakenNames
564getVarNames var takenNames =
565 do (Descriptor content rank mark copy) <- UF.descriptor var
566 if mark == getVarNamesMark
567 then return takenNames
569 do UF.setDescriptor var (Descriptor content rank getVarNamesMark copy)
580 addName 0 name var (FlexVar . Just) takenNames
582 FlexSuper super maybeName ->
588 addName 0 name var (FlexSuper super . Just) takenNames
591 addName 0 name var RigidVar takenNames
593 RigidSuper super name ->
594 addName 0 name var (RigidSuper super) takenNames
597 foldrM getVarNames takenNames (map snd args)
599 Structure flatType ->
602 foldrM getVarNames takenNames args
605 getVarNames arg =<< getVarNames body takenNames
610 Record1 fields extension ->
611 getVarNames extension =<<
612 foldrM getVarNames takenNames (Map.elems fields)
617 Tuple1 a b Nothing ->
618 getVarNames a =<< getVarNames b takenNames
620 Tuple1 a b (Just c) ->
621 getVarNames a =<< getVarNames b =<< getVarNames c takenNames
625-- REGISTER NAME / RENAME DUPLICATES
628addName :: Int -> N.Name -> Variable -> (N.Name -> Content) -> TakenNames -> IO TakenNames
629addName index givenName var makeContent takenNames =
634 else if Char.isDigit (Text.last givenName) then
635 givenName <> Text.pack ('_' : show index)
637 givenName <> Text.pack (show index)
639 case Map.lookup indexedName takenNames of
641 do if indexedName == givenName then return () else
642 UF.modifyDescriptor var $ \(Descriptor _ rank mark copy) ->
643 Descriptor (makeContent indexedName) rank mark copy
644 return $ Map.insert indexedName var takenNames
647 do same <- UF.equivalent var otherVar
649 then return takenNames
650 else addName (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