Command Parser

M

Magius96

I'm writing a command parser for an application that I'm developing. I've
got everything up and working right now, but I'm currently using a switch
statement to determine what command was entered by the user.

I want to get away from using a switch statement for valid reasons. First,
I want to be able to readily return a list of all commands available to the
user when they type 'comm' into the command parser. Secondly, using a select
statement to determine what command was entered by the user will eventually
become very unwieldly to update and maintain.

I can't provide source code because the development machine isn't permitted
access to any networks for security reasons, but here's a generalized
explanation of how it's currently working.

The user is faced with a command prompt from within the application(it's
fully command driven). The user enteres a command and any arguments and
presses ENTER. The entire line entered by the user is split into two strings
(Command, ARGS). The Command string contains only the first word (all
characters until the first space). The ARGS string contains all the rest of
the users entry minus what's stored in Command and the space. All other
spaces are left in tact.

Currently, we're using "Switch (Command.ToLower())" to determine what
command the user entered. Like I said, this works fine, but it's going to
become unwieldly.

Every command will have it's own function in another class that should be
called by the command parser. For simplification, every function called by
the command parser has the exact same argument list (Connection ch, UserList
Users, string ARGS).

What I'd like, is to have some sort of table somewhere where every command
can be entered in this type of format
{ (string)CommandName, (pointer)FunctionName }
And some central core function that will pass through the table comparing
the value in Command to the CommandName in the table, then when a match is
found call FunctionName.

If you can help me find the best possible solution, I'd greatly appreciate
it. I'm not just looking for simplicity in updating, but I don't want to
make any major hits to performance as a result either. As you can probably
tell, this is a network application in which we expect to have thousands of
users at once, so increasing performance is a top priority for this project,
just second to making it work.
 
A

Alberto Poblacion

Magius96 said:
[...]
What I'd like, is to have some sort of table somewhere where every command
can be entered in this type of format
{ (string)CommandName, (pointer)FunctionName }
And some central core function that will pass through the table comparing
the value in Command to the CommandName in the table, then when a match is
found call FunctionName.

Yes, I think that this is an excellent approach, and indeed this was
what I was thinking of suggesting as I was reading your message, until I saw
that you had already thought of it.

This should be quite straightforward to implement in C# (except that you
would use a delegate instead of a function pointer). What kind of
difficulties are you encountering? Problems defining the class for the table
entries? Building the table? Examining the table to find a match? Invoking
the delegate?
 
A

Alberto Poblacion

Magius96 said:
What I'd like, is to have some sort of table somewhere where every command
can be entered in this type of format
{ (string)CommandName, (pointer)FunctionName }
And some central core function that will pass through the table comparing
the value in Command to the CommandName in the table, then when a match is
found call FunctionName.

Here is a possible implementation:

delegate void MyCommandInvoker(string[] args);

private Dictionary<string,MyCommandInvoker> commandTable;

void BuildCommandTable() //To be run once
{
commandTable = new Dictionary<string,MyCommandInvoker>();
commandTable.Add("FirstCommand", new MyCommandInvoker(function1));
commandTable.Add("SecondCommand", new MyCommandInvoker(function2));
//...
}

void InterpretCommand(string command, string[] args)
{
MyCommandInvoker cmd = commandTable[command];
cmd(args); //Invoke the function
}

void function1(string[] args) //Example.
{
//...
}
 
M

Magius96

Yes, that's an excellent starting point. My problem was in the
implementation itself. I couldn't for the life of me fathom how to implement
it. I think I was concentrating on the wrong things and therefor couldn't
see the obvious.

My only concern here is that I know that the command table will have to be
expanded to pass more variables later, but I'm pretty sure I can piece it
together from here.

Thank you very much for your help, it's greatly appreciated.
--
"Why live in the real world adhering to thier rules, when you can live in
code and write your own"


Alberto Poblacion said:
Magius96 said:
What I'd like, is to have some sort of table somewhere where every command
can be entered in this type of format
{ (string)CommandName, (pointer)FunctionName }
And some central core function that will pass through the table comparing
the value in Command to the CommandName in the table, then when a match is
found call FunctionName.

Here is a possible implementation:

delegate void MyCommandInvoker(string[] args);

private Dictionary<string,MyCommandInvoker> commandTable;

void BuildCommandTable() //To be run once
{
commandTable = new Dictionary<string,MyCommandInvoker>();
commandTable.Add("FirstCommand", new MyCommandInvoker(function1));
commandTable.Add("SecondCommand", new MyCommandInvoker(function2));
//...
}

void InterpretCommand(string command, string[] args)
{
MyCommandInvoker cmd = commandTable[command];
cmd(args); //Invoke the function
}

void function1(string[] args) //Example.
{
//...
}
 
M

Magius96

Your suggestion is almost perfect, but there's one more thing that I'm
looking for that I can't seem to express properly.

Using your example function1 would be defined as:
void function1(Connection ch, UserList users, string ARGS){..}

The argument list for all the functions will always be the same, and the
return value type of all the functions will also always be the same. I
remember seeing code in C that allowed me to define the above function as:
PCMD(function1){...}

My issue is that I don't remember how PCMD would have been defined in C. I
know that in the code that I saw that 'PCMD(function1)' was somehow a
shortcut to writing the full declaration of 'void function1(Connection ch,
UserList users, string ARGS)'.

I'm not familiar with the Dictionary type, so I'll have to ask some
questions on it. Eventually I'll have to implment a near match algorythm.
Meaning that if a user types 'sta' and the only command that starts with
those three letters is 'status' then I want the parser to call the
cooresponding function. I can implement the algorythm myself, but can the
Dictionary type handle that, or what would be a better array type to use for
that functionality? Also, there may at some point be more fields in the
command list than just the command and the function. Can the Dictionary type
handle that as well?
 
A

Alberto Poblacion

Magius96 said:
The argument list for all the functions will always be the same, and the
return value type of all the functions will also always be the same. I
remember seeing code in C that allowed me to define the above function as:
PCMD(function1){...}

My issue is that I don't remember how PCMD would have been defined in C.
I
know that in the code that I saw that 'PCMD(function1)' was somehow a
shortcut to writing the full declaration of 'void function1(Connection ch,
UserList users, string ARGS)'.

I've been programming in C# for so long that I have forgotten most of
what I knew about plain old C. I believe that what you are mentioning could
be implemented by a macro that was expanded by the preprocessor.
Unfortunately, I am not aware of a similar functionality in C#, unless you
are willing to write your own preprocessor and modify the compilation
process (via msbuild) to perform the expansions on your source code. It's
going to be easier to just write the complete declaration for each of the
functions that process your commands.
I'm not familiar with the Dictionary type, so I'll have to ask some
questions on it. Eventually I'll have to implment a near match algorythm.
Meaning that if a user types 'sta' and the only command that starts with
those three letters is 'status' then I want the parser to call the
cooresponding function. I can implement the algorythm myself, but can the
Dictionary type handle that, or what would be a better array type to use
for
that functionality?

No, the Dictionary can't handle that. You can use an array for the
command names and search through them. If you keep the names sorted in the
array, you can perform a dicothomic search if you want it to be optimized
for speed. The dicothomic search is only worth the effort if you are going
to have lots of different commands, otherwise it won't make much of a
difference.
Also, there may at some point be more fields in the
command list than just the command and the function. Can the Dictionary
type
handle that as well?

In that case, I suggest creating a separate class to hold all the
different fields, including the delegate. The Dictionary would take the
command name as Key, and an instance of your class (which encapsulates all
the fields) as Value. A similar technique could be applied if you decide to
use an array.
 
A

Alberto Poblacion

Peter Duniho said:
I've never heard of a "dicothomic search", but assuming Alberto means
something like a binary search,

Sorry for the spelling, it was missing an "h". Anyway, it refers to the
same thing. "Dichothomic" derives from the greek words for "split in two",
referring to the fact that every time that you compare a value while doing
the search, you reduce to a half the number of values that remain to be
searched. I was translating from the term "búsqueda dicotómica" that we use
in Spanish, without remembering that in English it is more common to say
"binary search".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top