packages feed

hat-2.8.0.0: trans/AuxFile.hs

module AuxFile (readAuxFiles,writeAuxFile) where

import Flags(Flags,sIncludes,sPreludes,sDbgTrusted)
import Environment(Environment,Entity,HxEntity,exports,imports,hxEnvironmentToList,listToHxEnvironment)
import Relation(unionRelations)
import Language.Haskell.Exts.Annotated
  (Module(..),ModuleHead(..),ExportSpecList(..),ExportSpec(..)
  ,ImportDecl(..),ModuleName(..),QName(..),SrcSpanInfo)
import SynHelp (mkQName,Id(getId),getModuleNameFromModule)
import System.FilePath (FilePath,addExtension,pathSeparator,(</>))
import System.IO (stderr,hPutStr)
import System.Exit (exitFailure)
import qualified Control.Exception(catch,IOException)
import Data.List (isPrefixOf)

-- Create environment for all imports.
-- Exception if hx-file of an imported module is not found.
readAuxFiles :: Flags -> Module l -> IO Environment
readAuxFiles flags mod@(Module l maybeModuleHead _ importDecls decls) = do
  importEnvs <- mapM (importEnv flags) (filter notNotHat importDecls)
  return (unionRelations importEnvs)

notNotHat :: ImportDecl l -> Bool
notNotHat importDecl = not ("NotHat" `isPrefixOf` getId (importModule importDecl))

importEnv :: Flags -> ImportDecl l -> IO Environment
importEnv flags importDecl = do
  entities <- readAuxFile flags (importModule importDecl)
  return (imports (listToHxEnvironment entities) importDecl)


-- Read whole content of .hx file of given module, using search paths in flags.
-- Aborts with error if no such .hx file is found.
readAuxFile :: Flags -> ModuleName l -> IO [HxEntity]
readAuxFile flags (ModuleName l moduleStr) = do
  let filePaths = potentialFilePaths flags moduleStr
  (filePath,contents) <- readFirst moduleStr filePaths filePaths
  return (map (myRead filePath) . tail . lines $ contents)

-- read function with better error message
myRead :: (Read a) => String -> String -> a
myRead file s =  
  case [x | (x,t) <- reads s, ("","") <- lex t] of
    [x] -> x
    []  -> error ("Interface file: Cannot parse in .hx file " ++ file ++ " line " ++ s)
    y   -> error ("Interface file: Ambiguous parse of .hx file " ++ file)

-- For given module name (String) and search paths in flags produce all file paths that .hx file may have.
potentialFilePaths :: Flags -> String -> [FilePath]
potentialFilePaths flags moduleStr = map (</> modPath) paths
  where
  modPath = addExtension (replaceBy '.' pathSeparator moduleStr) "hx"
  paths = sIncludes flags ++ sPreludes flags
  replaceBy :: Char -> Char -> String -> String
  replaceBy c1 c2 = map (\c -> if c==c1 then c2 else c)

-- Given a list of filenames, return filename and its content of first file
-- that was read successfully (intention: other files may not exist)

readFirst :: String -> [FilePath] -> [FilePath] -> IO (FilePath,String)
readFirst modStr [] paths = do
  hPutStr stderr ("Fail: .hx file for module `" ++ modStr ++ "' not found in " ++ show paths ++ 
                  ". Give path with -I or -P.\n") 
  exitFailure
readFirst modStr (x:xs) paths =
  Control.Exception.catch (do finput <- readFile x
                              return (x,finput))
        (\ y -> (y :: Control.Exception.IOException) `seq` readFirst modStr xs paths)

-- `writeAuxFile' writes out the .hx file given this module's complete
-- parse tree.  The .hx file has an entry for every exported identifier, whether
-- defined in this module or reexported from imports.
-- Note that an entry may also contain names that are not exported, e.g. for a type its data constructors
-- or for a method its class.
writeAuxFile :: Flags -> FilePath -> Environment -> Module SrcSpanInfo -> IO ()
writeAuxFile flags filePath env mod = 
  writeFile filePath 
    ((showString "module " . (getId (getModuleNameFromModule mod) ++) . showChar '\n' .
     showLines (hxEnvironmentToList (exports (not (sDbgTrusted flags)) mod env))) "")
  where
  showLines :: Show a => [a] -> ShowS
  showLines = foldr (\x y-> shows x . showChar '\n' . y) id