PFE2.hs

-- Programatica Front-End, level 2, see README.html
module PFE2(
  PFE2MT,runPFE2,clean2,
  getSt2ext,updSt2ext,setSt2ext,
  getAllExports,getExports,getModuleExports,getModuleExportsTime,getModuleTime
  ) where
import Prelude hiding (readFile,putStrLn)
import Maybe(isJust,isNothing,fromJust)
import List(nub,intersect,(\\))

import HsModule
import WorkModule(analyzeSCM,expRel,ExpRel,readRel,showRel)
import Relations(Rel)
--import Ents(Ent)
import PFE0

import PrettyPrint(pp,fsep,(<+>))
import MUtils
import StateMT(getSt,updSt_)
import AbstractIO
import FileUtils
import DirUtils(optCreateDirectory,getModificationTimeMaybe,rmR)

type Exports n = [(ModuleName,(ClockTime,ExpRel n))]

type PFE2Info n = Exports n
newtype State2 n = PFE2 {un2::Exports n}
pfe2info0 = PFE2 []
upd2 f (PFE2 e) = PFE2 (f e)

type PFE2MT n i ds ext m =  PFE0MT n i ds (State2 n,ext) m

runPFE2 ext = runPFE0 (pfe2info0,ext)

getSt2 :: Monad m => PFE2MT n i ds ext m (PFE2Info n)
updSt2 :: Monad m => Upd (PFE2Info n)->PFE2MT n i ds ext m ()
getSt2ext :: Monad m => PFE2MT n i ds ext m ext
updSt2ext :: Monad m => Upd ext->PFE2MT n i ds ext m ()

getSt2 = un2 . fst # getSt0ext
updSt2 = updSt0ext . apFst . upd2
setSt2 = updSt2 . const

getSt2ext = snd # getSt0ext
updSt2ext = updSt0ext . apSnd
setSt2ext = updSt2ext . const

-- The latest time at which the module or one of its imports was modified:
getModuleTime m =
    do (path,(_,imports)) <- findNode m
       srct <- getModificationTime path
       expts <- mapM getModuleExportsTime imports
       return (maximum (srct:expts))

getModuleExportsTime m = fst # getModuleExports m     

getModuleExports m =
    maybe (fail $ pp $ "Unknown module:"<+>m) return . lookup m
      =<< getExports (Just [m])

getAllExports = getExports Nothing

getExports optms =
  do oldExports <- getSt2
     ms <- maybe allModules return optms
     b <- batchMode
     if b && haveAll oldExports ms
       then return oldExports
       else do exports <- analyzeModules' oldExports optms
               setSt2 exports
               return exports
  where
    haveAll old ms = null (nub ms\\map fst old)

-- Compute the WorkModules for all modules in the project:
--analyzeModules = analyzeModules' pfe2info0 Nothing

-- Compute the WorkModules for a selected set of modules in the project:
analyzeModules' oldExports optms =
    do optCreateDirectory `projPath` scopedir
       g <- sortCurrentModuleGraph
       exports <- scopeScms [] [] (optSubGraph g optms)
       let newms = map fst exports
           oldexports = [oe|oe@(m,_)<-oldExports,m `notElem` newms]
       return (exports++oldexports)
  where

    scopeScms exports updated [] = return exports
    scopeScms exports updated (scm:scms) =
      do (exports',updated') <- scopeScm exports updated scm
	 scopeScms (exports'++exports) (updated'++updated) scms

    scopeScm exports updated scm =
      do es <- mapM getOldExport scm
	 let imps = imports scm
	     updimps = imps `intersect` updated -- updated imports
         case mapM id es of
	   Just exports' | null updimps ->
	        do let ms = map (fst.snd) scm
		   return (zip ms exports',[])
	   _ -> do epput $ "Analyzing:" <+>fsep (map fst scm)
		   a <- analyzeSCM (mapSnd snd exports) # preparseSCM scm
		   case a of
		     Left err -> fail (pp err)
		     Right wms' ->
		       do (unchanged,ts) <- unzip # mapM updateWorkModule wms'
			  let updated' = [m|((m,_),False)<-zip wms' unchanged]
			      exports' = zipWith f ts wms' 
			      f t (m,wm) = (m,(t,expRel wm))
			  return (exports',updated')

    imports scm = nub [i|(f,(m,is))<-scm,i<-is]
    preparseSCM = mapM (preparseSourceFile.fst)

    getOldExport (f,(m,_)) =
      do let opt_oexp = lookup m oldExports
	     oexpt = fmap fst opt_oexp
         -- Is the export relation in the internal cache present and fresh?
	 b <- batchMode
	 srct <- if b then return undefined else getModificationTime f
	 if if b then isNothing oexpt else srct `newerThan` oexpt
	   then do optdir <- projectDir
		   case optdir of
		     Nothing -> return Nothing -- no external cache
		     Just dir ->
		       do let expf = exportsFile dir m
			  expt <- getModificationTimeMaybe expf
			  srct <- if b
				  then getModificationTime f
				  else return srct
			  --Ditto for the export relation in the external cache?
			  if srct `newerThan` expt
			    then return Nothing
			    else maybeM (((,) (fromJust expt) . readRel)
                                          # readFile expf)
	   else return opt_oexp
    --fromJust' = fromMaybe (error "PFE2.getOldExport")

    --getInscope (_,(m,_)) = read # readFile (inscopeFile m)

    updateWorkModule (m,wm) =
      do optdir <- projectDir 
         case optdir of
           Nothing ->
	     do --No access to the old exports here, so we have to assume
	        --they have changed!! (This should be easy to improve.)
                t <- getClockTime
		return (True,t)
	   Just dir ->
             do --updateFile (inscopeFile dir m) (show (inscpRel wm))
                let expf = exportsFile dir m
		updated <- updateFile' expf (showRel (expRel wm))
		t <- getModificationTime expf
		return (updated,t)
--------------------------------------------------------------------------------
clean2 = withProjectDir clean
  where
    clean dir = do rmR [scopedir dir]
		   clean0

--------------------------------------------------------------------------------

scopedir dir=dir++"scope/"

exportsFile dir m = scopedir dir++moduleInfoPath m++".exports"
--inscopeFile dir (Module s) = scopedir dir++s++".inscope"

--------------------------------------------------------------------------------

Plain-text version of PFE2.hs | Valid HTML?