packages feed

ruler-0.4.1.0: src/Ruler.hs

module Main (main) where

import System.IO
import System.Environment
import System.Exit
import Control.Monad.State
import qualified Data.Map as Map

import System.Console.GetOpt
import UHC.Util.Pretty
import UHC.Util.Utils( panicJust )

import UHC.Util.ParseUtils
import UHC.Util.ParseErrPrettyPrint
import UHC.Util.CompileRun

import Version
import Err
import Gam( emptyGam )
import Common
import Opts

import qualified AbsSyn.AbsSyn1 as AS1

import qualified Main1AG as M1

import qualified Main2AG as M2

import AS1.Imports
import TrfAS2.GenARule
import TrfAS2.GenLaTeX
import KeywParser

import Parser
import Expr.Expr
import FmGam


-------------------------------------------------------------------------
-- Compile run state
-------------------------------------------------------------------------

data RCompileUnitState
  = RCUSUnknown | RCUSRuler | RCUSFail
  deriving (Show,Eq)

data RCompileUnit
  = RCompileUnit
      { rcuFilePath          :: FPath
      , rcuModNm             :: Nm
      , rcuMbOut             :: Maybe AS1.AGItf
      , rcuImpNmL            :: [Nm]
      , rcuState             :: RCompileUnitState
      }

emptyRCU :: RCompileUnit
emptyRCU
  = RCompileUnit
      { rcuFilePath          = emptyFPath
      , rcuModNm             = nmUnk
      , rcuMbOut             = Nothing
      , rcuImpNmL            = []
      , rcuState             = RCUSUnknown
      }

rcuStoreMbOut x rcu = rcu {rcuMbOut = x}
rcuStoreImpNmL x rcu = rcu {rcuImpNmL = x}

data RCompileRunStateInfo
  = RCompileRunStateInfo
      { crsiOpts        :: Opts
      , crsiImpPosMp    :: ImpModMp
      }

instance FileLocatable RCompileUnit String where
  fileLocation _ = "unknown"
  noFileLocation = "unknown"

instance CompileUnitState RCompileUnitState where
  cusDefault        = RCUSRuler
  cusUnk            = RCUSUnknown
  cusIsUnk          = (==RCUSUnknown)
  cusIsImpKnown s   = s /= RCUSUnknown

instance CompileUnit RCompileUnit Nm String RCompileUnitState where
  cuDefault         = emptyRCU
  cuFPath           = rcuFilePath
  cuLocation        = fileLocation
  cuKey             = rcuModNm
  cuState           = rcuState
  cuUpdFPath fp u   = u {rcuFilePath = fp}
  cuUpdLocation _ u = u
  cuUpdState st u   = u {rcuState = st}
  cuUpdKey   nm u   = u {rcuModNm = nm}
  cuImports         = rcuImpNmL

instance FPathError Err

instance CompileRunError Err SPos where
  crePPErrL                      = ppErrPPL
  creMkNotFoundErrL p fp sp sufs = [Err_FileNotFound p fp sp]
  creAreFatal                    = errLIsFatal

instance CompileRunStateInfo RCompileRunStateInfo Nm SPos where
  crsiImportPosOfCUKey n i = Map.findWithDefault emptySPos n (crsiImpPosMp i)

instance CompileModName Nm where
  mkCMNm = Nm

type RCompileRun     = CompileRun   Nm RCompileUnit RCompileRunStateInfo Err
type RCompilePhase a = CompilePhase Nm RCompileUnit RCompileRunStateInfo Err a

-------------------------------------------------------------------------
-- Pretty printing
-------------------------------------------------------------------------

instance PP RCompileUnitState where
  pp = pp . show

instance Show RCompileUnit where
  show _ = "RCU"

instance PP RCompileUnit where
  pp u = "RCU:" >#< pp (show $ rcuFilePath $ u) >#< ": state " >#< pp (rcuState u) >#< ": impL " >#< pp (show $ rcuImpNmL u)

-------------------------------------------------------------------------
-- File suffix
-------------------------------------------------------------------------

type FileSuffMp = [(FileSuffix,RCompileUnitState)]

fileSuffMp :: FileSuffMp
fileSuffMp = [ ( Just "rul", RCUSRuler ), ( Just "", RCUSRuler ), ( Just "*", RCUSRuler ) ]

-------------------------------------------------------------------------
-- Compile run actions
-------------------------------------------------------------------------

cpParseCU :: Nm -> RCompilePhase ()
cpParseCU modNm
  = do { cr <- get
       ; let cu     = crCU modNm cr
             fp     = cuFPath cu
             fNm    = fpathToStr fp
       ; (fn,fb,fh)
             <- if fpathIsEmpty fp
                then return ("<stdin>","<stdin>",stdin)
                else do { let fn = fpathToStr fp
                        ; h <- lift $ openFile fn ReadMode
                        ; return (fn,fpathToStr (fpathRemoveSuff fp),h)
                        }
       -- ; cpPP "crParseCU"
       ; tokens <- lift $ mkHScan fn fh
       ; let (pres,perrs) = parseToResMsgs pAGItf tokens
       ; if null perrs
         then do { let impMp = as1Imports pres
                       info = crStateInfo cr
                 ; cpUpdCU modNm (rcuStoreMbOut (Just pres) . rcuStoreImpNmL (Map.keys impMp))
                 ; modify (\cr -> (cr {crStateInfo = info {crsiImpPosMp = impMp `Map.union` crsiImpPosMp info}}))
                 }
         else cpSetLimitErrs 5 "" (map mkPPErr perrs)
       }

cpFindAndParseCU :: Maybe FPath -> Nm -> RCompilePhase ()
cpFindAndParseCU mbFp modNm
  =  do { cr <- get
        ; let opts = crsiOpts (crStateInfo cr)
              cpFind mn mbFp
                = do { _ <-  cpFindFileForFPath fileSuffMp (optSearchPath opts) (Just mn) mbFp ; return ()}
        ; cpSeq [cpFind modNm mbFp, cpParseCU modNm]
        }

cpFlattenAndCompileAllCU :: RCompilePhase ()
cpFlattenAndCompileAllCU
 = do { cr <- get
      -- ; lift $ hPutStrLn stderr (show $ crCompileOrder cr)
      ; let opts = crsiOpts (crStateInfo cr)
            isAS2 = fmAS2Fm (optGenFM opts) /= optGenFM opts
            parseRes = as1JoinAGItfs [ panicJust ("crFlattenAndCompileAllCU: " ++ show n) $ rcuMbOut $ crCU n $ cr | ns <- crCompileOrder cr, n <- ns ]
            sem1Res
              = M1.wrap_AGItf (M1.sem_AGItf parseRes)
                         (M1.Inh_AGItf
                            { M1.opts_Inh_AGItf = opts {optGenFM = fmAS2Fm (optGenFM opts)}
                            , M1.fmGam_Inh_AGItf = fmGamFromList' FmFmtCmd [ (Nm n,Expr_Var (Nm v)) | (n,v) <- optDefs opts ]
                            })
            hPutBld f h b = if f then hPutPPFile h b 2000 else return ()
            putBld  f   b = hPutBld f stdout b
            cpPutBld f b = lift $ putBld f b
            cpPutDbg = cpPutBld (optDebug opts) (M1.pp_Syn_AGItf sem1Res)
            cpMk1
              = do { let t1 = M1.as2_Syn_AGItf sem1Res
                         ((t2,_,t2errL),doPrint)
                           = case optGenFM opts of
                               FmTeX -> bld as2LaTeX
                               FmAG  -> bld as2ARule
                               FmHS  -> ((t1,empty,[]),True)
                               _ | optGenExpl opts -> ((t1,empty,[]),True)
                                 | otherwise            -> ((t1,empty,[]),False)
                           where bld f = (f opts (M1.dtInvGam_Syn_AGItf sem1Res) (M1.scGam_Syn_AGItf sem1Res) (M1.fmGam_Syn_AGItf sem1Res) (M1.rwGam_Syn_AGItf sem1Res) t1,True)
                   ; cpSeq [cpSetErrs t2errL, cpPutBld doPrint (M2.ppAS2 opts (M1.fmGam_Syn_AGItf sem1Res) t2)]
                   }
{-
            cpMk2
              = cpSeq [ cpPutBld True (M1.mkPP_Syn_AGItf sem1Res (optGenFM opts))
                      , cpPutBld (optGenExpl opts) (M1.scExplPP_Syn_AGItf sem1Res)
                      ]
            cpMk3 f
              = do { let t1 = M1.as2_Syn_AGItf sem1Res
                         (t2,t2ppDbg,t2errL)
                           = case f of
                               FmTeX -> as2LaTeX opts (M1.scGam_Syn_AGItf sem1Res) (M1.fmGam_Syn_AGItf sem1Res) (M1.rwGam_Syn_AGItf sem1Res) t1
                               FmAG  -> as2ARule opts (M1.scGam_Syn_AGItf sem1Res) (M1.fmGam_Syn_AGItf sem1Res) (M1.rwGam_Syn_AGItf sem1Res) t1
                   ; cpSeq [ cpSetErrs t2errL
                           , cpPutBld True t2ppDbg
                           , cpPutBld True (M2.ppAS2 opts t2)
                           , cpPutBld True (M1.mkPP_Syn_AGItf sem1Res f)
                           ]
                   }
-}
      ; cpSeq [ cpPutDbg
              , cpSetErrs (M1.errL_Syn_AGItf sem1Res)
              , cpMk1
{-
              , if optGenV2 opts && not isAS2
                then cpMk1
                else if not isAS2
                then cpMk2
                else case optGenFM opts of
                       FmAS2 f -> cpMk3 f
                       _       -> liftCR id
-}
              ]
      }

compileTopLevel :: FPath -> Opts -> IO ()
compileTopLevel fp opts
  = do { let topModNm       = Nm (fpathBase fp)
             opts'          = opts { optSearchPath = mkInitSearchPath fp ++ optSearchPath opts }
             cr             = mkEmptyCompileRun topModNm (RCompileRunStateInfo opts' Map.empty)
       ; _ <- runStateT (cpSeq [ cpFindAndParseCU (Just fp) topModNm
                               -- , crPP "crCompileTopLevel 1"
                               , cpImportGather (cpFindAndParseCU Nothing) topModNm
                               -- , crPP "crCompileTopLevel 2"
                               , cpFlattenAndCompileAllCU
                               ])
                         cr
       ; return ()
       }

-------------------------------------------------------------------------
-- main
-------------------------------------------------------------------------

main :: IO ()
main
  = do { args <- getArgs
       ; let oo@(o,n,errs)  = getOpt Permute cmdLineOpts args
             opts           = foldr ($) defaultOpts o
             optParseErrs   = optErrs opts
       ; if optHelp opts
         then putStrLn (usageInfo ("version: " ++ versionInfo ++ "\n\nUsage ruler [options] [file]\n\noptions:") cmdLineOpts)
         else if optVersion opts || optSvnVersion opts
         then do { let s =  (if optVersion    opts                       then versionDist else "")
                         ++ (if optVersion    opts && optSvnVersion opts then ", "        else "")
                         ++ (if optSvnVersion opts                       then versionSvn  else "")
                 ; putStr s
                 }
         else if not (null errs)
         then do { hPutStr stderr (head errs)
                 ; exitFailure
                 }
         else if not (null optParseErrs)
         then do { sequence_ optParseErrs
                 ; exitFailure
                 }
         else compileTopLevel (if null n then emptyFPath else mkFPath (head n)) opts
       }
  where optErrs o = catMaybes $ map (uncurry extr) [("selrule", fmap snd . optMbRlSel'), ("markchanges", fmap snd . optMbMarkChange')]
          where extr msg fld = do
                  e <- fld o
                  if null e then Nothing else return $ do 
                    hPutPPLn stderr $ "Option '" ++ msg ++ "' parsing errors" >-< indent 2 (ppErrPPL e)
{-
doCompile :: FPath -> Opts -> IO ()
doCompile fp opts
  = do { (fn,fb,fh)
             <- if fpathIsEmpty fp
                then return ("<stdin>","<stdin>",stdin)
                else do { let fn = fpathToStr fp
                        ; h <- openFile fn ReadMode
                        ; return (fn,fpathToStr (fpathRemoveSuff fp),h)
                        }
       ; tokens <- mkOffScan fn fh
       ; let (pres,perrs) = parseOffsideToResMsgs pAGItf tokens
             (showErrs,omitErrs) = splitAt 5 perrs
       ; putErr' (if null omitErrs then return () else hPutStrLn stderr "... and more parsing errors") (map mkPPErr showErrs)
       ; let res = M1.wrap_AGItf (M1.sem_AGItf pres)
                     (M1.Inh_AGItf
                        { M1.opts_Inh_AGItf = opts {optGenFM = fmAS2Fm (optGenFM opts)}
                        })
             putDbg = putBld (optDebug opts) (M1.pp_Syn_AGItf res)
             errL = M1.errL_Syn_AGItf res
       ; putDbg
       ; putErr errL
       ; let isAS2 = fmAS2Fm (optGenFM opts) /= optGenFM opts
       ; if optGenV2 opts && not isAS2
         then do { let t1 = M1.as2_Syn_AGItf res
                       ((t2,_,t2errL),doPrint)
                         = case optGenFM opts of
                             FmTeX -> bld as2LaTeX
                             FmAG  -> bld as2ARule
                             _     -> ((t1,empty,[]),False)
                         where bld f = (f opts (M1.scGam_Syn_AGItf res) (M1.fmGam_Syn_AGItf res) (M1.rwGam_Syn_AGItf res) t1,True)
                 ; putErr t2errL
                 ; putBld doPrint (M2.ppAS2 opts t2)
                 }
         else if not isAS2
         then do { putBld True (M1.mkPP_Syn_AGItf res (optGenFM opts))
                 ; putBld (optGenExpl opts) (M1.scExplPP_Syn_AGItf res)
                 }
         else case optGenFM opts of
                FmAS2 f
                    -> do { putErr t2errL
                          ; putBld True t2ppDbg
                          ; putBld True (M2.ppAS2 opts t2)
                          ; putBld True (M1.mkPP_Syn_AGItf res f)
                          }
                    where t1 = M1.as2_Syn_AGItf res
                          (t2,t2ppDbg,t2errL)
                            = case f of
                                FmTeX -> as2LaTeX opts (M1.scGam_Syn_AGItf res) (M1.fmGam_Syn_AGItf res) (M1.rwGam_Syn_AGItf res) t1
                                FmAG  -> as2ARule opts (M1.scGam_Syn_AGItf res) (M1.fmGam_Syn_AGItf res) (M1.rwGam_Syn_AGItf res) t1
                _   -> return ()
       }
  where hPutBld f h b = if f then hPutPPFile h b 2000 else return ()
        putBld  f   b = hPutBld f stdout b
        -- putErr' :: IO () -> [Err] -> IO ()
        putErr' m e   = if null e
                        then return ()
                        else do { hPutBld True stderr (ppErrPPL e)
                                ; m
                                ; if errLIsFatal e then exitFailure else return ()
                                }
        -- putErr :: [Err] -> IO ()
        putErr        = putErr' (return ())
-}