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

   1 from StateMachine import EBadCharacter, StateMachine
   2 
   3 def areacode ( self, token ) :
   4     
   5     needClosingParen = False
   6     if token == '(' :
   7         needClosingParen = True
   8         token = self . nextToken ( )
   9     else :
  10         self . allTokens . insert ( 0, '(' )
  11     for c in range ( 3 ) :
  12         if not token . isdigit ( ) : raise EBadCharacter ( ) 
  13         token = self . nextToken ( )
  14     if needClosingParen :
  15         if token == ')' : 
  16             token = self . nextToken ( )
  17         else : raise EBadCharacter ( ) 
  18     else : 
  19         self . allTokens . insert ( len ( self . allTokens ) - 1,  ')' )
  20     if token == ' ' : token = self . nextToken ( )
  21     else :
  22         self . allTokens . insert ( len ( self . allTokens ) - 1,  ' ' )
  23     return 'exchange', token
  24     
  25 def exchange ( self, token ) :
  26     
  27     for c in range ( 3 ) :
  28         if not token . isdigit ( ) : raise EBadCharacter ( ) 
  29         token = self . nextToken ( )
  30     if token == '-' : token = self . nextToken ( )
  31     elif token == ' ' : 
  32         token = self . nextToken ( )
  33         self . allTokens [ len ( self . allTokens ) - 2 ] = '-' 
  34     else :
  35         self . allTokens . insert ( len ( self . allTokens ) - 1,  '-' )
  36     return 'local', token
  37     
  38 def local ( self, token ) :
  39     
  40     for c in range ( 4 ) :
  41         if not token . isdigit ( ) : raise EBadCharacter ( ) 
  42         if c < 3 : token = self . nextToken ( )
  43     return 'complete', None
  44     
  45 def complete ( self, token ) : pass
  46     
  47 statemachine = StateMachine ( )
  48 statemachine . addState ( areacode )
  49 statemachine . addState ( exchange )
  50 statemachine . addState ( local )
  51 statemachine . addState ( complete, True )
  52 statemachine . setStartState ( areacode )

Canadian Postal Code State Machine

   1 from StateMachine import EBadCharacter, StateMachine
   2     
   3 def local ( self, token ) :
   4     
   5     if not token . isalpha ( ) : raise EBadCharacter ( ) 
   6     token = self . nextToken ( )
   7     if not token . isdigit ( ) : raise EBadCharacter ( ) 
   8     token = self . nextToken ( )
   9     if not token . isalpha ( ) : raise EBadCharacter ( ) 
  10     token = self . nextToken ( )
  11             
  12     if token == ' ' : token = self . nextToken ( )
  13             
  14     if not token . isdigit ( ) : raise EBadCharacter ( ) 
  15     token = self . nextToken ( )
  16     if not token . isalpha ( ) : raise EBadCharacter ( ) 
  17     token = self . nextToken ( )
  18     if not token . isdigit ( ) : raise EBadCharacter ( ) 
  19 
  20     return 'complete', None
  21     
  22 def complete ( self, token ) : pass
  23     
  24 statemachine = StateMachine ( )
  25 statemachine . addState ( local )
  26 statemachine . addState ( complete, True )
  27 statemachine . setStartState ( local )

UK Postcode State Machine

   1 from StateMachine import EBadCharacter, StateMachine
   2 
   3 def postcodeFirstPart ( self, token ) :
   4     
   5     firstToken = token
   6     if not firstToken . isalpha ( ) : raise EBadCharacter ( )
   7     secondToken = self . nextToken ( )
   8     if secondToken . isalpha ( ) :
   9         thirdToken = self . nextToken ( )
  10         if thirdToken . upper ( ) == 'R' and secondToken . upper ( ) == 'I' and firstToken . upper ( ) == 'G' :
  11             token = self . nextToken ( )
  12             return "postcodeAfterBlank", token
  13         self . pushToken ( )
  14     else :
  15         self . pushToken ( )
  16     if not token . isalpha ( ) : raise EBadCharacter ( )
  17     token = self . nextToken ( )
  18     if not token . isdigit ( ) : raise EBadCharacter ( )
  19     token = self . nextToken ( )
  20     if token == ' ' : return "postcodeAfterBlank", token
  21     if not token . isalnum ( ) : raise EBadCharacter ( )
  22     token = self . nextToken ( )
  23     if token != ' ' : raise EBadCharacter ( )
  24     return "postcodeAfterBlank", token
  25 
  26 def postcodeAfterBlank ( self, token ) :
  27     
  28     if token != ' ' : raise EBadCharacter ( ) 
  29     token = self . nextToken ( )
  30     if not token . isdigit ( ) : raise EBadCharacter ( ) 
  31     token = self . nextToken ( )
  32     if not token . isalpha ( ) : raise EBadCharacter ( ) 
  33     token = self . nextToken ( )
  34     if not token . isalpha ( ) : raise EBadCharacter ( ) 
  35     return 'complete', None
  36     
  37 def complete ( self, token ) : pass
  38     
  39 statemachine = StateMachine ( )
  40 statemachine . addState ( postcodeFirstPart )
  41 statemachine . addState ( postcodeAfterBlank )
  42 statemachine . addState ( complete, True )
  43 statemachine . setStartState ( postcodeFirstPart )

Zip Code State Machine

   1 from StateMachine import EBadCharacter, StateMachine
   2 
   3 def local ( self, token ) :
   4     
   5     for c in range ( 5 ) :
   6         if not token . isdigit ( ) : raise EBadCharacter ( )
   7         if c < 4 : token = self . nextToken ( )
   8     return 'complete', None
   9     
  10 def complete ( self, token ) :  pass
  11 
  12 statemachine = StateMachine ( )
  13 statemachine . addState ( local )
  14 statemachine . addState ( complete, True )
  15 statemachine . setStartState ( local )

Picture Processing State Machine

   1 from StateMachine import EBadCharacter, BADCHARACTER, STRINGTOOLONG, OK, StateMachine
   2 
   3 ##+ algebraic sign
   4 ##9 digit only
   5 ##A alphabetic only
   6 ##N alphanumeric only
   7 ##X anycharacter
   8 ##Y Y or N only
   9 ##? previous character not optional
  10 ##all other characters in the picture literal
  11 
  12 def local ( self, token ) :
  13     
  14     picture = self . picture . upper ( )
  15     
  16     optional = [ ]
  17     for i, c in enumerate ( picture ) :
  18         if c == '?' : optional [ -1 + len ( optional ) ] = True
  19         else : optional . append ( False )
  20     picture = picture . replace ( '?', '' )
  21     
  22     for i, pictureChar in enumerate ( picture ) :
  23         if pictureChar == 'A' and token . isalpha ( ) : pass
  24         elif pictureChar == '9' and token . isdigit ( ) : pass
  25         elif pictureChar == '+' and token in [ '+', '-', ] : pass
  26         elif pictureChar == 'N' and token . isalnum ( ) : pass
  27         elif pictureChar == 'Y' and token in [ 'Y', 'N', ] : pass
  28         elif pictureChar == 'X' : pass
  29         else :
  30             if pictureChar in [ 'A', '9', '+', 'N', 'Y', 'X', ] :
  31                 if optional [ i ] : continue
  32                 else : raise EBadCharacter ( )
  33             else :
  34                 if pictureChar == token  : pass
  35                 else : 
  36                     if optional [ i ] : continue
  37                     else : raise EBadCharacter ( )
  38         if i + 1 < len ( picture ) :
  39             token = self . nextToken ( )
  40         else :
  41             break
  42 
  43     return 'complete', None
  44     
  45 def complete ( self, token ) : pass
  46     
  47 statemachine = StateMachine ( )
  48 statemachine . addState ( local )
  49 statemachine . addState ( complete, True )
  50 statemachine . setStartState ( local )
  51 
  52 if __name__ == "__main__" :
  53     
  54     import unittest
  55     
  56     class TestAgainstPostalCodes ( unittest . TestCase ) :
  57         
  58         def test1 ( self ) :
  59             statemachine . picture = 'a9a ?9a9'
  60             self . assertEqual ( statemachine . testString ( 'N0A 1P0' ), OK )
  61 
  62         def test2 ( self ) :
  63             statemachine . picture = 'a9a ?9a9'
  64             self . assertEqual ( statemachine . testString ( 'N0A1P0' ), OK )
  65     
  66         def test3 ( self ) :
  67             statemachine . picture = 'a9a ?9a9'
  68             self . assertEqual ( statemachine . testString ( '-0A1P0' ), BADCHARACTER )
  69     
  70         def test4 ( self ) :
  71             statemachine . picture = 'a9a ?9a9'
  72             self . assertEqual ( statemachine . testString ( '10A1P0' ), BADCHARACTER )
  73     
  74         def test5 ( self ) :
  75             statemachine . picture = 'a9a ?9a9'
  76             self . assertEqual ( statemachine . testString ( 'NaA1P0' ), BADCHARACTER )
  77     
  78         def test6 ( self ) :
  79             statemachine . picture = 'a9a ?9a9'
  80             self . assertEqual ( statemachine . testString ( 'N021P0' ), BADCHARACTER )
  81     
  82         def test7 ( self ) :
  83             statemachine . picture = 'a9a ?9a9'
  84             self . assertEqual ( statemachine . testString ( 'N0AaP0' ), BADCHARACTER )
  85     
  86         def test8 ( self ) :
  87             statemachine . picture = 'a9a ?9a9'
  88             self . assertEqual ( statemachine . testString ( 'N0A120' ), BADCHARACTER )
  89     
  90         def test9 ( self ) :
  91             statemachine . picture = 'a9a ?9a9'
  92             self . assertEqual ( statemachine . testString ( 'N0A1Pz' ), BADCHARACTER )
  93 
  94         def test10 ( self ) :
  95             statemachine . picture = 'a9a ?9a9'
  96             self . assertEqual ( statemachine . testString ( 'N0A1P00' ), STRINGTOOLONG )
  97         
  98         def test11 ( self ) :
  99             statemachine . picture = '+?999.?9?'
 100             self . assertEqual ( statemachine . testString ( '-111.1' ), OK )
 101 
 102         def test12 ( self ) :
 103             statemachine . picture = '+?999.?9?'
 104             self . assertEqual ( statemachine . testString ( '-1111' ), OK )
 105     
 106         def test13 ( self ) :
 107             statemachine . picture = '+?999.?9?'
 108             self . assertEqual ( statemachine . testString ( '-111.11' ), STRINGTOOLONG )
 109     
 110         def test14 ( self ) :
 111             statemachine . picture = '+?999.?9?'
 112             self . assertEqual ( statemachine . testString ( '+111.1' ), OK )
 113     
 114         def test15 ( self ) :
 115             statemachine . picture = '+?999.?9?'
 116             self . assertEqual ( statemachine . testString ( '111.1' ), OK )
 117     
 118         def test16 ( self ) :
 119             statemachine . picture = '+?999.?9?'
 120             self . assertEqual ( statemachine . testString ( '+111.1' ), OK )
 121 
 122     unittest . main ( )

A CareFree State Machine -- Accepts Anything

   1 from StateMachine import EBadCharacter, BADCHARACTER, STRINGTOOLONG, OK, StateMachine
   2 
   3 class StateMachine :
   4     
   5     def testString ( self, str ) : return OK
   6         
   7 statemachine = StateMachine ( )
   8 
   9 if __name__ == "__main__" :
  10 
  11     import unittest
  12     
  13     class TestAgainstPostalCodes ( unittest . TestCase ) :
  14         
  15         def test1 ( self ) : self . assertEqual ( statemachine . testString ( 'N0A 1P0' ), OK )
  16         def test2 ( self ) : self . assertEqual ( statemachine . testString ( None ), OK )
  17         def test3 ( self ) : self . assertEqual ( statemachine . testString ( '' ), OK )
  18         def test4 ( self ) : self . assertEqual ( statemachine . testString ( '22' ), OK )
  19         def test5 ( self ) : self . assertEqual ( statemachine . testString ( '22x' ), OK )
  20         def test6 ( self ) : self . assertEqual ( statemachine . testString ( '2291' ), OK )
  21         def test7 ( self ) : self . assertEqual ( statemachine . testString ( '229x' ), OK )
  22         def test8 ( self ) : self . assertEqual ( statemachine . testString ( '22999' ), OK )
  23 
  24     unittest . main ( )

Comments

I welcome your comments. - Bill Bell

Creating Validators Based on State Machines, Part IV (last edited 2008-03-11 10:50:17 by localhost)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.