forked from purescript/purescript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRenamer.hs
More file actions
193 lines (174 loc) · 6.4 KB
/
Renamer.hs
File metadata and controls
193 lines (174 loc) · 6.4 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
-- |
-- Renaming pass that prevents shadowing of local identifiers.
--
module Language.PureScript.Renamer (renameInModules) where
import Prelude.Compat
import Control.Monad.State
import Data.List (find)
import Data.Maybe (fromJust, fromMaybe)
import qualified Data.Map as M
import Data.Monoid ((<>))
import qualified Data.Set as S
import qualified Data.Text as T
import Language.PureScript.CoreFn
import Language.PureScript.Names
import Language.PureScript.Traversals
import qualified Language.PureScript.Constants as C
-- |
-- The state object used in this module
--
data RenameState = RenameState {
-- |
-- A map from names bound (in the input) to their names (in the output)
--
rsBoundNames :: M.Map Ident Ident
-- |
-- The set of names which have been used and are in scope in the output
--
, rsUsedNames :: S.Set Ident
}
type Rename = State RenameState
initState :: [Ident] -> RenameState
initState scope = RenameState (M.fromList (zip scope scope)) (S.fromList scope)
-- |
-- Runs renaming starting with a list of idents for the initial scope.
--
runRename :: [Ident] -> Rename a -> a
runRename scope = flip evalState (initState scope)
-- |
-- Creates a new renaming scope using the current as a basis. Used to backtrack
-- when leaving an Abs.
--
newScope :: Rename a -> Rename a
newScope x = do
scope <- get
a <- x
put scope
return a
-- |
-- Adds a new scope entry for an ident. If the ident is already present, a new
-- unique name is generated and stored.
--
updateScope :: Ident -> Rename Ident
updateScope ident =
case ident of
Ident name | name == C.__unused -> return ident
GenIdent name _ -> go ident $ Ident (fromMaybe "v" name)
_ -> go ident ident
where
go :: Ident -> Ident -> Rename Ident
go keyName baseName = do
scope <- get
let usedNames = rsUsedNames scope
name' =
if baseName `S.member` usedNames
then getNewName usedNames baseName
else baseName
modify $ \s -> s { rsBoundNames = M.insert keyName name' (rsBoundNames s)
, rsUsedNames = S.insert name' (rsUsedNames s)
}
return name'
getNewName :: S.Set Ident -> Ident -> Ident
getNewName usedNames name =
fromJust $ find
(`S.notMember` usedNames)
[ Ident (runIdent name <> T.pack (show (i :: Int))) | i <- [1..] ]
-- |
-- Finds the new name to use for an ident.
--
lookupIdent :: Ident -> Rename Ident
lookupIdent i@(Ident name) | name == C.__unused = return i
lookupIdent name = do
name' <- gets $ M.lookup name . rsBoundNames
case name' of
Just name'' -> return name''
Nothing -> error $ "Rename scope is missing ident '" ++ T.unpack (showIdent name) ++ "'"
-- |
-- Finds idents introduced by declarations.
--
findDeclIdents :: [Bind Ann] -> [Ident]
findDeclIdents = concatMap go
where
go (NonRec _ ident _) = [ident]
go (Rec ds) = map (snd . fst) ds
-- |
-- Renames within each declaration in a module.
--
renameInModules :: [Module Ann] -> [Module Ann]
renameInModules = map go
where
go :: Module Ann -> Module Ann
go m@(Module _ _ _ _ _ decls) = m { moduleDecls = map (renameInDecl' (findDeclIdents decls)) decls }
renameInDecl' :: [Ident] -> Bind Ann -> Bind Ann
renameInDecl' scope = runRename scope . renameInDecl True
-- |
-- Renames within a declaration. isTopLevel is used to determine whether the
-- declaration is a module member or appearing within a Let. At the top level
-- declarations are not renamed or added to the scope (they should already have
-- been added), whereas in a Let declarations are renamed if their name shadows
-- another in the current scope.
--
renameInDecl :: Bool -> Bind Ann -> Rename (Bind Ann)
renameInDecl isTopLevel (NonRec a name val) = do
name' <- if isTopLevel then return name else updateScope name
NonRec a name' <$> renameInValue val
renameInDecl isTopLevel (Rec ds) = do
ds' <- traverse updateNames ds
Rec <$> traverse updateValues ds'
where
updateNames :: ((Ann, Ident), Expr Ann) -> Rename ((Ann, Ident), Expr Ann)
updateNames ((a, name), val) = do
name' <- if isTopLevel then return name else updateScope name
return ((a, name'), val)
updateValues :: ((Ann, Ident), Expr Ann) -> Rename ((Ann, Ident), Expr Ann)
updateValues (aname, val) = (,) aname <$> renameInValue val
-- |
-- Renames within a value.
--
renameInValue :: Expr Ann -> Rename (Expr Ann)
renameInValue (Literal ann l) =
Literal ann <$> renameInLiteral renameInValue l
renameInValue c@Constructor{} = return c
renameInValue (Accessor ann prop v) =
Accessor ann prop <$> renameInValue v
renameInValue (ObjectUpdate ann obj vs) =
ObjectUpdate ann <$> renameInValue obj <*> traverse (\(name, v) -> (,) name <$> renameInValue v) vs
renameInValue e@(Abs (_, _, _, Just IsTypeClassConstructor) _ _) = return e
renameInValue (Abs ann name v) =
newScope $ Abs ann <$> updateScope name <*> renameInValue v
renameInValue (App ann v1 v2) =
App ann <$> renameInValue v1 <*> renameInValue v2
renameInValue (Var ann (Qualified Nothing name)) =
Var ann . Qualified Nothing <$> lookupIdent name
renameInValue v@Var{} = return v
renameInValue (Case ann vs alts) =
newScope $ Case ann <$> traverse renameInValue vs <*> traverse renameInCaseAlternative alts
renameInValue (Let ann ds v) =
newScope $ Let ann <$> traverse (renameInDecl False) ds <*> renameInValue v
-- |
-- Renames within literals.
--
renameInLiteral :: (a -> Rename a) -> Literal a -> Rename (Literal a)
renameInLiteral rename (ArrayLiteral bs) = ArrayLiteral <$> traverse rename bs
renameInLiteral rename (ObjectLiteral bs) = ObjectLiteral <$> traverse (sndM rename) bs
renameInLiteral _ l = return l
-- |
-- Renames within case alternatives.
--
renameInCaseAlternative :: CaseAlternative Ann -> Rename (CaseAlternative Ann)
renameInCaseAlternative (CaseAlternative bs v) = newScope $
CaseAlternative <$> traverse renameInBinder bs
<*> eitherM (traverse (pairM renameInValue renameInValue)) renameInValue v
-- |
-- Renames within binders.
--
renameInBinder :: Binder a -> Rename (Binder a)
renameInBinder n@NullBinder{} = return n
renameInBinder (LiteralBinder ann b) =
LiteralBinder ann <$> renameInLiteral renameInBinder b
renameInBinder (VarBinder ann name) =
VarBinder ann <$> updateScope name
renameInBinder (ConstructorBinder ann tctor dctor bs) =
ConstructorBinder ann tctor dctor <$> traverse renameInBinder bs
renameInBinder (NamedBinder ann name b) =
NamedBinder ann <$> updateScope name <*> renameInBinder b