Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm writing an interpreter for a simple scripting language where each line is a complete, executable command. (Like the instructions in assembler)

When parsing a line I have to map the requested command to actual code. My current solution looks like this:

std::string op, param1, param2;
//parse line, identify op, param1, param2
...
//call command specific code
if(op == "MOV")
    ExecuteMov(AsNumber(param1));
else if(op == "ROT")
    ExecuteRot(AsNumber(param1));
else if(op == "SZE")
    ExecuteSze(AsNumber(param1));
else if(op == "POS")
    ExecutePos((AsNumber(param1), AsNumber(param2));
else if(op == "DIR")
    ExecuteDir((AsNumber(param1), AsNumber(param2));
else if(op == "SET")
    ExecuteSet(param1, AsNumber(param2));
else if(op == "EVL")
    ...

The more commands are supported the more string comparisions I'll have to do to identify and call the associated method.

Can you point me to a more efficient implementation in the described scenario?

share|improve this question
    
I cannot say strongly enough how much you should avoid rolling your own language. While I personally love such projects (compilers are the only topic in CS I adore as much as games), experience has shown that every last single "simple" scripting engine eventually (out of increasingly complex user needs) gains all the features of a "real" scripting language, but ends up being a complete mess of an implementation, is horribly difficult to use and error prone, has no documentation or dev tools, and is slow. You should strongly consider just embedding Lua or the like. –  Sean Middleditch Nov 30 '12 at 23:42
    
@Sean: I agree that in almost all game projects you're better of with a "real" scripting language or none at all. But in this specific case my custom language will have some unique features that I can't afford missing. –  lithander Dec 1 '12 at 0:05
    
Such as? It's entirely possible there's another existing lesser known option out there I or someone else can point you to. –  Sean Middleditch Dec 1 '12 at 1:21
add comment

2 Answers

You can use a hash table that maps the command names to either an integer enum/define or to function pointers that implement the actual command.

Hash Tables

I usually roll my own simple hash tables so you'll need to find a library you like or do the same. I'll use a placeholder "HashTable" in these examples.

Enum/Define

If you use an Enum/Define you might also put all the command execution in a big switch/case. This will save a lot of overhead on function calls.

Example:

enum OpType { OP_MOV, OP_ROT, ... };

HashTable<string,OpType> op_lookup;  // HashTable is a placeholder type
op_lookup["MOV"] = OP_MOV;
...

// parse 'op', 'param1', 'param2'
switch (op_lookup[op])
{
  case OP_MOV:
    // perform mov code here
    break;  // or 'continue'
  ...
}

Function Pointers

Use globals or class variables for 'op', 'param1', and 'param2' values to avoid having to pass them around as parameters.

typedef void (*OpFn)();

void OpMov()
{
  // use global 'param1' etc.
}
...

HashTable<string,OpFn> op_dispatch;
op_dispatch["mov"] = OpMov;
...

// parse 'op', 'param1', 'param2'
op_dispatch[op]();  // call the function that 'op' maps to.

Note: code examples are untested.

share|improve this answer
    
Modern C++ compilers come with a standard hash table, namely std::unordered_map. –  Kylotan Dec 1 '12 at 0:57
    
Using a hash table is clever. But it seems as if the look-up cost varies greatly between implementations: xiongz-hft.blogspot.de/2012/01/… –  lithander Dec 1 '12 at 4:52
    
When looking at that comparison it's worth noting that neither std::map or a trie are hash tables. They are trees which will indeed perform very differently because they are different algorithms. –  Adam Dec 2 '12 at 14:13
    
@lithander: games typically avoid the STL containers simply because they do have such bad performance characters for the needs we run into. it's not even that std::unordered_map is slow so much as it was designed for specific use cases that games almost never care about (e.g., storing large or expensive-to-copy objects vs small primitives and pointers/handles). building one's own game-friendly efficient hash table is a one-afternoon exercise, thankfully. that said, a hash table for interpreter operations is going to be pretty slow compared to other options. –  Sean Middleditch Dec 2 '12 at 20:32
add comment

You could use the concept of opcodes, which it seems like you're doing already (to some extent, with strings).

To gain some efficiency, you could parse your script files and generate a list of opcodes. The opcodes could be an enum or just integers. This would allow you to use a simple switch where the cases are your opcodes. To run the script, you just keep looping through the list of codes until you get to the end.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.