{-# LANGUAGE CPP
, DataKinds
, OverloadedStrings
, FlexibleContexts
#-}
module Language.Hakaru.Command where
import Language.Hakaru.Syntax.ABT
import qualified Language.Hakaru.Syntax.AST as T
import Language.Hakaru.Parser.Import (expandImports)
import Language.Hakaru.Parser.Parser (parseHakaru, parseHakaruWithImports)
import Language.Hakaru.Parser.SymbolResolve (resolveAST)
import Language.Hakaru.Syntax.TypeCheck
import Control.Monad.Trans.Except
import Control.Monad (when)
import qualified Data.Text as Text
import qualified Data.Text.IO as IO
import qualified Data.Text.Utf8 as U
import qualified Options.Applicative as O
import Data.Vector
import System.IO (stderr)
import System.Environment (getArgs)
import Data.Monoid ((<>),mconcat)
import System.FilePath (takeDirectory)
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative (Applicative(..), (<$>))
#endif
type Term a = TrivialABT T.Term '[] a
parseAndInfer :: Text.Text
-> Either Text.Text (TypedAST (TrivialABT T.Term))
parseAndInfer x = parseAndInferWithMode x LaxMode
parseAndInferWithMode
:: ABT T.Term abt
=> Text.Text
-> TypeCheckMode
-> Either Text.Text (TypedAST abt)
parseAndInferWithMode x mode =
case parseHakaru x of
Left err -> Left (Text.pack . show $ err)
Right past ->
let m = inferType (resolveAST past) in
runTCM m (splitLines x) mode
data Source = Source { file :: Maybe FilePath, source :: Text.Text }
sourceInput :: Source -> Maybe (Vector Text.Text)
sourceInput = splitLines . source
noFileSource :: Text.Text -> Source
noFileSource = Source Nothing
fileSource :: FilePath -> Text.Text -> Source
fileSource = Source . Just
parseAndInfer' :: Source
-> IO (Either Text.Text (TypedAST (TrivialABT T.Term)))
parseAndInfer' s = parseAndInferWithMode' s LaxMode
parseAndInferWithMode'
:: ABT T.Term abt
=> Source
-> TypeCheckMode
-> IO (Either Text.Text (TypedAST abt))
parseAndInferWithMode' (Source dir x) mode =
case parseHakaruWithImports x of
Left err -> return . Left $ Text.pack . show $ err
Right past -> do
past' <- runExceptT (expandImports (fmap takeDirectory dir) past)
case past' of
Left err -> return . Left $ Text.pack . show $ err
Right past'' -> do
let m = inferType (resolveAST past'')
return (runTCM m (splitLines x) mode)
parseAndInferWithDebug
:: Bool
-> Text.Text
-> IO (Either Text.Text (TypedAST (TrivialABT T.Term)))
parseAndInferWithDebug debug x =
case parseHakaru x of
Left err -> return $ Left (Text.pack . show $ err)
Right past -> do
when debug $ putErrorLn $ hrule "Parsed AST"
when debug $ putErrorLn . Text.pack . show $ past
let resolved = resolveAST past
let inferred = runTCM (inferType resolved) (splitLines x) LaxMode
when debug $ putErrorLn $ hrule "Inferred AST"
when debug $ putErrorLn . Text.pack . show $ inferred
return $ inferred
where hrule s = Text.concat ["\n<=======================| "
,s," |=======================>\n"]
putErrorLn = IO.hPutStrLn stderr
splitLines :: Text.Text -> Maybe (Vector Text.Text)
splitLines = Just . fromList . Text.lines
readFromFile :: String -> IO Text.Text
readFromFile "-" = U.getContents
readFromFile x = U.readFile x
readFromFile' :: String -> IO Source
readFromFile' x = Source (if x=="-" then Nothing else Just x) <$> readFromFile x
simpleCommand :: (Text.Text -> IO ()) -> Text.Text -> IO ()
simpleCommand k fnName =
let parser =
O.info (O.helper <*> opts)
(O.fullDesc <> O.progDesc
(mconcat["Hakaru:", Text.unpack fnName, " command"]))
opts =
O.strArgument
( O.metavar "PROGRAM" <>
O.showDefault <> O.value "-" <>
O.help "Filepath to Hakaru program OR \"-\"" )
in O.execParser parser >>= readFromFile >>= k
writeToFile :: String -> (Text.Text -> IO ())
writeToFile "-" = U.putStrLn
writeToFile x = U.writeFile x