package freeboogie.ast.gen;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Recognizes abstract grammar (AG) tokens.
*
* @author rgrig
* @author reviewed by TODO
*/
public class AgLexer extends PeekStream<AgToken> {
private static final Map<Character, AgToken.Type> oneCharTokens
= new HashMap<Character, AgToken.Type>(17);
static {
oneCharTokens.put('=', AgToken.Type.EQ);
oneCharTokens.put('!', AgToken.Type.BANG);
oneCharTokens.put(',', AgToken.Type.COMMA);
oneCharTokens.put(';', AgToken.Type.SEMICOLON);
oneCharTokens.put('(', AgToken.Type.LP);
oneCharTokens.put(')', AgToken.Type.RP);
oneCharTokens.put('\n', AgToken.Type.NL);
}
private CharStream stream;
private Character lastChar;
private StringBuilder repBuilder;
/**
* Initializes the lexer.
* @param stream the underlying stream
* @throws IOException if thrown by the underlying stream
*/
public AgLexer(CharStream stream) throws IOException {
super(new TokenLocation<AgToken>());
this.stream = stream;
repBuilder = new StringBuilder();
readChar();
}
private void readChar() throws IOException {
if (lastChar != null) repBuilder.append(lastChar);
lastChar = stream.next();
}
private String rep() {
String r = repBuilder.toString();
repBuilder = new StringBuilder();
return r;
}
/*
* This method always reads one more character than the recognized
* token and also eats the read characters from the underlying stream.
*
* The method is a bit too complex but lexers usually are.
*
* @see freeboogie.ast.gen.PeekStream#read()
*/
@Override
protected AgToken read() throws IOException {
if (lastChar == null) return null;
AgToken.Type type = oneCharTokens.get(lastChar);
if (type != null) {
readChar();
} else if (Character.isWhitespace(lastChar)) {
type = AgToken.Type.WS;
do readChar(); while (Character.isWhitespace(lastChar));
} else if (lastChar == ':') {
readChar();
if (lastChar == '>') {
type = AgToken.Type.SUPERTYPE;
readChar();
} else
type = AgToken.Type.COLON;
} else if (lastChar == '/') {
readChar();
if (lastChar != '/') type = AgToken.Type.ERROR;
else {
type = AgToken.Type.COMMENT;
while (lastChar != '\n') readChar();
readChar();
}
} else if (Character.isLetter(lastChar)) {
do readChar();
while (Character.isLetter(lastChar));
if (repBuilder.toString().equals("enum"))
type = AgToken.Type.ENUM;
else type = AgToken.Type.ID;
} else {
type = AgToken.Type.ERROR;
readChar();
}
stream.eat();
return new AgToken(type, rep());
}
/**
* Like {@code next}, but skips WS, COMMENT, and ERROR tokens.
* It also gives error messages for ERROR tokens.
* @return the next good token
* @throws IOException if thrown by the underlying stream
*/
public AgToken nextGood() throws IOException {
AgToken r;
do r = next(); while (r != null && !r.isGood());
return r;
}
/**
* For testing. (TODO)
*
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}