{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE DeriveAnyClass #-} module Elab.Monad where import Control.Monad.Reader import Control.Exception import Data.Text.Prettyprint.Doc.Render.Terminal (AnsiStyle) import qualified Data.Map.Strict as Map import Data.Text.Prettyprint.Doc import Data.Map.Strict (Map) import Data.Sequence (Seq) import Data.Text (Text) import Data.Set (Set) import Data.Typeable import Data.IORef import qualified Presyntax.Presyntax as P import Syntax data ElabEnv = ElabEnv { getEnv :: Map Name (NFType, Value) , nameMap :: Map Text Name , pingPong :: {-# UNPACK #-} !Int , commHook :: Doc AnsiStyle -> IO () , currentSpan :: Maybe (P.Posn, P.Posn) , currentFile :: Maybe Text , whereBound :: Map Name (P.Posn, P.Posn) , definedNames :: Set Name , boundaries :: Map Name Boundary , unsolvedMetas :: {-# UNPACK #-} !(IORef (Map MV [(Seq Projection, Value)])) , loadedFiles :: [String] } newtype ElabM a = ElabM { runElab :: ElabEnv -> IO a } deriving (Functor, Applicative, Monad, MonadReader ElabEnv, MonadIO) via ReaderT ElabEnv IO emptyEnv :: IO ElabEnv emptyEnv = do u <- newIORef mempty pure $ ElabEnv { getEnv = mempty , nameMap = mempty , pingPong = 0 , commHook = const (pure ()) , currentSpan = Nothing , currentFile = Nothing , whereBound = mempty , definedNames = mempty , boundaries = mempty , unsolvedMetas = u , loadedFiles = [] } addBoundary :: Name -> Boundary -> ElabM a -> ElabM a addBoundary nm boundary = local (\e -> e { boundaries = Map.insert nm boundary (boundaries e)} ) assume :: Name -> Value -> (Name -> ElabM a) -> ElabM a assume nm ty k = defineInternal nm ty VVar k define :: Name -> Value -> Value -> (Name -> ElabM a) -> ElabM a define nm vty val = defineInternal nm vty (const val) makeLetDef :: Name -> Value -> Value -> (Name -> ElabM a) -> ElabM a makeLetDef nm vty val = defineInternal nm vty (\nm -> GluedVl (HVar nm) mempty val) replaceLetDef :: Name -> Value -> Value -> ElabM a -> ElabM a replaceLetDef nm vty val = redefine nm vty (GluedVl (HVar nm) mempty val) assumes :: [Name] -> Value -> ([Name] -> ElabM a) -> ElabM a assumes nms ty k = do let go acc [] k = k acc go acc (x:xs) k = assume x ty $ \n -> go (n:acc) xs k in go [] nms k defineInternal :: Name -> Value -> (Name -> Value) -> (Name -> ElabM a) -> ElabM a defineInternal nm vty val k = do env <- ask let (env', name') = go env local (const env') (k name') where go x = let nm' = case Map.lookup (getNameText nm) (nameMap x) of Just name -> incName nm name Nothing -> nm in ( x { getEnv = Map.insert nm' (vty, val nm') (getEnv x) , nameMap = Map.insert (getNameText nm) nm' (nameMap x) , whereBound = maybe (whereBound x) (flip (Map.insert nm') (whereBound x)) (currentSpan x) } , nm') redefine :: Name -> Value -> Value -> ElabM a -> ElabM a redefine nm vty val = local go where go x = x { getEnv = Map.insert nm (vty, val) (getEnv x) , nameMap = Map.insert (getNameText nm) nm (nameMap x) , whereBound = maybe (whereBound x) (flip (Map.insert nm) (whereBound x)) (currentSpan x) } getValue :: Name -> ElabM Value getValue nm = do vl <- asks (Map.lookup nm . getEnv) case vl of Just v -> pure (snd v) Nothing -> throwElab $ NotInScope nm getNfType :: Name -> ElabM NFType getNfType nm = do vl <- asks (Map.lookup nm . getEnv) case vl of Just v -> pure (fst v) Nothing -> throwElab $ NotInScope nm getNameFor :: Text -> ElabM Name getNameFor x = do vl <- asks (Map.lookup x . nameMap) case vl of Just v -> pure v Nothing -> liftIO . throwIO $ NotInScope (Bound x 0) switch :: ElabM a -> ElabM a switch k = do depth <- asks pingPong when (depth >= 128) $ throwElab StackOverflow local go k where go e = e { pingPong = pingPong e + 1 } newtype NotInScope = NotInScope { nameNotInScope :: Name } deriving (Show, Typeable) deriving anyclass (Exception) data AttachedNote = AttachedNote { getNote :: Doc AnsiStyle, getExc :: SomeException } deriving (Show, Typeable) deriving anyclass (Exception) withNote :: ElabM a -> Doc AnsiStyle -> ElabM a withNote k note = do env <- ask liftIO $ runElab k env `catch` \e -> throwIO (AttachedNote note e) data WhileChecking = WhileChecking { startPos :: P.Posn, endPos :: P.Posn, exc :: SomeException } deriving (Show, Typeable, Exception) withSpan :: P.Posn -> P.Posn -> ElabM a -> ElabM a withSpan a b k = do env <- ask liftIO $ runElab k env{ currentSpan = Just (a, b) } `catches` [ Handler $ \e@WhileChecking{} -> throwIO e , Handler $ \e -> throwIO (WhileChecking a b e) ] data SeeAlso = SeeAlso { saStartPos :: P.Posn, saEndPos :: P.Posn, saExc :: SomeException } deriving (Show, Typeable, Exception) seeAlso :: ElabM a -> Name -> ElabM a seeAlso k nm = do env <- ask case Map.lookup nm (whereBound env) of Just l -> liftIO $ runElab k env `catch` \e -> throwIO (SeeAlso (fst l) (snd l) e) Nothing -> k catchElab :: Exception e => ElabM a -> (e -> ElabM a) -> ElabM a catchElab k h = do env <- ask liftIO $ runElab k env `catch` \e -> runElab (h e) env tryElab :: Exception e => ElabM a -> ElabM (Either e a) tryElab k = do env <- ask liftIO $ (Right <$> runElab k env) `catch` \e -> pure (Left e) throwElab :: Exception e => e -> ElabM a throwElab = liftIO . throwIO incName :: Name -> Name -> Name incName (Bound x _) n = Bound x (getNameNum n + 1) incName (Defined x _) n = Defined x (getNameNum n + 1) incName (ConName x _ s a) n = ConName x (getNameNum n + 1) s a