parseargs 0.1 → 0.1.2
raw patch · 4 files changed
+198/−143 lines, 4 files
Files
- README +8/−5
- System/Console/ParseArgs.hs +184/−132
- parseargs-example.hs +4/−4
- parseargs.cabal +2/−2
README view
@@ -1,7 +1,7 @@ parseargs -- command-line argument parsing for Haskell programs-version 0.1+version 0.1.2 Bart Massey <bart@cs.pdx.edu>-8 March 2008+28 September 2008 This library provides System.Console.Parseargs, a module to assist in argument parsing for Haskell stand-alone command@@ -13,11 +13,14 @@ structure from which parsed arguments can be extracted as needed. See the Haddock documentation for the gory details. -I have used this code with ghc 6.6 and ghc 6.8 on Linux,-although the current cabal setup has only been tested with-cabal 1.2 and ghc 6.8. It is a fairly standard+I have used this code with ghc 6.6, ghc 6.8, and current GHC+on Linux, although the current cabal setup has only been+tested with cabal 1.2 and ghc 6.8. It is a fairly standard Hackage-ready package, to the extent I know how to construct such.++The 1.2 release includes a typeclass for argument types for+easier use. This is not what I set out to build. It definitely could also use some work. I use it all the time for writing
System/Console/ParseArgs.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE FlexibleInstances #-} -- Full-featured argument parsing library for Haskell programs -- Bart Massey <bart@cs.pdx.edu> @@ -14,20 +15,23 @@ -- PARTICULAR PURPOSE. -- |This module supplies an argument parser.--- Given a description of type ['Arg'] of the legal+-- Given a description of type [`Arg`] of the legal -- arguments to the program, a list of argument strings,--- and a bit of extra information, the 'parseArgs' function+-- and a bit of extra information, the `parseArgs` function -- in this module returns an--- 'Args' data structure suitable for querying using the--- provided functions 'gotArg', 'getArgString', etc.+-- `Args` data structure suitable for querying using the+-- provided functions `gotArg`, `getArg`, etc. module System.Console.ParseArgs ( -- * Describing allowed arguments -- |The argument parser requires a description of -- the arguments that will be parsed. This is- -- supplied as a list of 'Arg' records, built up+ -- supplied as a list of `Arg` records, built up -- using the functions described here.- Argtype(..), DataArg, Arg(..),+ Arg(..),+ Argtype(..), ArgsComplete(..),+ -- ** DataArg and its pseudo-constructors+ DataArg, argDataRequired, argDataOptional, argDataDefaulted, -- * Argument processing -- |The argument descriptions are used to parse@@ -44,10 +48,11 @@ -- ** Using parse results -- |Query functions permit checking for the existence -- and values of command-line arguments.- gotArg,+ gotArg, ArgType(..), getArgString, getArgFile, getArgStdio, getArgInteger, getArgInt, getArgDouble, getArgFloat,+ ArgFileOpener(..), -- * Misc baseName, usageError, System.IO.IOMode(ReadMode, WriteMode, AppendMode))@@ -68,38 +73,24 @@ -- Provided datatypes. -- --- |The types of arguments carrying data;--- the constructor arguments are for default values.-data Argtype = ArgtypeString (Maybe String)- | ArgtypeInteger (Maybe Integer)- | ArgtypeInt (Maybe Int)- | ArgtypeDouble (Maybe Double)- | ArgtypeFloat (Maybe Float)---- |Information specific to an argument carrying a datum.-data DataArg = DataArg { dataArgName :: String -- ^Print name of datum.- , dataArgArgtype :: Argtype -- ^Type of datum.- , dataArgOptional :: Bool -- ^Datum is not required.- }- -- |The description of an argument, suitable for--- messages and for parsing. The 'argData' field+-- messages and for parsing. The `argData` field -- is used both for flags with a data argument, and -- for positional data arguments. -- -- There are two cases: -- -- (1) The argument is a flag, in which case at least--- one of 'argAbbr' and 'argName' is provided;+-- one of `argAbbr` and `argName` is provided; -- -- (2) The argument is positional, in which case neither--- 'argAbbr' nor 'argName' are provided, but 'argData' is.+-- `argAbbr` nor `argName` are provided, but `argData` is. -- --- If none of 'argAbbr', 'argName', or 'argData' are+-- If none of `argAbbr`, `argName`, or `argData` are -- provided, this is an error. See also the--- 'argDataRequired', 'argDataOptional', and--- 'argDataDefaulted' functions below, which are used to--- generate 'argData'.+-- `argDataRequired`, `argDataOptional`, and+-- `argDataDefaulted` functions below, which are used to+-- generate `argData`. data (Ord a) => Arg a = Arg { argIndex :: a -- ^Connects the input description -- to the output argument.@@ -109,7 +100,58 @@ , argDesc :: String -- ^Documentation for the argument. } ++-- |The types of an argument carrying data. The constructor+-- argument is used to carry a default value. --+-- The constructor argument should really be hidden.+-- Values of this type are normally constructed within+-- the pseudo-constructors pseudo-constructors+-- `argDataRequired`, `argDataOptional`, and+-- `argDataDefaulted`, to which only the constructor+-- function itself is passed.+data Argtype = ArgtypeString (Maybe String)+ | ArgtypeInteger (Maybe Integer)+ | ArgtypeInt (Maybe Int)+ | ArgtypeDouble (Maybe Double)+ | ArgtypeFloat (Maybe Float)+++-- |Information specific to an argument carrying a datum. This+-- is an opaque type, whose instances are constructed using the+-- pseudo-constructors `argDataRequired`, `argDataOptional`,+-- and `argDataDefaulted`.+data DataArg = DataArg { dataArgName :: String -- ^Print name of datum.+ , dataArgArgtype :: Argtype -- ^Type of datum.+ , dataArgOptional :: Bool -- ^Datum is not required.+ }++-- |Generate the `argData` for the given non-optional argument.+argDataRequired :: String -- ^Datum print name.+ -> (Maybe a -> Argtype) -- ^Type constructor for datum.+ -> Maybe DataArg -- ^Result is `argData`-ready.+argDataRequired s c = Just (DataArg { dataArgName = s,+ dataArgArgtype = c Nothing,+ dataArgOptional = False })++-- |Generate the `argData` for the given optional argument with no default.+argDataOptional :: String -- ^Datum print name.+ -> (Maybe a -> Argtype) -- ^Type constructor for datum.+ -> Maybe DataArg -- ^Result is `argData`-ready.+argDataOptional s c = Just (DataArg { dataArgName = s,+ dataArgArgtype = c Nothing,+ dataArgOptional = True })++-- |Generate the `argData` for the given optional argument with the+-- given default.+argDataDefaulted :: String -- ^Datum print name.+ -> (Maybe a -> Argtype) -- ^Type constructor for datum.+ -> a -- ^Datum default value.+ -> Maybe DataArg -- ^Result is `argData`-ready.+argDataDefaulted s c d = Just (DataArg { dataArgName = s,+ dataArgArgtype = c (Just d),+ dataArgOptional = True })+-- -- Returned datatypes. -- @@ -124,8 +166,8 @@ -- |The type of the mapping from argument index to value. newtype ArgRecord a = ArgRecord (Map.Map a Argval) --- |The data structure 'parseArgs' produces. The key--- element is the 'ArgRecord' 'args'.+-- |The data structure `parseArgs` produces. The key+-- element is the `ArgRecord` `args`. data (Ord a) => Args a = Args { args :: ArgRecord a -- ^The argument map. , argsProgName :: String -- ^Basename of 0th argument.@@ -137,16 +179,6 @@ -- Implementation. -- --- |Return the filename part of a pathname.--- Unnecessarily efficient implementation does a single--- tail-call traversal with no construction.-baseName :: String -- ^Pathname.- -> String -- ^Rightmost component of pathname.-baseName s =- let s' = dropWhile (/= '/') s in- if null s' then s else baseName (tail s')-- -- |True if the described argument is positional. arg_posn :: (Ord a) => Arg a -- ^Argument.@@ -263,7 +295,7 @@ -- |The iteration function is given a state and a list, and -- expected to produce a new state and list. The function -- is again invoked with the resulting state and list.--- When the function returns the empty list, 'exhaust' returns+-- When the function returns the empty list, `exhaust` returns -- the final state produced. exhaust :: (s -> [e] -> ([e], s)) -- ^Function to iterate. -> s -- ^Initial state.@@ -281,9 +313,12 @@ parse_error usage msg = error (usage ++ "\n" ++ msg) --- |Given a description of the arguments, 'parseArgs' produces+-- |Given a description of the arguments, `parseArgs` produces -- a map from the arguments to their \"values\" and some other--- useful byproducts.+-- useful byproducts. `parseArgs` requires that the argument+-- descriptions occur in the order 1) flag arguments, 2) required+-- positional arguments, 3) optional positional arguments; otherwise+-- a runtime error will be thrown. parseArgs :: (Show a, Ord a) => ArgsComplete -- ^Degree of completeness of parse. -> [ Arg a ] -- ^Argument descriptions.@@ -440,7 +475,7 @@ -- |Most of the time, you just want the environment's -- arguments and are willing to live in the IO monad.--- This version of 'parseArgs' digs the pathname and arguments+-- This version of `parseArgs` digs the pathname and arguments -- out of the system directly. parseArgsIO :: (Show a, Ord a) => ArgsComplete -- ^Degree of completeness of parse.@@ -462,122 +497,139 @@ Just _ -> True Nothing -> False --- |Return the String, if any, of the given argument.+-- |Type of values that can be parsed by the argument parser.+class ArgType b where+ -- |Fetch an argument's value if it is present.+ getArg :: (Show a, Ord a)+ => Args a -- ^Parsed arguments.+ -> a -- ^Index of argument to be retrieved.+ -> Maybe b -- ^Argument value if present.+ -- |Fetch the value of a required argument.+ getRequiredArg :: (Show a, Ord a)+ => Args a -- ^Parsed arguments.+ -> a -- ^Index of argument to be retrieved.+ -> b -- ^Argument value.+ getRequiredArg args index =+ case getArg args index of+ Just v -> v+ Nothing -> error ("internal error: required argument "+ ++ show index ++ "not supplied")++getArgPrimitive decons (Args { args = ArgRecord am }) k =+ case Map.lookup k am of+ Just v -> Just (decons v)+ Nothing -> Nothing++instance ArgType ([] Char) where+ getArg = getArgPrimitive (\(ArgvalString s) -> s)++-- |[Deprecated] Return the `String` value, if any, of the given argument. getArgString :: (Show a, Ord a) => Args a -- ^Parsed arguments. -> a -- ^Index of argument to be retrieved. -> Maybe String -- ^Argument value if present.-getArgString (Args { args = ArgRecord am }) k =- case Map.lookup k am of- Just (ArgvalString s) -> Just s- Nothing -> Nothing- _ -> error ("internal error: getArgString " ++ (show k))---- |Treat the 'String', if any, of the given argument as--- a file handle and try to open it as requested.-getArgFile :: (Show a, Ord a) =>- Args a -- ^Parsed arguments.- -> a -- ^Index of argument to be retrieved.- -> IOMode -- ^IO mode the file should be opened in.- -> IO (Maybe Handle) -- ^Handle of opened file, if the argument- -- was present.-getArgFile args k m =- case getArgString args k of- Just s -> do- h <- openFile s m- return (Just h)- Nothing -> return Nothing+getArgString = getArg --- |Treat the 'String', if any, of the given argument as a--- file handle and try to open it as requested. If not--- present, substitute the appropriate one of stdin or--- stdout as indicated by 'IOMode'.-getArgStdio :: (Show a, Ord a) =>- Args a -- ^Parsed arguments.- -> a -- ^Index of argument to be retrieved.- -> IOMode -- ^IO mode the file should be opened in.- -- Must not be 'ReadWriteMode'.- -> IO Handle -- ^Appropriate file handle.-getArgStdio args k m = do- mh <- getArgFile args k m- case mh of- Just h -> return h- Nothing -> case m of- ReadMode -> return stdin- WriteMode -> return stdout- AppendMode -> return stdout- ReadWriteMode -> error ("internal error: getArgStdio " ++- "called with ReadWriteMode")+instance ArgType Integer where+ getArg = getArgPrimitive (\(ArgvalInteger i) -> i) --- |Return the Integer, if any, of the given argument.+-- |[Deprecated] Return the `Integer` value, if any, of the given argument. getArgInteger :: (Show a, Ord a) => Args a -- ^Parsed arguments. -> a -- ^Index of argument to be retrieved. -> Maybe Integer -- ^Argument value if present.-getArgInteger (Args { args = ArgRecord am }) k =- case Map.lookup k am of- Just (ArgvalInteger s) -> Just s- Nothing -> Nothing- _ -> error ("internal error: getArgInteger " ++ (show k))+getArgInteger = getArg --- |Return the Int, if any, of the given argument.+instance ArgType Int where+ getArg = getArgPrimitive (\(ArgvalInt i) -> i)++-- |[Deprecated] Return the `Int` value, if any, of the given argument. getArgInt :: (Show a, Ord a) => Args a -- ^Parsed arguments. -> a -- ^Index of argument to be retrieved. -> Maybe Int -- ^Argument value if present.-getArgInt (Args { args = ArgRecord am }) k =- case Map.lookup k am of- Just (ArgvalInt s) -> Just s- Nothing -> Nothing- _ -> error ("internal error: getArgInt " ++ (show k))+getArgInt = getArg --- |Return the Double, if any, of the given argument.+instance ArgType Double where+ getArg = getArgPrimitive (\(ArgvalDouble i) -> i)++-- |[Deprecated] Return the `Double` value, if any, of the given argument. getArgDouble :: (Show a, Ord a) => Args a -- ^Parsed arguments. -> a -- ^Index of argument to be retrieved. -> Maybe Double -- ^Argument value if present.-getArgDouble (Args { args = ArgRecord am }) k =- case Map.lookup k am of- Just (ArgvalDouble s) -> Just s- Nothing -> Nothing- _ -> error ("internal error: getArgDouble " ++ (show k))+getArgDouble = getArg --- |Return the Float, if any, of the given argument.+instance ArgType Float where+ getArg = getArgPrimitive (\(ArgvalFloat i) -> i)++-- |[Deprecated] Return the `Float` value, if any, of the given argument. getArgFloat :: (Show a, Ord a) => Args a -- ^Parsed arguments. -> a -- ^Index of argument to be retrieved. -> Maybe Float -- ^Argument value if present.-getArgFloat (Args { args = ArgRecord am }) k =- case Map.lookup k am of- Just (ArgvalFloat s) -> Just s- Nothing -> Nothing- _ -> error ("internal error: getArgFloat " ++ (show k))+getArgFloat = getArg --- |Generate the 'argData' for the given non-optional argument.-argDataRequired :: String -- ^Datum print name.- -> (Maybe a -> Argtype) -- ^Type constructor for datum.- -> Maybe DataArg -- ^Result is 'argData'-ready.-argDataRequired s c = Just (DataArg { dataArgName = s,- dataArgArgtype = c Nothing,- dataArgOptional = False })+-- |`ArgType` instance for opening a file from its string name.+newtype ArgFileOpener = ArgFileOpener {+ argFileOpener :: IOMode -> IO Handle -- ^Function to open the file+ } --- |Generate the 'argData' for the given optional argument with no default.-argDataOptional :: String -- ^Datum print name.- -> (Maybe a -> Argtype) -- ^Type constructor for datum.- -> Maybe DataArg -- ^Result is 'argData'-ready.-argDataOptional s c = Just (DataArg { dataArgName = s,- dataArgArgtype = c Nothing,- dataArgOptional = True })+instance ArgType ArgFileOpener where+ getArg args index =+ case getArg args index of+ Nothing -> Nothing+ Just s -> Just (ArgFileOpener { argFileOpener = openFile s }) --- |Generate the 'argData' for the given optional argument with the--- given default.-argDataDefaulted :: String -- ^Datum print name.- -> (Maybe a -> Argtype) -- ^Type constructor for datum.- -> a -- ^Datum default value.- -> Maybe DataArg -- ^Result is 'argData'-ready.-argDataDefaulted s c d = Just (DataArg { dataArgName = s,- dataArgArgtype = c (Just d),- dataArgOptional = True })+-- |[Deprecated] Treat the `String` value, if any, of the given argument as+-- a file handle and try to open it as requested.+getArgFile :: (Show a, Ord a) =>+ Args a -- ^Parsed arguments.+ -> a -- ^Index of argument to be retrieved.+ -> IOMode -- ^IO mode the file should be opened in.+ -> IO (Maybe Handle) -- ^Handle of opened file, if the argument+ -- was present.+getArgFile args k m =+ case getArg args k of+ Just fo -> (do h <- argFileOpener fo m; return (Just h))+ Nothing -> return Nothing+++-- |Treat the `String` value, if any, of the given argument as a+-- file handle and try to open it as requested. If not+-- present, substitute the appropriate one of stdin or+-- stdout as indicated by `IOMode`.+getArgStdio :: (Show a, Ord a) =>+ Args a -- ^Parsed arguments.+ -> a -- ^Index of argument to be retrieved.+ -> IOMode -- ^IO mode the file should be opened in.+ -- Must not be `ReadWriteMode`.+ -> IO Handle -- ^Appropriate file handle.+getArgStdio args k m =+ case getArg args k of+ Just s -> openFile s m+ Nothing ->+ case m of+ ReadMode -> return stdin+ WriteMode -> return stdout+ AppendMode -> return stdout+ ReadWriteMode ->+ error ("internal error: tried to open stdio "+ ++ "in ReadWriteMode")++---+--- Misc+---++-- |Return the filename part of a pathname.+-- Unnecessarily efficient implementation does a single+-- tail-call traversal with no construction.+baseName :: String -- ^Pathname.+ -> String -- ^Rightmost component of pathname.+baseName s =+ let s' = dropWhile (/= '/') s in+ if null s' then s else baseName (tail s')+ -- |Generate a usage error with the given supplementary message string. usageError :: (Ord a) => Args a -> String -> b
parseargs-example.hs view
@@ -46,13 +46,13 @@ putStrLn "parse successful" when (gotArg args OptionFlag) (putStrLn "saw flag")- case (getArgString args OptionFlagString) of+ case (getArg args OptionFlagString) of Just s -> putStrLn ("saw string " ++ s) Nothing -> return ()- case (getArgInt args OptionFlagInt) of- Just d -> putStrLn ("saw int " ++ (show d))+ case (getArg args OptionFlagInt) of+ Just d -> putStrLn ("saw int " ++ (show (d::Int))) Nothing -> return () putStrLn ("saw fixed " ++ (fromJust (getArgString args OptionFixed)))- case (getArgString args OptionOptional) of+ case (getArg args OptionOptional) of Just s -> putStrLn ("saw optional " ++ s) Nothing -> return ()
parseargs.cabal view
@@ -1,12 +1,12 @@ Name: parseargs Build-Type: Simple Description: Parse command-line arguments-Version: 0.1+Version: 0.1.2 Cabal-Version: >= 1.2 License: BSD3 License-File: COPYING Author: Bart Massey <bart@cs.pdx.edu>-Copyright: Copyright (C) 2007 Bart Massey+Copyright: Copyright (C) 2008 Bart Massey Maintainer: Bart Massey <bart@cs.pdx.edu> Homepage: http://wiki.cs.pdx.edu/bartforge/parseargs Category: System.Console