Posts Tagged ‘DSL’

A Text Adventure DSL in Groovy

Thursday, May 21st, 2009

Reading Peter Bell’s write-up on Guillaume LaForge’s talk on creating DSLs with Groovy has inspired me to chime in with my own experience with Groovy DSLs. The following is a transcription of sorts of the first part of a talk I’ve given a few times titled “Building DSLs with Grooy: A Real-World Case Study” (slides available here). This section of the talk introduces the idea of DSLs, and how Groovy is well-suited to creating them, by building a small adventure game interpreter. Enjoy.

If you’re like me, you have intense nostalgia for the text adventure games of the early 80s. Games like Zork, or the Sierra line of adventure games like King’s Quest were popular in part because of their natural language interface.

As an homage to these types of games, I thought we could write our own simple one. We’ll use Java as a starting point, since that’s what I know best, and will just be purely text-based.  In fact our only interface will be a nice big text area where the player can type in commands, and get responses from the game.

And you know, I don’t really want to go through the trouble of writing a natural language parser, so I think we’ll just have the player enter straight Java code into the text area and we’ll have our game compile it, run it, and then return the result.

Here’s some example output right here:

game.go("north");
> You go North.
game.look();
> You see a dagger on the ground.
game.take("dagger");
> You take the dagger.

Note that the player needs to know that they’re interacting with a “game” object, but that’s OK, we’ll be sure to put that in the Javadoc — or the player’s manual. As long as they preface every command with “game.” they’ll be OK.

We’re immediately, however, faced with the problem of Java’s static compilation. I suppose we could grab each command from the user, wrap it in a Java class file, invoke javac on it, and run it, but clearly that’s no good.

No worries though — this is a Groovy talk, so let’s use Groovy! In fact, we can just rename all our Java files to Groovy files, and we’re where we were with the Java version except now we are able to dynamically execute code via the GroovyShell. Great! Now we can actually implement this contrived example.

Right away too here, let’s just squirrel away the call to “game.” since we know every command needs to operate on this object. This will save the user a bit of hassle.

new GroovyShell().evaluate("game.${playerInput}")

And too, this doesn’t sit quite right as being a very “groovy” thing to do, but make a note of this, and we’ll come back to it later. So now the player can just enter commands without the “game.” preface, like so:

go('north');
look();
take('dagger');

Well, but since we made the switch to Groovy, we might as well take advantage of the syntactic sugar. In Groovy semicolons are completely optional, and parentheses are optional provided the method that you’re calling has at least one argument. So now we have this.

go 'north'
look()
take 'dagger'

And here’s our Game class where just every command is mapped to a method with whatever arguments they take. The player just has to remember to enclose any arguments in quotes so it will be treated as a String.

class Game {
  void go(dir) {
    println "You go $dir"
  }
  void look() {
    println "You look around and see nothing"
  }
  void take(it) {
    println "You take the $it"
  }
  ...
}

So here’s where things will start to get interesting. Groovy is a dynamic language, and because of that, it allows us to intercept things like method calls and property access. If we leave off the quotes on a String argument, it will assume that we’re referring to a property (or field) by that name and it will try to call get “whatever” on it. Of course things like “north” and “dagger” don’t exist as properties, so a special method called “propertyMissing” will be called to give us a chance to do something before the exceptions start flying.

If we override this method for the Game class then, we can just assume that any missing property was meant to be an argument of that name, so we’ll just return the name, which is a way of faking that the property actually exists.

class Game {
  ...
  def propertyMissing(String name) {
    name
  }
}

Which enables the player to now leave off the parens on command arguments:

go north
look()
take dagger

Good. So that gets around the quote problem. Now the only wart on the player’s experience is having to remember to use parenthesis on any command that doesn’t take arguments, such as “look”.
So let’s fix that now. Remember, if an identifier is standing off by itself like this, then Groovy will assume that it’s a property — so we’ll just go back to our friend “propertyMissing” and add in this little chunk of code.

def propertyMissing(String name) {
  if (metaClass.respondsTo(this, name)) {
    this."$name"()
  }
  name
}

This says that if you are missing a property by this name, first see if you have a method by this name, and if so call it, otherwise, just return the property like before. So now we have this:

go north
look
take dagger

Hey, this is basically a Zork level of understanding! But we can do better still…

Remember the contrived “game.” preface?

new GroovyShell().evaluate("game.${playerInput}")

That’s fine for a serial one command after another type thing, but let’s say that we want to open this up for players to write bots against so they can write automated scripts and “level grind” without the hassle of actually playing.

So let’s employ the Groovy keyword “with”, which takes a closure as an argument and basically sets up a little mini-context for us.

new GroovyShell().evaluate("game.with{${playerInput}}")

So what we’re saying here is “use this game object as the context for the following statements”. That is, this closure is within the scope of the game object first and foremost. Now that opens us up to scripting some of our code thusly:

3.times {
  go north
}
look
take dagger

I can now use regular Groovy syntax, such as closures, and say “go north three times”.

Depending on how I construct my command interpreter, one could imagine constructing arbitrarily powerful scripts like “keep wandering until you see a monster, and attack it as long as it’s 3 or more levels below me and I’m above half health.” Great, so now we can level grind while at work, and reap the benefits when we log on in the evening without having to resort to Gold Farming.

So let’s look at one more problem that plagues any natural language interface, and especially text adventure games of this era — the Synonym Problem (or as my 5 year old says, “Cinnamons” — so adorable). This problem can be best illustrated via an example.

Here the game only understands “take”, but the player (quite reasonably) doesn’t know this and is having difficulty finding the correct command:

look
> You see a dagger on the ground
get dagger
> I don’t understand that
grab dagger
> I don’t understand that
Just let me have the %$!#@ dagger!
> I don’t understand that

In this example “get”, “grab” and “take” all seem like very reasonable commands that a game should understand. Since we’ve implemented each command as a method, it stands to reason that we could simply try to anticipate all of the terms that a player might come up with and implement each one as a method that calls the main “take” method. But that’s not very Groovy.

In Groovy, if a method is called that doesn’t exist, “methodMissing” is called to give the class a chance to do something about it.

def methodMissing(String name, args) {
  if (['grab', 'hold', 'yoink'].contains(name)) {
    this.take(args[0])
  }
}

Here we override methodMissing and attempt to match the unknown command with a list of known synonyms. For illustrative purposes it’s implemented as a static list, but one could just as easily imagine dynamically looking the command up in a thesaurus – this is happening at runtime after all. This little change makes for a happier player:

look
> You see a dagger on the ground
grab dagger
> You take the dagger
You’re darn right I do!
> I don’t understand that

Better still, if we do find a match, we can graft the synonym onto the Game class as a new dynamic method since it stands to reason that if a player uses “grab” instead of “take” once, they will probably continue to do so, so we might as well make it a method. Incidentally this is how GORM works with finder methods, or so I’ve been told.

def methodMissing(String name, args) {
  def funcName = ThesaurusLookup.contains(name)
  if (funcName) {
    Game.metaClass."$name" = { item ->
      this."$funcName"(item)
    }
    this."$name"(args[0])
  }
}

Here all the new found method does is forward to whatever the program-sanctioned method was supposed to be. We can do this through the power of Groovy’s ExpandoMetaClass, which every Groovy class has.

So if we take a step back here, we can see how far some of Groovy’s language features have been able to take us. Here’s the original Java version that we started with:

game.go("north");
game.look();
game.take("dagger");
game.take("fish");

…and here’s the Groovy version that we ended with:

go north
look
take dagger
grab fish

I don’t advocate writing an actual game this way (for example, this way only supports “verb noun” commands), but as an experiment, it’s interesting to see how far we can go without writing any sort of lexer or parser.

The Map vs. the Terrain (or DSLs as Documentation)

Friday, December 12th, 2008

In the late David Foster Wallace’s Infinite Jest, several of the main characters participate in a grand scale “Scorched Earth” style turn-based artillery game on a set of tennis courts. The object for each player was to lob tennis balls across court to opposing player’s targets where the balls represent some sort of ballistic warhead complete with a damage radius.

At one point it starts snowing, and one of the players argues that the damage caused by a recent “explosion” would not be as great due to the snow. One of the other players retorts that “It’s snowing on the map, not the [gosh darn] terrain!”, meaning that there was a confusion between where the line lay between the game world and that which represented the game world — that is, the terrain and the map.

It seems to me that there is a similar struggle going on within software engineering: between the software we write (the terrain), and that which describes the software (the map). Typically the “map” to software’s “terrain” has been documentation. One of the eternal (or more accurately: perennial) issues in the projects that I’ve been a part of has revolved around the tension of documentation.

In a previous project of years past, we were told to document every single method and variable. That of course didn’t work out since at that point, you get a map that perfectly describes (or even over-describes) the terrain. Beyond the maintenance nightmare (and gross violation of the DRY principle), when your “map” effectively covers your “terrain”, you start to call into question just who is supposed to be describing who.

Thankfully I’ve spent the vast majority of my time on the other end of the spectrum, more toward the realm of (attempting) “self-documenting code”, supported by languages that are designed more for humans than computers.  This is because in-line comments as documentation is not the entire story — code can be used to document itself — and this is where things start to get interesting.

Some advocate that unit tests should serve as a primary source of documentation. Neal Ford said in a recent talk that the first thing he does when he returns to a project after being away is not to catch up on documentation or even the codebase, but is to read the tests.

Unfortunately I have a passive-aggressive approach to writing documentation, in that when I’m told to write it for its own sake, I actually end up writing less. So if it starts sinking into my head that writing tests is like writing documentation, I’m likely to just write fewer tests.

But this idea goes further still: with so-called Behavior Driven Development (BDD) we start approaching an “executable specification”. With BDD the idea is almost completely turned around and the software becomes the map to describe the terrain of behaviors.

I’m not sure what I think about BDD yet, or for that matter even TDD. It may be that they are super productive, super-collaborative and error-less ways to develop software — but they are also extremely intrusive. I think it would be difficult to be lukewarm about either of the “DD”s since it requires such a significant buy-in.

Fortunately self-describing code found another way into my life.

In my most recent project, we used an internal DSL, partly for ease of development, but also, as a nice side-effect, for its self-documenting nature. Internal DSLs (more or less by definition) aim to be more readable than the host language in which they reside. This makes for code that more or less matches anything that I would put in a javadoc comment.

In many ways I think it makes sense to write entire applications in a combination of various DSLs. This isn’t much of a stretch since we basically do this already (SQL in the DA layer, declarative UI languages in the presentation layer).

It seems that most “business” layers would additionally benefit from their own DSL. What’s more is that we’re starting to see tools that will aid in generating DSLs, such as Language Workbenches, and on a much smaller scale, Groovy’s MetaBuilder.

As we migrate more toward each piece of an application being written in a language that perfectly describes the domain, perhaps we’ll even live to see the death of the in-line comment in our lifetimes.

Incidentally, if you want to learn more about DSLs and how you might benefit from them (and you live in the Chicago area), then come to my talk “Using Groovy to Create DSLs: A Real-World Case Study” at the first ever Chicago Groovy User Group on January 13th 2009.