1 3 package org.jmock.examples.calculator; 4 5 import java.io.IOException ; 6 import java.io.PushbackReader ; 7 import java.io.Reader ; 8 import java.io.StringReader ; 9 import java.util.Stack ; 10 11 12 public class Lexer 13 { 14 private static final int END_OF_STREAM = -1; 15 private static final char DECIMAL_PLACE = '.'; 16 17 private PushbackReader input; 18 private StringBuffer tokenValue = new StringBuffer (); 19 private Stack pushBack = new Stack (); 20 21 public Lexer( Reader input ) { 22 this.input = new PushbackReader (input); 23 } 24 25 public Lexer( String inputString ) { 26 this(new StringReader (inputString)); 27 } 28 29 public void pushBack( Token token ) { 30 pushBack.push(token); 31 } 32 33 public Token nextToken() throws IOException , ParseException { 34 if (pushBack.isEmpty()) { 35 return readToken(); 36 } 37 return (Token)pushBack.pop(); 38 } 39 40 private Token readToken() throws IOException , ParseException { 41 tokenValue.setLength(0); 42 43 skipWhitespace(); 44 45 int ch = peekChar(); 46 47 if (ch == END_OF_STREAM) { 48 return new Token(Token.END, null); 49 } else if (isDigit(ch)) { 50 return readNumber(); 51 } else if (isIdentifierStart(ch)) { 52 return readIdentifier(); 53 } else { 54 return readOperator(); 55 } 56 } 57 58 public void skipWhitespace() throws IOException { 59 int ch; 60 61 do { 62 ch = readChar(); 63 } 64 while (ch != END_OF_STREAM && Character.isWhitespace((char)ch)); 65 66 unreadChar(ch); 67 } 68 69 private boolean isDigit( int ch ) { 70 return ch != END_OF_STREAM && Character.isDigit((char)ch); 71 } 72 73 private Token readNumber() throws IOException { 74 collectDigits(); 75 if (peekChar() == DECIMAL_PLACE) { 76 tokenValue.append((char)readChar()); 77 collectDigits(); 78 } 79 80 return createToken(Token.NUMBER); 81 } 82 83 private void collectDigits() throws IOException { 84 int ch; 85 86 while (isDigit(ch = readChar())) { 87 tokenValue.append((char)ch); 88 } 89 90 unreadChar(ch); 91 } 92 93 private boolean isIdentifierStart( int ch ) { 94 return Character.isUnicodeIdentifierStart((char)ch); 95 } 96 97 private boolean isIdentifierPart( int ch ) { 98 return ch != -1 && Character.isUnicodeIdentifierPart((char)ch); 99 } 100 101 private Token readIdentifier() throws IOException { 102 tokenValue.append((char)readChar()); 103 104 int ch; 105 while (isIdentifierPart((ch = readChar()))) { 106 tokenValue.append((char)ch); 107 } 108 unreadChar(ch); 109 110 return createToken(Token.IDENTIFIER); 111 } 112 113 private Token readOperator() throws IOException , ParseException { 114 int ch = readChar(); 115 tokenValue.append((char)ch); 116 117 switch (ch) { 118 case '+': 119 return createToken(Token.ADD); 120 case '-': 121 return createToken(Token.SUBTRACT); 122 case '*': 123 return createToken(Token.MULTIPLY); 124 case '/': 125 return createToken(Token.DIVIDE); 126 case '^': 127 return createToken(Token.POWER); 128 case '(': 129 return createToken(Token.PAREN_OPEN); 130 case ')': 131 return createToken(Token.PAREN_CLOSE); 132 133 default: 134 throw new ParseException("unrecognised character '" + (char)ch + "'"); 135 } 136 } 137 138 139 private Token createToken( int tokenType ) { 140 return new Token(tokenType, tokenValue.toString()); 141 } 142 143 private int peekChar() throws IOException { 144 int ch = readChar(); 145 unreadChar(ch); 146 return ch; 147 } 148 149 private int readChar() throws IOException { 150 return input.read(); 151 } 152 153 private void unreadChar( int ch ) throws IOException { 154 if (ch != END_OF_STREAM) input.unread(ch); 155 } 156 } 157 | Popular Tags |