Implementing a Finite State Machine with a Ruby Domain Specific Language
August 14th, 2007 by proj
DSL-FSM.. that kinda sounds like some wierd internet sub-culture. It stands for domain specific language - finite state machine. I proposed both topics as presentation material for the austin-ruby group that I meet with once a month. Steven Harms the organizer recommended both/either so I combined it into one piece of code as it actually works pretty well that way.
The DSL that I’m using is pretty small right now, which lends itself to easier understanding and explanation.
Ruby code tarball can be downloaded here.
A FSM is defined in a ruby script file without any class definition using the following rules:
- All attributes are specified in a method named ’setup’. Setup takes an argument hash which may contain arguments that can be used to setup a FSM entity.
- All states are specified by defining top level methods with the name ‘while<statename>’. These methods are called in a tick update while that state is active.
- All transitions are initiated by saying start :statename
def whileidleAll FSM entity templates can be loaded in the world using world.load(’template-name’). The world is currently driven by a simple script named ’simulate.rb’ which allows you to single step the world simulation and watch all the state machines make their choices. There are some debugging tools available. You can list all active entities and dump the world grid.target_nearestendstart :chasing if @target
World#load uses the Object#instance_eval method to evaluate the contents of the FSM definition in terms of a FSM base instance. The FSM class defines some methods that can be called within the FSM definition to control state transitions and to access common methods.
Practically speak this is a very simple way to enable easy extension of a system. It provides an easier path to refactor the individual state machines and lowers the barrier to entry for writing state machine code. Additionally, it would be nice to include certain types of behavior from modules so that state machine authors could have easy access to some of the methods currently defined in creature FSM. This would include methods like target_nearest. You could also mix in native C modules to get access to things like a path finding grid.
Ideas for playing with the code:
- Make creatures pick up the ‘ammo’ entities
- Make creatures compete for the ‘fark’ entities
- Add a new term to the FSM for state transitions. The method names should be named ‘whenchangingfrom<statename>, whenchangingto<statename>, whenchangingfrom<statename>to_<statename>’ and should be executed when those state change patterns are matched.
- Allow entities to die
A great article about some of the different types of DSL creation and the mindset from one of the coders at 37signals:
http://weblog.jamisbuck.org/2006/4/20/writing-domain-specific-languages
Careful of using missing_method, it bit me in the arse a number of times:
http://redhanded.hobix.com/inspect/theBestOfMethod_missing.html
It’s really hard to track down bugs related to methodmissing, any code you write inside an object implementing this needs to be carefully checked. It can also lead to blowing your stack when an error in methodmissing recalls method_missing.. and on and on.
This little gem is nice to keep in your back pocket when trying to raise a descriptive exception:
http://blog.nicksieger.com/articles/2006/09/06/rubys-exception-hierarchy
Related posts:







Hi, I thought of doing a ‘fsm’ module for Ruby, based on your idea to design it as a DSL. Just letting you know, since I don’t want to think I ripped your idea if you see the code on rubyforge or something. I had a non-DSL version at http://trac.v01d.com.ar/fsm . I’ll upload the DSL version there, if you’re interested.
That’s great! I would love to see what you come up with. I had a lot of ideas on how I could enhance the features of the FSM within the DSL framework I initially provided. If you’re interested in hosting it on rubyforge and would like to have an extra contributor let me know.
Hi again, I just uploaded this version to svn, and updated the wiki at http://trac.v01d.com.ar
I’m getting:
Sorry, I just fixed it. The direct URL for the project is http://trac.v01d.com.ar/fsm (/ lists all my trac projects).
I think I might need anonymous read access. I tried:
svn list http://svn.v01d.com.ar/fsm/trunk
It asks me for my credentials.
Anonymous access is enabled, try leaving user and password blank, if it asks. In any case, you should be able to browse the sources using the “Browse Source” on http://trac.v01d.com.ar/fsm. Also, at the bottom of that page, there’s a tgz file of the last version.
OH, didn’t notice you were using http, the svn address is svn://. But, as I said, it is not necessary to access using the svn command if you just want to look at the code.