--
I tackled the task of getting Narwhal to support command line options
today. To that end, I've created an "args" module (vaguely inspired
by Python's optparse module) that contains a Parser object that
supports jQuery-like chaining for specifying options. The parser is
prototypically inheritable, and supports the usual bovid options.
-s -- short option
-d: -- short option with a value
-sd: -- two short options, the last of which has a value
--long -- long option
--long=value -- long option with a value
--long value -- long option with a value
-- -s --long -- everything after -- is treated as a plain argument
Defining options on the new args.Parser() object calls for an
.option() method that takes a variadic list of short and long option
names, and optionally the name of the option which is used for setting
values in the resultant options object, and for the formatted help.
You can chain your option with a variety of tools and shortcuts, but
the workhorses are .action(callback(options, name, [value], [...]))
and .validate(callback(value)). The callback you give .action() gets
called when an option is encountered, and .validate() is used to
convert and verify the validity of the value or values. The parser
intuits the number of values to consume based on the number of values
accepted by the action callback, using Function().length. .validate()
must throw an error if the value is not valid. There's also a
.def(value) for setting the initial/default value for an option.
The first order of conveniences are .set([value]) and .push(). .set()
with no specific value configures the option to set the option to a
single value. .set(value) configures the option to be a flag that
sets itself to a particular value, like .set(false) or .set(true).
.push() is similar, but it implicitly calls .def([]) and each time the
option is encountered on the command args, an additional value is
validated and pushed onto the option's array.
The next order of conveniences are for particular types. .number(),
.integer(), .whole(), .natural(), .octal(), and .hex() validate the
value to particular numeric domains and formats. .inc() and .dec()
are useful for multi-level verbosity or optimization flags as they
increment and decrement a value, implicitly defaulting the option to
0. .input() and .output() validate the given file name to a stream
object, and recognize '-' as either standard input or output
respectively. .choices(array) validates the value to one of the
strings in the array and .choices(object) translates the value by
looking it up on the object mapping.
Then there are little decorations, .hidden() which suppresses
mentioning the option when parser.printHelp() is called,
.help(description), and .halt() which signifies that this option
terminates option parsing if it's encountered.
The choices and whether an option halts are both automatically
mentioned in the help message when you call parser.printHelp(), or
when your user invokes it, if you've set up:
parser.option('-h', '--help').action(parser.printHelp);
The parser object also has a .usage(text) and .help(text) function,
and you can override parser.print and parser.exit to redirect those to
your own devices.
The invocation looks like:
var options = parser.parse([system.args]);
The parser statefully consumes the given array and returns the
resulting options object. It also sets options.command to the first
argument consumed if it wasn't a switch; this is handy for consuming
the invocation command on system.args so you don't have to do a
slice(1) on your arguments object.
Narwhal's shiny new option parser description is in lib/narwhal.js
http://github.com/kriskowal/narwhal/blob/master/lib/narwhal.js
Here's the help text:
kris@imoo:~/narwhal $ narwhal --help
Usage: /Users/kris/narwhal/platforms/rhino/bin/narwhal-rhino [options] [script]
Runs the Narwhal JavaScript interpreter.
If no script is specified, runs interactively.
-e -c --command command: evaluate command (final option)
-r --require module: pre-load a module
-m --module main: run a library module as a script (final option)
-p --package packagePrefixes: add a package prefix directory
-d --debug: debug
-P --no-packages: don't load packages automatically
-l --log level: set the log level (critical, error, warn, info, debug)
-V --version: print Narwhal version number and exit.
-h --help
Some options are hidden either because they're not yet implemented or
because they're easter eggs.
While I was at it, I ported minimal versions of some of the
conveniences I've grown accustomed to and put them in a "base" module.
It contains implementations of "repr", "keys", "eq", "enquote", and
"trim" among other things, with more to come for sure.
I also added Object.keys, Array.every, Array.some, and Function().bind
to platforms/default/lib/global.js per the new ES spec (I think,
mostly; I don't think I handled the constructor case for bind).
This is all a very hacky one-day product, so please feel free to
fiddle with it or replace it.
Kris Kowal