Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute new entrypoint root when loading a file in the REPL #1615

Merged
merged 3 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 74 additions & 45 deletions app/Commands/Repl.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,36 @@ import Juvix.Compiler.Core.Translation.FromInternal.Data qualified as Core
import Juvix.Compiler.Internal.Language qualified as Internal
import Juvix.Compiler.Internal.Pretty qualified as Internal
import Juvix.Data.Error.GenericError qualified as Error
import Juvix.Extra.Paths
import Juvix.Extra.Version
import Juvix.Prelude.Pretty qualified as P
import Root
import System.Console.ANSI qualified as Ansi
import System.Console.Haskeline
import System.Console.Repline
import System.Console.Repline qualified as Repline
import Text.Megaparsec qualified as M

type ReplS = State.StateT ReplState IO

type Repl a = HaskelineT ReplS a

data ReplContext = ReplContext
{ _replContextBuiltins :: BuiltinsState,
_replContextExpContext :: ExpressionContext,
_replContextEntryPoint :: EntryPoint
}

data ReplState = ReplState
{ _replStateRoot :: FilePath,
{ _replStateReplRoot :: FilePath,
_replStateContext :: Maybe ReplContext,
_replStateGlobalOptions :: GlobalOptions,
_replStateMkEntryPoint :: FilePath -> EntryPoint
_replStateMkEntryPoint :: FilePath -> Repl EntryPoint
}

makeLenses ''ReplState
makeLenses ''ReplContext

type ReplS = State.StateT ReplState IO

type Repl a = HaskelineT ReplS a

helpTxt :: MonadIO m => m ()
helpTxt =
liftIO
Expand Down Expand Up @@ -99,35 +101,39 @@ runCommand opts = do
)
)
)
let epPath :: FilePath = ep ^. entryPointModulePaths . _head1
liftIO (putStrLn [i|OK loaded: #{epPath}|])

reloadFile :: String -> Repl ()
reloadFile _ = do
mentryPoint <- State.gets (fmap (^. replContextEntryPoint) . (^. replStateContext))
case mentryPoint of
Just entryPoint -> do
loadEntryPoint entryPoint
let epPath :: FilePath = entryPoint ^. entryPointModulePaths . _head1
liftIO (putStrLn [i|OK reloaded: #{epPath}|])
Nothing -> noFileLoadedMsg

loadFile :: String -> Repl ()
loadFile args = do
mkEntryPoint <- State.gets (^. replStateMkEntryPoint)
let f = unpack (strip (pack args))
entryPoint = mkEntryPoint f
entryPoint <- mkEntryPoint f
loadEntryPoint entryPoint
liftIO (putStrLn [i|OK loaded: #{f}|])

loadPrelude :: Repl ()
loadPrelude = do
mStdlibPath <- State.gets (^. replStateGlobalOptions . globalStdlibPath)
r <- State.gets (^. replStateRoot)
let stdlibDir = fromMaybe (defaultStdlibPath r) mStdlibPath
loadFile (stdlibDir </> "Stdlib" </> "Prelude.juvix")
case mStdlibPath of
Nothing -> loadDefaultPrelude
Just stdlibDir' -> do
absStdlibDir <- liftIO (makeAbsolute stdlibDir')
loadFile (absStdlibDir </> preludePath)

loadDefaultPrelude :: Repl ()
loadDefaultPrelude = defaultPreludeEntryPoint >>= loadEntryPoint

printRoot :: String -> Repl ()
printRoot _ = do
r <- State.gets (^. replStateRoot)
r <- State.gets (^. replStateReplRoot)
liftIO $ putStrLn (pack r)

displayVersion :: String -> Repl ()
Expand All @@ -136,15 +142,14 @@ runCommand opts = do
command :: String -> Repl ()
command input = Repline.dontCrash $ do
ctx <- State.gets (^. replStateContext)
gopts <- State.gets (^. replStateGlobalOptions)
case ctx of
Just ctx' -> do
evalRes <- compileThenEval ctx' input
case evalRes of
Left err -> printError gopts err
Left err -> printError err
Right n
| Info.member Info.kNoDisplayInfo (Core.getInfo n) -> return ()
Right n -> renderOut gopts (Core.ppOut (ctx' ^. replContextEntryPoint . entryPointGenericOptions) n)
Right n -> renderOut (Core.ppOut (ctx' ^. replContextEntryPoint . entryPointGenericOptions) n)
Nothing -> noFileLoadedMsg
where
defaultLoc :: Interval
Expand Down Expand Up @@ -174,8 +179,8 @@ runCommand opts = do
Just ctx' -> do
compileRes <- liftIO (compileExpressionIO' ctx' (strip (pack input)))
case compileRes of
Left err -> printError gopts err
Right n -> renderOut gopts (Core.ppOut (project' @GenericOptions gopts) n)
Left err -> printError err
Right n -> renderOut (Core.ppOut (project' @GenericOptions gopts) n)
Nothing -> noFileLoadedMsg

inferType :: String -> Repl ()
Expand All @@ -186,8 +191,8 @@ runCommand opts = do
Just ctx' -> do
compileRes <- liftIO (inferExpressionIO' ctx' (strip (pack input)))
case compileRes of
Left err -> printError gopts err
Right n -> renderOut gopts (Internal.ppOut (project' @GenericOptions gopts) n)
Left err -> printError err
Right n -> renderOut (Internal.ppOut (project' @GenericOptions gopts) n)
Nothing -> noFileLoadedMsg

options :: [(String, String -> Repl ())]
Expand Down Expand Up @@ -252,43 +257,67 @@ runCommand opts = do
( State.evalStateT
replAction
( ReplState
{ _replStateRoot = root,
{ _replStateReplRoot = root,
_replStateContext = Nothing,
_replStateGlobalOptions = globalOptions,
_replStateMkEntryPoint = getReplEntryPoint globalOptions root
_replStateMkEntryPoint = getReplEntryPoint
}
)
)

getReplEntryPoint :: GlobalOptions -> FilePath -> FilePath -> EntryPoint
getReplEntryPoint opts root inputFile =
EntryPoint
{ _entryPointRoot = root,
_entryPointNoTermination = opts ^. globalNoTermination,
_entryPointNoPositivity = opts ^. globalNoPositivity,
_entryPointNoStdlib = opts ^. globalNoStdlib,
_entryPointStdlibPath = opts ^. globalStdlibPath,
_entryPointPackage = emptyPackage,
_entryPointModulePaths = pure inputFile,
_entryPointGenericOptions = project opts,
_entryPointStdin = Nothing
}
defaultPreludeEntryPoint :: Repl EntryPoint
defaultPreludeEntryPoint = do
opts <- State.gets (^. replStateGlobalOptions)
root <- State.gets (^. replStateReplRoot)
return $
EntryPoint
{ _entryPointRoot = root,
_entryPointNoTermination = opts ^. globalNoTermination,
_entryPointNoPositivity = opts ^. globalNoPositivity,
_entryPointNoStdlib = opts ^. globalNoStdlib,
_entryPointStdlibPath = opts ^. globalStdlibPath,
_entryPointPackage = emptyPackage,
_entryPointModulePaths = pure (defaultStdlibPath root </> preludePath),
_entryPointGenericOptions = project opts,
_entryPointStdin = Nothing
}

getReplEntryPoint :: FilePath -> Repl EntryPoint
getReplEntryPoint inputFile = do
opts <- State.gets (^. replStateGlobalOptions)
absInputFile <- liftIO (makeAbsolute inputFile)
absStdlibPath <- liftIO (mapM makeAbsolute (opts ^. globalStdlibPath))
(root, package) <- liftIO (findRoot (Just absInputFile))
return $
EntryPoint
{ _entryPointRoot = root,
_entryPointNoTermination = opts ^. globalNoTermination,
_entryPointNoPositivity = opts ^. globalNoPositivity,
_entryPointNoStdlib = opts ^. globalNoStdlib,
_entryPointStdlibPath = absStdlibPath,
_entryPointPackage = package,
_entryPointModulePaths = pure absInputFile,
_entryPointGenericOptions = project opts,
_entryPointStdin = Nothing
}

inferExpressionIO' :: ReplContext -> Text -> IO (Either JuvixError Internal.Expression)
inferExpressionIO' ctx = inferExpressionIO "" (ctx ^. replContextExpContext) (ctx ^. replContextBuiltins)

compileExpressionIO' :: ReplContext -> Text -> IO (Either JuvixError Core.Node)
compileExpressionIO' ctx = compileExpressionIO "" (ctx ^. replContextExpContext) (ctx ^. replContextBuiltins)

render' :: (MonadIO m, P.HasAnsiBackend a, P.HasTextBackend a) => GlobalOptions -> a -> m ()
render' g t = liftIO $ do
hasAnsi <- Ansi.hSupportsANSIColor stdout
P.renderIO (not (g ^. globalNoColors) && hasAnsi) t
render' :: (P.HasAnsiBackend a, P.HasTextBackend a) => a -> Repl ()
render' t = do
opts <- State.gets (^. replStateGlobalOptions)
hasAnsi <- liftIO (Ansi.hSupportsANSIColor stdout)
liftIO (P.renderIO (not (opts ^. globalNoColors) && hasAnsi) t)

renderOut :: (MonadIO m, P.HasAnsiBackend a, P.HasTextBackend a) => GlobalOptions -> a -> m ()
renderOut g t = render' g t >> liftIO (putStrLn "")
renderOut :: (P.HasAnsiBackend a, P.HasTextBackend a) => a -> Repl ()
renderOut t = render' t >> liftIO (putStrLn "")

printError :: MonadIO m => GlobalOptions -> JuvixError -> m ()
printError opts e = liftIO $ do
hasAnsi <- Ansi.hSupportsANSIColor stderr
printError :: JuvixError -> Repl ()
printError e = do
opts <- State.gets (^. replStateGlobalOptions)
hasAnsi <- liftIO (Ansi.hSupportsANSIColor stderr)
liftIO $ hPutStrLn stderr $ run (runReader (project' @GenericOptions opts) (Error.render (not (opts ^. globalNoColors) && hasAnsi) False e))
41 changes: 4 additions & 37 deletions app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,17 @@ module Main (main) where

import App
import CommonOptions
import Control.Exception qualified as IO
import Data.ByteString qualified as ByteString
import Data.Yaml
import Juvix.Compiler.Pipeline
import Juvix.Extra.Paths qualified as Paths
import Root
import TopCommand
import TopCommand.Options

main :: IO ()
main = do
let p = prefs showHelpOnEmpty
(global, cli) <- customExecParser p descr >>= secondM makeAbsPaths
(root, pkg) <- findRoot cli
(root, pkg) <- findRoot' cli
runM (runAppIO global root pkg (runTopCommand cli))

findRoot :: TopCommand -> IO (FilePath, Package)
findRoot cli = do
let dir :: Maybe FilePath
dir = takeDirectory <$> topCommandInputFile cli
whenJust dir setCurrentDirectory
r <- IO.try go
case r of
Left (err :: IO.SomeException) -> do
putStrLn "Something went wrong when figuring out the root of the project."
putStrLn (pack (IO.displayException err))
exitFailure
Right root -> return root
where
possiblePaths :: FilePath -> [FilePath]
possiblePaths start = takeWhile (/= "/") (aux start)
where
aux f = f : aux (takeDirectory f)

go :: IO (FilePath, Package)
go = do
c <- getCurrentDirectory
l <- findFile (possiblePaths c) Paths.juvixYamlFile
case l of
Nothing -> return (c, emptyPackage)
Just yaml -> do
bs <- ByteString.readFile yaml
let isEmpty = ByteString.null bs
pkg <-
if
| isEmpty -> return emptyPackage
| otherwise -> decodeThrow bs
return (takeDirectory yaml, pkg)
findRoot' :: TopCommand -> IO (FilePath, Package)
findRoot' cli = findRoot (topCommandInputFile cli)
39 changes: 39 additions & 0 deletions app/Root.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Root where

import Control.Exception qualified as IO
import Data.ByteString qualified as ByteString
import Data.Yaml
import Juvix.Compiler.Pipeline
import Juvix.Extra.Paths qualified as Paths
import Juvix.Prelude

findRoot :: Maybe FilePath -> IO (FilePath, Package)
findRoot minputFile = do
whenJust (takeDirectory <$> minputFile) setCurrentDirectory
r <- IO.try go
case r of
Left (err :: IO.SomeException) -> do
putStrLn "Something went wrong when figuring out the root of the project."
putStrLn (pack (IO.displayException err))
exitFailure
Right root -> return root
where
possiblePaths :: FilePath -> [FilePath]
possiblePaths start = takeWhile (/= "/") (aux start)
where
aux f = f : aux (takeDirectory f)

go :: IO (FilePath, Package)
go = do
c <- getCurrentDirectory
l <- findFile (possiblePaths c) Paths.juvixYamlFile
case l of
Nothing -> return (c, emptyPackage)
Just yaml -> do
bs <- ByteString.readFile yaml
let isEmpty = ByteString.null bs
pkg <-
if
| isEmpty -> return emptyPackage
| otherwise -> decodeThrow bs
return (takeDirectory yaml, pkg)
3 changes: 3 additions & 0 deletions src/Juvix/Extra/Paths.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ juvixBuildDir = ".juvix-build"

juvixStdlibDir :: FilePath
juvixStdlibDir = juvixBuildDir </> "stdlib"

preludePath :: FilePath
preludePath = "Stdlib" </> "Prelude.juvix"
Loading