Skip to content

Commit

Permalink
[CommandLine] Rewrite mode dispatch to use a new ModeCondition object…
Browse files Browse the repository at this point in the history
…. Remove lots of old crufty code. Several misc cleanups
  • Loading branch information
Whiteknight committed Aug 28, 2012
1 parent e69e868 commit 0631b95
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 127 deletions.
1 change: 1 addition & 0 deletions setup.winxed
Expand Up @@ -284,6 +284,7 @@ function setup_stable_libraries(var rosella)
setup_winxed_lib(rosella, "commandline", ["Core", "String"], setup_winxed_lib(rosella, "commandline", ["Core", "String"],
"commandline/Arguments", "commandline/Arguments",
"commandline/ArgumentDef", "commandline/ArgumentDef",
"commandline/ModeCondition",
"commandline/Program", "commandline/Program",
"commandline/ProgramMode" "commandline/ProgramMode"
); );
Expand Down
62 changes: 62 additions & 0 deletions src/commandline/ModeCondition.winxed
@@ -0,0 +1,62 @@
class Rosella.CommandLine.ModeCondition
{
function is_satisfied(var args)
{
return false;
}
}

class Rosella.CommandLine.ModeCondition.FlagSet : Rosella.CommandLine.ModeCondition
{
var flagname;

function FlagSet(string flagname)
{
self.flagname = flagname;
}

function is_satisfied(var args)
{
string flagname = string(self.flagname);
int value = args[flagname];
return value;
}
}

class Rosella.CommandLine.ModeCondition.ScalarValue : Rosella.CommandLine.ModeCondition
{
var name;
var value;

function ScalarValue(string name, string value)
{
self.name = name;
self.value = value;
}

function is_satisfied(var args)
{
string name = self.name;
string expectedvalue = self.value;
string actualvalue = args[name];
if (expectedvalue == actualvalue)
return true;
return false;
}
}

class Rosella.CommandLine.ModeCondition.GenericFunc : Rosella.CommandLine.ModeCondition
{
var func;

function GenericFunc(var f)
{
self.func = f;
}

function is_satisfied(var args)
{
var f = self.func;
return f(args);
}
}
18 changes: 9 additions & 9 deletions src/commandline/Program.winxed
Expand Up @@ -18,7 +18,7 @@ class Rosella.CommandLine.Program
function Program(string program_name) function Program(string program_name)
{ {
self.program_name = program_name; self.program_name = program_name;
self.modes = []; self.modes = {};
self.default_mode = new Rosella.CommandLine.ProgramMode("default"); self.default_mode = new Rosella.CommandLine.ProgramMode("default");
self.on_error = function(var e) { self.on_error = function(var e) {
say("Error parsing arguments"); say("Error parsing arguments");
Expand All @@ -29,7 +29,7 @@ class Rosella.CommandLine.Program


function arguments(var raw_defs) function arguments(var raw_defs)
{ {
self.arg_defs = new Rosella.CommandLine.ArgumentDefs(raw_defs); self.arg_defs = new Rosella.CommandLine.ArgumentDef(raw_defs);
} }


// Set a function to execute on errors // Set a function to execute on errors
Expand All @@ -42,7 +42,7 @@ class Rosella.CommandLine.Program
function add_mode(string name) function add_mode(string name)
{ {
var mode = new Rosella.CommandLine.ProgramMode(name); var mode = new Rosella.CommandLine.ProgramMode(name);
push(self.modes, mode); self.modes[name] = mode;
return mode; return mode;
} }


Expand All @@ -55,11 +55,12 @@ class Rosella.CommandLine.Program
// Execute the program with the given args // Execute the program with the given args
function run(var raw_args) function run(var raw_args)
{ {
// TODO: Take some optional parameters, such as handles to use for
// overriding standard handles, other options.
var args = new Rosella.CommandLine.Arguments(self); var args = new Rosella.CommandLine.Arguments(self);
args.parse(raw_args, self.arg_defs); args.parse(raw_args, self.arg_defs);


var mode = self.__find_mode(args); var mode = self.__find_mode(args);
mode.fetch_all_args(args);
var main_func = mode.main_function(); var main_func = mode.main_function();
if (main_func == null) { if (main_func == null) {
self.try_handle_error(null); self.try_handle_error(null);
Expand All @@ -79,12 +80,12 @@ class Rosella.CommandLine.Program


function __find_mode(var args) function __find_mode(var args)
{ {
for (var mode in self.modes) for (string modename in self.modes) {
{ var mode = self.modes[modename];
if (mode.can_accept(args)) if (mode.can_accept(args))
return mode; return mode;
} }
return self.defaule_mode; return self.default_mode;
} }


// On error, try to execute the error handler, if any. // On error, try to execute the error handler, if any.
Expand All @@ -93,11 +94,10 @@ class Rosella.CommandLine.Program
var on_error = self.on_error; var on_error = self.on_error;
if (on_error == null) { if (on_error == null) {
if (e == null) if (e == null)
Rosella.Error.invalid(__FUNCTION__, "Invalid parameter combination"); Rosella.Error.error("Unknown error during dispatch");
else else
throw e; throw e;
} }
on_error(e); on_error(e);
} }
} }

142 changes: 24 additions & 118 deletions src/commandline/ProgramMode.winxed
Expand Up @@ -15,16 +15,12 @@ class Rosella.CommandLine.ProgramMode


var name; // The name of the mode var name; // The name of the mode
var func; // The function to call var func; // The function to call
var condition; // The condition that determines if we use this mode var conditions; // The conditions that determine if we use this mode
var required_positionals; // The required positionals
var required_args; // The required arguments
var optional_args; // The optional arguments


// Constructor. Each ProgramMode has a name // Constructor. Each ProgramMode has a name
function ProgramMode(string name) function ProgramMode(string name)
{ {
self.name = name; self.name = name;
self.required_positionals = {};
} }


// Set the main function to call when this mode is activated // Set the main function to call when this mode is activated
Expand All @@ -34,137 +30,47 @@ class Rosella.CommandLine.ProgramMode
return self; return self;
} }


// Set a flag that must be present on the commandline for this mode to be
// selected
function set_flag(string name)
{
self.condition = [name, ""];
return self;
}

// Set a name/value pair that must be present for this program mode to be
// selected
function set_flag(string name, string value)
{
self.condition = [name, value];
return self;
}

// Require a positional argument at the given position. All positional
// arguments must appear before all named and slurpy arguments
function require_positional(string name, int position)
{
self.required_positionals[name] = position;
return self;
}

function require_positional(string name)
{
self.required_positionals[name] = -1;
return self;
}

// Take a hash of arguments and the type of values that they require. This
// hash represents arguments that must appear on the command-line for this
// program mode to be satisfied
function require_args(var args)
{
self.required_args = args;
return self;
}

// Take a hash of arguments and the value types that they require. This hash
// represents a series of arguments which are optional and do not need to be
// provided.
function optional_args(var args)
{
self.optional_args = args;
return self;
}

// Called by the Program class internally. This function determines if the // Called by the Program class internally. This function determines if the
// given list of arguments satisfies the list of required arguments for this // given list of arguments satisfies the list of required arguments for this
// ProgramMode. If so, the mode will be activated and executed // ProgramMode. If so, the mode will be activated and executed
function can_accept(var args) function can_accept(var args)
{ {
if (self.condition == null) if (self.conditions == null)
return true; return true;

for (var condition in self.conditions) {
string c_name = self.condition[0]; if (!condition.is_satisfied(args))
string c_value = self.condition[1]; return false;
if (c_value == "") {
int have_flag = args.fetch_flag(c_name, 0);
return have_flag;
} else {
string flag_value = args.fetch_scalar(c_name, 0);
return flag_value == c_value;
} }
return true;
} }


// Attempt to match the positionals. If the keep argument is true, the // Get the main function to dispatch to.
// arguments will be parsed completely and values will be extracted. function main_function()
// Otherwise, we will just look and not parse. Notice that some subtle
// errors during parsing may not be found during simple matching.
function match_positionals(var args, int keep)
{ {
for (string key in self.required_positionals) { return self.func;
int idx = int(self.required_positionals[key]);
string arg;
if (idx < 0)
arg = args.fetch_positional(key, keep);
else
arg = args.fetch_positional(key, keep, idx);
if (arg == "")
Rosella.Error.invalid(__FUNCTION__, "Can not find positional argument '%s' (%d)", key, idx);
}
} }


// Attempt to match the named args. If the keep argument is true the function require_flag(string name)
// arguments will be parsed completely and values will be extracted. Once
// extracted, we cannot go back and attempt to match other ProgramModes
function match_args(var args, var arg_defs, int keep)
{ {
if (arg_defs == null || elements(arg_defs) == 0) if (self.conditions == null)
return true; self.conditions = [];

var condition = new Rosella.CommandLine.ModeCondition.FlagSet(name);
for (string key in arg_defs) { push(self.conditions, condition);
string def = arg_defs[key];

if (def == "f")
args.fetch_flag(key, keep);
else if (def == "s")
args.fetch_scalar(key, keep);
else if (def == "l")
args.fetch_scalar_list(key, keep);
else if (def == "p")
args.fetch_pair(key, keep);
else if (def == "h")
args.fetch_pair_hash(key, keep);
else if (def == "*")
args.fetch_unparsed(key, keep);
}
} }


// This ProgramMode has been activated. Fully-parse all arguments and function require_value(string name, string value)
// extract values
function fetch_all_args(var args)
{ {
if (args == null) if (self.conditions == null)
return; self.conditions = [];
if (self.condition != null) { var condition = new Rosella.CommandLine.ModeCondition.ScalarValue(name, value);
string c_name = self.condition[0]; push(self.conditions, condition);
args.fetch_flag(c_name, 1);
}
self.match_args(args, self.required_args, 1);
self.match_args(args, self.optional_args, 1);
self.match_positionals(args, 1);
if (args.has_unparsed_args())
Rosella.Error.invalid(__FUNCTION__, "Unrecognized arguments: %s", join(" ", args.fetch_unparsed("args", 0)));
} }


// Get the main function to dispatch to. function require_condition(var f)
function main_function()
{ {
return self.func; if (self.conditions == null)
self.conditions = [];
var condition = new Rosella.CommandLine.ModeCondition.GenericFunc(f);
push(self.conditions, condition);
} }
} }

0 comments on commit 0631b95

Please sign in to comment.