ParseArgs xjs Module
Update This article likely requires a bit of updating to work the the newest version of xjs. The general ideas are still as described below.
I've added a new xjs module called ParseArgs for parsing arguments to a script. Parsing arguments, even with argument parsing libraries in other scripting languages, is not fun. I think that the ParseArgs Module should make the process relatively painless in most situations.
To get a feel for how arguments are passed to a script. Take this script echoArgsas an example,
#!/usr/bin/env xjs
// defines println
require('helpers');
for (var i=0, ilen=arguments.length; i<ilen; i++) {
var arg = arguments[i];
println('"' + arg + '" is type ' + (typeof arg));
}
Now run the script from the command line sending some arguments.
$ chmod 755 echoArgs
$ ./echoArgs --verbose --size 5 -f someFile.txt "as df"
"--verbose" is type string
"--size" is type string
"5" is type string
"-f" is type string
"someFile.txt" is type string
"as df" is type string
The ParseArgs module helps turn these strings into something more useful. Here is an example script "echoParsedArgs"
#!/usr/bin/env xjs
require('helpers');
require('ParseArgs');
try {
var [options, rest] = ParseArgs.parse("[--verbose,-v,--noisy] [--size,-s, Int10] --file,-f String String+", arguments);
}
catch (e) {
println('usage: echoParsedArgs [--verbose,-v] [--size,-s, size] --file,-f filename atLeastOneMoreString');
java.lang.System.exit(1); // to be just exit(1) soon
}
println('The options: ');
for (var p in options) {
println(' ' + p + ' is type ' + (typeof options[p]));
}
for (var i=0, ilen=rest.length; i<ilen; i++) {
var arg = rest[i];
println('"' + arg + '" is type ' + (typeof arg));
}
And now run the script
$ chmod 755 echoParsedArgs
$ ./echoParsedArgs --verbose --size 5 -f someFile.txt "as df"
The options:
--file: "someFile.txt" is type string
--size: "5" is type number
--verbose: "" is type string
The rest:
"as df" is type string
Quite a few things are going on here but I hope some are intuitive.
The call to ParseArgs.parse takes two arguments: a string and the global arguments array. The string argument is a domain-specific matcher. You can kind of think of it like a regular expression for the arguments passed to the script but it does more.
In the example the --file,-f String specifies a manditory options. All manditory options (the --file,-f part) must be followed by a manditory argument (the String part). The --file,-f part contains all the names by which the option can be specified. The first of these names is the canonical name and is the one returned by ParseArgs.parse to the options object in the example above. The String part specifies the constructor that will be used on the manditory argument. Currently the ParseArgs module supports constructors String, Float, Int10, Int16, Int2, etc where the number after "Int" is used in a call to JavaScript's parseInt as the radix argument.
[--verbose,-v,--noisy] specifies an optional option that has no argument.
[--size,-s, Int10] specifies an optional option that has a manditory argument.
You can also have optional options that have optional arguments. For example, [--help,-h [String]].
The order of the options can change but all options must come before the "rest" arguments.
The rest arguments are specified after the last option. In the example above, there is only one rest argument String+. The last of the rest arguments can have a repetition modifier ? (zero or one), * (zero or more), +(one or more) and the ParseArgs.parse function will insure that the user has supplied the correct number of arguments.
I've wanted to have a script argument parser like this for a long time. I need to write many more unit tests on this one especially as it is easy to make a mistake writing a parser. I think this module will take at least another evening of work.
ParseArgs is a another pure JavaScript module and these modules are becoming more fun to write now that Java is disappearing farther down in the abstraction chain. Anyone else tried to write a module yet?
Comments
Have something to write? Comment on this article.
Adrien, A hello world tutorial is a good idea and I will try to write one soon. It is certainly in the plans. I may delay a little bit because the way the Makefile works for a module may change and especially for modules that involve Java. I need to investigate some things a bit more.
It might be best to start with a pure JavaScript module. You could model it after the Logger module which is a pretty simple one.
Have something to write? Comment on this article.
feed
I was tackling the idea of writing a basic http request module : Something simple to start with (ability to do some get/post etc.). Then other features could be built on top of that.
But then again, as I already said, I never used Java nor Rhino before... So it's kind of tricky to get things done.
Could you write a module tutorial ? Let's say, given a hello world class in Java, how you would write the module to add it to xjs ?
Keep up the good work ;)