{-# LANGUAGE CPP #-} module Agda.Compiler.MAlonzo.Misc where import Control.Monad.Reader ( ask ) import Control.Monad.State ( modify ) import Control.Monad.Trans ( MonadTrans(lift) ) import Control.Monad.Trans.Except ( ExceptT ) import Control.Monad.Trans.Identity ( IdentityT ) import Control.Monad.Trans.Maybe ( MaybeT ) import Control.Monad.Trans.Reader ( ReaderT(runReaderT) ) import Control.Monad.Trans.State ( StateT(runStateT) ) import Data.Char import Data.Set (Set) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T #if !(MIN_VERSION_base(4,11,0)) import Data.Semigroup #endif import qualified Agda.Utils.Haskell.Syntax as HS import Agda.Compiler.Common import Agda.Syntax.Common import Agda.Syntax.Internal import Agda.TypeChecking.Monad import Agda.Utils.Pretty import Agda.Utils.Impossible -------------------------------------------------- -- Setting up Interface before compile -------------------------------------------------- data HsModuleEnv = HsModuleEnv { mazModuleName :: ModuleName -- ^ The name of the Agda module , mazIsMainModule :: Bool -- ^ Whether this is the compilation root and therefore should have the `main` function. -- This corresponds to the @IsMain@ flag provided to the backend, -- not necessarily whether the GHC module has a `main` function defined. } -- | The options derived from -- 'Agda.Compiler.MAlonzo.Compiler.GHCFlags' and other shared options. data GHCOptions = GHCOptions { optGhcCallGhc :: Bool , optGhcBin :: FilePath -- ^ Use the compiler at PATH instead of "ghc" , optGhcFlags :: [String] , optGhcCompileDir :: FilePath , optGhcStrictData :: Bool -- ^ Make inductive constructors strict? , optGhcStrict :: Bool -- ^ Make functions strict? } -- | A static part of the GHC backend's environment that does not -- change from module to module. data GHCEnv = GHCEnv { ghcEnvOpts :: GHCOptions , ghcEnvBool , ghcEnvTrue , ghcEnvFalse , ghcEnvMaybe , ghcEnvNothing , ghcEnvJust , ghcEnvList , ghcEnvNil , ghcEnvCons , ghcEnvNat , ghcEnvInteger , ghcEnvWord64 , ghcEnvInf , ghcEnvSharp , ghcEnvFlat , ghcEnvInterval , ghcEnvIZero , ghcEnvIOne , ghcEnvIsOne , ghcEnvItIsOne , ghcEnvIsOne1 , ghcEnvIsOne2 , ghcEnvIsOneEmpty , ghcEnvPathP , ghcEnvSub , ghcEnvSubIn , ghcEnvId , ghcEnvConId :: Maybe QName -- Various (possibly) builtin names. , ghcEnvIsTCBuiltin :: QName -> Bool -- ^ Is the given name a @TC@ builtin (except for @TC@ itself)? } -- | Module compilation environment, bundling the overall -- backend session options along with the module's basic -- readable properties. data GHCModuleEnv = GHCModuleEnv { ghcModEnv :: GHCEnv , ghcModHsModuleEnv :: HsModuleEnv } -- | Monads that can produce a 'GHCModuleEnv'. class Monad m => ReadGHCModuleEnv m where askGHCModuleEnv :: m GHCModuleEnv default askGHCModuleEnv :: (MonadTrans t, Monad n, m ~ (t n), ReadGHCModuleEnv n) => m GHCModuleEnv askGHCModuleEnv = lift askGHCModuleEnv askHsModuleEnv :: m HsModuleEnv askHsModuleEnv = ghcModHsModuleEnv <$> askGHCModuleEnv askGHCEnv :: m GHCEnv askGHCEnv = ghcModEnv <$> askGHCModuleEnv instance Monad m => ReadGHCModuleEnv (ReaderT GHCModuleEnv m) where askGHCModuleEnv = ask instance ReadGHCModuleEnv m => ReadGHCModuleEnv (ExceptT e m) instance ReadGHCModuleEnv m => ReadGHCModuleEnv (IdentityT m) instance ReadGHCModuleEnv m => ReadGHCModuleEnv (MaybeT m) instance ReadGHCModuleEnv m => ReadGHCModuleEnv (StateT s m) newtype HsCompileState = HsCompileState { mazAccumlatedImports :: Set ModuleName } deriving (Eq, Semigroup, Monoid) -- | Transformer adding read-only module info and a writable set of imported modules type HsCompileT m = ReaderT GHCModuleEnv (StateT HsCompileState m) -- | The default compilation monad is the entire TCM (☹️) enriched with our state and module info type HsCompileM = HsCompileT TCM runHsCompileT' :: HsCompileT m a -> GHCModuleEnv -> HsCompileState -> m (a, HsCompileState) runHsCompileT' t e s = (flip runStateT s) . (flip runReaderT e) $ t runHsCompileT :: HsCompileT m a -> GHCModuleEnv -> m (a, HsCompileState) runHsCompileT t e = runHsCompileT' t e mempty -------------------------------------------------- -- utilities for haskell names -------------------------------------------------- -- | Whether the current module is expected to have the `main` function. -- This corresponds to the @IsMain@ flag provided to the backend, -- not necessarily whether the GHC module actually has a `main` function defined. curIsMainModule :: ReadGHCModuleEnv m => m Bool curIsMainModule = mazIsMainModule <$> askHsModuleEnv -- | This is the same value as @curMName@, but does not rely on the TCM's state. -- (@curMName@ and co. should be removed, but the current @Backend@ interface -- is not sufficient yet to allow that) curAgdaMod :: ReadGHCModuleEnv m => m ModuleName curAgdaMod = mazModuleName <$> askHsModuleEnv -- | Get the Haskell module name of the currently-focused Agda module curHsMod :: ReadGHCModuleEnv m => m HS.ModuleName curHsMod = mazMod <$> curAgdaMod -- | There are two kinds of functions: those definitely without unused -- arguments, and those that might have unused arguments. data FunctionKind = NoUnused | PossiblyUnused -- | Different kinds of variables: those starting with @a@, those -- starting with @v@, and those starting with @x@. data VariableKind = A | V | X -- | Different kinds of names. data NameKind = TypeK -- ^ Types. | ConK -- ^ Constructors. | VarK VariableKind -- ^ Variables. | CoverK -- ^ Used for coverage checking. | CheckK -- ^ Used for constructor type checking. | FunK FunctionKind -- ^ Other functions. -- | Turns strings into valid Haskell identifiers. -- -- In order to avoid clashes with names of regular Haskell definitions -- (those not generated from Agda definitions), make sure that the -- Haskell names are always used qualified, with the exception of -- names from the prelude. encodeString :: NameKind -> String -> String encodeString k s = prefix ++ concatMap encode s where encode '\'' = "''" encode c | isLower c || isUpper c || c == '_' || generalCategory c == DecimalNumber = [c] | otherwise = "'" ++ show (fromEnum c) ++ "'" prefix = case k of TypeK -> "T" ConK -> "C" VarK A -> "a" VarK V -> "v" VarK X -> "x" CoverK -> "cover" CheckK -> "check" FunK NoUnused -> "du" FunK PossiblyUnused -> "d" ihname :: VariableKind -> Nat -> HS.Name ihname k i = HS.Ident $ encodeString (VarK k) (show i) unqhname :: NameKind -> QName -> HS.Name unqhname k q = HS.Ident $ encodeString k $ "_" ++ prettyShow (nameCanonical n) ++ "_" ++ idnum (nameId n) where n = qnameName q idnum (NameId x _) = show (fromIntegral x) -- the toplevel module containing the given one tlmodOf :: ReadTCState m => ModuleName -> m HS.ModuleName tlmodOf = fmap mazMod . topLevelModuleName -- qualify HS.Name n by the module of QName q, if necessary; -- accumulates the used module in stImportedModules at the same time. xqual :: QName -> HS.Name -> HsCompileM HS.QName xqual q n = do m1 <- topLevelModuleName (qnameModule q) m2 <- curAgdaMod if m1 == m2 then return (HS.UnQual n) else do modify (HsCompileState . Set.insert m1 . mazAccumlatedImports) return (HS.Qual (mazMod m1) n) xhqn :: NameKind -> QName -> HsCompileM HS.QName xhqn k q = xqual q (unqhname k q) hsName :: String -> HS.QName hsName s = HS.UnQual (HS.Ident s) -- always use the original name for a constructor even when it's redefined. conhqn :: QName -> HsCompileM HS.QName conhqn q = xhqn ConK =<< canonicalName q -- qualify name s by the module of builtin b bltQual :: String -> String -> HsCompileM HS.QName bltQual b s = do Def q _ <- getBuiltin b xqual q (HS.Ident s) dname :: QName -> HS.Name dname q = unqhname (FunK PossiblyUnused) q -- | Name for definition stripped of unused arguments duname :: QName -> HS.Name duname q = unqhname (FunK NoUnused) q hsPrimOp :: String -> HS.QOp hsPrimOp s = HS.QVarOp $ HS.UnQual $ HS.Symbol s hsPrimOpApp :: String -> HS.Exp -> HS.Exp -> HS.Exp hsPrimOpApp op e e1 = HS.InfixApp e (hsPrimOp op) e1 hsInt :: Integer -> HS.Exp hsInt n = HS.Lit (HS.Int n) hsTypedInt :: Integral a => a -> HS.Exp hsTypedInt n = HS.ExpTypeSig (HS.Lit (HS.Int $ fromIntegral n)) (HS.TyCon (hsName "Integer")) hsTypedDouble :: Real a => a -> HS.Exp hsTypedDouble n = HS.ExpTypeSig (HS.Lit (HS.Frac $ toRational n)) (HS.TyCon (hsName "Double")) hsLet :: HS.Name -> HS.Exp -> HS.Exp -> HS.Exp hsLet x e b = HS.Let (HS.BDecls [HS.LocalBind HS.Lazy x (HS.UnGuardedRhs e)]) b hsVarUQ :: HS.Name -> HS.Exp hsVarUQ = HS.Var . HS.UnQual hsAppView :: HS.Exp -> [HS.Exp] hsAppView = reverse . view where view (HS.App e e1) = e1 : view e view (HS.InfixApp e1 op e2) = [e2, e1, hsOpToExp op] view e = [e] hsOpToExp :: HS.QOp -> HS.Exp hsOpToExp (HS.QVarOp x) = HS.Var x hsLambda :: [HS.Pat] -> HS.Exp -> HS.Exp hsLambda ps (HS.Lambda ps1 e) = HS.Lambda (ps ++ ps1) e hsLambda ps e = HS.Lambda ps e hsMapAlt :: (HS.Exp -> HS.Exp) -> HS.Alt -> HS.Alt hsMapAlt f (HS.Alt p rhs wh) = HS.Alt p (hsMapRHS f rhs) wh hsMapRHS :: (HS.Exp -> HS.Exp) -> HS.Rhs -> HS.Rhs hsMapRHS f (HS.UnGuardedRhs def) = HS.UnGuardedRhs (f def) hsMapRHS f (HS.GuardedRhss es) = HS.GuardedRhss [ HS.GuardedRhs g (f e) | HS.GuardedRhs g e <- es ] -------------------------------------------------- -- Hard coded module names -------------------------------------------------- mazstr :: String mazstr = "MAlonzo.Code" mazName :: Name mazName = mkName_ __IMPOSSIBLE__ mazstr mazMod' :: String -> HS.ModuleName mazMod' s = HS.ModuleName $ mazstr ++ "." ++ s mazMod :: ModuleName -> HS.ModuleName mazMod = mazMod' . prettyShow mazCoerceName :: String mazCoerceName = "coe" mazErasedName :: String mazErasedName = "erased" mazAnyTypeName :: String mazAnyTypeName = "AgdaAny" mazCoerce :: HS.Exp -- mazCoerce = HS.Var $ HS.Qual unsafeCoerceMod (HS.Ident "unsafeCoerce") -- mazCoerce = HS.Var $ HS.Qual mazRTE $ HS.Ident mazCoerceName mazCoerce = HS.Var $ HS.UnQual $ HS.Ident mazCoerceName mazUnreachableError :: HS.Exp mazUnreachableError = HS.Var $ HS.Qual mazRTE $ HS.Ident "mazUnreachableError" rtmUnreachableError :: HS.Exp rtmUnreachableError = mazUnreachableError mazHole :: HS.Exp mazHole = HS.Var $ HS.Qual mazRTE $ HS.Ident "mazHole" rtmHole :: String -> HS.Exp rtmHole s = mazHole `HS.App` HS.Lit (HS.String $ T.pack s) mazAnyType :: HS.Type mazAnyType = HS.TyCon (hsName mazAnyTypeName) mazRTE :: HS.ModuleName mazRTE = HS.ModuleName "MAlonzo.RTE" mazRTEFloat :: HS.ModuleName mazRTEFloat = HS.ModuleName "MAlonzo.RTE.Float" rtmQual :: String -> HS.QName rtmQual = HS.UnQual . HS.Ident rtmVar :: String -> HS.Exp rtmVar = HS.Var . rtmQual rtmError :: Text -> HS.Exp rtmError s = rtmVar "error" `HS.App` HS.Lit (HS.String $ T.append "MAlonzo Runtime Error: " s) unsafeCoerceMod :: HS.ModuleName unsafeCoerceMod = HS.ModuleName "Unsafe.Coerce" -------------------------------------------------- -- Sloppy ways to declare = -------------------------------------------------- fakeD :: HS.Name -> String -> HS.Decl fakeD v s = HS.FunBind [HS.Match v [] (HS.UnGuardedRhs $ fakeExp s) emptyBinds] fakeDS :: String -> String -> HS.Decl fakeDS = fakeD . HS.Ident fakeDQ :: QName -> String -> HS.Decl fakeDQ = fakeD . dname fakeType :: String -> HS.Type fakeType = HS.FakeType fakeExp :: String -> HS.Exp fakeExp = HS.FakeExp fakeDecl :: String -> HS.Decl fakeDecl = HS.FakeDecl -------------------------------------------------- -- Auxiliary definitions -------------------------------------------------- emptyBinds :: Maybe HS.Binds emptyBinds = Nothing -------------------------------------------------- -- Utilities for Haskell modules names -------------------------------------------------- -- | Can the character be used in a Haskell module name part -- (@conid@)? This function is more restrictive than what the Haskell -- report allows. isModChar :: Char -> Bool isModChar c = isLower c || isUpper c || isDigit c || c == '_' || c == '\''