{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Axel.Entry where
import Axel.AST (ToHaskell(toHaskell))
import Axel.Error (Error)
import Axel.GHC (runWithGHC)
import Axel.Macros (exhaustivelyExpandMacros, stripMacroDefinitions)
import Axel.Normalize (normalizeStatement)
import Axel.Parse (Expression(Symbol), parseSource)
import Axel.Utils.Directory (withTempDirectory)
import Axel.Utils.Recursion (Recursive(bottomUpFmap))
import Axel.Utils.Resources (readResource)
import qualified Axel.Utils.Resources as Res (astDefinition)
import Control.Lens.Operators ((.~))
import Control.Monad.Except (MonadError, runExceptT, throwError)
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.Control (MonadBaseControl)
import Data.Maybe (fromMaybe)
import Data.Semigroup ((<>))
import qualified Data.Text as T (isSuffixOf, pack)
import System.FilePath ((</>), stripExtension)
import System.FilePath.Lens (directory)
import qualified System.IO.Strict as S (readFile)
convertList :: Expression -> Expression
convertList =
bottomUpFmap $ \case
Symbol "List" -> Symbol "[]"
x -> x
convertUnit :: Expression -> Expression
convertUnit =
bottomUpFmap $ \case
Symbol "Unit" -> Symbol "()"
Symbol "unit" -> Symbol "()"
x -> x
transpileSource ::
(MonadBaseControl IO m, MonadError Error m, MonadIO m)
=> String
-> m String
transpileSource source =
toHaskell . stripMacroDefinitions <$>
(parseSource source >>= exhaustivelyExpandMacros . convertList . convertUnit >>=
normalizeStatement)
axelPathToHaskellPath :: FilePath -> FilePath
axelPathToHaskellPath axelPath =
let basePath =
if ".axel" `T.isSuffixOf` T.pack axelPath
then fromMaybe axelPath $ stripExtension ".axel" axelPath
else axelPath
in basePath <> ".hs"
transpileFile :: FilePath -> FilePath -> IO ()
transpileFile path newPath = do
fileContents <- S.readFile path
result <- runExceptT $ transpileSource fileContents
case result of
Left err -> throwError $ userError $ show err
Right newContents -> writeFile newPath newContents
transpileFile' :: FilePath -> IO FilePath
transpileFile' path = do
let newPath = axelPathToHaskellPath path
transpileFile path newPath
pure newPath
evalFile :: FilePath -> IO ()
evalFile path =
withTempDirectory $ \tempDirectoryPath -> do
let astDefinitionPath = tempDirectoryPath </> "Axel.hs"
readResource Res.astDefinition >>= writeFile astDefinitionPath
let newPath = directory .~ tempDirectoryPath $ axelPathToHaskellPath path
transpileFile path newPath
evalResult <- runExceptT $ runWithGHC newPath
either (throwError . userError . show) putStr evalResult