-
Notifications
You must be signed in to change notification settings - Fork 459
Scope graphs #211
Scope graphs #211
Changes from all commits
455990d
8c59f9d
5a40e01
057a4f8
f6cd84e
53e08b3
3654aac
b8f7bb0
cb5b0fb
09f2362
0c10686
16b1442
99a4f8e
4296244
3bafa9a
c7bee13
a04ccbb
34da6ad
3bf6cb6
6cb9af2
a4066b9
ed94104
a73b267
196f2bf
100d6a1
e58dea9
255cddb
901014d
c30e5ff
0515d7c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,130 @@ | ||
| {-# LANGUAGE FlexibleContexts, OverloadedStrings, RankNTypes, RecordWildCards, TypeApplications, TypeOperators #-} | ||
| module Analysis.ScopeGraph | ||
| ( ScopeGraph | ||
| , Entry(..) | ||
| ( ScopeGraph(..) | ||
| , Decl(..) | ||
| , scopeGraph | ||
| , scopeGraphAnalysis | ||
| ) where | ||
|
|
||
| import Analysis.Eval | ||
| import Analysis.FlowInsensitive | ||
| import Control.Applicative (Alternative (..)) | ||
| import Control.Effect.Carrier | ||
| import Control.Effect.Fail | ||
| import Control.Effect.Fresh | ||
| import Control.Effect.Reader | ||
| import Control.Effect.State | ||
| import Control.Monad ((>=>)) | ||
| import Data.File | ||
| import Data.Foldable (fold) | ||
| import Data.Function (fix) | ||
| import Data.List.NonEmpty | ||
| import Data.Loc | ||
| import qualified Data.Map as Map | ||
| import Data.Name | ||
| import Data.Proxy | ||
| import qualified Data.Set as Set | ||
| import Data.Text (Text) | ||
| import Data.Traversable (for) | ||
| import Prelude hiding (fail) | ||
|
|
||
| data Entry = Entry | ||
| { entrySymbol :: String -- FIXME: Text | ||
| , entryLoc :: Loc | ||
| data Decl = Decl | ||
| { declSymbol :: Text | ||
| , declLoc :: Loc | ||
| } | ||
| deriving (Eq, Ord, Show) | ||
|
|
||
| type ScopeGraph = Map.Map Entry (Set.Set Entry) | ||
| newtype Ref = Ref Loc | ||
| deriving (Eq, Ord, Show) | ||
|
|
||
| newtype ScopeGraph = ScopeGraph { unScopeGraph :: Map.Map Decl (Set.Set Ref) } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This model of scope graphs has no recursive structure; references map directly to declarations, and not e.g. through other declarations in a path. This makes this scope graph strictly less informative than those we’re computing in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unlike |
||
| deriving (Eq, Ord, Show) | ||
|
|
||
| instance Semigroup ScopeGraph where | ||
| ScopeGraph a <> ScopeGraph b = ScopeGraph (Map.unionWith (<>) a b) | ||
|
|
||
| instance Monoid ScopeGraph where | ||
| mempty = ScopeGraph Map.empty | ||
|
|
||
| scopeGraph | ||
| :: Ord term | ||
| => (forall sig m | ||
| . (Carrier sig m, Member (Reader Loc) sig, MonadFail m) | ||
| => Analysis term User ScopeGraph m | ||
| -> (term -> m ScopeGraph) | ||
| -> (term -> m ScopeGraph) | ||
| ) | ||
| -> [File term] | ||
| -> (Heap User ScopeGraph, [File (Either (Loc, String) ScopeGraph)]) | ||
| scopeGraph eval | ||
| = run | ||
| . runFresh | ||
| . runHeap | ||
| . traverse (runFile eval) | ||
|
|
||
| runFile | ||
| :: ( Carrier sig m | ||
| , Effect sig | ||
| , Member Fresh sig | ||
| , Member (State (Heap User ScopeGraph)) sig | ||
| , Ord term | ||
| ) | ||
| => (forall sig m | ||
| . (Carrier sig m, Member (Reader Loc) sig, MonadFail m) | ||
| => Analysis term User ScopeGraph m | ||
| -> (term -> m ScopeGraph) | ||
| -> (term -> m ScopeGraph) | ||
| ) | ||
| -> File term | ||
| -> m (File (Either (Loc, String) ScopeGraph)) | ||
| runFile eval file = traverse run file | ||
| where run = runReader (fileLoc file) | ||
| . runReader (Map.empty @User @Loc) | ||
| . runFailWithLoc | ||
| . fmap fold | ||
| . convergeTerm (Proxy @User) (fix (cacheTerm . eval scopeGraphAnalysis)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is so readable 😍 |
||
|
|
||
| scopeGraphAnalysis | ||
| :: ( Alternative m | ||
| , Carrier sig m | ||
| , Member (Reader Loc) sig | ||
| , Member (Reader (Map.Map User Loc)) sig | ||
| , Member (State (Heap User ScopeGraph)) sig | ||
| ) | ||
| => Analysis term User ScopeGraph m | ||
| scopeGraphAnalysis = Analysis{..} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m normally an anti-
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it’ll do until such time as we replace these with effects. |
||
| where alloc = pure | ||
| bind name _ m = do | ||
| loc <- ask @Loc | ||
| local (Map.insert name loc) m | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Binding remembers (locally) the location for the binding site. Unfortunately, this will generally be the location (and especially the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 File an issue about this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m actually just tackling it in a follow-up PR. |
||
| lookupEnv = pure . Just | ||
| deref addr = do | ||
| ref <- asks Ref | ||
| bindLoc <- asks (Map.lookup addr) | ||
| cell <- gets (Map.lookup addr >=> nonEmpty . Set.toList) | ||
| let extending = mappend (extendBinding addr ref bindLoc) | ||
| maybe (pure Nothing) (foldMapA (pure . Just . extending)) cell | ||
| assign addr v = do | ||
| ref <- asks Ref | ||
| bindLoc <- asks (Map.lookup addr) | ||
| modify (Map.insertWith (<>) addr (Set.singleton (extendBinding addr ref bindLoc <> v))) | ||
| abstract eval name body = do | ||
| addr <- alloc name | ||
| assign name (mempty @ScopeGraph) | ||
| bind name addr (eval body) | ||
| apply _ f a = pure (f <> a) | ||
| unit = pure mempty | ||
| bool _ = pure mempty | ||
| asBool _ = pure True <|> pure False | ||
| string _ = pure mempty | ||
| asString _ = pure mempty | ||
| record fields = do | ||
| fields' <- for fields $ \ (k, v) -> do | ||
| addr <- alloc k | ||
| loc <- ask @Loc | ||
| let v' = ScopeGraph (Map.singleton (Decl k loc) mempty) <> v | ||
| (k, v') <$ assign addr v' | ||
| pure (foldMap snd fields') | ||
| _ ... m = pure (Just m) | ||
|
|
||
| extendBinding addr ref bindLoc = ScopeGraph (maybe Map.empty (\ bindLoc -> Map.singleton (Decl addr bindLoc) (Set.singleton ref)) bindLoc) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,7 @@ import Data.Scope | |
| import Data.Semigroup (Last (..)) | ||
| import qualified Data.Set as Set | ||
| import Data.Term | ||
| import Data.Traversable (for) | ||
| import Data.Void | ||
| import GHC.Generics (Generic1) | ||
| import Prelude hiding (fail) | ||
|
|
@@ -175,7 +176,12 @@ typecheckingAnalysis = Analysis{..} | |
| asBool b = unify (Term Bool) b >> pure True <|> pure False | ||
| string _ = pure (Term String) | ||
| asString s = unify (Term String) s $> mempty | ||
| record fields = pure (Term (Record (Map.fromList fields))) | ||
| record fields = do | ||
| fields' <- for fields $ \ (k, v) -> do | ||
| addr <- alloc k | ||
| (k, v) <$ assign addr v | ||
| -- FIXME: should records reference types by address instead? | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this varies based on language? Not sure, though.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn’t for the types, but might for values. |
||
| pure (Term (Record (Map.fromList fields'))) | ||
| _ ... m = pure (Just m) | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We were leaving record fields uninitialized in the abstract analyses, which meant that later lookups would fail.