on master help sign in
Split canonicalization setup into new module

To make things a bit clearer, it made sense to put all of the environment setup code by itself.

commit25eaad128ec6cf56b066d1295813a03bb3655ad1 authorEvan Czaplicki <info@elm-lang.org> dateSun 1 Jun 2014, 22:41 (12 years ago) parente2c3e2c
2 files changed+217−145
compiler/Transform/Canonicalize.hs+3−145 compiler/Transform/Canonicalize/Setup.hs+214
compiler/Transform/Canonicalize.hs+3−145
@@ -2,8 +2,6 @@
22module Transform.Canonicalize (module', filterExports) where
33
44import Control.Applicative ((<$>),(<*>))
5−import Control.Arrow (first)
6−import Control.Monad (foldM)
75import qualified Data.Either as Either
86import qualified Data.List as List
97import qualified Data.Map as Map
@@ -13,7 +11,7 @@ import AST.Expression.General (Expr'(..), dummyLet)
1311import qualified AST.Expression.Valid as Valid
1412import qualified AST.Expression.Canonical as Canonical
1513
16−import AST.Module (Interface(iAdts, iTypes, iAliases), CanonicalBody(..))
14+import AST.Module (CanonicalBody(..))
1715import qualified AST.Module as Module
1816import qualified AST.Type as Type
1917import qualified AST.Variable as Var
@@ -24,137 +22,15 @@ import qualified AST.Pattern as P
2422import Text.PrettyPrint as P
2523
2624import Transform.Canonicalize.Environment as Env
25+import qualified Transform.Canonicalize.Setup as Setup
2726import qualified Transform.Canonicalize.Type as Canonicalize
2827import qualified Transform.Canonicalize.Variable as Canonicalize
2928import qualified Transform.SortDefinitions as Transform
3029import qualified Transform.Declaration as Transform
3130
32−environment :: Module.Interfaces -> Module.ValidModule -> Either [Doc] Environment
33−environment interfaces m@(Module.Module _ _ _ ims decls) =
34− do () <- allImportsAvailable interfaces ims
35− nonLocalEnv <- foldM (toEnv interfaces) Env.builtIns ims
36− let moduleName = Module.getName m
37− foldM (addDecl moduleName) nonLocalEnv decls
38−
39−toEnv :: Module.Interfaces -> Environment -> (String, Module.ImportMethod)
40− -> Either [Doc] Environment
41−toEnv interfaces environ (name,method)
42− | List.isPrefixOf "Native." name = return environ
43− | otherwise =
44− case method of
45− Module.As name' ->
46− return (updateEnviron (name' ++ "."))
47−
48− Module.Open (Var.Listing vs open)
49− | open -> return (updateEnviron "")
50− | otherwise -> foldM (addValue name interface) environ vs
51− where
52− interface = (Map.!) interfaces name
53−
54− updateEnviron prefix =
55− let dict' = dict . map (first (prefix++)) in
56− merge environ $
57− Env { _values = dict' $ map pair (Map.keys (iTypes interface)) ++ ctors
58− , _adts = dict' $ map pair (Map.keys (iAdts interface))
59− , _aliases = dict' $ map alias (Map.toList (iAliases interface))
60− , _patterns = dict' $ ctors
61− }
62−
63− canonical :: String -> Var.Canonical
64− canonical = Var.Canonical (Var.Module name)
65−
66− pair :: String -> (String, Var.Canonical)
67− pair key = (key, canonical key)
68−
69− alias (x,(tvars,tipe)) = (x, (canonical x, tvars, tipe))
70−
71− ctors = concatMap (map (pair . fst) . snd . snd) (Map.toList (iAdts interface))
72−
73−addValue :: String -> Module.Interface -> Environment -> Var.Value
74− -> Either [Doc] Environment
75−addValue name interface env value =
76− let insert' x = Env.insert x (Var.Canonical (Var.Module name) x)
77− msg x = "Import Error: Could not import value '" ++ name ++ "." ++ x ++
78− "'.\n It is not exported by module " ++ name ++ "."
79− notFound x = Left [ P.text (msg x) ]
80− in
81− case value of
82− Var.Value x
83− | Map.notMember x (iTypes interface) -> notFound x
84− | otherwise ->
85− return $ env { _values = insert' x (_values env) }
86−
87− Var.Alias x ->
88− case Map.lookup x (iAliases interface) of
89− Just (tvars, t) ->
90− let v = (Var.Canonical (Var.Module name) x, tvars, t) in
91− return $ env { _aliases = Env.insert x v (_aliases env) }
92− Nothing ->
93− case Map.lookup x (iAdts interface) of
94− Nothing -> notFound x
95− Just (_,_) ->
96− return $ env { _adts = insert' x (_adts env) }
97−
98− Var.ADT x (Var.Listing xs open) ->
99− case Map.lookup x (iAdts interface) of
100− Nothing -> notFound x
101− Just (_tvars, ctors) ->
102− do ctors <- filterNames (map fst ctors)
103− return $ env { _adts = insert' x (_adts env)
104− , _values = foldr insert' (_values env) ctors
105− , _patterns = foldr insert' (_patterns env) ctors
106− }
107− where
108− filterNames names
109− | open = return names
110− | otherwise =
111− case filter (`notElem` names) xs of
112− [] -> return names
113− c:_ -> notFound c
114−
115−-- When canonicalizing, all _values should be Local, but all _adts and _patterns
116−-- should be fully namespaced. With _adts, they may appear in types that can
117−-- escape the module.
118−addDecl :: String -> Environment -> D.ValidDecl -> Either [Doc] Environment
119−addDecl moduleName env decl =
120− let namespacedVar = Var.Canonical (Var.Module moduleName)
121− addLocal x e = Env.insert x (Var.local x) e
122− addNamespaced x e = Env.insert x (namespacedVar x) e
123− in
124− case decl of
125− D.Definition (Valid.Definition pattern _ _) ->
126− return $ env
127− { _values = foldr addLocal (_values env) (P.boundVarList pattern) }
128−
129− D.Datatype name _ ctors ->
130− return $ env
131− { _values = addCtors addLocal (_values env)
132− , _adts = addNamespaced name (_adts env)
133− , _patterns = addCtors addNamespaced (_patterns env)
134− }
135− where
136− addCtors how e = foldr how e (map fst ctors)
137−
138− D.TypeAlias name tvars alias ->
139− do alias' <- Either.either throw return (Canonicalize.tipe env alias)
140− return $ env
141− { _aliases = Env.insert name (namespacedVar name, tvars, alias') (_aliases env)
142− , _values = case alias of
143− Type.Record _ _ -> addLocal name (_values env)
144− _ -> _values env
145− }
146− where
147− throw err =
148− let msg = "Error in type alias '" ++ name ++ "':"
149− in Left [ P.vcat [ P.text msg, P.text err ] ]
150−
151− D.Port _ -> return env
152−
153− D.Fixity _ _ _ -> return env
154−
15531module' :: Module.Interfaces -> Module.ValidModule -> Either [Doc] Module.CanonicalModule
15632module' interfaces modul@(Module.Module _ _ exs _ decls) =
157− do env <- environment interfaces modul
33+ do env <- Setup.environment interfaces modul
15834 canonicalDecls <- mapM (declaration env) decls
15935 exports' <- delist locals exs
16036 return $ modul { Module.exports = exports'
@@ -285,24 +161,6 @@ declaration env decl =
285161 D.Fixity assoc prec op -> return $ D.Fixity assoc prec op
286162
287163
288−allImportsAvailable :: Module.Interfaces
289− -> [(String, Module.ImportMethod)]
290− -> Either [Doc] ()
291−allImportsAvailable interfaces imports =
292− case filter (not . found) modules of
293− [] -> Right ()
294− missings -> Left [ P.text (missingModuleError missings) ]
295− where
296− modules = map fst imports
297−
298− found m = Map.member m interfaces || List.isPrefixOf "Native." m
299−
300− missingModuleError missings =
301− concat [ "The following imports were not found: "
302− , List.intercalate ", " missings
303− , "\n You may need to compile with the --make "
304− , "flag to detect modules you have written." ]
305−
306164expression :: Environment -> Valid.Expr -> Either [Doc] Canonical.Expr
307165expression env (A.A ann expr) =
308166 let go = expression env
compiler/Transform/Canonicalize/Setup.hsnew file+214
@@ -0,0 +1,214 @@
1+{-# OPTIONS_GHC -Wall #-}
2+module Transform.Canonicalize.Setup (environment) where
3+
4+import Control.Arrow (first)
5+import Control.Monad (foldM)
6+import qualified Data.Either as Either
7+import qualified Data.Graph as Graph
8+import qualified Data.List as List
9+import qualified Data.Map as Map
10+
11+import qualified AST.Expression.Valid as Valid
12+
13+import AST.Module (Interface(iAdts, iTypes, iAliases))
14+import qualified AST.Module as Module
15+import qualified AST.Type as Type
16+import qualified AST.Variable as Var
17+import qualified AST.Declaration as D
18+import AST.PrettyPrint (pretty, eightyCharLines)
19+import qualified AST.Pattern as P
20+import Text.PrettyPrint as P
21+
22+import Transform.Canonicalize.Environment as Env
23+import qualified Transform.Canonicalize.Type as Canonicalize
24+
25+environment :: Module.Interfaces -> Module.ValidModule -> Either [Doc] Environment
26+environment interfaces modul@(Module.Module _ _ _ imports decls) =
27+ do () <- allImportsAvailable
28+ nonLocalEnv <- foldM (addImports interfaces) builtIns imports
29+ let moduleName = Module.getName modul
30+ (aliases, env) = List.foldl' (addDecl moduleName) ([], nonLocalEnv) decls
31+ addTypeAliases moduleName aliases env
32+ where
33+ allImportsAvailable :: Either [Doc] ()
34+ allImportsAvailable =
35+ case filter (not . found) modules of
36+ [] -> Right ()
37+ missings -> Left [ P.text (missingModuleError missings) ]
38+ where
39+ modules = map fst imports
40+
41+ found m = Map.member m interfaces || List.isPrefixOf "Native." m
42+
43+ missingModuleError missings =
44+ concat [ "The following imports were not found: "
45+ , List.intercalate ", " missings
46+ , "\n You may need to compile with the --make "
47+ , "flag to detect modules you have written." ]
48+
49+addImports :: Module.Interfaces -> Environment -> (String, Module.ImportMethod)
50+ -> Either [Doc] Environment
51+addImports interfaces environ (name,method)
52+ | List.isPrefixOf "Native." name = return environ
53+ | otherwise =
54+ case method of
55+ Module.As name' ->
56+ return (updateEnviron (name' ++ "."))
57+
58+ Module.Open (Var.Listing vs open)
59+ | open -> return (updateEnviron "")
60+ | otherwise -> foldM (addValue name interface) environ vs
61+ where
62+ interface = (Map.!) interfaces name
63+
64+ updateEnviron prefix =
65+ let dict' = dict . map (first (prefix++)) in
66+ merge environ $
67+ Env { _values = dict' $ map pair (Map.keys (iTypes interface)) ++ ctors
68+ , _adts = dict' $ map pair (Map.keys (iAdts interface))
69+ , _aliases = dict' $ map alias (Map.toList (iAliases interface))
70+ , _patterns = dict' $ ctors
71+ }
72+
73+ canonical :: String -> Var.Canonical
74+ canonical = Var.Canonical (Var.Module name)
75+
76+ pair :: String -> (String, Var.Canonical)
77+ pair key = (key, canonical key)
78+
79+ alias (x,(tvars,tipe)) = (x, (canonical x, tvars, tipe))
80+
81+ ctors = concatMap (map (pair . fst) . snd . snd) (Map.toList (iAdts interface))
82+
83+addValue :: String -> Module.Interface -> Environment -> Var.Value
84+ -> Either [Doc] Environment
85+addValue name interface env value =
86+ let insert' x = insert x (Var.Canonical (Var.Module name) x)
87+ msg x = "Import Error: Could not import value '" ++ name ++ "." ++ x ++
88+ "'.\n It is not exported by module " ++ name ++ "."
89+ notFound x = Left [ P.text (msg x) ]
90+ in
91+ case value of
92+ Var.Value x
93+ | Map.notMember x (iTypes interface) -> notFound x
94+ | otherwise ->
95+ return $ env { _values = insert' x (_values env) }
96+
97+ Var.Alias x ->
98+ case Map.lookup x (iAliases interface) of
99+ Just (tvars, t) ->
100+ let v = (Var.Canonical (Var.Module name) x, tvars, t) in
101+ return $ env { _aliases = insert x v (_aliases env) }
102+ Nothing ->
103+ case Map.lookup x (iAdts interface) of
104+ Nothing -> notFound x
105+ Just (_,_) ->
106+ return $ env { _adts = insert' x (_adts env) }
107+
108+ Var.ADT x (Var.Listing xs open) ->
109+ case Map.lookup x (iAdts interface) of
110+ Nothing -> notFound x
111+ Just (_tvars, ctors) ->
112+ do ctors' <- filterNames (map fst ctors)
113+ return $ env { _adts = insert' x (_adts env)
114+ , _values = foldr insert' (_values env) ctors'
115+ , _patterns = foldr insert' (_patterns env) ctors'
116+ }
117+ where
118+ filterNames names
119+ | open = return names
120+ | otherwise =
121+ case filter (`notElem` names) xs of
122+ [] -> return names
123+ c:_ -> notFound c
124+
125+type Node = ((String, [String], Type.RawType), String, [String])
126+
127+node :: String -> [String] -> Type.RawType -> Node
128+node name tvars alias = ((name, tvars, alias), name, edges alias)
129+ where
130+ edges tipe =
131+ case tipe of
132+ Type.Lambda t1 t2 -> edges t1 ++ edges t2
133+ Type.Var _ -> []
134+ Type.Type (Var.Raw x) -> [x]
135+ Type.App t ts -> edges t ++ concatMap edges ts
136+ Type.Record fs ext -> maybe [] edges ext ++ concatMap (edges . snd) fs
137+ Type.Aliased _ t -> edges t
138+
139+addTypeAliases :: String -> [Node] -> Environment -> Either [Doc] Environment
140+addTypeAliases moduleName nodes environ =
141+ foldM addTypeAlias environ (Graph.stronglyConnComp nodes)
142+ where
143+ addTypeAlias :: Environment -> Graph.SCC (String, [String], Type.RawType)
144+ -> Either [Doc] Environment
145+ addTypeAlias env scc =
146+ case Graph.flattenSCC scc of
147+ [(name, tvars, alias)] ->
148+ do alias' <- Either.either throw return (Canonicalize.tipe env alias)
149+ let value = (Var.Canonical (Var.Module moduleName) name, tvars, alias')
150+ return $ env { _aliases = insert name value (_aliases env) }
151+ where
152+ throw err =
153+ let msg = "Problem with type alias '" ++ name ++ "':"
154+ in Left [ P.vcat [ P.text msg, P.text err ] ]
155+
156+ aliases ->
157+ Left [ P.vcat [ P.text (eightyCharLines 0 msg1)
158+ , indented (map typeAlias aliases)
159+ , P.text (eightyCharLines 0 msg2)
160+ , indented (map datatype aliases)
161+ , P.text (eightyCharLines 0 msg3)
162+ ]
163+ ]
164+
165+ typeAlias (n,ts,t) = D.TypeAlias n ts t
166+ datatype (n,ts,t) = D.Datatype n ts [(n,[t])]
167+
168+ indented :: [D.ValidDecl] -> Doc
169+ indented decls = P.vcat (map prty decls)
170+ where
171+ prty decl = P.text "\n " <> pretty decl <> P.text "\n"
172+
173+ msg1 = "The following type aliases are mutually recursive, forming an \
174+ \infinite type. When you expand them, they just keep getting bigger:"
175+ msg2 = "Instead, you can try something like this:"
176+ msg3 = "It looks very similar, but an algebraic data type (ADT) \
177+ \actually creates a new type. Unlike with a type alias, this \
178+ \freshly created type is meaningful on its own, so an ADT \
179+ \does not need to be expanded."
180+
181+
182+-- When canonicalizing, all _values should be Local, but all _adts and _patterns
183+-- should be fully namespaced. With _adts, they may appear in types that can
184+-- escape the module.
185+addDecl :: String -> ([Node], Environment) -> D.ValidDecl -> ([Node], Environment)
186+addDecl moduleName info@(nodes,env) decl =
187+ let namespacedVar = Var.Canonical (Var.Module moduleName)
188+ addLocal x e = insert x (Var.local x) e
189+ addNamespaced x e = insert x (namespacedVar x) e
190+ in
191+ case decl of
192+ D.Definition (Valid.Definition pattern _ _) ->
193+ (,) nodes $ env
194+ { _values = foldr addLocal (_values env) (P.boundVarList pattern) }
195+
196+ D.Datatype name _ ctors ->
197+ (,) nodes $ env
198+ { _values = addCtors addLocal (_values env)
199+ , _adts = addNamespaced name (_adts env)
200+ , _patterns = addCtors addNamespaced (_patterns env)
201+ }
202+ where
203+ addCtors how e = foldr how e (map fst ctors)
204+
205+ D.TypeAlias name tvars alias ->
206+ (,) (node name tvars alias : nodes) $ env
207+ { _values = case alias of
208+ Type.Record _ _ -> addLocal name (_values env)
209+ _ -> _values env
210+ }
211+
212+ D.Port _ -> info
213+
214+ D.Fixity _ _ _ -> info