Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/greencard/GetOptSOF.lhs

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


--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
>

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.