Skip to content

Instantly share code, notes, and snippets.

@ovaillancourt
Created October 16, 2012 02:54
Show Gist options
  • Save ovaillancourt/3897003 to your computer and use it in GitHub Desktop.
Save ovaillancourt/3897003 to your computer and use it in GitHub Desktop.
/**
* Very simple Mealy style finite state machine relying on a nodejs event emitter. Absolutely not tested
* and contains absolutely no fancy feature. You can put multiple "from" and "to" states by separating
* them with a space. Putting no "from" or "to" amounts to setting them as "undefined", a supported state.
*/
// Example:
/*
var fsm = new Fsm([
{ name : 'wakingup' , from: 'sleeping' , to : 'awake' },
{ name : 'goingToSleep' , from: 'awake' , to : 'sleeping' },
{ name : 'transition' , from: '*' , to : '*' },
{ name : 'birth' , to : '*' },
{ name : 'death' , from: '*' }
]);
fsm.on( 'wakingup', function(){ console.log( 'waking up' ); } );
fsm.on( 'goingToSleep', function(){ console.log( 'going to sleep' ); } );
fsm.on( 'transition', function(){ console.log( 'transition' ); } );
fsm.on( 'birth', function(){ console.log( 'birth' ); } );
fsm.on( 'death', function(){ console.log( 'death' ); } );
fsm.start( 'awake' );
console.log( '----' );
fsm.transitionTo( 'sleeping' );
console.log( '----' );
fsm.transitionTo( 'awake' );
console.log( '----' );
fsm.end();
*/
// Constructor
function Fsm( transitionTable ){
require( 'events' ).EventEmitter.call( this );
if( !Array.isArray( transitionTable ) ){
throw new Error( 'the transitionTable argument must be an array in the fsm constructor' );
}
this.tt = transitionTable;
this.current = undefined;
this.context = {};
this._initTable();
}
// Inheritance
Fsm.prototype = Object.create( require( 'events').EventEmitter.prototype );
// Methods
function convert( obj ){
return typeof obj === 'string' && obj !== '*'
? obj.split( ' ' ) : obj;
}
Fsm.prototype._initTable = function(){
for( var i = 0; i < this.tt.length; ++i ){
this.tt[ i ].from = convert( this.tt[ i ].from );
this.tt[ i ].to = convert( this.tt[ i ].to );
}
}
Fsm.prototype.start = function( stateName ){
this.transitionTo( stateName );
}
Fsm.prototype.end = function(){
this.transitionTo( undefined );
}
function isMatch( ref, target ){
return ( Array.isArray( target ) && ~target.indexOf( ref ) ) ||
( typeof ref === 'undefined' && typeof target === 'undefined' ) ||
( typeof target === 'string' && target === '*' );
}
Fsm.prototype.transitionTo = function( stateName ){
var transitioned = false;
for( var i = 0; i < this.tt.length; ++i ){
var cur = this.tt[ i ];
var match = isMatch( this.current, cur.from ) &&
isMatch( stateName, cur.to );
if( match && cur.name ){
this.emit( cur.name, this.context );
transitioned = true;
}
}
if( transitioned ){
this.current = stateName;
}
else{
this.emit( '-invalid-', this.current, stateName );
}
}
module.exports = Fsm;
@tj
Copy link

tj commented Oct 16, 2012

state machine would make a nice component :D :D :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment