ministg-0.3: src/Ministg/Options.hs
-----------------------------------------------------------------------------
-- |
-- Module : Ministg.Options
-- Copyright : (c) 2009 Bernie Pope
-- License : BSD-style
-- Maintainer : bjpop@csse.unimelb.edu.au
-- Stability : experimental
-- Portability : ghc
--
-- Command line option processing.
-----------------------------------------------------------------------------
module Ministg.Options
( processOptions
, programName
, defaultMaxSteps
, defaultEvalStyle
, defaultTraceDir
, Flag (..)
, EvalStyle (..)
, Dumped (..)
, probeFlags
, probeFlagsFirst
, existsFlag
, getTraceDir
, getMaxTraceSteps
, getEvalStyle
)
where
import System.Console.GetOpt
import Data.Maybe (fromMaybe)
import Data.Char (toLower, isDigit)
import Data.Maybe (catMaybes)
import System.IO (stderr, hPutStrLn)
import System.Exit
programName :: String
programName = "ministg"
-- This should really come from the cabal file somehow.
versionNumber :: String
versionNumber = "0.2"
versionInfo :: String
versionInfo = unwords [programName, "version", versionNumber]
processOptions :: [String] -> IO ([Flag], String)
processOptions argv =
case getOpt RequireOrder options argv of
(flags, nonOpts, [])
| existsFlag flags Help -> do
putStrLn $ usageInfo header options
exitWith ExitSuccess
| existsFlag flags Version -> do
putStrLn versionInfo
exitWith ExitSuccess
| length nonOpts /= 1 ->
raiseError ["You must specify a single input stg file.\n"]
| otherwise -> return (flags, head nonOpts)
(_, _, errs) -> raiseError errs
where
header = "Usage: " ++ programName ++ " [OPTION...] file"
failureMsg = programName ++ ": command line error.\n"
raiseError errs = do
hPutStrLn stderr $ failureMsg ++ concat errs ++ usageInfo header options
exitFailure
probeFlags :: [Flag] -> (Flag -> Maybe a) -> [a]
probeFlags flags probe = catMaybes (map probe flags)
probeFlagsFirst :: [Flag] -> (Flag -> Maybe a) -> a -> a
probeFlagsFirst flags probe defaultValue
| null probed = defaultValue
| otherwise = head probed
where
probed = probeFlags flags probe
existsFlag :: [Flag] -> Flag -> Bool
existsFlag flags f
= probeFlagsFirst flags probe False
where
probe someFlag = if f == someFlag then Just True else Nothing
data Flag
= Style EvalStyle -- ^ Which evaluation rules to use (eval/apply or push enter)
| Trace -- ^ Turn tracing on.
| TraceDir String -- ^ Directory to save trace file.
| MaxTraceSteps Integer -- ^ Maximum trace steps to record.
| CallStack -- ^ Include call stack in trace.
| Dump Dumped -- ^ Dump something out to debug the interpreter.
| NoPrelude -- ^ Do not automatically include the Prelude.
| NoGC -- ^ Disable garbage collection.
| Help -- ^ Print a help message and exit.
| Version -- ^ Print the version number.
| Annotate -- ^ Auto annotate the program with stack markers.
deriving (Eq, Ord, Show)
data EvalStyle
= EvalApply
| PushEnter
deriving (Eq, Ord, Show)
data Dumped
= DumpAST
| DumpParsed
| DumpArity
| DumpNothing
deriving (Eq, Ord, Show)
options :: [OptDescr Flag]
options =
[ Option ['s'] ["style"] (ReqArg mkStyle "STYLE") "evaluation STYLE to use (EA = eval apply, PE = push enter)"
, Option ['t'] ["trace"] (NoArg Trace) "record a trace of program evaluation"
, Option [] ["tracedir"] (ReqArg TraceDir "DIR") "directory (DIR) to store trace files"
, Option ['m'] ["maxsteps"] (ReqArg mkMaxSteps "STEPS") "maximum number of evaluation STEPS to record in trace"
, Option ['c'] ["callstack"] (NoArg CallStack) "enable call stack tracing"
, Option [] ["noprelude"] (NoArg NoPrelude) "do not import the Prelude"
, Option [] ["nogc"] (NoArg NoGC) "disable garbage collector"
, Option ['d'] ["dump"] (ReqArg mkDumped "DUMPED") "output DUMPED for debugging purposes (ast, parsed, arity)"
, Option ['v'] ["version"] (NoArg Version) "show version number"
, Option ['h'] ["help"] (NoArg Help) "get help about using this program"
, Option ['a'] ["annotate"] (NoArg Annotate) "automatically annotate the program with stack markers"
]
defaultTraceDir :: String
defaultTraceDir = "trace"
defaultEvalStyle :: EvalStyle
defaultEvalStyle = PushEnter
mkStyle :: String -> Flag
mkStyle = normalMkStyle . map toLower
where
normalMkStyle "ea" = Style EvalApply
normalMkStyle "pe" = Style PushEnter
normalMkStyle other = Style defaultEvalStyle
mkDumped :: String -> Flag
mkDumped = normalMkDumped . map toLower
where
normalMkDumped "ast" = Dump DumpAST
normalMkDumped "parsed" = Dump DumpParsed
normalMkDumped "arity" = Dump DumpArity
normalMkDumped other = Dump DumpNothing
defaultMaxSteps :: Integer
defaultMaxSteps = 1000
mkMaxSteps :: String -> Flag
mkMaxSteps [] = MaxTraceSteps defaultMaxSteps
mkMaxSteps n
| all isDigit n = MaxTraceSteps $ read n
| otherwise = MaxTraceSteps defaultMaxSteps
getMaxTraceSteps :: [Flag] -> Integer
getMaxTraceSteps flags =
probeFlagsFirst flags probe defaultMaxSteps
where probe (MaxTraceSteps i) = Just i
probe other = Nothing
getTraceDir :: [Flag] -> String
getTraceDir flags =
probeFlagsFirst flags probe defaultTraceDir
where probe (TraceDir d) = Just d
probe other = Nothing
getEvalStyle :: [Flag] -> EvalStyle
getEvalStyle flags =
probeFlagsFirst flags probe defaultEvalStyle
where
probe :: Flag -> Maybe EvalStyle
probe (Style style) = Just style
probe other = Nothing