Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
249 lines (222 sloc) 8.54 KB
/* Argument Parser and Value Accessor
This class takes a series of argument specifications, and parses the
argument list to extract the necessary values.
*/
class Rosella.CommandLine.Arguments
{
var program; // The Program object
var remainder; // The list of all arguments after -- which are not parsed
var named_cache; // The cache of already-parsed args
var pos_cache; // All positional arguments
var raw_args; // The raw argument list
var arg_defs; // The ArgumentDef object
// Constructor. Take the list of arguments and do a fast presort on them.
function Arguments(var program)
{
self.program = program;
}
/* Public API Functions
*/
// Get the program name
function program()
{
return self.program;
}
function parse(var args, var arg_defs)
{
string lookahead[] = [];
var named_cache = {};
string remainders[];
string positionals[] = [];
int i = 0;
while (self.__has_more(args, i, lookahead)) {
:(string arg, i, int single) = self.__get_next(args, i, lookahead);
// All arguments after "--" are not parsed. Add them all to the
// remainders list
if (arg == "--") {
remainders = [];
for (; i < elements(args); i++)
push(remainders, args[i]);
break;
}
string arg_name = "";
int has_extra = false;
// An arg of the form "--foo", "--foo bar", "--foo:bar",
// "--foo bar=baz", "--foo:bar=baz"
if (substr(arg, 0, 2) == "--")
arg_name = substr(arg, 2);
// An arg of the form "-X", "-Xfoo", "-X foo", "-f:bar", "-Xfoo=bar",
// "-X foo=bar", "-X:foo=bar" or "-XYZ" (where -X, -Y, -Z are flags)
else if (substr(arg, 0, 1) == "-") {
if (length(arg) > 2) {
arg_name = substr(arg, 1, 1);
string extra = substr(arg, 2);
unshift(lookahead, extra);
has_extra = true;
} else if (length(arg) == 2)
arg_name = substr(arg, 1, 1);
else
Rosella.Error.error("Unknown argument format '%s'", arg);
}
// A positional arg
else {
push(positionals, arg);
continue;
}
:(arg_name, string def) = arg_defs.get_definition(null, arg_name);
switch(def) {
case "b":
if (single == false)
Rosella.Error.error("Unexpected scalar value for flag '%s'", arg_name);
named_cache[arg_name] = true;
if (has_extra) {
// If we have an arg of the form -xyz, and all of them
// are flags, break them apart and set them each
// individually -x -y -z
:(arg, i, single) = self.__get_next(args, i, lookahead);
if (!single)
Rosella.Error.error("Unknown syntax for flag '%s'", arg_name);
for (int i = 0; i < length(arg); i++) {
string extra_arg = substr(arg, i, 1);
unshift(lookahead, "-" + extra_arg);
}
}
break;
case "i":
Rosella.Error.unimplemented(__FUNCTION__ + " 'i'");
case "f":
Rosella.Error.unimplemented(__FUNCTION__ + " 'f'");
case "s":
:(arg, i, single) = self.__get_next(args, i, lookahead);
if (single == false)
Rosella.Error.error("Unknown syntax for argument '%s'", arg_name);
named_cache[arg_name] = var(arg);
break;
case "[]":
:(arg, i, single) = self.__get_next(args, i, lookahead);
if (single == false)
Rosella.Error.error("Unexpected pair value for list '%s'", arg_name);
if (!(exists named_cache[arg_name]))
named_cache[arg_name] = new 'ResizableStringArray';
push(named_cache[arg_name], arg);
break;
case "{}":
:(arg, i, single) = self.__get_next(args, i, lookahead);
if (single == false)
Rosella.Error.error("Unexpected pair value for list '%s'", arg_name);
if (!(exists named_cache[arg_name]))
named_cache[arg_name] = Rosella.get_string_string_hash();
var h = named_cache[arg_name];
var parts = split("=", arg);
if (elements(parts) != 2)
Rosella.Error.error("Unexpected syntax for pair '%s': '%s'", arg_name, arg);
string val_name = parts[0];
string value = parts[1];
h[val_name] = value;
break;
default:
Rosella.Error.error("Unknown argument '%s'", arg_name);
}
}
// Finally, store values into object fields
self.named_cache = named_cache;
self.pos_cache = positionals;
self.remainder = remainders;
self.raw_args = args;
self.arg_defs = arg_defs;
}
function alias_positionals(req_pos)
{
int num_req = elements(req_pos);
if (elements(self.pos_cache) < num_req)
Rosella.Error.error("Expected %d positionals to alias. Have %d instead", num_req, elements(self.pos_cache));
for (int i = 0; i < elements(req_pos); i++) {
string name = req_pos[i];
self.named_cache[name] = self.pos_cache[i];
}
}
// Get the remaining args
function remainder()
{
return self.remainder;
}
// Get the raw list of args.
function raw_args()
{
return self.raw_args;
}
/* VTABLEs
*/
function get_integer_keyed[vtable](string name)
{
:(name, string def) = self.arg_defs.get_definition(self.named_cache, name);
if (def != "b" && def != "i")
Rosella.Error.error("Argument '%s' is not an integer or boolean", name);
if (exists self.named_cache[name])
return self.named_cache[name];
return false;
}
function get_string_keyed[vtable](string name)
{
:(name, string def) = self.arg_defs.get_definition(self.named_cache, name);
if (def != "s")
Rosella.Error.error("Argument '%s' is not a string", name);
if (exists self.named_cache[name])
return self.named_cache[name];
return "";
}
function get_number_keyed[vtable](string name)
{
:(name, string def) = self.arg_defs.get_definition(self.named_cache, name);
if (def != "f")
Rosella.Error.error("Argument '%s' is not a floating point number", name);
if (exists self.named_cache[name])
return self.named_cache[name];
return 0.0;
}
function get_pmc_keyed[vtable](string name)
{
:(name, string def) = self.arg_defs.get_definition(self.named_cache, name);
if (exists self.named_cache[name])
return self.named_cache[name];
return null;
}
function get_string_keyed_int[vtable](int idx)
{
if (elements(self.pos_cache) > idx)
return self.pos_cache[idx];
return "";
}
function get_pmc_keyed_int[vtable](int idx)
{
if (elements(self.pos_cache) > idx)
return self.pos_cache[idx];
return "";
}
/* Private Helper Methods
*/
function __get_next(var args, int i, var lookahead)
{
int single = true;
string arg;
if (elements(lookahead) > 0)
arg = lookahead.shift();
else if (i < elements(args)){
arg = args[i];
i++;
} else
Rosella.Error.error("Additional arg expected");
int idx = indexof(arg, ":");
if (idx != -1) {
var parts = split(":", arg);
arg = parts[0];
unshift(lookahead, parts[1]);
single = false;
}
return arg, i, single;
}
function __has_more(var args, int i, var lookahead)
{
return elements(lookahead) > 0 || i < elements(args);
}
}
Jump to Line
Something went wrong with that request. Please try again.