(or rather Getopt mark III, since I've already rewritten the current one, although no functionality was changed then)
I've been working a bit on a new Argument parser, and would like some input. In reality there will be two different way of using the argument parser, which really doesn't share any code but is convenient to do at the same time. The first one is a "I just want to get this over with" kind of interface:
void main(int num, array argv) { mapping args = Arg.parse(argv); argv = args[Arg.REST]; }
The output will contain ([ "foo":1 ]) if the argument is --foo and ([ "foo":"bar" ]) if the argument is --foo=bar. There is no fancy stuff to handle default values (just or it with another mapping) or options that may or may not have values (e.g. -bar=foo will produce ([ "b":1, "a":1, "r":"foo" ]) and --foo x --bar will just produce ([ "foo":1 ]) and then stop processing the line).
The other mode is the (supposedly) easy yet powerful mode that you would use for more mature applications.
class Parser { inherit Arg.Options; Arg verbose = NoArg("-v")|NoArg("--verbose")|Env("VERBOSE"); Arg level = HasArg("-l")|HasArg("--level"); }
void main(int n, array argv) { Parser p = Parser(argv);
// Index magic. Some like it, some not so much. if( p->verbose ) werror("Level %s\n", p->level); }
Which is extended in
class Parser { // The name for now. inherit Arg.OptionsSupreme; Arg verbose = NoArg("-v")|NoArg("--verbose")|Env("VERBOSE");
// These are used in the automatically generated --help response. string help_pre = "This program is like awesum."; string verbose_help = "Increases the verbosity of the program.";
// Called when/if verbose is set to a value. void verbose_cb(string arg) { werror("Verbosity is on.\n"); } }
Does this match the long list of requirements that I've heard from others?
The output will contain ([ "foo":1 ]) if the argument is --foo and ([ "foo":"bar" ]) if the argument is --foo=bar. There is no fancy stuff to handle default values (just or it with another mapping) or options that may or may not have values (e.g. -bar=foo will produce ([ "b":1, "a":1, "r":"foo" ]) and --foo x --bar will just produce ([ "foo":1 ]) and then stop processing the line).
I think --foo -- --bar should produce ([ "foo":1 ]) and put --bar as the first element in Arg.REST -- otherwise the above sounds great IMO.
The rest sounds strangely alluring. :-) Anything in there for comfy handling of input of misc data types?
You already have one of them above, if however unknown which:
Arg verbose = NoArg("-v")|NoArg("--verbose")|Env("VERBOSE"); string verbose_help = "Increases the verbosity of the program.";
Typical formatting output of what --help would/might produce on stdout might clarify the example a bit.
I'm not done implementing that part, but basically it will iterate over the Arg items, for each item figure out the list of possible options, figure out if there is a text blorb to put on the right side in the table and then do formatting. I would expect the example to be formatted as
-v, --verbose Increases the verbosity of the program.
Borrowing ideas from your Struct module, I presume, in preserving Arg order from declaration order?
This module is *such* a good idea. Getopt is I think the most cut-and- pasty API commonly used in Pike, and contributes to just hideous code. (It is probably only dwarfed by Process.create_process, which might be considered a feature completeness test of the refdoc system. :-)
This module is *such* a good idea.
tools% grep "For the love of all that is holy, write a new Getopt!" * | wc -l 6
Getopt is I think the most cut-and- pasty API commonly used in Pike, and contributes to just hideous code. (It is probably only dwarfed by Process.create_process, which might be considered a feature completeness test of the refdoc system. :-)
I'm hoping Process.run cleans up some of that.
Well, shouldn't it be? (Alpha sort is often not the most useful way of documenting the use and purpose of command line flags.)
Ah, you meant for documentation. Well, it's easy to add, but I don't want to overdesign this part. If you really care about documentation you are better off writing the whole text yourself. The purpose here is to allow for minimal documentation with no-to-minimal effort.
Yeah, the --help docs. But I can always chime in with some code for it -- don't feel pressured to write up and commit all features in one go. Just trying out the looks of how pike -x rsif would shape up here:
class Parser { // The name for now. inherit Arg.OptionsSupreme;
string help_pre = #"pike -x rsif [options] <from> <to> <files>
rsif ("replace string in file") replaces all occurrences of the string <from> with the string <to> in listed files. The name of the files that were changed are written to stdout. Directories may be given instead of files, in which case all the files in that directory will be processed. Available options:";
Arg backups = NoArg("-b")|NoArg("--backups"); string help_backups = "Saves the unaltered file in <filename>~";
Arg recurse = NoArg("-r")|NoArg("--recursive"); string help_recurse = "Processes directories recusively";
Arg verbose = NoArg("-v")|NoArg("--verbose"); string help_verbose = "Writes more junk to stderr.";
Arg quiet = NoArg("-q")|NoArg("--quiet"); string help_quiet = "Writes no output at all.";
Arg version = NoArg("-V")|NoArg("--version"); string help_version = "Writes the version number of rsif.";
Arg help = NoArg("-h")|NoArg("--help"); string help_help = "Shows this help message."; }
for the output:
pike -x rsif [options] <from> <to> <files>
rsif ("replace string in file") replaces all occurrences of the string <from> with the string <to> in listed files. The name of the files that were changed are written to stdout. Directories may be given instead of files, in which case all the files in that directory will be processed. Available options:
-b, --backups Saves the unaltered file in <filename>~ -r, --recursive Processes directories recusively -v, --verbose Writes more junk to stderr. -q, --quiet Writes no output at all. -V, --version Writes the version number of rsif. -h, --help Shows this help message.
I like it. It's a huge gain in readability (and maintainability) from the former code. We could add error() out featuritis for colliding flag names and likely other similar helpful housekeeping too.
Maybe it would make sense letting the *Arg constructors conveniently take an optional docstring argument, instead of the help_* variables:
Arg verbose = NoArg("-v", "Writes more junk to stderr.") | NoArg("--verbose");
I should add that it would of course be possible to create additional parsers, so someone might do
NoArg("-v")|NoArg("--verbose")|Env("VERBOSE")|Settings("program.ini");
to read in settings from a settings file as well. Settings file format/parser is out of the scope for this module though, but I thought I should mention the possibility.
On Thu, Aug 16, 2007 at 06:45:00PM +0000, Martin Nilsson (Opera Mini - AFK!) @ Pike (-) developers forum wrote:
I've been working a bit on a new Argument parser
void main(int num, array argv) { mapping args = Arg.parse(argv); argv = args[Arg.REST]; }
i tried this, and it does not work like that.
Arg.REST does not exist, and Arg.parse(argv); produces Index parse not present in module "Arg".
if i change that to Arg()->parse(argv); i get: No cast method in object. /local/users/mbaehr/tomoyo/src/pike/Pike.g-7.7/lib/modules/Arg.pike:484:
from looking at the source i can't tell if there is stuff missing or if there is a different way to use it.
greetings, martin.
No, I've just not managed to finish it. Also missing is a big and very boring suite of tests... :(
On Mon, Apr 14, 2008 at 07:35:03PM +0200, Martin Nilsson (Opera Mini - AFK!) @ Pike (-) developers forum wrote:
No, I've just not managed to finish it. Also missing is a big and very boring suite of tests... :(
any chance to get it done before 7.8? i'd like to help if i can.
greetings, martin.
well at the moment the exampleyou gave on how to use Arg is not working, so there is more missing than just testsuites.
greetings, martin.
pike-devel@lists.lysator.liu.se