In article
<20********************************@munted.org.uk> ,
al********@munted.org.uk says...
[ ... ]
I tried doing <Rooms *> but found it too hard to use properly. So I've
stuck with <Rooms>. I just implemented my command handler, but it could
do with improvements, so here it is (all input is assumed to be lower
case but I can always change that to handle upper case):
Writing a parser ad hoc like this is more or less asking
for trouble. As long as it stays really simple, it's not
too terrible, but for anything vaguely similar to natural
language, it'll get ugly in a big hurry.
IMO, there are really two reasonable choices. One is to
use a separate generator for the language you're trying
to parse. The other is to write a recursive descent
parser.
Using a typical lex-like lexer generator, your lexer
would look something like this:
quit { return QUIT; }
go { return GO; }
north { return NORTH; }
south { return SOUTH; }
west { return WEST; }
east { return EAST; }
and using a typical yacc-like parser generator the
parser would look something like this:
%token QUIT GO NORTH SOUTH EAST WEST
%union {
enum commands command;
int dir;
};
%type <dir> direction
%type <commands> statements
%%
game: statements;
statements: go_stmt
| quit
go_stmt: GO direction
;
direction: NORTH | SOUTH | EAST | WEST
;
So far, this is really only intended to recognize whether
something is a correctly formatted command or not -- it
doesn't carry out any actions when it does recognize a
command. What you have for actions so far are pretty
trivial though -- basically just check whether you can go
a particular direction before moving that way.
A recursive descent parser is kind of similar in some
ways, but entirely different in others. The idea is
similar to the grammar you use with a parser generator,
but you implement each element with a function (or
functor):
class action {
enum A action;
enum D dir;
};
game() {
action a;
while (a = statement())
execute(a);
}
action statement() {
action ret;
ret.verb = get_token();
if (!is_action(ret.verb))
// error: didn't get a verb
if (ret.verb == GO) {
ret.dir = get_token();
if (!is_dir(ret.dir))
// error didn't get a direction.
}
// ...
This is different from your code in a couple of ways.
First of all, it separates the parsing from the
execution. Second, it's basic structure is built around
the grammar for the language -- the call tree of the
functions directly reflects the abstract syntax tree for
the grammar.
The advantage of using funtors is that you're often
generating a large number of functions that are quite
similar, so much of the commonality can often be moved
into a base class.
--
Later,
Jerry.
The universe is a figment of its own imagination.