1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-04 06:23:13 +03:00
juvix/app/App.hs

285 lines
9.7 KiB
Haskell

module App where
import CommonOptions
import Data.ByteString qualified as ByteString
import GlobalOptions
import Juvix.Compiler.Internal.Translation (InternalTypedResult)
import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.Termination.Checker
import Juvix.Compiler.Pipeline.Loader.PathResolver
import Juvix.Compiler.Pipeline.Root
import Juvix.Compiler.Pipeline.Run
import Juvix.Data.Error qualified as Error
import Juvix.Extra.Paths.Base hiding (rootBuildDir)
import Juvix.Parser.Error
import Juvix.Prelude.Pretty hiding
( Doc,
)
import System.Console.ANSI qualified as Ansi
data App :: Effect where
ExitMsg :: ExitCode -> Text -> App m a
ExitFailMsg :: Text -> App m a
ExitJuvixError :: JuvixError -> App m a
PrintJuvixError :: JuvixError -> App m ()
FromAppFile :: AppPath File -> App m (Path Abs File)
AskRoot :: App m Root
AskArgs :: App m RunAppIOArgs
AskInvokeDir :: App m (Path Abs Dir)
AskPkgDir :: App m (Path Abs Dir)
AskBuildDir :: App m (Path Abs Dir)
AskPackage :: App m Package
AskPackageGlobal :: App m Bool
AskGlobalOptions :: App m GlobalOptions
FromAppPathFile :: AppPath File -> App m (Path Abs File)
GetMainAppFile :: Maybe (AppPath File) -> App m (AppPath File)
GetMainFile :: Maybe (AppPath File) -> App m (Path Abs File)
FromAppPathDir :: AppPath Dir -> App m (Path Abs Dir)
RenderStdOut :: (HasAnsiBackend a, HasTextBackend a) => a -> App m ()
Say :: Text -> App m ()
SayRaw :: ByteString -> App m ()
data RunAppIOArgs = RunAppIOArgs
{ _runAppIOArgsGlobalOptions :: GlobalOptions,
_runAppIOArgsRoot :: Root
}
makeSem ''App
makeLenses ''RunAppIOArgs
runAppIO ::
forall r a.
(Members '[EmbedIO, TaggedLock] r) =>
RunAppIOArgs ->
Sem (App ': r) a ->
Sem r a
runAppIO args = evalSingletonCache (readPackageRootIO root) . reAppIO args
where
root = args ^. runAppIOArgsRoot
reAppIO ::
forall r a.
(Members '[EmbedIO, TaggedLock] r) =>
RunAppIOArgs ->
Sem (App ': r) a ->
Sem (SCache Package ': r) a
reAppIO args@RunAppIOArgs {..} =
interpretTop $ \case
AskPackageGlobal -> return (_runAppIOArgsRoot ^. rootPackageType `elem` [GlobalStdlib, GlobalPackageDescription, GlobalPackageBase])
FromAppPathFile p -> prepathToAbsFile invDir (p ^. pathPath)
FromAppFile m -> fromAppFile' m
GetMainAppFile m -> getMainAppFile' m
GetMainFile m -> getMainFile' m
FromAppPathDir p -> liftIO (prepathToAbsDir invDir (p ^. pathPath))
RenderStdOut t
| _runAppIOArgsGlobalOptions ^. globalOnlyErrors -> return ()
| otherwise -> do
sup <- liftIO (Ansi.hSupportsANSIColor stdout)
renderIO (not (_runAppIOArgsGlobalOptions ^. globalNoColors) && sup) t
AskGlobalOptions -> return _runAppIOArgsGlobalOptions
AskPackage -> getPkg
AskArgs -> return args
AskRoot -> return _runAppIOArgsRoot
AskInvokeDir -> return invDir
AskPkgDir -> return (_runAppIOArgsRoot ^. rootRootDir)
AskBuildDir -> return (resolveAbsBuildDir (_runAppIOArgsRoot ^. rootRootDir) (_runAppIOArgsRoot ^. rootBuildDir))
Say t
| g ^. globalOnlyErrors -> return ()
| otherwise -> putStrLn t
PrintJuvixError e -> printErr e
ExitJuvixError e -> do
printErr e
exitFailure
ExitMsg exitCode t -> exitMsg' (exitWith exitCode) t
ExitFailMsg t -> exitMsg' exitFailure t
SayRaw b -> liftIO (ByteString.putStr b)
where
getPkg :: (Members '[SCache Package] r') => Sem r' Package
getPkg = cacheSingletonGet
exitMsg' :: (Members '[EmbedIO] r') => IO x -> Text -> Sem r' x
exitMsg' onExit t = liftIO (putStrLn t >> hFlush stdout >> onExit)
fromAppFile' :: (Members '[EmbedIO] r') => AppPath File -> Sem r' (Path Abs File)
fromAppFile' f = prepathToAbsFile invDir (f ^. pathPath)
getMainFile' :: (Members '[SCache Package, EmbedIO] r') => Maybe (AppPath File) -> Sem r' (Path Abs File)
getMainFile' = getMainAppFile' >=> fromAppFile'
getMainAppFile' :: (Members '[SCache Package, EmbedIO] r') => Maybe (AppPath File) -> Sem r' (AppPath File)
getMainAppFile' = \case
Just p -> return p
Nothing -> do
pkg <- getPkg
case pkg ^. packageMain of
Just p ->
return
AppPath
{ _pathPath = p,
_pathIsInput = True
}
Nothing -> missingMainErr
missingMainErr :: (Members '[EmbedIO] r') => Sem r' x
missingMainErr =
exitMsg'
exitFailure
( "A path to the main file must be given in the CLI or specified in the `main` field of the "
<> pack (toFilePath juvixYamlFile)
<> " file"
)
invDir = _runAppIOArgsRoot ^. rootInvokeDir
g :: GlobalOptions
g = _runAppIOArgsGlobalOptions
printErr e =
hPutStrLn stderr
. run
. runReader (project' @GenericOptions g)
$ Error.render (not (_runAppIOArgsGlobalOptions ^. globalNoColors)) (g ^. globalOnlyErrors) e
getEntryPoint' ::
(Members '[App, EmbedIO, TaggedLock] r) =>
RunAppIOArgs ->
Maybe (AppPath File) ->
Sem r EntryPoint
getEntryPoint' RunAppIOArgs {..} inputFile = do
let opts = _runAppIOArgsGlobalOptions
root = _runAppIOArgsRoot
estdin <-
if
| opts ^. globalStdin -> Just <$> liftIO getContents
| otherwise -> return Nothing
mainFile <- getMainAppFile inputFile
set entryPointStdin estdin <$> entryPointFromGlobalOptionsPre root (mainFile ^. pathPath) opts
runPipelineEither ::
(Members '[EmbedIO, TaggedLock, App] r, EntryPointOptions opts) =>
opts ->
Maybe (AppPath File) ->
Sem (PipelineEff r) a ->
Sem r (Either JuvixError (ResolverState, PipelineResult a))
runPipelineEither opts input_ p = do
args <- askArgs
entry <- applyOptions opts <$> getEntryPoint' args input_
runIOEither entry p
runPipelineSetupEither ::
(Members '[EmbedIO, TaggedLock, App] r) =>
Sem (PipelineEff' r) a ->
Sem r (Either JuvixError (ResolverState, a))
runPipelineSetupEither p = do
args <- askArgs
entry <- getEntryPointStdin' args
runIOEitherPipeline entry p
getEntryPointStdin' :: (Members '[EmbedIO, TaggedLock] r) => RunAppIOArgs -> Sem r EntryPoint
getEntryPointStdin' RunAppIOArgs {..} = do
let opts = _runAppIOArgsGlobalOptions
root = _runAppIOArgsRoot
estdin <-
if
| opts ^. globalStdin -> Just <$> liftIO getContents
| otherwise -> return Nothing
set entryPointStdin estdin <$> entryPointFromGlobalOptionsNoFile root opts
fromRightGenericError :: (Members '[App] r, ToGenericError err, Typeable err) => Either err a -> Sem r a
fromRightGenericError = fromRightJuvixError . mapLeft JuvixError
fromRightJuvixError :: (Members '[App] r) => Either JuvixError a -> Sem r a
fromRightJuvixError = getRight
someBaseToAbs' :: (Members '[App] r) => SomeBase a -> Sem r (Path Abs a)
someBaseToAbs' f = do
r <- askInvokeDir
return (someBaseToAbs r f)
filePathToAbs :: (Members '[EmbedIO, App] r) => Prepath FileOrDir -> Sem r (Either (Path Abs File) (Path Abs Dir))
filePathToAbs fp = do
invokeDir <- askInvokeDir
fromPreFileOrDir invokeDir fp
askGenericOptions :: (Members '[App] r) => Sem r GenericOptions
askGenericOptions = project <$> askGlobalOptions
getEntryPoint :: (Members '[EmbedIO, App, TaggedLock] r) => Maybe (AppPath File) -> Sem r EntryPoint
getEntryPoint inputFile = do
_runAppIOArgsGlobalOptions <- askGlobalOptions
_runAppIOArgsRoot <- askRoot
getEntryPoint' RunAppIOArgs {..} inputFile
getEntryPointStdin :: (Members '[EmbedIO, App, TaggedLock] r) => Sem r EntryPoint
getEntryPointStdin = do
_runAppIOArgsGlobalOptions <- askGlobalOptions
_runAppIOArgsRoot <- askRoot
getEntryPointStdin' RunAppIOArgs {..}
runPipelineTermination ::
(Members '[EmbedIO, App, TaggedLock] r) =>
Maybe (AppPath File) ->
Sem (Termination ': PipelineEff r) a ->
Sem r (PipelineResult a)
runPipelineTermination input_ p = do
r <- runPipelineEither () input_ (evalTermination iniTerminationState p) >>= fromRightJuvixError
return (snd r)
runPipelineNoOptions ::
(Members '[App, EmbedIO, TaggedLock] r) =>
Maybe (AppPath File) ->
Sem (PipelineEff r) a ->
Sem r a
runPipelineNoOptions = runPipeline ()
runPipeline ::
(Members '[App, EmbedIO, TaggedLock] r, EntryPointOptions opts) =>
opts ->
Maybe (AppPath File) ->
Sem (PipelineEff r) a ->
Sem r a
runPipeline opts input_ p = do
r <- runPipelineEither opts input_ p >>= fromRightJuvixError
return (snd r ^. pipelineResult)
runPipelineHtml ::
(Members '[App, EmbedIO, TaggedLock] r) =>
Bool ->
Maybe (AppPath File) ->
Sem r (InternalTypedResult, [InternalTypedResult])
runPipelineHtml bNonRecursive input_
| bNonRecursive = do
r <- runPipelineNoOptions input_ upToInternalTyped
return (r, [])
| otherwise = do
args <- askArgs
entry <- getEntryPoint' args input_
runPipelineHtmlEither entry >>= fromRightJuvixError
runPipelineEntry :: (Members '[App, EmbedIO, TaggedLock] r) => EntryPoint -> Sem (PipelineEff r) a -> Sem r a
runPipelineEntry entry p = do
r <- runIOEither entry p >>= fromRightJuvixError
return (snd r ^. pipelineResult)
runPipelineSetup :: (Members '[App, EmbedIO, TaggedLock] r) => Sem (PipelineEff' r) a -> Sem r a
runPipelineSetup p = do
r <- runPipelineSetupEither p >>= fromRightJuvixError
return (snd r)
newline :: (Member App r) => Sem r ()
newline = say ""
printSuccessExit :: (Member App r) => Text -> Sem r a
printSuccessExit = exitMsg ExitSuccess
getRight :: forall e a r. (Members '[App] r, AppError e) => Either e a -> Sem r a
getRight = either appError return
instance AppError MegaparsecError where
appError = appError . JuvixError
instance AppError Text where
appError = exitFailMsg
instance AppError JuvixError where
appError = exitJuvixError
class AppError e where
appError :: (Members '[App] r) => e -> Sem r a