repl: Extend IState, handle bindings, add browse and debug commands
Bindings are now handled correctly and you can do ``` a = 2 b = 3 c = a + b :browse :t c ``` Converted IState to data type instead of `newtype`. Now tracks `replIt` for last entered expression that parsed successfully. `replCtx` is now extended with bindings and browseable via `:browse`. :`debug` can be used to enable dumping of `IState` on input. Scope taken from `replCtx` is now pushed to evaluated values.
This commit is contained in:
parent
d9303f385c
commit
0e8e73ec88
105
main/Repl.hs
105
main/Repl.hs
|
@ -34,12 +34,9 @@ import Nix.Type.Infer
|
||||||
import Nix.Utils
|
import Nix.Utils
|
||||||
|
|
||||||
import Control.Comonad
|
import Control.Comonad
|
||||||
import qualified Data.HashMap.Lazy as M
|
import qualified Data.List
|
||||||
import Data.List ( isPrefixOf
|
import qualified Data.HashMap.Lazy
|
||||||
, foldl'
|
import Data.Text (Text)
|
||||||
)
|
|
||||||
import qualified Data.Map as Map
|
|
||||||
import Data.Text (Text)
|
|
||||||
import qualified Data.Text
|
import qualified Data.Text
|
||||||
import qualified Data.Text.IO
|
import qualified Data.Text.IO
|
||||||
import Data.Version ( showVersion )
|
import Data.Version ( showVersion )
|
||||||
|
@ -89,12 +86,14 @@ main = flip evalStateT initState
|
||||||
-- Types
|
-- Types
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
newtype IState t f m = IState
|
data IState t f m = IState
|
||||||
{ tmctx :: AttrSet (NValue t f m) -- Value environment
|
{ replIt :: Maybe NExprLoc -- ^ Last expression entered
|
||||||
}
|
, replCtx :: AttrSet (NValue t f m) -- ^ Value environment
|
||||||
|
, replDbg :: Bool -- ^ Enable REPL debug output, dumping IState on each command
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
initState :: MonadIO m => IState t f m
|
initState :: MonadIO m => IState t f m
|
||||||
initState = IState mempty
|
initState = IState Nothing mempty False
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -112,19 +111,21 @@ exec update source = do
|
||||||
-- Get the current interpreter state
|
-- Get the current interpreter state
|
||||||
st <- get
|
st <- get
|
||||||
|
|
||||||
-- Parser ( returns AST )
|
when (replDbg st) $ liftIO $ print st
|
||||||
-- TODO: parse <var> = <expr>
|
|
||||||
case parseNixTextLoc source of
|
-- Parser ( returns AST as `NExprLoc` )
|
||||||
Failure err -> do
|
case parseExprOrBinding source of
|
||||||
|
(Failure err, _) -> do
|
||||||
liftIO $ print err
|
liftIO $ print err
|
||||||
return Nothing
|
return Nothing
|
||||||
Success expr -> do
|
(Success expr, isBinding) -> do
|
||||||
|
|
||||||
-- Type Inference ( returns Typing Environment )
|
-- Type Inference ( returns Typing Environment )
|
||||||
|
--
|
||||||
--let tyctx' = inferTop Env.empty [("repl", stripAnnotation expr)]
|
--let tyctx' = inferTop Env.empty [("repl", stripAnnotation expr)]
|
||||||
--liftIO $ print tyctx'
|
--liftIO $ print tyctx'
|
||||||
|
|
||||||
-- TODO: track scope with (tmctx st)
|
mVal <- lift $ lift $ try $ pushScope (replCtx st) (evalExprLoc expr)
|
||||||
mVal <- lift $ lift $ try $ pushScope mempty (evalExprLoc expr)
|
|
||||||
|
|
||||||
case mVal of
|
case mVal of
|
||||||
Left (NixException frames) -> do
|
Left (NixException frames) -> do
|
||||||
|
@ -132,10 +133,31 @@ exec update source = do
|
||||||
return Nothing
|
return Nothing
|
||||||
Right val -> do
|
Right val -> do
|
||||||
-- Update the interpreter state
|
-- Update the interpreter state
|
||||||
when update $ do
|
when (update && isBinding) $ do
|
||||||
-- Create the new environment
|
-- Set `replIt` to last entered expression
|
||||||
put st { tmctx = tmctx st } -- TODO: M.insert key val (tmctx st)
|
put st { replIt = Just expr }
|
||||||
|
|
||||||
|
-- If the result value is a set, update our context with it
|
||||||
|
case val of
|
||||||
|
NVSet xs _ -> put st { replCtx = Data.HashMap.Lazy.union xs (replCtx st) }
|
||||||
|
_ -> return ()
|
||||||
|
|
||||||
return $ Just val
|
return $ Just val
|
||||||
|
where
|
||||||
|
-- If parsing fails, turn the input into singleton attribute set
|
||||||
|
-- and try again.
|
||||||
|
--
|
||||||
|
-- This allows us to handle assignments like @a = 42@
|
||||||
|
-- which get turned into @{ a = 42; }@
|
||||||
|
parseExprOrBinding i =
|
||||||
|
case parseNixTextLoc i of
|
||||||
|
Success expr -> (Success expr, False)
|
||||||
|
Failure e ->
|
||||||
|
case parseNixTextLoc $ toAttrSet i of
|
||||||
|
Failure _ -> (Failure e, False) -- return the first parsing failure
|
||||||
|
Success e' -> (Success e', True)
|
||||||
|
|
||||||
|
toAttrSet i = "{" <> i <> (if Data.Text.isSuffixOf ";" i then mempty else ";") <> "}"
|
||||||
|
|
||||||
cmd
|
cmd
|
||||||
:: (MonadNix e t f m, MonadIO m)
|
:: (MonadNix e t f m, MonadIO m)
|
||||||
|
@ -145,24 +167,31 @@ cmd source = do
|
||||||
mVal <- exec True (Data.Text.pack source)
|
mVal <- exec True (Data.Text.pack source)
|
||||||
case mVal of
|
case mVal of
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
Just val -> do
|
Just val -> printValue val
|
||||||
lift $ lift $ do
|
|
||||||
opts :: Nix.Options <- asks (view hasLens)
|
printValue :: (MonadNix e t f m, MonadIO m)
|
||||||
if
|
=> NValue t f m
|
||||||
| strict opts -> liftIO . print . prettyNValue =<< normalForm val
|
-> Repl e t f m ()
|
||||||
| values opts -> liftIO . print . prettyNValueProv =<< removeEffects val
|
printValue val = lift $ lift $ do
|
||||||
| otherwise -> liftIO . print . prettyNValue =<< removeEffects val
|
opts :: Nix.Options <- asks (view hasLens)
|
||||||
|
if
|
||||||
|
| strict opts -> liftIO . print . prettyNValue =<< normalForm val
|
||||||
|
| values opts -> liftIO . print . prettyNValueProv =<< removeEffects val
|
||||||
|
| otherwise -> liftIO . print . prettyNValue =<< removeEffects val
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-- Commands
|
-- Commands
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- :browse command
|
-- :browse command
|
||||||
browse :: MonadNix e t f m => String -> Repl e t f m ()
|
browse :: (MonadNix e t f m, MonadIO m)
|
||||||
|
=> String
|
||||||
|
-> Repl e t f m ()
|
||||||
browse _ = do
|
browse _ = do
|
||||||
st <- get
|
st <- get
|
||||||
undefined
|
forM_ (Data.HashMap.Lazy.toList $ replCtx st) $ \(k, v) -> do
|
||||||
-- liftIO $ mapM_ putStrLn $ ppenv (tyctx st)
|
liftIO $ putStr $ Data.Text.unpack $ k <> " = "
|
||||||
|
printValue v
|
||||||
|
|
||||||
-- :load command
|
-- :load command
|
||||||
load
|
load
|
||||||
|
@ -180,7 +209,7 @@ typeof
|
||||||
-> Repl e t f m ()
|
-> Repl e t f m ()
|
||||||
typeof args = do
|
typeof args = do
|
||||||
st <- get
|
st <- get
|
||||||
mVal <- case M.lookup line (tmctx st) of
|
mVal <- case Data.HashMap.Lazy.lookup line (replCtx st) of
|
||||||
Just val -> return $ Just val
|
Just val -> return $ Just val
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
exec False line
|
exec False line
|
||||||
|
@ -195,6 +224,10 @@ 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
|
||||||
|
debug :: (MonadNix e t f m, MonadIO m) => a -> Repl e t f m ()
|
||||||
|
debug _ = modify (\x -> x { replDbg = True })
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-- Interactive Shell
|
-- Interactive Shell
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
@ -202,16 +235,17 @@ quit _ = liftIO System.Exit.exitSuccess
|
||||||
-- Prefix tab completer
|
-- Prefix tab completer
|
||||||
defaultMatcher :: MonadIO m => [(String, CompletionFunc m)]
|
defaultMatcher :: MonadIO m => [(String, CompletionFunc m)]
|
||||||
defaultMatcher =
|
defaultMatcher =
|
||||||
[(":load", System.Console.Repline.fileCompleter)
|
[ (":load", System.Console.Repline.fileCompleter)
|
||||||
--, (":type" , values)
|
--, (":type" , values)
|
||||||
]
|
]
|
||||||
|
|
||||||
-- Default tab completer
|
-- Default tab completer
|
||||||
comp :: Monad m => WordCompleter m
|
comp :: Monad m => WordCompleter m
|
||||||
comp n = do
|
comp n = do
|
||||||
let cmds = [":load", ":type", ":browse", ":quit"]
|
let cmds = [":load", ":type", ":browse", ":quit"]
|
||||||
-- Env.TypeEnv ctx <- gets tyctx
|
-- Env.TypeEnv ctx <- gets tyctx
|
||||||
-- let defs = map Data.Text.unpack $ Map.keys ctx
|
-- let defs = map Data.Text.unpack $ Map.keys ctx
|
||||||
return $ filter (isPrefixOf n) (cmds
|
return $ filter (Data.List.isPrefixOf n) (cmds
|
||||||
-- ++ defs
|
-- ++ defs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -220,10 +254,11 @@ options
|
||||||
=> System.Console.Repline.Options (Repl e t f m)
|
=> System.Console.Repline.Options (Repl e t f m)
|
||||||
options =
|
options =
|
||||||
[ ( "load" , load)
|
[ ( "load" , load)
|
||||||
--, ("browse" , browse)
|
, ("browse" , browse)
|
||||||
, ("quit", quit)
|
, ("quit", quit)
|
||||||
, ("type", typeof)
|
, ("type", typeof)
|
||||||
, ("help", help)
|
, ("help", help)
|
||||||
|
, ("debug", debug)
|
||||||
]
|
]
|
||||||
|
|
||||||
help
|
help
|
||||||
|
|
Loading…
Reference in New Issue