packages feed

libhbb-0.4.0.0: src/LibHbbCli.hs

{-# OPTIONS -Wall #-}

module Main where

import           Language.Haskell.HBB.TestModes.ApplyTTree
import           Language.Haskell.HBB.OccurrencesOf
import           Language.Haskell.HBB.SmartInline
import           Language.Haskell.HBB.ExprType
import           Language.Haskell.HBB.ApplyTo
import           Language.Haskell.HBB.Locate
import           Language.Haskell.HBB.Inline
import qualified Data.ByteString.Lazy.Char8 as LB
import           System.Console.GetOpt as O
import           System.Environment (getArgs)
import           System.Exit (exitFailure)
import           System.IO


usageStr :: String
usageStr = unlines
    ["Usage:"
    ,"libhbb-cli [-g ghcOpt ...] locate                     <filename> <line> <column>"
    ,"libhbb-cli [-g ghcOpt ...] inline       <inline-opts> <filename> <line> <column> [<line> <column>]"
    ,"libhbb-cli [-g ghcOpt ...] smart-inline [--adapt-ind] <filename> <line> <column> [<line> <column>]"
    ,"libhbb-cli [-g ghcOpt ...] occurrences-of             <filename> <line> <column> [<filename> ...]"
    ,"libhbb-cli [-g ghcOpt ...] exprtype                   <filename> <expression>"
    ,"libhbb-cli                 apply-to [-q] <function of type string to string> <string>"
    ,""
    ,"<inline-opts> is short for [--adapt-ind|--print-context|--with-color]"
    ,"(there are further modes for testing and development only documented in the sources)"]
    --
    -- Moreover libhbb-cli supports following integration and test modes:
    --
    -- libhbb-cli apply-ttree

data OperationMode = ModeInline InlineOptions
                   | ModeSmartInline
                   | ModeSmartInlineAdaptInd
                   | ModeLocate
                      
-- | This function is responsible to parse the optional parameters (called
-- options). If there is a parameter that doesn't match an option this function
-- stops and returns its accumulated result. The extraneous arguments then will
-- be the description of the file and the line.
takeOptions :: ([String],InlineOptions) -> ([String],InlineOptions)
takeOptions (("--print-context":rest),ops) = takeOptions (rest,(ops { showContext      = True }))
takeOptions (("--with-color"   :rest),ops) = takeOptions (rest,(ops { showAnsiColored  = True }))
takeOptions (("--adapt-ind"    :rest),ops) = takeOptions (rest,(ops { adaptToTargetEnv = AdaptIndToTargetEnv }))
takeOptions x@(_,_)                        = x

main :: IO ()
main = do
    programArgs <- getArgs

    -- First we want to filter out the options that GHC needs
    let (ghcOptions,otherArgs) = 
            let optdescr :: [OptDescr String]
                optdescr = [Option ['g'] [] (ReqArg id "ghc-option") "options passed to ghc"]
            in  case O.getOpt RequireOrder optdescr programArgs of
                (_,_,(_:_)) -> error "Wrong usage of ghc-specific options (every -g must be followed by a GHC option)"
                (g,o,[]   ) -> (g,o)

        putApplyToResult :: Bool -> (String,Maybe String) -> IO ()
        putApplyToResult False        (res,Just wa) = do hPutStr stderr wa 
                                                         hPutStrLn stderr "> "
                                                         hPutStrLn stderr "> Pass the flag '-q' to suppress this warning!"
                                                         putApplyToResult False (res,Nothing)
        putApplyToResult True         (res,Just wa) = hPutStr stderr wa >> putApplyToResult True (res,Nothing)
        putApplyToResult _            (res,Nothing) =  putStr res

    case (ghcOptions,otherArgs) of
        -- The following mode is for integration and testing and not documented
        -- by the API:
        (_ ,["apply-ttree"])                 -> testModeApplyTTree defaultApplyTTreeArgs                     >>= putStr
        -- These modes are 'productive' modes:
        (_ ,("occurrences-of":f:l:c:others)) -> occurrencesOf ghcOptions f (BufLoc (read l) (read c)) others >>= putStr   . showOccurrencesOfResult
        (_ ,["exprtype",f,expr])             -> exprtype      ghcOptions f expr                              >>= putStrLn . showExprTypeResult
        ([],["apply-to","-q",f,str ])        -> applyTo       True       f str                               >>= putApplyToResult True
        ([],["apply-to",     f,str ])        -> applyTo       False      f str                               >>= putApplyToResult False
        (_ ,("apply-to":_))                  -> error "Mode 'applyto' doesn't allow to specify ghc options (with -g)"
        _ -> do
            (opMode,occFile,loc1,maybeLoc2) <- do
                    case otherArgs of
                        ["locate"      ,f,l,c] -> return (ModeLocate     ,f,(BufLoc (read l::Int) (read c::Int)),Nothing)
                        ("smart-inline":rest)  -> 
                            case rest of
                                ("--adapt-ind":f:sl:sc:el:ec:[]) -> return (ModeSmartInlineAdaptInd,f,(BufLoc (read sl::Int) (read sc::Int)),
                                                                                               (Just $ BufLoc (read el::Int) (read ec::Int)))
                                ("--adapt-ind":f:sl:sc:[])       -> return (ModeSmartInlineAdaptInd,f,(BufLoc (read sl::Int) (read sc::Int)),Nothing)
                                (f:sl:sc:el:ec:[])               -> return (ModeSmartInline,f,(BufLoc (read sl::Int) (read sc::Int)),
                                                                                       (Just $ BufLoc (read el::Int) (read ec::Int)))
                                (f:sl:sc:[])                     -> return (ModeSmartInline,f,(BufLoc (read sl::Int) (read sc::Int)),Nothing)
                                _                  -> do putStrLn "Invalid parameters."
                                                         putStrLn usageStr; exitFailure
                        ("inline":rest) -> do
                            let (locspec,options) = takeOptions (rest,defaultInlineOptions)
                            case locspec of
                                (f:sl:sc:el:ec:[]) -> return (ModeInline options,f,(BufLoc (read sl::Int) (read sc::Int)),
                                                                            (Just $ BufLoc (read el::Int) (read ec::Int)))
                                (f:sl:sc:[])       -> return (ModeInline options,f,(BufLoc (read sl::Int) (read sc::Int)),Nothing)
                                _                  -> do putStrLn "Invalid parameters."
                                                         putStrLn usageStr; exitFailure
                        _                          -> do putStrLn usageStr; exitFailure
            case opMode of
                ModeInline options      -> inline      ghcOptions options              occFile loc1 maybeLoc2 >>= putStrLn  . showInlineResult
                ModeSmartInline         -> smartinline ghcOptions IgnoreIndOfTargetEnv occFile loc1 maybeLoc2 >>= LB.putStr . showSmartInlineResultAsByteString
                ModeSmartInlineAdaptInd -> smartinline ghcOptions AdaptIndToTargetEnv  occFile loc1 maybeLoc2 >>= LB.putStr . showSmartInlineResultAsByteString
                ModeLocate              -> locate      ghcOptions                  occFile loc1           >>= putStrLn  . showLocateResult