33{-# LANGUAGE GADTs #-}
44{-# LANGUAGE GeneralizedNewtypeDeriving #-}
55{-# LANGUAGE MultiParamTypeClasses #-}
6+ {-# LANGUAGE OverloadedStrings #-}
67{-# LANGUAGE RankNTypes #-}
8+ {-# LANGUAGE TupleSections #-}
79{-# LANGUAGE TypeApplications #-}
810{-# LANGUAGE TypeOperators #-}
911{-# LANGUAGE UndecidableInstances #-}
1012module Analysis.Analysis.Exception
1113( Exception (.. )
1214, ExcSet (.. )
1315, exceptionTracing
16+ , exceptionTracingIndependent
1417, fromExceptions
1518, var
1619, exc
20+ , str
1721, subst
22+ , nullExcSet
23+ , freeVariablesForLine
24+ , exceptionsForLine
25+ , printExcSet
26+ , refLines
1827 -- * Exception tracing analysis
1928, ExcC (.. )
2029) where
@@ -28,91 +37,158 @@ import Analysis.File
2837import Analysis.FlowInsensitive (cacheTerm , convergeTerm )
2938import Analysis.Module
3039import Analysis.Name
40+ import Analysis.Reference
3141import Control.Algebra
3242import Control.Applicative (Alternative (.. ))
43+ import Control.Carrier.Reader
3344import Control.Effect.Labelled
3445import Control.Effect.State
46+ import Control.Monad (unless )
47+ import Data.Foldable (for_ )
3548import qualified Data.Foldable as Foldable
3649import Data.Function (fix )
50+ import qualified Data.IntSet as IntSet
3751import qualified Data.Map as Map
3852import qualified Data.Set as Set
3953import qualified Data.Text as Text
54+ import qualified Data.Text.IO as Text
55+ import qualified Source.Source as Source
56+ import Source.Span
4057
4158-- | Names of exceptions thrown in the guest language and recorded by this analysis.
4259--
4360-- Not to be confused with exceptions thrown in Haskell itself.
44- newtype Exception = Exception { exceptionName :: Name }
61+ data Exception = Exception { exceptionName :: Name , exceptionLines :: IntSet. IntSet }
62+ deriving (Eq , Ord , Show )
63+
64+ data FreeVariable = FreeVariable { freeVariableName :: Name , freeVariableLines :: IntSet. IntSet }
4565 deriving (Eq , Ord , Show )
4666
4767-- | Sets whose elements are each a variable or an exception.
48- data ExcSet = ExcSet { freeVariables :: Set. Set Name , exceptions :: Set. Set Exception }
68+ data ExcSet = ExcSet { freeVariables :: Set. Set FreeVariable , exceptions :: Set. Set Exception , strings :: Set. Set Text. Text }
4969 deriving (Eq , Ord , Show )
5070
5171instance Semigroup ExcSet where
52- ExcSet v1 e1 <> ExcSet v2 e2 = ExcSet (v1 <> v2) (e1 <> e2)
72+ ExcSet v1 e1 s1 <> ExcSet v2 e2 s2 = ExcSet (v1 <> v2) (e1 <> e2) (s1 <> s2 )
5373
5474instance Monoid ExcSet where
55- mempty = ExcSet mempty mempty
75+ mempty = ExcSet mempty mempty mempty
5676
5777fromExceptions :: Foldable t => t Exception -> ExcSet
58- fromExceptions = ExcSet mempty . Set. fromList . Foldable. toList
78+ fromExceptions es = ExcSet mempty ( Set. fromList ( Foldable. toList es)) mempty
5979
60- var :: Name -> ExcSet
61- var v = ExcSet (Set. singleton v) mempty
80+ var :: FreeVariable -> ExcSet
81+ var v = ExcSet (Set. singleton v) mempty mempty
6282
6383exc :: Exception -> ExcSet
64- exc e = ExcSet mempty (Set. singleton e)
84+ exc e = ExcSet mempty (Set. singleton e) mempty
85+
86+ str :: Text. Text -> ExcSet
87+ str s = ExcSet mempty mempty (Set. singleton s)
6588
6689subst :: Name -> ExcSet -> ExcSet -> ExcSet
67- subst name (ExcSet fvs' es') (ExcSet fvs es) = ExcSet (Set. delete name fvs <> fvs') (es <> es')
90+ -- FIXME: this doesn't handle transitivity at all.
91+ subst name (ExcSet _ es' _) (ExcSet fvs es ss) = ExcSet fvs'' (es <> es'') ss
92+ where
93+ (fvs'', es'') = foldMap combine fvs
94+ combine fv
95+ | freeVariableName fv == name = (mempty , Set. map (\ (Exception n _) -> Exception n (freeVariableLines fv)) es')
96+ | otherwise = (Set. singleton fv, mempty )
97+
98+
99+ nullExcSet :: ExcSet -> Bool
100+ nullExcSet e = null (freeVariables e) && null (exceptions e)
101+
102+ freeVariablesForLine :: Int -> ExcSet -> Set. Set FreeVariable
103+ freeVariablesForLine l e = Set. filter (\ fv -> IntSet. member l (freeVariableLines fv)) (freeVariables e)
104+
105+ exceptionsForLine :: Int -> ExcSet -> Set. Set Exception
106+ exceptionsForLine l e = Set. filter (\ ex -> IntSet. member l (exceptionLines ex)) (exceptions e)
68107
108+ printExcSet :: Source. Source -> ExcSet -> IO ()
109+ printExcSet src e = for_ (zip [0 .. ] (Source. lines src)) $ \ (i, line) -> do
110+ Text. putStr (keywords (Text. dropWhileEnd (== ' \n ' ) (Source. toText line)))
111+ let es = exceptionsForLine i e
112+ fvs = freeVariablesForLine i e
113+ unless (null es && null fvs) $ do
114+ Text. putStr " \ESC [30;1m# "
115+ Text. putStr (" {" <> union
116+ ( formatFreeVariables fvs
117+ <> formatExceptions es ) <> " }" <> reset)
118+ Text. putStrLn mempty
119+ where
120+ keyword k s = Text. intercalate (" \ESC [34;1m" <> k <> reset) (Text. splitOn k s)
121+ keywords = keyword " raise" . keyword " import" . keyword " def" . keyword " pass"
122+ union = Text. intercalate " , "
123+ formatFreeVariables fvs = map (\ fv -> " ?" <> formatName (freeVariableName fv)) (Set. toList fvs)
124+ formatExceptions excs = map (Text. pack . show . formatName . exceptionName) (Set. toList excs)
125+ reset = " \ESC [0m"
126+
127+ refLines :: Reference -> IntSet. IntSet
128+ refLines (Reference _ (Span (Pos startLine _) (Pos endLine _))) = IntSet. fromAscList [startLine.. endLine]
69129
70130exceptionTracing
71131 :: Ord term
72132 => ( forall sig m
73- . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has A. Statement sig m )
133+ . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has ( Reader Reference ) sig m , Has A. Statement sig m )
74134 => (term -> m ExcSet )
75135 -> (term -> m ExcSet ) )
76136 -> [File term ]
77137 -> (A. MStore ExcSet , [File (Module ExcSet )])
78- exceptionTracing eval = A. runFiles (runFile eval)
138+ exceptionTracing eval = run . A. runFiles (runFile eval)
139+
140+ exceptionTracingIndependent
141+ :: Ord term
142+ => ( forall sig m
143+ . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has (Reader Reference ) sig m , Has A. Statement sig m )
144+ => (term -> m ExcSet )
145+ -> (term -> m ExcSet ) )
146+ -> File term
147+ -> (A. MStore ExcSet , File (Module ExcSet ))
148+ exceptionTracingIndependent eval = run . A. runStoreState . runFile eval
79149
80150runFile
81151 :: ( Has (State (A. MStore ExcSet )) sig m
82152 , Ord term )
83153 => ( forall sig m
84- . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has A. Statement sig m )
154+ . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has ( Reader Reference ) sig m , Has A. Statement sig m )
85155 => (term -> m ExcSet )
86156 -> (term -> m ExcSet ) )
87157 -> File term
88158 -> m (File (Module ExcSet ))
89- runFile eval = traverse run where
159+ runFile eval file = traverse run file where
90160 run
91161 = A. runStatement result
92162 . A. runEnv @ ExcSet
163+ . runReader (fileRef file)
93164 . convergeTerm (A. runStore @ ExcSet . runExcC . fix (cacheTerm . eval))
94165 result msgs sets = do
95166 exports <- gets @ (A. MStore ExcSet ) (fmap Foldable. fold . Map. mapKeys A. getMAddr . A. getMStore)
96167 let set = Foldable. fold sets
97168 imports = Set. fromList (map extractImport msgs)
98- pure (Module (Foldable. foldl' (flip (uncurry subst)) set . Map. toList) imports exports (freeVariables set))
99- extractImport (A. Import components) = name (Text. intercalate ( Text. pack " ." ) (Foldable. toList components))
169+ pure (Module (Foldable. foldl' (flip (uncurry subst)) set . Map. toList) imports exports (Set. map freeVariableName ( freeVariables set) ))
170+ extractImport (A. Import components) = name (Text. intercalate " ." (Foldable. toList components))
100171
101172newtype ExcC m a = ExcC { runExcC :: m a }
102173 deriving (Alternative , Applicative , Functor , Monad )
103174
104- instance (Algebra sig m , Alternative m ) => Algebra (Dom ExcSet :+: sig ) (ExcC m ) where
175+ instance (Has ( Reader Reference ) sig m , Alternative m ) => Algebra (Dom ExcSet :+: sig ) (ExcC m ) where
105176 alg hdl sig ctx = ExcC $ case sig of
106177 L dom -> case dom of
107- DVar n -> pure $ var n <$ ctx
178+ DVar n -> do
179+ lines <- asks refLines
180+ pure $ var (FreeVariable n lines ) <$ ctx
108181 DAbs _ b -> runExcC (hdl (b mempty <$ ctx))
109182 DApp f a -> pure $ f <> Foldable. fold a <$ ctx
110183 DInt _ -> pure nil
111184 DUnit -> pure nil
112185 DBool _ -> pure nil
113186 DIf c t e -> fmap (mappend c) <$> runExcC (hdl (t <$ ctx) <|> hdl (e <$ ctx))
114- DString _ -> pure nil
115- DDie e -> pure $ e <> fromExceptions [Exception n | n <- Set. toList (freeVariables e)] <$ ctx
187+ DString s -> pure (str (Text. dropAround (== ' "' ) s) <$ ctx)
188+ t :>>> u -> pure (t <> u <$ ctx)
189+ DDie e -> do
190+ lines <- asks refLines
191+ pure $ e{ strings = mempty } <> fromExceptions [Exception (name n) lines | n <- Set. toList (strings e)] <$ ctx
116192 where
117193 nil = (mempty :: ExcSet ) <$ ctx
118194
0 commit comments