== Introduction == This the fourth and last part of this recipe provides a few state machines for use with the validator. == North American Telephone Number State Machine == {{{ #!python from StateMachine import EBadCharacter, StateMachine def areacode ( self, token ) : needClosingParen = False if token == '(' : needClosingParen = True token = self . nextToken ( ) else : self . allTokens . insert ( 0, '(' ) for c in range ( 3 ) : if not token . isdigit ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if needClosingParen : if token == ')' : token = self . nextToken ( ) else : raise EBadCharacter ( ) else : self . allTokens . insert ( len ( self . allTokens ) - 1, ')' ) if token == ' ' : token = self . nextToken ( ) else : self . allTokens . insert ( len ( self . allTokens ) - 1, ' ' ) return 'exchange', token def exchange ( self, token ) : for c in range ( 3 ) : if not token . isdigit ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if token == '-' : token = self . nextToken ( ) elif token == ' ' : token = self . nextToken ( ) self . allTokens [ len ( self . allTokens ) - 2 ] = '-' else : self . allTokens . insert ( len ( self . allTokens ) - 1, '-' ) return 'local', token def local ( self, token ) : for c in range ( 4 ) : if not token . isdigit ( ) : raise EBadCharacter ( ) if c < 3 : token = self . nextToken ( ) return 'complete', None def complete ( self, token ) : pass statemachine = StateMachine ( ) statemachine . addState ( areacode ) statemachine . addState ( exchange ) statemachine . addState ( local ) statemachine . addState ( complete, True ) statemachine . setStartState ( areacode ) }}} == Canadian Postal Code State Machine == {{{ #!python from StateMachine import EBadCharacter, StateMachine def local ( self, token ) : if not token . isalpha ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if not token . isdigit ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if not token . isalpha ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if token == ' ' : token = self . nextToken ( ) if not token . isdigit ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if not token . isalpha ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if not token . isdigit ( ) : raise EBadCharacter ( ) return 'complete', None def complete ( self, token ) : pass statemachine = StateMachine ( ) statemachine . addState ( local ) statemachine . addState ( complete, True ) statemachine . setStartState ( local ) }}} == UK Postcode State Machine == {{{ #!python from StateMachine import EBadCharacter, StateMachine def postcodeFirstPart ( self, token ) : firstToken = token if not firstToken . isalpha ( ) : raise EBadCharacter ( ) secondToken = self . nextToken ( ) if secondToken . isalpha ( ) : thirdToken = self . nextToken ( ) if thirdToken . upper ( ) == 'R' and secondToken . upper ( ) == 'I' and firstToken . upper ( ) == 'G' : token = self . nextToken ( ) return "postcodeAfterBlank", token self . pushToken ( ) else : self . pushToken ( ) if not token . isalpha ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if not token . isdigit ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if token == ' ' : return "postcodeAfterBlank", token if not token . isalnum ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if token != ' ' : raise EBadCharacter ( ) return "postcodeAfterBlank", token def postcodeAfterBlank ( self, token ) : if token != ' ' : raise EBadCharacter ( ) token = self . nextToken ( ) if not token . isdigit ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if not token . isalpha ( ) : raise EBadCharacter ( ) token = self . nextToken ( ) if not token . isalpha ( ) : raise EBadCharacter ( ) return 'complete', None def complete ( self, token ) : pass statemachine = StateMachine ( ) statemachine . addState ( postcodeFirstPart ) statemachine . addState ( postcodeAfterBlank ) statemachine . addState ( complete, True ) statemachine . setStartState ( postcodeFirstPart ) }}} == Zip Code State Machine == {{{ #!python from StateMachine import EBadCharacter, StateMachine def local ( self, token ) : for c in range ( 5 ) : if not token . isdigit ( ) : raise EBadCharacter ( ) if c < 4 : token = self . nextToken ( ) return 'complete', None def complete ( self, token ) : pass statemachine = StateMachine ( ) statemachine . addState ( local ) statemachine . addState ( complete, True ) statemachine . setStartState ( local ) }}} == Picture Processing State Machine == {{{ #!python from StateMachine import EBadCharacter, BADCHARACTER, STRINGTOOLONG, OK, StateMachine ##+ algebraic sign ##9 digit only ##A alphabetic only ##N alphanumeric only ##X anycharacter ##Y Y or N only ##? previous character not optional ##all other characters in the picture literal def local ( self, token ) : picture = self . picture . upper ( ) optional = [ ] for i, c in enumerate ( picture ) : if c == '?' : optional [ -1 + len ( optional ) ] = True else : optional . append ( False ) picture = picture . replace ( '?', '' ) for i, pictureChar in enumerate ( picture ) : if pictureChar == 'A' and token . isalpha ( ) : pass elif pictureChar == '9' and token . isdigit ( ) : pass elif pictureChar == '+' and token in [ '+', '-', ] : pass elif pictureChar == 'N' and token . isalnum ( ) : pass elif pictureChar == 'Y' and token in [ 'Y', 'N', ] : pass elif pictureChar == 'X' : pass else : if pictureChar in [ 'A', '9', '+', 'N', 'Y', 'X', ] : if optional [ i ] : continue else : raise EBadCharacter ( ) else : if pictureChar == token : pass else : if optional [ i ] : continue else : raise EBadCharacter ( ) if i + 1 < len ( picture ) : token = self . nextToken ( ) else : break return 'complete', None def complete ( self, token ) : pass statemachine = StateMachine ( ) statemachine . addState ( local ) statemachine . addState ( complete, True ) statemachine . setStartState ( local ) if __name__ == "__main__" : import unittest class TestAgainstPostalCodes ( unittest . TestCase ) : def test1 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( 'N0A 1P0' ), OK ) def test2 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( 'N0A1P0' ), OK ) def test3 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( '-0A1P0' ), BADCHARACTER ) def test4 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( '10A1P0' ), BADCHARACTER ) def test5 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( 'NaA1P0' ), BADCHARACTER ) def test6 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( 'N021P0' ), BADCHARACTER ) def test7 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( 'N0AaP0' ), BADCHARACTER ) def test8 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( 'N0A120' ), BADCHARACTER ) def test9 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( 'N0A1Pz' ), BADCHARACTER ) def test10 ( self ) : statemachine . picture = 'a9a ?9a9' self . assertEqual ( statemachine . testString ( 'N0A1P00' ), STRINGTOOLONG ) def test11 ( self ) : statemachine . picture = '+?999.?9?' self . assertEqual ( statemachine . testString ( '-111.1' ), OK ) def test12 ( self ) : statemachine . picture = '+?999.?9?' self . assertEqual ( statemachine . testString ( '-1111' ), OK ) def test13 ( self ) : statemachine . picture = '+?999.?9?' self . assertEqual ( statemachine . testString ( '-111.11' ), STRINGTOOLONG ) def test14 ( self ) : statemachine . picture = '+?999.?9?' self . assertEqual ( statemachine . testString ( '+111.1' ), OK ) def test15 ( self ) : statemachine . picture = '+?999.?9?' self . assertEqual ( statemachine . testString ( '111.1' ), OK ) def test16 ( self ) : statemachine . picture = '+?999.?9?' self . assertEqual ( statemachine . testString ( '+111.1' ), OK ) unittest . main ( ) }}} == A CareFree State Machine -- Accepts Anything == {{{ #!python from StateMachine import EBadCharacter, BADCHARACTER, STRINGTOOLONG, OK, StateMachine class StateMachine : def testString ( self, str ) : return OK statemachine = StateMachine ( ) if __name__ == "__main__" : import unittest class TestAgainstPostalCodes ( unittest . TestCase ) : def test1 ( self ) : self . assertEqual ( statemachine . testString ( 'N0A 1P0' ), OK ) def test2 ( self ) : self . assertEqual ( statemachine . testString ( None ), OK ) def test3 ( self ) : self . assertEqual ( statemachine . testString ( '' ), OK ) def test4 ( self ) : self . assertEqual ( statemachine . testString ( '22' ), OK ) def test5 ( self ) : self . assertEqual ( statemachine . testString ( '22x' ), OK ) def test6 ( self ) : self . assertEqual ( statemachine . testString ( '2291' ), OK ) def test7 ( self ) : self . assertEqual ( statemachine . testString ( '229x' ), OK ) def test8 ( self ) : self . assertEqual ( statemachine . testString ( '22999' ), OK ) unittest . main ( ) }}} === Comments === I welcome your comments. - [[Bill Bell]]