参加ThoughtWorks University的一个来月没啥事情,闲了写写compiler玩。发现Lexer部分比较基础也比较常用,有很多相似的东西,每次都要写一遍也太麻烦了,下面是我按着JSL写的一个common java-like lexer,对于大多数接近java语法的语言估计是够用了。BTW:这个Lexer定义是TDD出来,以通过测试为要务,可能可读性不太强。

1.WhiteSpace

 1  WhiteSpace
 2      : ( '   '      //  ASCII SP
 3       |    ' \t '     //  ASCII HT
 4       |    ' \f '     //  ASCII FF
 5       |   LineTerminator {newline();}
 6      ) + {$setType(Token.SKIP);}
 7      ;
 8  protected  LineTerminator
 9      options {generateAmbigWarnings = false ;}
10      :  ' \n '     //  ASCII LF
11       |   ' \r '     //  ASCII CR
12       |   " \r\n "   //  ASCII CR followed ASCII LF
13      ;

2.Comments

 1  Comment
 2      : (SingleLineComment  |  MultiLineComment)
 3      {$setType(Token.SKIP);}
 4      ;
 5  protected  SingleLineComment
 6      :  " // "  ( ~ ( ' \n ' | ' \r ' )) *  (LineTerminator{newline();}) ?
 7      ;
 8  protected  MultiLineComment
 9      :  " /* "
10        ( ~ ( ' \n ' | ' \r ' | ' * ' |  LineTerminator{newline();}) *  
11         " */ "
12      ;

3.Escape Sequences

 1  protected  EscapeSequence
 2      : ' \\ ' !
 3          ( ' n '  {$setText( " \n " );}
 4           | ' r '  {$setText( " \r " );}
 5           | ' t '  {$setText( " \t " );}
 6           | ' b '  {$setText( " \b " );}
 7           | ' f '  {$setText( " \f " );}
 8           | ' " '  
 9           | ' \ ''
10           | ' \\ '
11           //  octal escape
12           | ' 0 ' .. ' 3 '
13              ( options { warnWhenFollowAmbig  =   false ; }:  ' 0 ' .. ' 7 '
14              ( options { warnWhenFollowAmbig  =   false ; }:  ' 0 ' .. ' 7 ' ) ? ) ?
15          { char  c  =  ( char )Integer.parseInt($getText, 8 ); $setText(c);}
16           | ' 4 ' .. ' 7 '
17              ( options { warnWhenFollowAmbig  =   false ; }:  ' 0 ' .. ' 7 '  ) ?
18          { char  c  =  ( char )Integer.parseInt($getText, 8 ); $setText(c);}
19          )
20       |  ( " \\u " =>  UnicodeEscape
21      ;
22  protected  UnicodeEscape
23      :  ' \\ ' !  ( ' u ' ) + {$setText( "" );} HexDigit HexDigit HexDigit HexDigit
24      { char  c  =  ( char )Integer.parseInt($getText, 16 ); $setText(c);}
25      ;
26  protected  HexDigit:  ' 0 ' .. ' 9 '   |   ' a ' .. ' f '   |   ' A ' .. ' F ' ;
27 

这个东西比较麻烦,种类很多,有像\t \n \r这样的escape,也有\uuu1234这样的unicode escape,还有octal escape,说实话,这个东西还是这次写compiler的时候新发现的,以前还真不知道有这么个东西,也从来没用过...汗啊...octal escape是对于小于255的数,可以用\012这样的八进制数表示,这个东西没想明白有什么用。反正JSL上写了,就按这个来吧。

4. String & Character Literal

1  StringLiteral
2      :  ' " ' !  (EscapeSequence |~ ' " ' ) *   ' " ' !
3      ;
4  CharacterLiteral
5      :  ' \ '' ! (EscapeSequence|~ '" ')? '\''!
6      ;

5. NumericLiteral

 1  NumericLiteral
 2      options{testLiterals  =   true ;}
 3       { int  type  =   0 ;}
 4       : (( " .end " =>  type  =  EndOfDirective
 5           | ( " .max " =>  type  =  MaxDirective
 6          | ( ' . '   ' a ' .. ' z ' =>  type  =  Directives         
 7          |  ( ' + ' !   |   ' - ' ) ?  (type  =  IntegerLiteral  |  type  =  HexIntegerLiteral  |  type  =  DoubleLiteral)
 8        )
 9      {$setType(type);}
10      ;
11
26  protected  IntegerLiteral
27      returns [ int  type  =   0 ]
28      {$setType(DecimalIntegerLiteral);}
29      : ( ' 0 '  
30       |   ' 0 ' !  (  ' 0 ' .. ' 7 '  {$setType(OctalIntegerLiteral);}) +
31       |   ' 1 ' .. ' 9 '  ( ' 0 ' .. ' 9 ' ) *
32       ((LongTypeSuffix !  {
33               if  (_ttype  ==  OctalIntegerLiteral) 
34                $setType(OctalLongLiteral);
35               else  
36                $setType(DecimalLongLiteral);
37         })  ?  
38         |  {_ttype  ==  DecimalIntegerLiteral} ?  
39          (FloatingPointPart  |  ExponentPart) {$setType(DoubleLiteral);}
40          (DoubleTypeSuffix !   |  FloatTypeSuffix ! {$setType(FloatLiteral);}) ?
41        ){type  =  _ttype;}
42      ;
43  protected  HexIntegerLiteral
44      returns [ int  type  =   0 ]
45      : ( ' 0 ' !  ( ' x ' !   |   ' X ' ! ) (HexDigit) +  
46        (LongTypeSuffix !  {$setType(HexLongLiteral);})  ? )
47        {type  =  _ttype;}
48      ;
49  protected  DoubleLiteral
50      returns [ int  type  =   0 ]
51      : (FloatingPointPart (DoubleTypeSuffix !   |  FloatTypeSuffix ! {$setType(FloatLiteral);}) ? )
52        {type  =  _ttype;}
53      ;
54  protected  FloatingPointPart
55      :  ' . '  ( ' 0 ' .. ' 9 ' ) +  (ExponentPart) ?
56      ;
57  protected  ExponentPart 
58      : ( ' E ' | ' e ' ) ( ' + ' | ' - ' ) ?  ( ' 0 ' .. ' 9 ' ) +
59      ;
60  protected  LongTypeSuffix :  ' l '   |   ' L ' ;
61  protected  DoubleTypeSuffix :  ' d '   |   ' D ' ;
62  protected  FloatTypeSuffix :  ' f '   |   ' F ' ;

这个是最复杂的一部分...

Unit Test比较长,节选吧

  1  public   void  testShouldIgnoreWhiteSpaces()  throws  Exception {
  2    assertRecognized(OctaneTokenTypes.EOF,  "   " );
  3    assertRecognized(OctaneTokenTypes.EOF,  " \t " );
  4    assertRecognized(OctaneTokenTypes.EOF,  " \f " );
  5  }
  6 
  7  public   void  testShouldIgnoreLineTerminators()  throws  Exception {
  8    assertRecognized(OctaneTokenTypes.EOF,  " \r " );
  9    assertRecognized(OctaneTokenTypes.EOF,  " \n " );
 10    assertRecognized(OctaneTokenTypes.EOF,  " \r\n " );
 11  }
 12 
 13  public   void  testShouldIgnoreSingleLineComment()  throws  Exception {
 14    assertRecognized(OctaneTokenTypes.EOF,  " // comments 1234 &*^$\n " );
 15  }
 16 
 17  public   void  testShouldIgnoreMultiLineComment()  throws  Exception {
 18    assertRecognized(OctaneLexer.EOF,  " /* comment line 1\ncomment line 2\n*/ " );
 19  }
 20 
 21  public   void  testShouldIncreaseLineNumberIfLineTerminatorsGiven()  throws  Exception {
 22    assertEquals( 2 , createLexer( " \r " ).nextToken().getLine());
 23    assertEquals( 2 , createLexer( " \n " ).nextToken().getLine());
 24    assertEquals( 2 , createLexer( " \r\n " ).nextToken().getLine());
 25  }
 26 
 27  public   void  testShouldRecognizeBasicEscapeInCharacterLiteral()  throws  Exception {
 28    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \n " " '\\n' " );
 29    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \r " " '\\r' " );
 30    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \t " " '\\t' " );
 31    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \b " " '\\b' " );
 32    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \f " " '\\f' " );
 33    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \ "" "' \\\" '" );
 34    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \\ " " '\\\\' " );
 35    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \' " " '\\\'' " );
 36  }
 37 
 38  public   void  testShouldRecognizeBasicEscapeInStringLiteral()  throws  Exception {
 39    assertRecognized(OctaneTokenTypes.StringLiteral,  " \n " " \ " \\n\ "" );
 40    assertRecognized(OctaneTokenTypes.StringLiteral,  " \r " " \ " \\r\ "" );
 41    assertRecognized(OctaneTokenTypes.StringLiteral,  " \t " " \ " \\t\ "" );
 42    assertRecognized(OctaneTokenTypes.StringLiteral,  " \b " " \ " \\b\ "" );
 43    assertRecognized(OctaneTokenTypes.StringLiteral,  " \f " " \ " \\f\ "" );
 44    assertRecognized(OctaneTokenTypes.StringLiteral,  " \ "" " \ " \\\ " \ "" );
 45    assertRecognized(OctaneTokenTypes.StringLiteral,  " \\ " " \ " \\\\\ "" );
 46    assertRecognized(OctaneTokenTypes.StringLiteral,  " \' " " \ " \\\ ' \"");
 47  }
 48 
 49  public   void  testShouldRecognizeOctalEscapeInCharacterLiteral()  throws  Exception {
 50    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \077 " " '\\077' " );
 51    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \77 " " '\\77' " );
 52    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \37 " " '\\37' " );
 53    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \7 " " '\\7' " );
 54  }
 55 
 56  public   void  testShouldRecognizeOctalEscapeInStringLiteral()  throws  Exception {
 57    assertRecognized(OctaneTokenTypes.StringLiteral,  " \077 " " \ " \\ 077 \ "" );
 58    assertRecognized(OctaneTokenTypes.StringLiteral,  " \77 " " \ " \\ 77 \ "" );
 59    assertRecognized(OctaneTokenTypes.StringLiteral,  " \37 " " \ " \\ 37 \ "" );
 60    assertRecognized(OctaneTokenTypes.StringLiteral,  " \7 " " \ " \\ 7 \ "" );
 61  }
 62 
 63  public   void  testShouldRecognizeUnicodeEscapeInCharacterLiteral()  throws  Exception {
 64    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \u1234 " " '\\u1234' " );
 65    assertRecognized(OctaneTokenTypes.CharacterLiteral,  " \uu1234 " , " '\\uu1234\' " );
 66  }
 67 
 68  public   void  testShouldRecognizeUnicodeEscapeInStringLiteral()  throws  Exception {
 69    assertRecognized(OctaneTokenTypes.StringLiteral,  " \u1234 " " \ " \\u1234\ "" );
 70    assertRecognized(OctaneTokenTypes.StringLiteral,  " \uu1234 " " \ " \\uu1234\ "" );
 71  }
 72 
 73  public   void  testShouldRecognizeUnicodeInStringLiteral()  throws  Exception {
 74    assertRecognized(OctaneTokenTypes.StringLiteral,  " \ " 这是一行中文\ "" );
 75  }
 76 
 77  public   void  testShouldRecognizeDecimalIntegerLiteral()  throws  Exception {
 78    assertRecognized(OctaneTokenTypes.DecimalIntegerLiteral,  " 0 " " 0 " );
 79    assertRecognized(OctaneTokenTypes.DecimalIntegerLiteral,  " -123 " " -123 " );
 80  }
 81 
 82  public   void  testShouldRecognizeDecimalLongLiteral()  throws  Exception {
 83    assertRecognized(OctaneTokenTypes.DecimalLongLiteral,  " 0 " " 0l " );
 84    assertRecognized(OctaneTokenTypes.DecimalLongLiteral,  " -123 " " -123L " );
 85  }
 86 
 87  public   void  testShouldRecognizeHexIntegerLiteral()  throws  Exception {
 88    assertRecognized(OctaneTokenTypes.HexIntegerLiteral,  " 1A3B " " +0x1A3B " );
 89    assertRecognized(OctaneTokenTypes.HexIntegerLiteral,  " -1A3B " " -0x1A3B " );
 90  }
 91 
 92  public   void  testShouldRecognizeHexLongLiteral()  throws  Exception {
 93    assertRecognized(OctaneTokenTypes.HexLongLiteral,  " 1A3B " " +0x1A3BL " );
 94    assertRecognized(OctaneTokenTypes.HexLongLiteral,  " -1A3F " " -0x1A3Fl " );
 95  }
 96 
 97  public   void  testShouldRecognizeOctalIntegerLiteral()  throws  Exception {
 98    assertRecognized(OctaneTokenTypes.OctalIntegerLiteral,  " 123 " " +0123 " );
 99    assertRecognized(OctaneTokenTypes.OctalIntegerLiteral,  " -123 " " -0123 " );
100  }
101 
102  public   void  testShouldRecognizeOctalLongLiteral()  throws  Exception {
103    assertRecognized(OctaneTokenTypes.OctalLongLiteral,  " 1237 " " +01237L " );
104    assertRecognized(OctaneTokenTypes.OctalLongLiteral,  " -1237 " " -01237l " );
105  }
106 
107  public   void  testShouldRecognizeDoubleLiteral()  throws  Exception {
108    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " 0.5 " " +0.5 " );
109    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " -.5 " " -.5 " );
110    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " 0.5 " " +0.5D " );
111    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " -.5 " " -.5d " );
112  }
113 
114  public   void  testShouldRecognizeDoubleLiteralInExponentialForm()  throws  Exception {
115    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " 0.5e+10 " " +0.5e+10 " );
116    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " -.5E-10 " " -.5E-10 " );
117    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " 0.5E+5 " " +0.5E+5D " );
118    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " -.5E-5 " " -.5E-5d " );
119    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " 10E+5 " " +10E+5d " );
120    assertRecognized(OctaneTokenTypes.DoubleLiteral,  " -10e-5 " " -10e-5D " );
121  }
122 
123  public   void  testShouldRecognizeFloatLiteral()  throws  Exception {
124    assertRecognized(OctaneTokenTypes.FloatLiteral,  " 0.5 " " +0.5F " );
125    assertRecognized(OctaneTokenTypes.FloatLiteral,  " -.5 " " -.5f " );
126    assertRecognized(OctaneTokenTypes.FloatLiteral,  " 10E+5 " " +10E+5f " );
127    assertRecognized(OctaneTokenTypes.FloatLiteral,  " -10e-5 " " -10e-5F " );
128  }
129 
130  public   void  testShouldRecognizeFloatLiteralInExponentialForm()  throws  Exception {
131    assertRecognized(OctaneTokenTypes.FloatLiteral,  " 0.5E+5 " " +0.5E+5F " );
132    assertRecognized(OctaneTokenTypes.FloatLiteral,  " -.5e-5 " " -.5e-5f " );
133  }
134 
135  protected   void  assertRecognized( int  tokenType, String sourceString)  throws  Exception {
136    assertRecognized(tokenType,  null , sourceString);
137 
138  }
139 
140  protected   void  assertRecognized( int  tokenType, String exceptedText, String sourceString)  throws  Exception {
141    assertRecognized( new   int [] { tokenType }, exceptedText  ==   null   ?   null  :  new  String[] { exceptedText }, sourceString);
142  }
143 
144  protected   void  assertRecognized( int [] tokenTypes, String[] exceptedText, String sourceString)  throws  TokenStreamException {
145    TokenStream lexer  =  createLexer(sourceString);
146     for  ( int  i  =   0 ; i  <  tokenTypes.length; i ++ ) {
147      Token token  =  lexer.nextToken();
148      assertEquals(tokenTypes[i], token.getType());
149       if  (exceptedText  !=   null ) assertEquals(exceptedText[i], token.getText());
150    }
151    assertEquals(OctaneTokenTypes.EOF, lexer.nextToken().getType());
152  }