repl: Add ReplConfig and :set command

This commit is contained in:
Richard Marko 2020-07-02 11:15:58 +02:00
parent b84f4f6c19
commit 8d0e325cf5

View file

@ -14,6 +14,7 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE TypeSynonymInstances #-}
@ -38,6 +39,7 @@ import Nix.Utils
import Control.Comonad import Control.Comonad
import qualified Data.List import qualified Data.List
import qualified Data.Maybe
import qualified Data.HashMap.Lazy import qualified Data.HashMap.Lazy
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text import qualified Data.Text
@ -72,7 +74,7 @@ main = main' Nothing
-- --
-- Passed value is stored in context with "input" key. -- Passed value is stored in context with "input" key.
main' :: (MonadNix e t f m, MonadIO m, MonadMask m) => Maybe (NValue t f m) -> m () main' :: (MonadNix e t f m, MonadIO m, MonadMask m) => Maybe (NValue t f m) -> m ()
main' iniVal = flip evalStateT (initState iniVal) main' iniVal = initState iniVal >>= \s -> flip evalStateT s
$ System.Console.Repline.evalRepl $ System.Console.Repline.evalRepl
banner banner
cmd cmd
@ -132,15 +134,45 @@ main' iniVal = flip evalStateT (initState iniVal)
data IState t f m = IState data IState t f m = IState
{ replIt :: Maybe NExprLoc -- ^ Last expression entered { replIt :: Maybe NExprLoc -- ^ Last expression entered
, replCtx :: AttrSet (NValue t f m) -- ^ Value environment , replCtx :: AttrSet (NValue t f m) -- ^ Value environment
, replDbg :: Bool -- ^ Enable REPL debug output, dumping IState on each command , replCfg :: ReplConfig -- ^ REPL configuration
} deriving (Eq, Show) } deriving (Eq, Show)
initState :: MonadIO m => Maybe (NValue t f m) -> IState t f m data ReplConfig = ReplConfig
initState mIni = { cfgDebug :: Bool
IState , cfgStrict :: Bool
, cfgValues :: Bool
} deriving (Eq, Show)
defReplConfig :: ReplConfig
defReplConfig = ReplConfig
{ cfgDebug = False
, cfgStrict = False
, cfgValues = False
}
-- | Create initial IState for REPL
initState :: MonadNix e t f m => Maybe (NValue t f m) -> m (IState t f m)
initState mIni = do
builtins <- evalText "builtins"
opts :: Nix.Options <- asks (view hasLens)
pure $ IState
Nothing Nothing
(maybe mempty (\x -> Data.HashMap.Lazy.fromList [("input", x)]) mIni) (Data.HashMap.Lazy.fromList
False $ ("builtins", builtins) : fmap ("input",) (Data.Maybe.maybeToList mIni))
defReplConfig
{ cfgStrict = strict opts
, cfgValues = values opts
}
where
evalText :: (MonadNix e t f m) => Text -> m (NValue t f m)
evalText expr = case parseNixTextLoc expr of
Failure e -> error $ "Impossible happened: Unable to parse expression - '" ++ (Data.Text.unpack expr) ++ "' error was " ++ show e
Success e -> do
value <- evalExprLoc e
pure value
type Repl e t f m = HaskelineT (StateT (IState t f m) m) type Repl e t f m = HaskelineT (StateT (IState t f m) m)
@ -158,7 +190,7 @@ exec update source = do
-- Get the current interpreter state -- Get the current interpreter state
st <- get st <- get
when (replDbg st) $ liftIO $ print st when (cfgDebug $ replCfg st) $ liftIO $ print st
-- Parser ( returns AST as `NExprLoc` ) -- Parser ( returns AST as `NExprLoc` )
case parseExprOrBinding source of case parseExprOrBinding source of
@ -219,12 +251,13 @@ cmd source = do
printValue :: (MonadNix e t f m, MonadIO m) printValue :: (MonadNix e t f m, MonadIO m)
=> NValue t f m => NValue t f m
-> Repl e t f m () -> Repl e t f m ()
printValue val = lift $ lift $ do printValue val = do
opts :: Nix.Options <- asks (view hasLens) cfg <- replCfg <$> get
if lift $ lift $ do
| strict opts -> liftIO . print . prettyNValue =<< normalForm val if
| values opts -> liftIO . print . prettyNValueProv =<< removeEffects val | cfgStrict cfg -> liftIO . print . prettyNValue =<< normalForm val
| otherwise -> liftIO . print . prettyNValue =<< removeEffects val | cfgValues cfg -> liftIO . print . prettyNValueProv =<< removeEffects val
| otherwise -> liftIO . print . prettyNValue =<< removeEffects val
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Commands -- Commands
@ -275,9 +308,14 @@ typeof args = do
quit :: (MonadNix e t f m, MonadIO m) => a -> Repl e t f m () quit :: (MonadNix e t f m, MonadIO m) => a -> Repl e t f m ()
quit _ = liftIO System.Exit.exitSuccess quit _ = liftIO System.Exit.exitSuccess
-- :debug command -- :set command
debug :: (MonadNix e t f m, MonadIO m) => a -> Repl e t f m () setConfig :: (MonadNix e t f m, MonadIO m) => String -> Repl e t f m ()
debug _ = modify (\x -> x { replDbg = True }) setConfig args = case words args of
[] -> liftIO $ putStrLn "No option to set specified"
(x:_xs) ->
case filter ((==x) . helpSetOptionName) helpSetOptions of
[opt] -> modify (\s -> s { replCfg = helpSetOptionFunction opt (replCfg s) })
_ -> liftIO $ putStrLn "No such option"
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Interactive Shell -- Interactive Shell
@ -344,12 +382,70 @@ helpOptions =
"Quit interpreter" "Quit interpreter"
quit quit
, HelpOption , HelpOption
"set"
""
( "Set REPL option"
<> Data.Text.Prettyprint.Doc.line
<> "Available options:"
<> Data.Text.Prettyprint.Doc.line
<> (renderSetOptions helpSetOptions)
)
setConfig
]
-- Options for :set
data HelpSetOption = HelpSetOption
{ helpSetOptionName :: String
, helpSetOptionSyntax :: Doc ()
, helpSetOptionDoc :: Doc ()
, helpSetOptionFunction :: ReplConfig -> ReplConfig
}
helpSetOptions :: [HelpSetOption]
helpSetOptions =
[ HelpSetOption
"strict"
""
"Enable strict evaluation of REPL expressions"
(\x -> x { cfgStrict = True})
, HelpSetOption
"lazy"
""
"Disable strict evaluation of REPL expressions"
(\x -> x { cfgStrict = False})
, HelpSetOption
"values"
""
"Enable printing of value provenance information"
(\x -> x { cfgValues = True})
, HelpSetOption
"novalues"
""
"Disable printing of value provenance information"
(\x -> x { cfgValues = False})
, HelpSetOption
"debug" "debug"
"" ""
"Enable REPL debugging output" "Enable printing of REPL debug information"
debug (\x -> x { cfgDebug = True})
, HelpSetOption
"nodebug"
""
"Disable REPL debugging"
(\x -> x { cfgDebug = False})
] ]
renderSetOptions :: [HelpSetOption] -> Doc ()
renderSetOptions so =
Data.Text.Prettyprint.Doc.indent 4
$ Data.Text.Prettyprint.Doc.vsep
$ flip map so
$ \h ->
Data.Text.Prettyprint.Doc.pretty (helpSetOptionName h)
<+> helpSetOptionSyntax h
<> Data.Text.Prettyprint.Doc.line
<> Data.Text.Prettyprint.Doc.indent 4 (helpSetOptionDoc h)
help :: (MonadNix e t f m, MonadIO m) help :: (MonadNix e t f m, MonadIO m)
=> HelpOptions e t f m => HelpOptions e t f m
-> String -> String