-
Notifications
You must be signed in to change notification settings - Fork 172
/
Copy pathEnv.hs
667 lines (580 loc) · 24.3 KB
/
Env.hs
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
{-# LANGUAGE TupleSections #-}
module Env
( EnvironmentError,
Environment (..),
Mode (..),
-- utils
empty,
new,
parent,
setParent,
nested,
recursive,
binders,
------------------------
-- lookups
getType,
getTypeBinder,
findType,
findTypeBinder,
searchType,
searchTypeBinder,
getValue,
getValueBinder,
findValue,
findValueBinder,
searchValue,
searchValueBinder,
-------------------------
-- Environment getters
getInnerEnv,
contextEnv,
envIsExternal,
envPublicBindingNames,
-------------------------
-- mutation
insert,
insertX,
replace,
addBinding,
deleteBinding,
addListOfBindings,
addUsePath,
-------------------------
-- finds
findPoly,
findAllByMeta,
findChildren,
findImplementations,
findAllGlobalVariables,
findModules,
allImportedEnvs,
-------------------------
-- lookups
lookupContextually,
lookupMeta,
lookupChildren,
lookupInUsed,
lookupEverywhere,
lookupBinderEverywhere,
progenitor,
replaceInPlace,
)
where
import Data.Either (fromRight, rights)
import Data.List (foldl', unfoldr)
import Data.Maybe (fromMaybe)
import qualified Map
import qualified Meta
import Obj
import qualified Set
import Types
--------------------------------------------------------------------------------
-- Data
data EnvironmentError
= NoEnvInNonModule
| NoReplaceInNonModule
| BindingNotFound String Env
| NoMatchingBindingFound String
| NestedTypeError String
instance Show EnvironmentError where
show NoEnvInNonModule = "Can't get an environment from a non-module."
show NoReplaceInNonModule = "Can't replace an environment in a non-module."
show (BindingNotFound name e) = "Failed to find " ++ name ++ "in the given environment: " ++ show e
show (NoMatchingBindingFound predicate) = "Couldn't find any bindings with " ++ predicate ++ "in the given environment."
show (NestedTypeError name) =
"Couldn't insert the top-level type " ++ name
++ " in a module environment."
data Mode = Types | Values
--------------------------------------------------------------------------------
-- The Environment class and implementations
-- | Class for generically handling type and value environments.
class Environment e where
inj :: Env -> e
prj :: e -> Env
update :: e -> Binder -> Either EnvironmentError Binder
modality :: e -> Mode
-- | The value environment
instance Environment Env where
inj = id
prj = id
update e (Binder meta (XObj (Mod _ et) i t)) = Right (Binder meta (XObj (Mod e et) i t))
update _ _ = Left NoReplaceInNonModule
modality _ = Values
-- | The type environment
instance Environment TypeEnv where
inj = TypeEnv
prj = getTypeEnv
update e (Binder meta (XObj (Mod ev _) i t)) = Right (Binder meta (XObj (Mod ev e) i t))
update _ _ = Left NoReplaceInNonModule
modality _ = Types
--------------------------------------------------------------------------------
-- Misc. Environment utilities
-- | Returns an unnamed empty environment with no parent.
empty :: Environment e => e
empty = inj $ Env (Map.fromList []) Nothing Nothing Set.empty ExternalEnv 0
-- | Returns a new environment with a given parent and name.
new :: Environment e => Maybe e -> Maybe String -> e
new p name =
let e' = Env (Map.fromList []) (fmap prj p) name Set.empty ExternalEnv 0
in inj e'
-- | Returns a new environment with a designated nesting level.
nested :: Environment e => Maybe e -> Maybe String -> Int -> e
nested p name lvl = inj ((prj (new p name)) {envMode = InternalEnv, envFunctionNestingLevel = lvl})
-- | Returns a new recursive environment with a designated nesting level.
recursive :: Environment e => Maybe e -> Maybe String -> Int -> e
recursive p name lvl = inj ((prj (new p name)) {envMode = RecursionEnv, envFunctionNestingLevel = lvl})
-- | Returns the binders stored in an environment.
binders :: Environment e => e -> Map.Map String Binder
binders = envBindings . prj
-- | Get the parent of an environment.
parent :: Environment e => e -> Maybe e
parent = fmap inj . envParent . prj
-- | Set the parent of an environment.
setParent :: Environment e => e -> e -> e
setParent e p = inj ((prj e) {envParent = Just (prj p)})
-- | Get an environment stored in a module binder.
nextEnv :: Mode -> Binder -> Either EnvironmentError Env
nextEnv Types (Binder _ (XObj (Mod _ et) _ _)) = Right $ prj et
nextEnv Values (Binder _ (XObj (Mod ev _) _ _)) = Right $ prj ev
nextEnv _ _ = Left NoEnvInNonModule
-- | Replace an environment stored in a module binder.
updateEnv :: Mode -> Env -> Binder -> Either EnvironmentError Binder
updateEnv Values e (Binder meta (XObj (Mod _ et) i t)) = Right (Binder meta (XObj (Mod e et) i t))
updateEnv Types e (Binder meta (XObj (Mod ev _) i t)) = Right (Binder meta (XObj (Mod ev (TypeEnv e)) i t))
updateEnv _ _ _ = Left NoEnvInNonModule
--------------------------------------------------------------------------------
-- Environment traversal
--
-- Naming conventions:
--
-- get: Direct lookup. Try to get the designated binder directly from an
-- environment, without traversing into parents or children. If not found,
-- fail.
--
-- find: Preorder lookup. Try to get the designated binder by proceeding from
-- the root environment down to its children. If not found in a child, fail.
--
-- search: pre and post order lookup: Try to get the designated binder by
-- proceeding from the root to children. If not found, try to find the binder
-- by proceeding from the root's parent, if it exists, to its children. If
-- not found, fail.
-- | Walk down an environment chain.
walk' :: Mode -> Env -> SymPath -> Either EnvironmentError Env
walk' _ e (SymPath [] _) = pure e
walk' mode' e (SymPath (p : ps) name) =
do
(_, binder) <- get e p
go (SymPath ps name) binder
where
go :: SymPath -> Binder -> Either EnvironmentError Env
go (SymPath [] _) binder = nextEnv mode' binder
go path binder =
do
env <- nextEnv Values binder
walk' mode' env path
-- | Generic *unidirectional* retrieval of binders (does not check parents).
walkAndGet :: Environment e => e -> SymPath -> (Either EnvironmentError e, Either EnvironmentError Binder)
walkAndGet e path@(SymPath _ name) =
let target = walk' (modality e) (prj e) path
binder = target >>= \t -> get t name
in (fmap inj target, fmap snd binder)
-- | Direct lookup for a binder in environment `e`.
-- The environment returned in the output will be the same as that given as input.
--
-- Returns an error if not found.
get :: Environment e => e -> String -> Either EnvironmentError (e, Binder)
get e name =
case Map.lookup name (binders e) of
Nothing -> Left $ BindingNotFound name (prj e)
Just b -> Right (e, b)
-- | Same as `get` but only returns a binder.
getBinder :: Environment e => e -> String -> Either EnvironmentError Binder
getBinder e name = fmap snd (get e name)
-- | Generic unidirectional retrieval of binders.
-- Searches the children of `e` using a given path, stopping at the terminus.
--
-- Returns an error if not found.
find' :: Environment e => e -> SymPath -> Either EnvironmentError (e, Binder)
find' e path =
case walkAndGet e path of
(Right e', Right b) -> Right (e', b)
(Left err, _) -> Left err
(_, Left err) -> Left err
-- | Same as `find` but only returns a binder.
findBinder :: Environment e => e -> SymPath -> Either EnvironmentError Binder
findBinder e path = fmap snd (find' e path)
-- | Generic *multidirectional* retrieval of binders.
-- Searches the children and parents of `e` (or the parent of a sub-environment
-- found in `e` and given by `path`).
--
-- Returns an error if not found.
search :: Environment e => e -> SymPath -> Either EnvironmentError (e, Binder)
search e path =
case walkAndGet e path of
(Right e', Right b) -> Right (e', b)
(Right e', Left err) -> (checkParent e' err)
(Left err, Left _) -> (checkParent e err) <> Left err
-- impossible case. Included to keep `walk` honest.
(Left _, Right _) -> error "impossible"
where
checkParent env err = maybe (Left err) (`search` path) (parent env)
-- | Same as `search` but only returns a binder.
searchBinder :: Environment e => e -> SymPath -> Either EnvironmentError Binder
searchBinder e path = fmap snd (search e path)
--------------------------------------------------------------------------------
-- Specialized retrievals
--
-- These functions are all equivalent to the generic retrieval functions
-- defined above but they enforce further restrictions at type level. Thus,
-- they can be used to help enforce constraints at call sites.
--
-- For example, suppose we want to search for a binder that may name a type
-- * or* module, preferring types. One could cast to enforce a type search
-- starting from the global env:
--
-- search typeEnv path
-- <> search (TypeEnv global) path
-- <> search global path
--
-- But:
--
-- searchType typeEnv path
-- searchType global path
-- <> searchValue global path
--
-- Is arguably much clearer.
--------------------------------------------------------------------------------
-- Type retrievals
-- | Get a type from a type environment.
getType :: TypeEnv -> String -> Either EnvironmentError (TypeEnv, Binder)
getType = get
-- | Get a type binder from a type environment.
getTypeBinder :: TypeEnv -> String -> Either EnvironmentError Binder
getTypeBinder = getBinder
-- | Unidirectional binder retrieval specialized to types.
--
-- Restricts the final step of a search to binders in a module's *type* environment.
findType :: Environment e => e -> SymPath -> Either EnvironmentError (TypeEnv, Binder)
findType e path = go $ find' (inj (prj e)) path
where
-- Make sure the binder is actually a type.
go :: Either EnvironmentError (TypeEnv, Binder) -> Either EnvironmentError (TypeEnv, Binder)
go (Right (t, b)) =
if isType (binderXObj b)
then Right (t, b)
else Left (BindingNotFound (show path) (prj e))
go x = x
findTypeBinder :: Environment e => e -> SymPath -> Either EnvironmentError Binder
findTypeBinder e path = fmap snd (findType e path)
-- | Multidirectional binder retrieval specialized to types.
--
-- Restricts the final step of a search to binders in a module's *type* environment.
searchType :: Environment e => e -> SymPath -> Either EnvironmentError (TypeEnv, Binder)
searchType e path = search (inj (prj e)) path
searchTypeBinder :: Environment e => e -> SymPath -> Either EnvironmentError Binder
searchTypeBinder e path = fmap snd (searchType e path)
--------------------------------------------------------------------------------
-- Value retrievals
-- | Get a value from a value environment.
getValue :: Env -> String -> Either EnvironmentError (Env, Binder)
getValue = get
getValueBinder :: Env -> String -> Either EnvironmentError Binder
getValueBinder = getBinder
-- | Unidirectional binder retrieval specialized to values.
findValue :: Env -> SymPath -> Either EnvironmentError (Env, Binder)
findValue = find'
findValueBinder :: Env -> SymPath -> Either EnvironmentError Binder
findValueBinder = findBinder
-- | Multidirectional binder retrieval specialized to values.
searchValue :: Env -> SymPath -> Either EnvironmentError (Env, Binder)
searchValue = search
searchValueBinder :: Env -> SymPath -> Either EnvironmentError Binder
searchValueBinder = searchBinder
--------------------------------------------------------------------------------
-- Environment mutation
--------------------------------------------------------------------------------
-- Mutation primitives
-- N.B. The following functions returns an Either for compatibility with other
-- functions in this module. It is a constant function in the co-domain of
-- Either, as they always returns Right.
-- | Add a new binding to an environment.
addBinding :: Environment e => e -> String -> Binder -> Either EnvironmentError e
addBinding e name b = pure (inj ((prj e) {envBindings = Map.insert name b (binders e)}))
-- | Replace the value of a binding in an environment, but only if it already
-- exists.
replaceBinding :: Environment e => e -> String -> Binder -> Either EnvironmentError e
replaceBinding e name b =
pure (inj ((prj e) {envBindings = Map.adjust (const b) name (binders e)}))
-- | Delete a binding in an environment.
deleteBinding :: Environment e => e -> String -> Either EnvironmentError e
deleteBinding e name = pure (inj ((prj e) {envBindings = Map.delete name (binders e)}))
--------------------------------------------------------------------------------
-- Generic environment mutation
type EnvironmentProducer e = (e -> String -> Binder -> Either EnvironmentError e)
-- | Given an environment and a complete identifier path, traverse a chain of
-- environments until the path is exhausted, if requested, mutating the
-- environments along the way:
mutate :: Environment e => (EnvironmentProducer e) -> e -> SymPath -> Binder -> Either EnvironmentError e
mutate f e path binder = go path
where
go (SymPath [] name) = f e name binder
go (SymPath (p : ps) name) =
getBinder e p
>>= \modu ->
nextEnv (modality e) modu
>>= \oldEnv ->
mutate f (inj oldEnv) (SymPath ps name) binder
>>= \result ->
updateEnv (modality e) (prj result) modu
>>= addBinding e p
-- | Insert a binding into an environment at the given path.
insert :: Environment e => e -> SymPath -> Binder -> Either EnvironmentError e
insert = mutate addBinding
-- | Insert an XObj into an environment at the specified path.
-- This function does not perform insertions into parents.
insertX :: Environment e => e -> SymPath -> XObj -> Either EnvironmentError e
insertX e path x = insert e path (toBinder x)
-- | Replace a binding at the given path in an environment.
replace :: Environment e => e -> SymPath -> Binder -> Either EnvironmentError e
replace = mutate replaceBinding
-- | Replaces a binding "in-place" in an environment chain.
--
-- This function *only* considers members of an environment chain, that is,
-- it's limited to the given input environment and all of its ancestors (it's
-- parent and the parent of its parent all the way up).
--
-- It does not look in any "external" environments (used environments or
-- "children" (environments stored in module bindings)).
replaceInPlace :: Environment e => e -> String -> Binder -> Either EnvironmentError e
replaceInPlace e name b =
(get e name >>= \_ -> addBinding e name b)
<> case parent e of
Just p -> replaceInPlace p name b >>= \p' -> pure (inj ((prj e) {envParent = Just (prj p')}))
Nothing -> Left (BindingNotFound name (prj e))
-- | Add a list of bindings to an environment.
addListOfBindings :: Environment e => e -> [(String, Binder)] -> e
addListOfBindings e bindings =
foldl' (\e' (n, b) -> fromRight e (addBinding e' n b)) e bindings
-- | Add a module path to an environment's list of used modules.
addUsePath :: Environment e => e -> SymPath -> e
addUsePath e path = inj ((prj e) {envUseModules = Set.insert path (envUseModules (prj e))})
--------------------------------------------------------------------------------
-- Additional binding lookup functions
--
-- find* functions perform lookup in a single environment, without recursion.
-- lookup* functions perform lookups in an environment chain, with recursion.
-- | Get the metadata associated with the binder at the specified path in an environment.
lookupMeta :: Environment e => e -> SymPath -> Either EnvironmentError MetaData
lookupMeta e path = searchBinder e path >>= pure . Meta.fromBinder
-- | Find all binders in an environment that have a specified meta key.
findAllByMeta :: Environment e => e -> String -> Either EnvironmentError [Binder]
findAllByMeta e metaKey =
let candidates = Map.elems (Map.filter (Meta.binderMember metaKey) (binders e))
in case candidates of
[] -> Left (NoMatchingBindingFound ("metadata " ++ metaKey))
_ -> Right $ candidates
-- | Find all modules directly stored in environment `e`.
findModules :: Environment e => e -> [XObj]
findModules e =
map binderXObj (filter modsOnly (Map.elems (binders e)))
where
modsOnly :: Binder -> Bool
modsOnly binder =
case binderXObj binder of
XObj (Mod _ _) _ _ -> True
_ -> False
-- | It's more efficient to specialize this function as it can take advantage
-- of laziness; once we found the candidate function for a polymorphic
-- function, there's no need to consume the rest of the environment.
findPoly :: Environment e => e -> String -> Ty -> Either EnvironmentError (e, Binder)
findPoly env name ty =
case getBinder env name of
Right b ->
if unify b
then Right (env, b)
else (foldl' go (Left (BindingNotFound name (prj env))) (findChildren env))
Left _ -> foldl' go (Left (BindingNotFound name (prj env))) (findChildren env)
where
go x e = x <> (findPoly e name ty)
unify = areUnifiable ty . fromMaybe Universe . xobjTy . binderXObj
-- | Find all environments that are *direct* children of an environment (one
-- level down).
--
-- The modality of the children is determined by the modality of the root.
--
-- N.B. Don't use find here. We access binders directly, so there's no need to
-- perform additional O(n) lookup calls.
findChildren :: Environment e => e -> [e]
findChildren e =
foldl' getEnv [] (binders e)
where
getEnv acc binder =
case (nextEnv (modality e) binder) of
Left _ -> acc
Right e' -> ((inj e') : acc)
-- | Find all the environments contained in the modules initial environment,
-- plus any module environments contained in *those* modules.
lookupChildren :: Environment e => e -> [e]
lookupChildren e =
foldl' go [] (findChildren e)
where
go acc e' = case findChildren e' of
[] -> (e' : acc)
xs -> (foldl' go [] xs ++ acc)
-- | Find all the environments designated by the use paths in an environment.
findImportedEnvs :: Environment e => e -> [e]
findImportedEnvs e =
let eMode = modality e
usePaths = Set.toList (envUseModules (prj e))
getter path =
walk' eMode (prj e) path
>>= \e' ->
get e' (getName' path)
>>= nextEnv eMode . snd
>>= pure . inj
used = fmap getter usePaths
in (rights used)
where
getName' (SymPath _ name) = name
-- | Given an environment, get its topmost parent up the environment chain.
--
-- For nearly all environments, this should be the global environment.
progenitor :: Environment e => e -> e
progenitor e = fromMaybe e (parent e >>= \p -> pure (progenitor p))
-- | Find all possible environments imported at some point *upwards* from e in a chain of environments.
allImportedEnvs :: Environment e => e -> Env -> [e]
allImportedEnvs e global =
let env = prj e
paths = (Set.toList (foldl' og (envUseModules env) (unfoldr go env)))
in (rights (map get' paths))
where
go e' = parent e' >>= \p -> pure (p, p)
og acc e' = (envUseModules e') <> acc
get' path =
findBinder global path
>>= nextEnv (modality e)
>>= pure . inj
-- | Find all binders the implement a given interface, designated by its path.
findImplementations :: Environment e => e -> SymPath -> Either EnvironmentError [Binder]
findImplementations e interface =
( (findAllByMeta e "implements")
>>= \is -> (pure (filter (isImpl . Meta.fromBinder) is))
)
<> Left (NoMatchingBindingFound ("implementation meta for " ++ show interface))
where
isImpl :: MetaData -> Bool
isImpl meta =
case Meta.get "implements" meta of
Just (XObj (Lst interfaces) _ _) -> interface `elem` map getPath interfaces
_ -> False
-- | Searches for binders exhaustively in the given environment, a list of
-- child environments it contains derived using a function and its parent, if
-- it has one.
--
-- The parent environment, when it exists, is also searched exhaustively
-- (derived children of the parent are searched, as well as the parent of the
-- parent, should it exist).
lookupExhuastive :: Environment e => (e -> [e]) -> e -> String -> [(e, Binder)]
lookupExhuastive f e name =
let envs = [e] ++ (f e)
in (go (parent e) envs)
where
go _ [] = []
go Nothing xs = foldl' accum [] xs
go (Just p) xs = go (parent p) (xs ++ [p] ++ (f p))
accum acc e' = case getBinder e' name of
Right b -> ((e', b) : acc)
_ -> acc
lookupBinderExhuastive :: Environment e => (e -> [e]) -> e -> String -> [Binder]
lookupBinderExhuastive f e name = fmap snd (lookupExhuastive f e name)
lookupEverywhere :: Environment e => e -> String -> [(e, Binder)]
lookupEverywhere = lookupExhuastive lookupChildren
lookupInImports :: Environment e => e -> String -> [(e, Binder)]
lookupInImports = lookupExhuastive findImportedEnvs
lookupInUsed :: Environment e => e -> Env -> SymPath -> [(e, Binder)]
lookupInUsed e global spath =
foldl' go [] (allImportedEnvs e global)
where
go :: Environment e => [(e, Binder)] -> e -> [(e, Binder)]
go acc e' = case (search e' spath) of
Right (e'', b) -> ((e'', b) : acc)
_ -> acc
-- | Lookup a binder in *all* possible environments in the chain of an initial
-- environment (parents and children, including Use modules).
lookupBinderEverywhere :: Environment e => e -> String -> [Binder]
lookupBinderEverywhere = lookupBinderExhuastive lookupChildren
lookupContextually :: Environment e => e -> SymPath -> Either EnvironmentError [(e, Binder)]
lookupContextually e (SymPath [] name) =
case lookupInImports e name of
[] -> Left (BindingNotFound name (prj e))
xs -> Right xs
lookupContextually e path@(SymPath (p : ps) name) =
lookupDirectly <> lookupInUsedAndParent
where
lookupDirectly =
(getBinder e p)
>>= nextEnv (modality e)
>>= \e' ->
search (inj e') (SymPath ps name)
>>= pure . (: [])
lookupInUsedAndParent = case rights (fmap ((flip search) path) (findImportedEnvs e)) of
[] -> Left (BindingNotFound name (prj e))
xs ->
case parent e of
Nothing -> Right xs
Just e' -> (Env.search e' path >>= \found -> Right $ xs ++ [found]) <> Right xs
--------------------------------------------------------------------------------
-- Environment retrieval functions
-- | Get the environment at a given path that corresponds to the type of an
-- initial environment.
--
-- Returns the initial environment when given an empty path.
getInnerEnv :: Environment e => e -> [String] -> Either EnvironmentError e
getInnerEnv e [] = Right e
getInnerEnv e (p : ps) =
(getBinder e p)
>>= nextEnv (modality e)
>>= \moduleEnv -> getInnerEnv (inj moduleEnv) ps
-- | Get a context's internal environment if it exists, otherwise get the
-- innermost module's value environment based on the context path.
contextEnv :: Environment e => Context -> e
contextEnv Context {contextInternalEnv = Just e} = inj e
contextEnv Context {contextGlobalEnv = e, contextPath = p} = inj (fromRight e (getInnerEnv e p))
--------------------------------------------------------------------------------
-- Utility functions
-- | Checks if an environment is "external", meaning it's either the global
-- scope or a module scope.
envIsExternal :: Environment e => e -> Bool
envIsExternal e =
case envMode (prj e) of
ExternalEnv -> True
InternalEnv -> False
RecursionEnv -> True
--------------------------------------------------------------------------------
-- Binding Utilities
-- | Get a list of all the names of bindings in an environment that aren't
-- hidden or private.
envPublicBindingNames :: Environment e => e -> [String]
envPublicBindingNames e = concatMap select (Map.toList (binders e))
where
select :: (String, Binder) -> [String]
select (name, binder) =
case (nextEnv (modality e) binder) of
Left _ ->
if metaIsTrue (binderMeta binder) "private" || metaIsTrue (binderMeta binder) "hidden"
then []
else [name]
Right e' -> map (\n -> name ++ "." ++ n) (envPublicBindingNames e')
-- | Recursively look through all environments for (def ...) forms.
--
-- N.B. Don't use find here. We access binders directly, so there's no need to
-- perform additional O(n) lookup calls.
findAllGlobalVariables :: Env -> [Binder]
findAllGlobalVariables e =
foldl' finder [] (Map.elems (binders e))
where
finder :: [Binder] -> Binder -> [Binder]
finder acc (Binder _ (XObj (Mod ev _) _ _)) = acc ++ (findAllGlobalVariables (inj ev))
finder acc def@(Binder _ (XObj (Lst (XObj Def _ _ : _)) _ _)) = (def : acc)
finder acc _ = acc