diff --git a/.gitignore b/.gitignore index eaf73b3..06a2715 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ result TAGS ctags dist-newstyle +/.history diff --git a/hnix.cabal b/hnix.cabal index 8b3deb3..d340a8a 100644 --- a/hnix.cabal +++ b/hnix.cabal @@ -2,7 +2,7 @@ -- -- see: https://github.com/sol/hpack -- --- hash: d8e569439f4d52d30cbcfda7126f2175566ff4b9550032b2b7f91eabacf8c04a +-- hash: aadafd05c71b074e10eb082201d88f7358d995ee842fc8308b3222482f43971c name: hnix version: 0.5.0 @@ -121,6 +121,7 @@ executable hnix , exceptions , filepath , hnix + , megaparsec , mtl , optparse-applicative , repline diff --git a/main/Main.hs b/main/Main.hs index a87916f..d198819 100644 --- a/main/Main.hs +++ b/main/Main.hs @@ -27,6 +27,7 @@ import Nix.Parser import Nix.Pretty import Nix.Stack (NixException(..)) import qualified Nix.Value as V +import qualified Repl -- import Nix.TH import Options.Applicative hiding (ParserResult(..)) import System.IO @@ -48,9 +49,7 @@ main = do Just path -> mapM_ (processFile opts) =<< (lines <$> readFile path) Nothing -> case filePaths opts of - [] -> - handleResult opts Nothing . parseNixTextLoc - =<< Text.getContents + [] -> Repl.shell (pure ()) ["-"] -> handleResult opts Nothing . parseNixTextLoc =<< Text.getContents @@ -74,8 +73,7 @@ main = do process opts mpath expr = do when (check opts) $ - putStrLn $ runST $ Nix.runLintM . renderSymbolic - =<< Nix.lint expr + putStrLn $ runST $ runLintM . renderSymbolic =<< lint expr let parseArg s = case parseNixText s of Success x -> x @@ -144,3 +142,6 @@ main = do . renderPretty 0.4 80 . prettyNix . stripAnnotation $ expr + + when (repl opts) $ + Repl.shell (pure ()) diff --git a/main/Repl.hs b/main/Repl.hs index 1d5a339..a5b8bd9 100644 --- a/main/Repl.hs +++ b/main/Repl.hs @@ -10,6 +10,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeSynonymInstances #-} {-# OPTIONS_GHC -Wno-unused-matches #-} @@ -17,13 +18,23 @@ module Repl where -import Nix.Type.Infer +import qualified Nix +import qualified Nix.Eval as Eval +import Nix.Exec (Lazy) +import qualified Nix.Exec as Exec +import Nix.Normal +import Nix.Parser +import Nix.Pretty +import Nix.Scope import qualified Nix.Type.Env as Env +import Nix.Type.Infer +import Nix.Value +import qualified Data.HashMap.Lazy as M import qualified Data.Map as Map import Data.Monoid -import qualified Data.Text.Lazy as L -import qualified Data.Text.Lazy.IO as L +import qualified Data.Text as Text +import qualified Data.Text.IO as Text import Control.Monad.Identity import Control.Monad.State.Strict @@ -43,17 +54,18 @@ import System.Console.Repline data TermEnv data IState = IState - { tyctx :: Env.Env -- Type environment - , tmctx :: TermEnv -- Value environment + { -- tyctx :: Env.Env -- Type environment + -- , + tmctx :: TermEnv -- Value environment } initState :: IState -initState = IState Env.empty undefined +initState = IState {-Env.empty-} undefined type Repl a = HaskelineT (StateT IState IO) a -hoistErr :: Show e => Either e a -> Repl a -hoistErr (Right val) = return val -hoistErr (Left err) = do +hoistErr :: Result a -> Repl a +hoistErr (Success val) = return val +hoistErr (Failure err) = do liftIO $ print err abort @@ -61,42 +73,39 @@ hoistErr (Left err) = do -- Execution ------------------------------------------------------------------------------- -exec :: Bool -> L.Text -> Repl () +exec :: Bool -> Text.Text -> Repl () exec update source = do -- Get the current interpreter state st <- get -- Parser ( returns AST ) - -- mod <- hoistErr $ parseModule "" source + expr <- hoistErr $ parseNixTextLoc source -- Type Inference ( returns Typing Environment ) - -- tyctx' <- hoistErr $ inferTop (tyctx st) mod + -- tyctx' <- hoistErr $ inferTop (tyctx st) expr -- Create the new environment - -- let st' = st { tmctx = foldl' evalDef (tmctx st) mod - -- , tyctx = tyctx' <> tyctx st - -- } + let st' = st { tmctx = tmctx st -- foldl' evalDef (tmctx st) expr + -- , tyctx = tyctx' <> tyctx st + } -- Update the interpreter state - -- when update (put st') + when update (put st') -- If a value is entered, print it. - -- case lookup "it" mod of - -- Nothing -> return () - -- Just ex -> do - -- let (val, _) = runEval (tmctx st') "it" ex - -- showOutput (show val) st' - - undefined - -showOutput :: String -> IState -> Repl () -showOutput arg st = - case Env.lookup "it" (tyctx st) of - Just val -> liftIO $ putStrLn $ undefined -- ppsignature (arg, val) - Nothing -> return () + val <- liftIO $ Exec.runLazyM $ + Nix.evalTopLevelExprGen + -- jww (2018-04-12): Once the user is able to establish definitions + -- in the repl, they should be passed here. + (pushScope @(NThunk (Lazy IO)) M.empty + . Eval.framedEvalExpr + (Eval.eval @_ @(NValue (Lazy IO)) + @(NThunk (Lazy IO)) @(Lazy IO))) + Nothing [] expr + liftIO $ putStrLn $ printNix val cmd :: String -> Repl () -cmd source = exec True (L.pack source) +cmd source = exec True (Text.pack source) ------------------------------------------------------------------------------- -- Commands @@ -112,21 +121,21 @@ browse _ = do -- :load command load :: [String] -> Repl () load args = do - contents <- liftIO $ L.readFile (unwords args) + contents <- liftIO $ Text.readFile (unwords args) exec True contents -- :type command -typeof :: [String] -> Repl () -typeof args = do - st <- get - let arg = unwords args - case Env.lookup (pack arg) (tyctx st) of - Just val -> liftIO $ putStrLn $ undefined -- ppsignature (arg, val) - Nothing -> exec False (L.pack arg) +-- typeof :: [String] -> Repl () +-- typeof args = do +-- st <- get +-- let arg = unwords args +-- case Env.lookup (pack arg) (tyctx st) of +-- Just val -> liftIO $ putStrLn $ undefined -- ppsignature (arg, val) +-- Nothing -> exec False (Text.pack arg) -- :quit command quit :: a -> Repl () -quit _ = liftIO $ exitSuccess +quit _ = liftIO exitSuccess ------------------------------------------------------------------------------- -- Interactive Shell @@ -143,16 +152,16 @@ defaultMatcher = [ comp :: (Monad m, MonadState IState m) => WordCompleter m comp n = do let cmds = [":load", ":type", ":browse", ":quit"] - Env.TypeEnv ctx <- gets tyctx - let defs = map unpack $ Map.keys ctx - return $ filter (isPrefixOf n) (cmds ++ defs) + -- Env.TypeEnv ctx <- gets tyctx + -- let defs = map unpack $ Map.keys ctx + return $ filter (isPrefixOf n) (cmds {-++ defs-}) options :: [(String, [String] -> Repl ())] options = [ ("load" , load) , ("browse" , browse) , ("quit" , quit) - , ("type" , Repl.typeof) + -- , ("type" , Repl.typeof) ] ------------------------------------------------------------------------------- @@ -163,18 +172,18 @@ completer :: CompleterStyle (StateT IState IO) completer = Prefix (wordCompleter comp) defaultMatcher shell :: Repl a -> IO () -shell pre = flip evalStateT initState - $ evalRepl "Poly> " cmd options completer pre +shell pre = flip evalStateT initState $ + evalRepl "hnix> " cmd options completer pre ------------------------------------------------------------------------------- -- Toplevel ------------------------------------------------------------------------------- -main :: IO () -main = do - args <- getArgs - case args of - [] -> shell (return ()) - [fname] -> shell (load [fname]) - ["test", fname] -> shell (load [fname] >> browse [] >> quit ()) - _ -> putStrLn "invalid arguments" +-- main :: IO () +-- main = do +-- args <- getArgs +-- case args of +-- [] -> shell (return ()) +-- [fname] -> shell (load [fname]) +-- ["test", fname] -> shell (load [fname] >> browse [] >> quit ()) +-- _ -> putStrLn "invalid arguments" diff --git a/package.yaml b/package.yaml index 1e4097d..f0b64be 100644 --- a/package.yaml +++ b/package.yaml @@ -68,6 +68,7 @@ executables: dependencies: - hnix - repline + - megaparsec tests: hnix-tests: diff --git a/src/Nix.hs b/src/Nix.hs index 96a3256..05b3319 100644 --- a/src/Nix.hs +++ b/src/Nix.hs @@ -5,7 +5,7 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} -module Nix (eval, evalLoc, tracingEvalLoc, lint, runLintM) where +module Nix where import Control.Applicative import Control.Monad.Catch @@ -15,11 +15,9 @@ import qualified Data.HashMap.Lazy as M import qualified Data.Text as Text import Nix.Builtins import qualified Nix.Eval as Eval -import Nix.Eval hiding (eval) import Nix.Exec import Nix.Expr.Types (NExpr) import Nix.Expr.Types.Annotated (NExprLoc) -import Nix.Lint (lint, runLintM) import Nix.Normal import Nix.Scope import Nix.Thunk @@ -58,7 +56,7 @@ eval mpath incls = runLazyM . evalTopLevelExpr mpath incls evalTopLevelExprLoc :: forall e m. MonadBuiltins e m => Maybe FilePath -> [String] -> NExprLoc -> m (NValueNF m) evalTopLevelExprLoc = evalTopLevelExprGen $ - framedEvalExpr (Eval.eval @_ @(NValue m) @(NThunk m) @m) + Eval.framedEvalExpr (Eval.eval @_ @(NValue m) @(NThunk m) @m) evalLoc :: (MonadFix m, MonadThrow m, MonadCatch m, MonadIO m) => Maybe FilePath -> [String] -> NExprLoc -> m (NValueNF (Lazy m)) @@ -70,6 +68,6 @@ tracingEvalLoc => Maybe FilePath -> [String] -> NExprLoc -> m (NValueNF (Lazy m)) tracingEvalLoc mpath incls expr = runLazyM . evalTopLevelExprGen id mpath incls - =<< tracingEvalExpr @_ @(Lazy m) @_ @(NValue (Lazy m)) + =<< Eval.tracingEvalExpr @_ @(Lazy m) @_ @(NValue (Lazy m)) (Eval.eval @_ @(NValue (Lazy m)) @(NThunk (Lazy m)) @(Lazy m)) expr diff --git a/src/Nix/Options.hs b/src/Nix/Options.hs index 19e8c6f..be9f0b4 100644 --- a/src/Nix/Options.hs +++ b/src/Nix/Options.hs @@ -21,6 +21,7 @@ data Options = Options , check :: Bool , readFrom :: Maybe FilePath , cache :: Bool + , repl :: Bool , ignoreErrors :: Bool , expression :: Maybe Text , arg :: [(Text, Text)] @@ -84,6 +85,9 @@ nixOptions = Options <*> switch ( long "cache" <> help "Write out the parsed expression tree to a binary cache") + <*> switch + ( long "repl" + <> help "After performing any indicated actions, enter the REPL") <*> switch ( long "ignore-errors" <> help "Continue parsing files, even if there are errors") diff --git a/tests/NixLanguageTests.hs b/tests/NixLanguageTests.hs index 7c2576a..2ad8972 100644 --- a/tests/NixLanguageTests.hs +++ b/tests/NixLanguageTests.hs @@ -15,7 +15,7 @@ import qualified Data.Map as Map import qualified Data.Text as Text import qualified Data.Text.IO as Text import GHC.Exts -import Nix +import Nix.Lint import Nix.Options import Nix.Parser import Nix.Pretty