mirror of
https://github.com/GaloisInc/cryptol.git
synced 2024-12-18 21:41:52 +03:00
191 lines
6.1 KiB
Haskell
191 lines
6.1 KiB
Haskell
-- |
|
|
-- Module : $Header$
|
|
-- Copyright : (c) 2013-2014 Galois, Inc.
|
|
-- License : BSD3
|
|
-- Maintainer : cryptol@galois.com
|
|
-- Stability : provisional
|
|
-- Portability : portable
|
|
|
|
{-# LANGUAGE DeriveDataTypeable #-}
|
|
module Notebook where
|
|
|
|
import REPL.Command (loadPrelude,findNbCommand,parseCommand,runCommand,replParse,liftModuleCmd)
|
|
import REPL.Monad (REPL(..) ,runREPL, lName, lPath)
|
|
import qualified REPL.Monad as REPL
|
|
|
|
import qualified Cryptol.ModuleSystem as M
|
|
import Cryptol.Parser (defaultConfig, parseModule, Config(..), ParseError)
|
|
import qualified Cryptol.Parser.AST as P
|
|
import Cryptol.Parser.Names (allNamesD, tnamesNT)
|
|
import Cryptol.Parser.Position (Located(..), emptyRange)
|
|
import qualified Cryptol.TypeCheck.AST as T
|
|
import Cryptol.Utils.PP (PP(..), pp, hang, text)
|
|
|
|
import Control.Applicative ((<$>))
|
|
import qualified Control.Exception as X
|
|
import Control.Monad (forever)
|
|
import Data.IORef (IORef, newIORef, readIORef, modifyIORef)
|
|
import Data.List (isPrefixOf)
|
|
import qualified Data.Set as Set
|
|
import Data.Typeable (Typeable)
|
|
import System.IO (hFlush, stdout)
|
|
|
|
-- Notebook Environment --------------------------------------------------------
|
|
|
|
-- | All of the top-level declarations along with all of the names
|
|
-- that they define. We need to associate the names in order to remove
|
|
-- declarations from the module context when they're overwritten.
|
|
type NamedDecls = [([P.QName], P.TopDecl)]
|
|
|
|
data RW = RW
|
|
{ eNamedDecls :: NamedDecls
|
|
}
|
|
|
|
-- | The default environment is simple now but might get more
|
|
-- complicated, so it's made in IO.
|
|
defaultRW :: IO RW
|
|
defaultRW = return RW { eNamedDecls = [] }
|
|
|
|
-- Notebook Monad --------------------------------------------------------------
|
|
|
|
-- | The Notebook is just a REPL augmented with an incrementally-built module.
|
|
newtype NB a = NB { unNB :: IORef RW -> REPL a }
|
|
|
|
instance Functor NB where
|
|
{-# INLINE fmap #-}
|
|
fmap f m = NB (\ref -> fmap f (unNB m ref))
|
|
|
|
instance Monad NB where
|
|
{-# INLINE return #-}
|
|
return x = NB (\_ -> return x)
|
|
|
|
{-# INLINE (>>=) #-}
|
|
m >>= f = NB $ \ref -> do
|
|
x <- unNB m ref
|
|
unNB (f x) ref
|
|
|
|
-- | Run a NB action with a fresh environment.
|
|
runNB :: NB a -> IO a
|
|
runNB m = do
|
|
ref <- newIORef =<< defaultRW
|
|
runREPL True $ unNB m ref
|
|
|
|
-- | Lift a REPL action into the NB monad.
|
|
liftREPL :: REPL a -> NB a
|
|
liftREPL m = NB (\_ -> m)
|
|
|
|
-- Primitives ------------------------------------------------------------------
|
|
|
|
io :: IO a -> NB a
|
|
io m = liftREPL (REPL.io m)
|
|
|
|
getRW :: NB RW
|
|
getRW = NB (\ref -> REPL.io (readIORef ref))
|
|
|
|
modifyRW_ :: (RW -> RW) -> NB ()
|
|
modifyRW_ f = NB (\ref -> REPL.io (modifyIORef ref f))
|
|
|
|
getTopDecls :: NB NamedDecls
|
|
getTopDecls = eNamedDecls `fmap` getRW
|
|
|
|
setTopDecls :: NamedDecls -> NB ()
|
|
setTopDecls nds = modifyRW_ (\rw -> rw { eNamedDecls = nds })
|
|
|
|
modifyTopDecls :: (NamedDecls -> NamedDecls) -> NB NamedDecls
|
|
modifyTopDecls f = do
|
|
nds <- f `fmap` getTopDecls
|
|
setTopDecls nds
|
|
return nds
|
|
|
|
-- Exceptions ------------------------------------------------------------------
|
|
|
|
-- | Notebook exceptions.
|
|
data NBException
|
|
= REPLException REPL.REPLException
|
|
| AutoParseError ParseError
|
|
deriving (Show, Typeable)
|
|
|
|
instance X.Exception NBException
|
|
|
|
instance PP NBException where
|
|
ppPrec _ nbe = case nbe of
|
|
REPLException exn -> pp exn
|
|
AutoParseError exn ->
|
|
hang (text "[error] Failed to parse cell as a module or as interactive input")
|
|
4 (pp exn)
|
|
|
|
-- | Raise an exception
|
|
raise :: NBException -> NB a
|
|
raise exn = io (X.throwIO exn)
|
|
|
|
-- | Catch an exception
|
|
catch :: NB a -> (NBException -> NB a) -> NB a
|
|
catch m k = NB (\ref ->
|
|
REPL (\replRef -> unREPL (unNB m ref) replRef
|
|
`X.catches`
|
|
-- catch a REPLException or a NBException
|
|
[ X.Handler $ \e -> unREPL (unNB (k (REPLException e)) ref) replRef
|
|
, X.Handler $ \e -> unREPL (unNB (k e) ref) replRef
|
|
]))
|
|
|
|
-- | Try running a possibly-excepting computation
|
|
try :: NB a -> NB (Either NBException a)
|
|
try m = catch (Right `fmap` m) (return . Left)
|
|
|
|
-- | Try running the given action, printing any exceptions that arise.
|
|
runExns :: NB () -> NB ()
|
|
runExns m = m `catch` \x -> io $ print $ pp x
|
|
|
|
-- Module Manipulation ---------------------------------------------------------
|
|
|
|
nbName :: P.Located P.ModName
|
|
nbName = Located { srcRange = emptyRange
|
|
, thing = P.ModName ["Notebook"]
|
|
}
|
|
|
|
-- | Distill a module into a list of decls along with the names
|
|
-- defined by those decls.
|
|
modNamedDecls :: P.Module -> NamedDecls
|
|
modNamedDecls m = [(tdNames td, td) | td <- P.mDecls m]
|
|
|
|
-- | Build a module of the given name using the given list of
|
|
-- declarations.
|
|
moduleFromDecls :: P.Located P.ModName -> NamedDecls -> P.Module
|
|
moduleFromDecls name nds =
|
|
P.Module { P.mName = name
|
|
, P.mImports = []
|
|
, P.mDecls = map snd nds
|
|
}
|
|
|
|
-- | In @updateNamedDecls old new = result@, @result@ is a
|
|
-- right-biased combination of @old@ and @new@ with the following
|
|
-- semantics:
|
|
--
|
|
-- If a name @x@ is defined in @old@ and not @new@, or in @new@ and
|
|
-- not @old@, all declarations of @x@ are in @result@.
|
|
--
|
|
-- If a name @x@ is defined in both @old@ and @new@, /none/ of the
|
|
-- declarations of @x@ from @old@ are in @result@, and all
|
|
-- declarations of @x@ from @new@ are in @result@.
|
|
updateNamedDecls :: NamedDecls -> NamedDecls -> NamedDecls
|
|
updateNamedDecls old new = filteredOld ++ new
|
|
where newNames = Set.fromList $ concat $ map fst new
|
|
containsNewName = any (\x -> Set.member x newNames)
|
|
filteredOld = filter (\(xs,_) -> not (containsNewName xs)) old
|
|
|
|
|
|
-- | The names defined by a top level declaration
|
|
tdNames :: P.TopDecl -> [P.QName]
|
|
tdNames (P.Decl d) = map P.thing $ allNamesD $ P.tlValue d
|
|
tdNames (P.TDNewtype d) = map P.thing $ fst $ tnamesNT $ P.tlValue d
|
|
tdNames (P.Include _) = []
|
|
|
|
removeIncludes :: P.Module -> P.Module
|
|
removeIncludes m = m { P.mDecls = decls' }
|
|
where decls' = filter (not . isInclude) $ P.mDecls m
|
|
isInclude (P.Include _) = True
|
|
isInclude _ = False
|
|
|
|
removeImports :: P.Module -> P.Module
|
|
removeImports m = m { P.mImports = [] }
|