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
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ function setup_stable_libraries(var rosella)
setup_winxed_lib(rosella, "commandline", ["Core", "String"],
"commandline/Arguments",
"commandline/ArgumentDef",
"commandline/ModeCondition",
"commandline/Program",
"commandline/ProgramMode"
);
Expand Down
62 changes: 62 additions & 0 deletions src/commandline/ModeCondition.winxed
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Rosella.CommandLine.Program
function Program(string program_name)
{
self.program_name = program_name;
self.modes = [];
self.modes = {};
self.default_mode = new Rosella.CommandLine.ProgramMode("default");
self.on_error = function(var e) {
say("Error parsing arguments");
Expand All @@ -29,7 +29,7 @@ class Rosella.CommandLine.Program

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
Expand All @@ -42,7 +42,7 @@ class Rosella.CommandLine.Program
function add_mode(string name)
{
var mode = new Rosella.CommandLine.ProgramMode(name);
push(self.modes, mode);
self.modes[name] = mode;
return mode;
}

Expand All @@ -55,11 +55,12 @@ class Rosella.CommandLine.Program
// Execute the program with the given 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);
args.parse(raw_args, self.arg_defs);

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

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))
return mode;
}
return self.defaule_mode;
return self.default_mode;
}

// 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;
if (on_error == null) {
if (e == null)
Rosella.Error.invalid(__FUNCTION__, "Invalid parameter combination");
Rosella.Error.error("Unknown error during dispatch");
else
throw e;
}
on_error(e);
}
}

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

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

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

// Set the main function to call when this mode is activated
Expand All @@ -34,137 +30,47 @@ class Rosella.CommandLine.ProgramMode
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
// given list of arguments satisfies the list of required arguments for this
// ProgramMode. If so, the mode will be activated and executed
function can_accept(var args)
{
if (self.condition == null)
if (self.conditions == null)
return true;

string c_name = self.condition[0];
string c_value = self.condition[1];
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;
for (var condition in self.conditions) {
if (!condition.is_satisfied(args))
return false;
}
return true;
}

// Attempt to match the positionals. If the keep argument is true, the
// arguments will be parsed completely and values will be extracted.
// 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)
// Get the main function to dispatch to.
function main_function()
{
for (string key in self.required_positionals) {
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);
}
return self.func;
}

// Attempt to match the named args. If the keep argument is true the
// 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)
function require_flag(string name)
{
if (arg_defs == null || elements(arg_defs) == 0)
return true;

for (string key in arg_defs) {
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);
}
if (self.conditions == null)
self.conditions = [];
var condition = new Rosella.CommandLine.ModeCondition.FlagSet(name);
push(self.conditions, condition);
}

// This ProgramMode has been activated. Fully-parse all arguments and
// extract values
function fetch_all_args(var args)
function require_value(string name, string value)
{
if (args == null)
return;
if (self.condition != null) {
string c_name = self.condition[0];
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)));
if (self.conditions == null)
self.conditions = [];
var condition = new Rosella.CommandLine.ModeCondition.ScalarValue(name, value);
push(self.conditions, condition);
}

// Get the main function to dispatch to.
function main_function()
function require_condition(var f)
{
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.