== Introduction ==

Validators are objects that are used for checking syntax, often of the fairly short items that users enter by way of edit boxes in dialogs. State machines are an easily understood, easily comprehended way of representing the small grammars that typically describe these items of data. Hence the motivation to use state machines as the basis for validators.

Incidentally, regular expressions, which are equivalent to state machines, can be used to check input. However, in the context of accepting input from a user one character at a time, say in an edit box, one would like to be able to test this input incrementally, that is, as it arrives, to be able to offer immediate feedback.

This recipe is in four parts.

The first part provides state machine code that is derived from that presented by David Mertz' in his 
introduction to using state machines, which is available at 
http://www-106.ibm.com/developerworks/linux/library/l-python-state.html.

The second part shows how to use the state machine code to test the acceptability of strings against a simple grammar.

The third part exhibits the validator that uses state machines.

The fourth part provides some state machines that work with the validator.

== What Objects are Involved ==

This code imports only one function from the `new` module that is standard Python. 

== Process Overview ==

One begins the job of creating a validator that is based on a state machine by creating and verifying the state machine. In this recipe part, though, the aim is not to build a state machine that will be used in a validator, only to exhibit a complete state machine in a simple setting.

A state machine is driven through its various states by a series of tokens. The sequence of tokens sent to the machine is said to be "acceptable" if it drives the machine to an "acceptable" end state. Otherwise, the sequence is deemed to be "unacceptable". In this example, the tokens that drive the machine are numbers that are generated by `math_func`. In a validator the sequence of tokens sent to the state machine is the sequence of characters entered by the user.

The functions called `ones_counter`, `tens_counter`, and so on, in fact all those with formal parameters `self` and `token`, represent the "states" of the machine. It is the responsibility of each of these functions to examine incoming tokens and, in some cases and in come circumstances, to accept further tokens for examination, and to decide which state should be selected next--or to reject the token, possibly by raising an exception.

Enough about details like that. Suffice to say that the `StateMachine` class presented here breathes life into the states, and that I hope to have saved you the need to change this class.

However, you should note that my class differs a little from Mertz':


    * Each state function has two parameters, the second being the one for the token that it has been passed either by `StateMachine` directly, or by another state function. This source of tokens has to be written as a generator--like my version of math_func.

    * Use the `StateMachine` method called `setTokens` to register the tokens source.

    * The `StateMachine` method called 'run' takes no parameters (unlike Mertz' version). Rather, it obtains the first token itself, from the tokens source.

    * `addState` does a little more than Mertz' version. It arranges to add the function passed to it to the class instance at run-time. I am indebted to Brett Cannon and Alex Martelli for their recipe entitled "Dynamically added methods to a class" at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732.

    * I have added some convenience functions, for passing strings to be tested to the state machine and then exercising it, and also to ease the task of returning results and interpreting them.

== Code Sample ==

{{{
#!python
from new import instancemethod

class EStringTooShort ( Exception ) : pass
class EBadCharacter ( Exception ) : pass

class TestStringResult :
    
    def __init__ ( self, name, value ) :
        
        self . name = name
        self . value = value
        
    def rejectCharacter ( self ) :
        
        return self . value in [ 1, 4 ]
        
    def validatedString ( self ) :
        
        return self . value == 3
        
    def __str__ ( self ) :
        
        return self . name
        
BADCHARACTER = TestStringResult ( 'BADCHARACTER', 1 )
STRINGTOOSHORT = TestStringResult ( 'STRINGTOOSHORT', 2 )
OK = TestStringResult ( 'OK', 3 )
STRINGTOOLONG = TestStringResult ( 'STRINGTOOLONG', 4 )

class StateMachine :
    
    def __init__ ( self ) :
        
        self . handlers = { }
        self . startState = None
        self . endStates = [ ]
        self . allTokens = [ ]

    def addState ( self, handler, endState = False ) :
        
        method = instancemethod ( handler, self, StateMachine )
        self . __dict__ [ handler . __name__ ] = method
        name = handler . __name__ 
        self . handlers [ name ] = method 
        if endState : self . endStates . append ( name )
            
    def testString ( self, stringToTest ) :
        
        def oneAtATime ( str ) :
            
            for c in str : yield c
            raise EStringTooShort ( )
            
        self . allTokens = [ ]
        self . setTokens ( oneAtATime ( stringToTest ) )
        try : self . run ( )
        except EBadCharacter : return BADCHARACTER 
        except EStringTooShort : return STRINGTOOSHORT
    
        try : self . nextToken ( )
        except EStringTooShort : return OK
        else : return STRINGTOOLONG
            
    def setTokens ( self, tokens ) :
        
        self . tokens = tokens
    
    def nextToken ( self ) :
        
        if len ( self . pushedTokens ) :
            newToken = self . pushedTokens . pop ( -1 )
        else :
            newToken = self . tokens . next ( )
        self . allTokens . append ( newToken )
        return newToken 
        
    def pushToken ( self ) :
        
        token = self . allTokens . pop ( -1 )
        self . pushedTokens . append ( token )

    def setStartState ( self, handler ) :
        
        self . startState = handler . __name__ 

    def run ( self ) :
        
        try :
            handler = self . handlers [ self . startState ]
        except :
            raise "InitializationError", "must call .setStartState() before .run()"
            
        self . pushedTokens = [ ]
        token = self . nextToken ( )    
        
        if not self . endStates :
            raise "InitializationError", "at least one state must be an end state"
        
        while 1 :
            newState, token = handler ( token )
            if newState  in self . endStates:
                if self . handlers [ newState ] : 
                    self . handlers [ newState ] ( token )
                break 
            else:
                handler = self . handlers [ newState ] 
                
if __name__ == "__main__" :
    
    from math import sin
                
    def ones_state ( self, token ) :
        
        print "ONES_STATE: ",
        while 1:
            if token <= 0 or token >= 30 : newState =  "end_state" ; break
            elif 20 <= token < 30 : newState =  "twenties_state"; break
            elif 10 <= token < 20 : newState =  "tens_state"; break
            else : print " @ %2.1f+" % token,
            token = self . nextToken ( )
        print " >>"
        return newState, token

    def tens_state ( self, token ) :
        
        print "TENS_STATE: ",
        while 1:
            if token <= 0 or token >= 30 : newState =  "end_state"; break
            elif 1 <= token < 10 : newState =  "ones_state"; break
            elif 20 <= token < 30 : newState =  "twenties_state"; break
            else : print " #%2.1f+" % token,
            token = self . nextToken ( )
        print " >>"
        return newState, token

    def twenties_state ( self, token ) :
        
        print "TWENTIES_STATE: ",
        while 1:
            if token <= 0  or  token >= 30 : newState =  "end_state"; break
            elif 1 <= token < 10 : newState =  "ones_state"; break
            elif 10 <= token < 20 : newState =  "tens_state"; break
            else : print " *%2.1f+" % token,
            token = self . nextToken ( )
        print " >>"
        return newState, token
        
    def end_state ( self, token ) :
        
        print "END_STATE: %2.1f" % token
        
    def math_func ( n ) : 
        
        while 1 :
            n = abs ( sin ( n ) ) * 31
            yield n

    statemachine = StateMachine ( )
    statemachine . addState ( ones_state )
    statemachine . addState ( tens_state )
    statemachine . addState ( twenties_state )
    statemachine . addState ( end_state, True )
    statemachine . setStartState ( ones_state )
    statemachine . setTokens ( math_func ( 1 ) )
    statemachine . run ( )
    }}}
    
=== Comments ===

I welcome your comments. - [[Bill Bell]]