Reputation: 2847
We have the classic flags in the command line tools, those that enable something (without arguments, e.g --help
or --version
) and others kind of flags that accept arguments (e.g. --output-dir=/home/
or --input-file="in.a"
, whatever).
But this time, I would like to implement the following kind of.
$ myprogram --GCC-option="--stdlib 11" --debug
In a general way, the flag is like "--PROGRAM-option=ARGUMENT"
. Then, I keep from this flag, PROGRAM
and ARGUMENT
values, they are variables. In the example above, we have PROG=GCC
and ARGUMENT=--stdlib 11
.
How should can I implement this feature in Haskell? I have some experience parsing options in the classic way.
Upvotes: 0
Views: 141
Reputation: 1600
In a recent project of mine I used an approach based on a 'Data.Tree' of option handling nodes. Of course, I haven't released this code so it's of very limited use, but I think the scheme might be helpful.
data OptHandle = Op { optSat :: String -> Bool
, opBuild :: [String] -> State Env [String]
}
The node fields: check to see if the argument satisfied the node; and incrementally built up an initial program environment based on the the remaining arguments (returning unused arguments to be processed by nodes lower in the tree.)
An option processing tree is then hard coded, such as below.
pgmOptTree :: [Tree OptHandle]
pgmOptTree = [mainHelpT,pgmOptT,dbgT]
mainHelpT :: Tree OptHandle
mainHelpT = Node (Op sat bld) []
where
sat "--help" = True
sat _ = False
bld _ = do
mySetEnvShowHelp
return []
pgmOptT :: Tree OptHandle
pgmOptT = Node (Op sat bld) [dbgT]
where
sat = functionOn . someParse
bld ss = do
let (d,ss') = parsePgmOption ss
mySetEnvPgmOpt d
return ss'
You will also need a function which feeds the command line arguments to the tree, checking satisfiability of each node in a forest, executing the opBuild, and calling subforests. After running the handler in the state monad, you should be returned an initial starting environment which can be used to tell main the functionality you want to call.
The option handler I used was actually a little more complicated than this, as my program communicated with Bash to perform tab completions, and included help for most major options. I found the benefit to the approach was that I could more easily keep in sync three command line concerns: enabling tab completions which could inform users the next available commands; providing help for incomplete commands; and actually running the program for complete commands.
Maintaining a tree like this is nice because you can reuse nodes at different points, and add options that work with the others fairly easily.
Upvotes: 2