--A command-line options library - sof 1/97
> module GetOptSOF
> (
> Opt(..), -- instance Functor, Monad, MonadZero, MonadPlus (in 1.3)
>
> -- The Opt monad primitives:
>
> -- add another item (to the front)
> pushArg, -- :: String -> Opt a ()
> -- transform the threaded state
> updState, -- :: (a -> a) -> Opt a ()
> -- aka zero
> failed, -- :: Opt a b
> -- Opt try
> catchOpt, -- :: Opt a b -> Opt a b -> Opt a b
>
> -- useful Opt matchers
>
> -- match if string is prefix of current element.
>
> prefixMatch, -- :: String -> Opt a String
> prefixed, -- :: String -> Opt a b -> Opt a b
> -- if current option matches pred, try Opt argument.
> matches, -- :: (String -> Bool) -> (String -> Opt a b) -> Opt a b
> -- test if flag is set
> flag, -- :: String -> (a -> a) -> Opt a ()
> flags, -- :: [(String,a->a)] -> Opt a ()
> -- n-way disjunction
> opts, -- :: [Opt a b] -> Opt a b
> orOpt, -- :: Opt a b -> Opt a b -> Opt a b
> -- `seqOpt`, really.
> thenOpt, -- :: Opt a b -> Opt a b -> Opt a b
> -- try matching --{disable,enable}-foo
> toggle, -- :: String
> -- -> String
> -- -> String
> -- -> (Bool -> a -> a)
> -- -> Opt a ()
> toggles, -- :: String
> -- -> String
> -- -> [(String,Bool -> a->a)]
> -- -> Opt a ()
>
> -- try matching -ifoo (where -i is the prefix)
> prefixArg, -- :: String -> (String -> a -> a) -> Opt a ()
> -- rey matching -o foo
> optionArg, -- :: String -> (String -> Opt a b) -> Opt a b
> optionWithOptArg, -- :: String -> Opt a b -> Opt a b
> -- exact string match
> string, -- :: String -> Opt a ()
> -- useful combinators for when using attribute-lists
> -- to gather options
> (-=), -- :: String -> a -> Opt [a] ()
> (-==), -- :: String -> (String -> a) -> Opt [a] ()
> (-===), -- :: String -> (String -> a) -> Opt [a] ()
> (-====), -- :: String -> (Maybe String -> a) -> Opt [a] ()
> (-?), -- :: (String -> Bool) -> (String -> a) -> Opt [a] ()
> -- Do the actual matching.
>
> getOpts, -- :: Opt a b -> a -> [String] -> ([String],a)
>
> Maybe(..)
> ) where
> import System
> infixr 1 `bindOpt`, `seqOpt`
-- Use a monad to encode the matching operations we want
-- to do on the command line contents, threading a value
-- that will record what we've seen so far plus the remainder
-- of the command-line.
--
> -- 1.2 does not have this
> --data Maybe a = Nothing | Just a
>
> data Opt a b = Opt ([String] -> a -> Maybe ([String],a,b))
>
> -- bind & return over Opt
>
> bindOpt :: Opt a b -> (b -> Opt a c) -> Opt a c
> bindOpt (Opt opt_a) fopt =
> Opt
> (\ args st ->
> case opt_a args st of
> Nothing -> Nothing
> Just (args',st',v) ->
> case fopt v of
> Opt opt_b -> opt_b args' st')
>
> seqOpt :: Opt a b -> Opt a c -> Opt a c
> seqOpt a b = a `bindOpt` (\ _ -> b)
>
> returnOpt :: b -> Opt a b
> returnOpt v = Opt (\ args st -> Just (args,st,v))
>
> -- The Opt primitives for pop and push of cmd line options, plus
> -- primitive for updating the threaded state.
> --
>
> pushArg :: String -> Opt a ()
> pushArg str = Opt (\ args st -> Just (str:args,st,()))
>
> popArg :: Opt a String
> popArg =
> Opt
> (\ args st ->
> case args of
> [] -> Nothing
> (x:xs) -> Just (xs,st,x))
>
> updState :: (a -> a) -> Opt a ()
> updState f = Opt (\ args st -> Just (args, f st, ()))
>
> result :: a -> Opt a ()
> result v = updState (\ _ -> v)
>
> -- a not-that-useful operation on Opt.
> mapOpt :: (b -> c) -> Opt a b -> Opt a c
> mapOpt f (Opt opt) =
> Opt (\ args st ->
> case opt args st of
> Nothing -> Nothing
> Just (args',st',v) -> Just (args',st',f v))
>
> -- Let's overload!
> {-
> instance Monad (Opt s) where
> a >>= b = bindOpt a b
> return = returnOpt
>
> instance Functor (Opt s) where
> map = mapOpt
>
> instance MonadZero (Opt s) where
> zero = failed
>
> instance MonadPlus (Opt s) where
> (++) = thenOpt
> -}
> -- no match.
> failed :: Opt a b
> failed = Opt (\ _ _ -> Nothing)
>
> -- try left, if not successful, give right a spin.
> catchOpt :: Opt a b -> Opt a b -> Opt a b
> catchOpt (Opt opt_a) (Opt opt_b) =
> Opt
> (\ args st ->
> case opt_a args st of
> Nothing -> opt_b args st
> Just x -> Just x)
>
>
{-
Scanning a list of command-line options using
an Opt action that encodes what's interesting and
worth noting.
ToDo: add error support (in the monad?)
-}
> getOpts :: Opt a b -> a -> [String] -> ([String],a)
> getOpts _ st [] = ([],st)
> getOpts o@(Opt opt) st args@(x:xs) =
> case opt args st of
> Nothing -> let (args',st') = getOpts o st xs in (x:args',st')
> Just (args',st',_) -> getOpts o st' args'
>
{-
A number of useful matching combinators for command-line
options follow:
-}
>
> prefixMatch :: String -> Opt a String
> prefixMatch str =
> popArg `bindOpt` \ arg ->
> case prefix str arg of
> Nothing -> failed
> Just arg' -> returnOpt arg'
>
> prefixed :: String -> Opt a b -> Opt a b
> prefixed pre n_opt =
> prefixMatch pre `bindOpt` \ arg ->
> -- push back what's left of the option, and continue.
> pushArg arg `seqOpt`
> n_opt
>
>
> matches :: (String -> Bool) -> (String -> Opt a b) -> Opt a b
> matches matcher opt =
> popArg `bindOpt` \ arg ->
> if matcher arg then
> opt arg
> else
> failed
> flag :: String -> (a -> a) -> Opt a ()
> flag str f =
> popArg `bindOpt` \ arg ->
> case prefix str arg of
> Nothing -> failed
> Just arg' -> updState f
>
> opts :: [Opt a b] -> Opt a b
> opts ls = foldl1 (orOpt) ls
> orOpt :: Opt a b -> Opt a b -> Opt a b
> orOpt = catchOpt
>
> thenOpt :: Opt a b -> Opt a b -> Opt a b
> thenOpt opt_a opt_b = opt_a `seqOpt` opt_b
>
> flags :: [(String,a->a)] -> Opt a ()
> flags ls = opts (map (\ (str,f) -> flag str f) ls)
>
> toggle :: String -> String -> String -> (Bool -> a -> a) -> Opt a ()
> toggle on off str f =
> ((prefixed on (returnOpt True)) `orOpt`
> (prefixed off (returnOpt False))) `bindOpt` \ flg ->
> prefixed str (popArg `seqOpt` updState (f flg))
>
> toggles :: String -> String -> [(String,Bool -> a->a)] -> Opt a ()
> toggles on off ls = opts (map (\ (str,f) -> toggle on off str f) ls)
>
> prefixArg :: String -> (String -> a -> a) -> Opt a ()
> prefixArg str f =
> popArg `bindOpt` \arg ->
> case prefix str arg of
> Nothing -> failed
> Just arg' -> updState (f arg')
>
> optionArg :: String -> (String -> Opt a b) -> Opt a b
> optionArg str f =
> -- get current option
> popArg `bindOpt` \ arg ->
> case prefix str arg of
> Nothing -> failed
> Just arg' ->
> -- get option value
> popArg `bindOpt` \ arg ->
> f arg
>
> optionWithOptArg :: String -> Opt a b -> Opt a b
> optionWithOptArg str f =
> popArg `bindOpt` \ arg ->
> case prefix str arg of
> Nothing -> failed
> Just arg' -> f
>
> string :: String -> Opt a ()
> string str =
> prefixMatch str `bindOpt` \ rest ->
> case rest of
> [] -> returnOpt ()
> _ -> failed
>
> (-=) :: String -> a -> Opt [a] ()
> (-=) str v = flag str (v:)
>
> (-==) :: String -> (String -> a) -> Opt [a] ()
> (-==) str f = prefixArg str (\ ls -> ((f ls):))
>
> (-===) :: String -> (String -> a) -> Opt [a] ()
> (-===) str f = optionArg str (\ val -> updState ((f val):))
>
> (-====) :: String -> (Maybe String -> a) -> Opt [a] ()
> (-====) str f =
> optionWithOptArg
> str
> (popArg `bindOpt` \ val -> updState ((f (Just val)):))
>
> (-?) :: (String -> Bool) -> (String -> a) -> Opt [a] ()
> (-?) matcher f = matches matcher (\ ls -> updState ((f ls):))
>
-- Utils
>
> prefix :: Eq a => [a] -> [a] -> Maybe [a] -- what's left
> prefix [] ls = Just ls
> prefix ls [] = Nothing
> prefix (x:xs) (y:ys)
> | x == y = prefix xs ys
> | otherwise = Nothing
>
>
> split :: Char -> String -> [String]
> split ch [] = []
> split ch ls =
> case break (==ch) ls of
> (xs,[]) -> [xs]
> (xs,_:ys) -> xs:split ch ys
>
|