-- | -- Module : $Header$ -- Copyright : (c) 2013-2015 Galois, Inc. -- License : BSD3 -- Maintainer : cryptol@galois.com -- Stability : provisional -- Portability : portable {-# LANGUAGE PatternGuards #-} module Cryptol.ModuleSystem.Interface ( Iface(..) , IfaceDecls(..) , IfaceTySyn, ifTySynName , IfaceNewtype , IfaceDecl(..), mkIfaceDecl , shadowing , interpImport , unqualified , genIface ) where import Cryptol.Parser.AST (mkQual) import Cryptol.TypeCheck.AST import qualified Data.Map as Map #if __GLASGOW_HASKELL__ < 710 import Data.Monoid (Monoid(..)) #endif -- | The resulting interface generated by a module that has been typechecked. data Iface = Iface { ifModName :: ModName , ifPublic :: IfaceDecls , ifPrivate :: IfaceDecls } deriving (Show) data IfaceDecls = IfaceDecls { ifTySyns :: Map.Map QName [IfaceTySyn] , ifNewtypes :: Map.Map QName [IfaceNewtype] , ifDecls :: Map.Map QName [IfaceDecl] } deriving (Show) instance Monoid IfaceDecls where mempty = IfaceDecls Map.empty Map.empty Map.empty mappend l r = IfaceDecls { ifTySyns = Map.unionWith (mergeByName ifTySynName) (ifTySyns l) (ifTySyns r) , ifNewtypes = Map.unionWith (mergeByName ntName) (ifNewtypes l) (ifNewtypes r) , ifDecls = Map.unionWith (mergeByName ifDeclName) (ifDecls l) (ifDecls r) } mconcat ds = IfaceDecls { ifTySyns = Map.unionsWith (mergeByName ifTySynName) (map ifTySyns ds) , ifNewtypes = Map.unionsWith (mergeByName ntName) (map ifNewtypes ds) , ifDecls = Map.unionsWith (mergeByName ifDeclName) (map ifDecls ds) } -- | Merge the entries in the simple case. mergeByName :: (a -> QName) -> [a] -> [a] -> [a] mergeByName f ls rs | [l] <- ls, [r] <- rs, f l == f r = ls | otherwise = ls ++ rs -- | Like mappend for IfaceDecls, but preferring entries on the left. shadowing :: IfaceDecls -> IfaceDecls -> IfaceDecls shadowing l r = IfaceDecls { ifTySyns = Map.union (ifTySyns l) (ifTySyns r) , ifNewtypes = Map.union (ifNewtypes l) (ifNewtypes r) , ifDecls = Map.union (ifDecls l) (ifDecls r) } type IfaceTySyn = TySyn ifTySynName :: TySyn -> QName ifTySynName = tsName type IfaceNewtype = Newtype data IfaceDecl = IfaceDecl { ifDeclName :: QName , ifDeclSig :: Schema , ifDeclPragmas :: [Pragma] } deriving (Show) mkIfaceDecl :: Decl -> IfaceDecl mkIfaceDecl d = IfaceDecl { ifDeclName = dName d , ifDeclSig = dSignature d , ifDeclPragmas = dPragmas d } mapIfaceDecls :: (QName -> QName) -> IfaceDecls -> IfaceDecls mapIfaceDecls f decls = IfaceDecls { ifTySyns = Map.mapKeys f (ifTySyns decls) , ifNewtypes = Map.mapKeys f (ifNewtypes decls) , ifDecls = Map.mapKeys f (ifDecls decls) } filterIfaceDecls :: (QName -> Bool) -> IfaceDecls -> IfaceDecls filterIfaceDecls p decls = IfaceDecls { ifTySyns = Map.filterWithKey check (ifTySyns decls) , ifNewtypes = Map.filterWithKey check (ifNewtypes decls) , ifDecls = Map.filterWithKey check (ifDecls decls) } where check :: QName -> a -> Bool check k _ = p k unqualified :: IfaceDecls -> IfaceDecls unqualified = mapIfaceDecls (mkUnqual . unqual) -- | Generate an Iface from a typechecked module. genIface :: Module -> Iface genIface m = Iface { ifModName = mName m , ifPublic = IfaceDecls { ifTySyns = tsPub , ifNewtypes = ntPub , ifDecls = dPub } , ifPrivate = IfaceDecls { ifTySyns = tsPriv , ifNewtypes = ntPriv , ifDecls = dPriv } } where (tsPub,tsPriv) = Map.partitionWithKey (\ qn _ -> qn `isExportedType` mExports m ) $ fmap return (mTySyns m) (ntPub,ntPriv) = Map.partitionWithKey (\ qn _ -> qn `isExportedType` mExports m ) $ fmap return (mNewtypes m) (dPub,dPriv) = Map.partitionWithKey (\ qn _ -> qn `isExportedBind` mExports m) $ Map.fromList [ (qn,[mkIfaceDecl d]) | dg <- mDecls m , d <- groupDecls dg , let qn = dName d ] -- | Interpret an import declaration in the scope of the interface it targets. interpImport :: Import -> Iface -> Iface interpImport i iface = Iface { ifModName = ifModName iface , ifPublic = qualify restricted , ifPrivate = mempty } where -- the initial set of names is {unqualified => qualified} public = unqualified (ifPublic iface) -- qualify imported names qualify | Just n <- iAs i = \ names -> qualifyNames n names | otherwise = id -- interpret an import spec to quotient a naming map restricted | Just (Hiding names) <- iSpec i = let qnames = map mkUnqual names in filterIfaceDecls (\qn -> not (qn `elem` qnames)) public | Just (Only names) <- iSpec i = let qnames = map mkUnqual names in filterIfaceDecls (\qn -> qn `elem` qnames) public | otherwise = public -- this assumes that it's getting a list of _only_ unqualified names qualifyNames pfx = mapIfaceDecls (\ n -> mkQual pfx (unqual n))