{-# 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 -- The filepath from which the text came and the text itself. If the filepath is -- Nothing, imports are searched for in the current directory. 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