agda2hs-1.3: src/Agda2Hs/Compile/Types.hs
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies #-}
module Agda2Hs.Compile.Types where
import Control.Applicative ( liftA2 )
import Control.Monad ( join )
import Control.Monad.Trans ( MonadTrans(lift) )
import Control.Monad.Trans.RWS.CPS (RWST(..), runRWST, rwsT )
import Control.Monad.Trans.Control (MonadTransControl(..))
import Control.Monad.State ( StateT(..) )
import Control.DeepSeq ( NFData(..) )
import Data.Maybe ( isJust )
import Data.Set ( Set )
import Data.Map ( Map )
import Data.String ( IsString(..) )
import qualified Language.Haskell.Exts.SrcLoc as Hs
import qualified Language.Haskell.Exts.Syntax as Hs
import qualified Language.Haskell.Exts.Extension as Hs
import qualified Language.Haskell.Exts.Comments as Hs
import Agda.Compiler.Backend
import Agda.Syntax.Position ( Range )
import Agda.Syntax.TopLevelModuleName ( TopLevelModuleName )
import Agda.Utils.Null
import Agda.Utils.Impossible
import Agda2Hs.HsUtils ( Strictness )
type ModuleEnv = TopLevelModuleName
type ModuleRes = ()
type CompiledDef = [Ranged [Hs.Decl ()]]
type Ranged a = (Range, a)
type Code = (Hs.Module Hs.SrcSpanInfo, [Hs.Comment])
-- | Custom substitution for a given definition.
data Rewrite = Rewrite
{ rewFrom :: String
-- ^ The fully qualified name.
, rewTo :: String
-- ^ The corresponding Haskell name.
, rewImport :: Maybe String
-- ^ Haskell module to import.
}
type Rewrites = [Rewrite]
-- | A lookup table for rewrite rules.
type SpecialRules = Map String (Hs.Name (), Maybe Import)
data PreludeOptions = PreludeOpts
{ preludeImplicit :: Bool
, preludeImports :: Maybe [String]
, preludeHiding :: [String]
}
-- ^ whether Prelude functions should be implicitly imported; if yes, then NamesToImport is a "hiding" list
data Options = Options
{ optIsEnabled :: Bool
, optOutDir :: Maybe FilePath
, optConfigFile :: Maybe FilePath
, optExtensions :: [Hs.Extension]
, optRewrites :: SpecialRules
, optPrelude :: PreludeOptions
}
-- Required by Agda-2.6.2, but we don't really care.
instance NFData Options where
rnf _ = ()
-- | Names of local declarations (in @where@ blocks).
type LocalDecls = [QName]
data CompileEnv = CompileEnv
{ currModule :: TopLevelModuleName
-- ^ the current module we are compiling
, minRecordName :: Maybe ModuleName
-- ^ keeps track of the current minimal record we are compiling
, isNestedInType :: Bool
-- ^ if we're inside an argument of a type and need an explicit forall
, locals :: LocalDecls
-- ^ keeps track of the current clause's where declarations
, compilingLocal :: Bool
-- ^ whether we are currently compiling a where clause or pattern-matching lambda
, whereModules :: [ModuleName]
-- ^ the where-blocks currently in scope. Hack until Agda adds where-prominence
, copatternsEnabled :: Bool
-- ^ whether copatterns should be allowed when compiling patterns
, rewrites :: SpecialRules
-- ^ Special compilation rules.
, writeImports :: Bool
-- ^ whether we should add imports of compiled names
}
type Qualifier = Maybe (Maybe (Hs.ModuleName ()))
pattern Unqualified = Nothing
pattern QualifiedAs m = Just m
isQualified :: Qualifier -> Bool
isQualified = isJust
qualifiedAs :: Qualifier -> Maybe (Hs.ModuleName ())
qualifiedAs = join
-- | Encoding of a Haskell module import statement.
data Import = Import
{ importModule :: Hs.ModuleName ()
, importQualified :: Qualifier
, importParent :: Maybe (Hs.Name ())
, importName :: Hs.Name ()
, importNamespace :: Hs.Namespace ()
-- ^^ if this is a type or something like that, we can add a namespace qualifier to the import spec
-- now it's only useful for differentiating type operators; so for others we always put Hs.NoNamespace () here
-- (we don't calculate it if it's not necessary)
}
type Imports = [Import]
-- | Output produced during the compilation of a module
data CompileOutput = CompileOutput
{ imports :: Imports
-- ^ Haskell import statements.
, haskellExtensions :: [Hs.KnownExtension]
-- ^ Required language extensions.
}
instance Semigroup CompileOutput where
CompileOutput imps exts <> CompileOutput imps' exts' =
CompileOutput (imps <> imps') (exts <> exts')
instance Monoid CompileOutput where
mempty = CompileOutput mempty mempty
-- | State used while compiling a single module.
newtype CompileState = CompileState
{ lcaseUsed :: Int
-- ^ How many @\case@ have been generated in the output Haskell code.
-- Can be removed by subsequent program transformations, hence the StateT.
}
-- NOTE: C should really be a newtype for abstraction's sake
-- but none of the Agda instances are defined for RWST(.CPS)
-- | Compilation monad.
type C = RWST CompileEnv CompileOutput CompileState TCM
-- some missing instances from the Agda side
instance HasFresh i => MonadFresh i C
instance MonadTCState C
instance Monoid w => MonadTransControl (RWST r w s) where
type StT (RWST r w s) a = (a, s, w)
liftWith f = rwsT $ \r s -> fmap (, s, mempty) (f $ \t -> runRWST t r s)
restoreT mSt = rwsT $ \_ _ -> mSt
instance MonadTCEnv C
instance HasOptions C
instance ReadTCState C
instance MonadTCM C
instance MonadTrace C where
traceClosureCall c f = rwsT $ \ r s -> traceClosureCall c $ runRWST f r s
instance MonadInteractionPoints C where
instance MonadDebug C where
instance HasConstInfo C where
instance MonadReduce C where
instance MonadAddContext C where
instance HasBuiltins C where
instance MonadBlock C where
catchPatternErr h m = rwsT $ \ r s ->
let run x = runRWST x r s in catchPatternErr (run . h) (run m)
instance MonadStConcreteNames C where
runStConcreteNames m = rwsT $ \r s -> runStConcreteNames $ StateT $ \ns -> do
((x, ns'), s', w) <- runRWST (runStateT m ns) r s
pure ((x, s', w), ns')
instance IsString a => IsString (C a) where fromString = pure . fromString
instance PureTCM C where
instance Null a => Null (C a) where
empty = lift empty
null = __IMPOSSIBLE__
instance Semigroup a => Semigroup (C a) where (<>) = liftA2 (<>)
-- | Agda @Dom Type@ can get compiled in three ways.
data CompiledDom
= DomType Strictness (Hs.Type ())
-- ^ To a Haskell type (with perhaps a strictness annotation)
| DomConstraint (Hs.Asst ())
-- ^ To a typeclass constraint
| DomForall (Hs.TyVarBind ())
-- ^ To an explicit forall
| DomDropped
-- ^ To nothing (e.g. erased proofs)
-- | Whether a datatype/record should be compiled as a @newtype@ haskell definition.
type AsNewType = Bool
-- | Compilation target for an Agda record definition.
data RecordTarget
= ToRecord AsNewType [Hs.Deriving ()]
| ToClass [String]
-- | Compilation target for an Agda instance declaration.
-- Either to an actual declaration, or a derived instance.
data InstanceTarget
= ToDefinition
| ToDerivation (Maybe (Hs.DerivStrategy ()))
-- Used when compiling imports. If there is a type operator, we can append a "type" namespace here.
data NamespacedName = NamespacedName
{ nnNamespace :: Hs.Namespace ()
, nnName :: Hs.Name ()
} deriving (Eq, Ord)