futhark-0.24.2: src/Futhark/CLI/Eval.hs
module Futhark.CLI.Eval (main) where
import Control.Exception
import Control.Monad
import Control.Monad.Except (ExceptT, runExceptT, throwError)
import Control.Monad.Free.Church
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Map qualified as M
import Data.Maybe
import Data.Text qualified as T
import Data.Text.IO qualified as T
import Futhark.Compiler
import Futhark.MonadFreshNames
import Futhark.Pipeline
import Futhark.Util.Options
import Futhark.Util.Pretty
import Language.Futhark.Interpreter qualified as I
import Language.Futhark.Parser
import Language.Futhark.Semantic qualified as T
import Language.Futhark.TypeChecker qualified as I
import Language.Futhark.TypeChecker qualified as T
import System.Exit
import System.FilePath
import System.IO
import Prelude
main :: String -> [String] -> IO ()
main = mainWithOptions interpreterConfig options "options... <exprs...>" run
where
run [] _ = Nothing
run exprs config = Just $ runExprs exprs config
runExprs :: [String] -> InterpreterConfig -> IO ()
runExprs exprs cfg = do
let InterpreterConfig _ file = cfg
maybe_new_state <- newFutharkiState cfg file
(src, env, ctx) <- case maybe_new_state of
Left _ -> do
hPutStrLn stderr $ fromJust file <> ": file not found."
exitWith $ ExitFailure 2
Right s -> pure s
mapM_ (runExpr src env ctx) exprs
-- Use parseExp, checkExp, then interpretExp.
runExpr :: VNameSource -> T.Env -> I.Ctx -> String -> IO ()
runExpr src env ctx str = do
uexp <- case parseExp "" (T.pack str) of
Left (SyntaxError _ serr) -> do
T.hPutStrLn stderr serr
exitWith $ ExitFailure 1
Right e -> pure e
fexp <- case T.checkExp [] src env uexp of
(_, Left terr) -> do
hPutDoc stderr $ I.prettyTypeError terr
exitWith $ ExitFailure 1
(_, Right (_, e)) -> pure e
pval <- runInterpreterNoBreak $ I.interpretExp ctx fexp
case pval of
Left err -> do
hPutDoc stderr $ I.prettyInterpreterError err
exitWith $ ExitFailure 1
Right val -> putDoc $ I.prettyValue val <> hardline
data InterpreterConfig = InterpreterConfig
{ interpreterPrintWarnings :: Bool,
interpreterFile :: Maybe String
}
interpreterConfig :: InterpreterConfig
interpreterConfig = InterpreterConfig True Nothing
options :: [FunOptDescr InterpreterConfig]
options =
[ Option
"f"
["file"]
( ReqArg
( \entry -> Right $ \config ->
config {interpreterFile = Just entry}
)
"NAME"
)
"The file to load before evaluating expressions.",
Option
"w"
["no-warnings"]
(NoArg $ Right $ \config -> config {interpreterPrintWarnings = False})
"Do not print warnings."
]
newFutharkiState ::
InterpreterConfig ->
Maybe FilePath ->
IO (Either (Doc AnsiStyle) (VNameSource, T.Env, I.Ctx))
newFutharkiState cfg maybe_file = runExceptT $ do
(ws, imports, src) <-
badOnLeft prettyCompilerError
=<< liftIO
( runExceptT (readProgramFiles [] $ maybeToList maybe_file)
`catch` \(err :: IOException) ->
pure (externalErrorS (show err))
)
when (interpreterPrintWarnings cfg) $
liftIO $
hPutDoc stderr $
prettyWarnings ws
ictx <-
foldM (\ctx -> badOnLeft I.prettyInterpreterError <=< runInterpreterNoBreak . I.interpretImport ctx) I.initialCtx $
map (fmap fileProg) imports
let (tenv, ienv) =
let (iname, fm) = last imports
in ( fileScope fm,
ictx {I.ctxEnv = I.ctxImports ictx M.! iname}
)
pure (src, tenv, ienv)
where
badOnLeft :: (err -> err') -> Either err a -> ExceptT err' IO a
badOnLeft _ (Right x) = pure x
badOnLeft p (Left err) = throwError $ p err
runInterpreterNoBreak :: MonadIO m => F I.ExtOp a -> m (Either I.InterpreterError a)
runInterpreterNoBreak m = runF m (pure . Right) intOp
where
intOp (I.ExtOpError err) = pure $ Left err
intOp (I.ExtOpTrace w v c) = do
liftIO $ putDocLn $ pretty w <> ":" <+> align (unAnnotate v)
c
intOp (I.ExtOpBreak _ _ _ c) = c