packages feed

hat-2.9.1: trans/Wrap.hs

{- --------------------------------------------------------------------------- 
Wrapping is still experimental and I don't think the current implementation
does anything useful.

 Prepare a parsed module ready for the tracing transformation, when
 the module is not to be traced at all, but rather the original
 functions will be called and wrapped.  Essentially, we need to strip
 the module down to reveal just its interface.

 We assume that any function exported from the module must have a
 top-level type signature.  (If not, then it cannot be wrapped, so emit
 an error message.)

 Preparation of the syntax tree consists of:
   * traverse the export list
   * for each datatype exported abstractly, generate type-wrapping functions
   * for each datatype exported concretely, *** TO DO ***
   * for each function exported, grab its type signature and generate a
     "foreign import haskell" decl from it
   * in each class instance, generate "f.i.h" functions for each method - ToDo!
   * delete all imports, except those that bring a needed type into scope
   * add a new import of the original untraced version of this module
   * delete all other decls

Would need proper handling of environment to determine exactly
what is exported and to know about types that have to be imported
because they appear in foreign Haskell declarations. 
Currently very rough approximation.

 After preparation, the module can be passed through the normal tracing
 transformation, which already knows how to handle "foreign import haskell".
 (Except for the new type-wrapping functions, which get added without
 transformation; does this actually work?)

Currently this is a phase before the normal transformation.
Should also consider it as an alternative, writing the wapper .hs file.

--------------------------------------------------------------------------- -}
module Wrap (wrap) where

import Language.Haskell.Exts
import System.FilePath(FilePath)
import SynHelp (nameFromOp,mkQual,declHeadName,declHeadTyVarBinds,getId
               ,isUnQual,getModuleNameFromModule,tyVarBind2Type,notSupported)

wrap :: FilePath -> Module SrcSpanInfo -> Module SrcSpanInfo
wrap filename mod@(Module l maybeModuleHead modulePragmas importDecls decls) =
  Module l maybeModuleHead modulePragmas importDecls' decls' 
  where
  importDecls' = mkImportOriginal modName : importDecls
    -- Only need to keep imports of type constructors used in foreign imports,
    -- but keeping all imports is a save over-approximation.
  decls' = concatMap (trans (getExported maybeModuleHead) modName) decls
  modName = getModuleNameFromModule mod

mkImportOriginal :: ModuleName l -> ImportDecl l
mkImportOriginal modName = 
  ImportDecl {importAnn = ann modName, importModule = modName, importQualified = True, importSrc = False,
    importSafe = False, importPkg = Nothing, importAs = Nothing, importSpecs = Nothing}

type Exported l = Name l -> Bool

getExported :: SrcInfo l => Maybe (ModuleHead l) -> Exported l
getExported Nothing = const True  -- everything exported
getExported (Just (ModuleHead l modName maybeWarningText Nothing)) = const True  -- everything exported
getExported (Just (ModuleHead l modName maybeWarningText (Just (ExportSpecList _ exportSpecs)))) = 
  \name -> (name `within`) `any` exportSpecs
  where
  within :: SrcInfo l2 => Name l1 -> ExportSpec l2 -> Bool
  name `within` (EVar l qname) = same modName name qname
  name `within` (EAbs l (NoNamespace _) qname) = same modName name qname
  name `within` (EAbs l _ qname) = notSupported l "type or pattern namespace in export specification"
  name `within` (EThingWith l (NoWildcard _) qname cnames) = same modName name qname
  name `within` (EThingWith l _ qname cnames) = notSupported l "wildcard in export specification"
  name `within` (EModuleContents l eModName) = getId modName == getId eModName
  same :: ModuleName l1 -> Name l2 -> QName l3 -> Bool
  same mod n q = getId n == getId q && (isUnQual q || (\(Qual _ qm qn) -> getId qm == getId mod) q)

trans :: Exported l -> ModuleName l -> Decl l -> [Decl l]
trans exported modName (TypeDecl l head ty) = []  -- ToDo: keep if exported
trans exported modName (ClassDecl l maybeContext declHead funDeps maybeClassDecls) = []  -- ToDo
trans exported modName (InstDecl l maybeContext instHead maybeInstDecls) = []  -- ToDo
trans exported modName (DataDecl l dataOrNew maybeContext declHead qualConDecls maybeDeriving) | exported name =
  -- Assume data constructors are not exported, only produce wrapper data type. 
  -- Use type name also as data constructor name for the wrapper type.
  -- Doesn't handle deriving.
  -- ToDo: general case.
  [DataDecl l (NewType l) Nothing declHead 
    [QualConDecl l Nothing Nothing (ConDecl l name [newType])] Nothing]
  where
  -- need to get type variable parameters
  name = declHeadName declHead
  tyVars = map tyVarBind2Type (declHeadTyVarBinds declHead)
  newType = foldr1 (TyApp l) (TyCon l (mkQual modName name) : tyVars)
trans exported modName (TypeSig l names ty) | not (null expNames) =
  map (mkForeignHaskell modName ty) expNames
  where
  expNames = filter exported names
trans exported modName (InfixDecl l assoc maybeInt ops) | not (null expOps) =
  [InfixDecl l assoc maybeInt expOps]
  where
  expOps = filter (exported . nameFromOp) ops
trans exported modName (FunBind l matches) = []  -- remove function declaration
trans exported modName (PatBind l pat rhs maybeBinds) = []  -- remove pattern declaration
trans exported modName (ForImp l callConv maybeSafety maybeString name ty) | exported name =
  [mkForeignHaskell modName ty name]
trans _ _ _ = []  -- ToDo: check which other declarations do need special treatment


mkForeignHaskell :: ModuleName l -> Type l -> Name l -> Decl l
mkForeignHaskell mod ty name =
  ForImp l (StdCall l) Nothing (Just ("NotHat." ++ getId mod ++ '.' : getId name)) name ty
  where
  l = ann name