{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE DeriveAnyClass #-} module Elab.Monad where import Control.Monad.Reader import Control.Exception import qualified Data.Map.Strict as Map import Data.Map.Strict (Map) import Data.Text (Text) import Data.Typeable import Syntax import qualified Data.Text as T data ElabEnv = ElabEnv { getEnv :: Map Name (NFType, Value), nameMap :: Map Text Name, pingPong :: Int } newtype ElabM a = ElabM { runElab :: ElabEnv -> IO a } deriving (Functor, Applicative, Monad, MonadReader ElabEnv, MonadIO) via ReaderT ElabEnv IO newtype NotInScope = NotInScope { nameNotInScope :: Name } deriving (Show, Typeable) deriving anyclass (Exception) emptyEnv :: ElabEnv emptyEnv = ElabEnv mempty (Map.singleton (T.pack "Type") (Builtin (T.pack "Type") WiType)) 0 assume :: Name -> Value -> ElabM a -> ElabM a assume nm ty = local go where go x = x { getEnv = Map.insert nm (ty, VVar nm) (getEnv x), nameMap = Map.insert (getNameText nm) nm (nameMap x) } getNameText :: Name -> Text getNameText (Bound x) = x getNameText (Defined x) = x getNameText (Builtin x _) = x define :: Name -> Value -> Value -> ElabM a -> ElabM a define nm ty vl = local go where go x = x { getEnv = Map.insert nm (ty, vl) (getEnv x), nameMap = Map.insert (getNameText nm) nm (nameMap x) } getValue :: Name -> ElabM Value getValue nm = do vl <- asks (Map.lookup nm . getEnv) case vl of Just v -> pure (snd v) Nothing -> liftIO . throwIO $ NotInScope nm getNfType :: Name -> ElabM NFType getNfType nm = do vl <- asks (Map.lookup nm . getEnv) case vl of Just v -> pure (fst v) Nothing -> liftIO . throwIO $ 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) switch :: ElabM a -> ElabM a switch k = do depth <- asks pingPong when (depth >= 128) $ liftIO $ throwIO StackOverflow local go k where go e = e { pingPong = pingPong e + 1 }