/* * GlslLexer.java * * Created on 19.08.2007, 18:31:16 * */ package net.java.nboglpack.glsleditor.lexer; import net.java.nboglpack.glsleditor.GlslVocabularyManager; import net.java.nboglpack.glsleditor.vocabulary.GLSLElementDescriptor; import org.netbeans.api.lexer.Token; import org.netbeans.spi.lexer.Lexer; import org.netbeans.spi.lexer.LexerInput; import org.netbeans.spi.lexer.LexerRestartInfo; import org.netbeans.spi.lexer.TokenFactory; /** * Lexer for the OpenGL Shading Language. * @author Michael Bien */ public class GlslLexer implements Lexer<GlslTokenId> { private final LexerInput input; private final TokenFactory<GlslTokenId> factory; private final GlslVocabularyManager manager; private final StringBuilder stringBuilder; public GlslLexer(LexerRestartInfo<GlslTokenId> info, GlslVocabularyManager manager) { this.input = info.input(); this.factory = info.tokenFactory(); this.manager = manager; this.stringBuilder = new StringBuilder(); } @SuppressWarnings("fallthrough") public Token<GlslTokenId> nextToken() { int character = input.read(); switch(character) { case '(': case ')': return factory.createToken(GlslTokenId.PAREN); case '{': case '}': return factory.createToken(GlslTokenId.BRACE); case '[': case ']': return factory.createToken(GlslTokenId.BRACKET); case '.': return factory.createToken(GlslTokenId.DOT); case ',': return factory.createToken(GlslTokenId.COMMA); case ':': return factory.createToken(GlslTokenId.COLON); case ';': return factory.createToken(GlslTokenId.SEMICOLON); case '?': return factory.createToken(GlslTokenId.QUESTION); case '~': return factory.createToken(GlslTokenId.TILDE); case '*': if(input.read() == '=') { return factory.createToken(GlslTokenId.MUL_ASSIGN); }else{ input.backup(1); return factory.createToken(GlslTokenId.STAR); } case '%': if(input.read() == '=') { return factory.createToken(GlslTokenId.MOD_ASSIGN); }else{ input.backup(1); return factory.createToken(GlslTokenId.PERCENT); } case '!': if(input.read() == '=') { return factory.createToken(GlslTokenId.NE); }else{ input.backup(1); return factory.createToken(GlslTokenId.BANG); } case '=': if(input.read() == '=') { return factory.createToken(GlslTokenId.EQEQ); }else{ input.backup(1); return factory.createToken(GlslTokenId.EQ); } case '^': switch(input.read()) { case('^'): return factory.createToken(GlslTokenId.CARETCARET); case('='): return factory.createToken(GlslTokenId.XOR_ASSIGN); default: input.backup(1); return factory.createToken(GlslTokenId.CARET); } case '+': switch(input.read()) { case('+'): return factory.createToken(GlslTokenId.PLUSPLUS); case('='): return factory.createToken(GlslTokenId.ADD_ASSIGN); default: input.backup(1); return factory.createToken(GlslTokenId.PLUS); } case '-': switch(input.read()) { case('-'): return factory.createToken(GlslTokenId.MINUSMINUS); case('='): return factory.createToken(GlslTokenId.SUB_ASSIGN); default: input.backup(1); return factory.createToken(GlslTokenId.MINUS); } case '&': switch(input.read()) { case('&'): return factory.createToken(GlslTokenId.AMPAMP); case('='): return factory.createToken(GlslTokenId.AND_ASSIGN); default: input.backup(1); return factory.createToken(GlslTokenId.AMP); } case '|': switch(input.read()) { case('|'): return factory.createToken(GlslTokenId.BARBAR); case ('='): return factory.createToken(GlslTokenId.OR_ASSIGN); default: input.backup(1); return factory.createToken(GlslTokenId.BAR); } case '<': switch(input.read()) { case('<'): if(input.read() == '=') { return factory.createToken(GlslTokenId.LEFT_BITSHIFT_ASSIGN); }else{ input.backup(1); return factory.createToken(GlslTokenId.LEFT_BITSHIFT); } case('='): return factory.createToken(GlslTokenId.LE); default: input.backup(1); return factory.createToken(GlslTokenId.LEFT_ANGLE); } case '>': switch(input.read()) { case('>'): if(input.read() == '=') { return factory.createToken(GlslTokenId.RIGHT_BITSHIFT_ASSIGN); }else{ input.backup(1); return factory.createToken(GlslTokenId.RIGHT_BITSHIFT); } case('='): return factory.createToken(GlslTokenId.GE); default: input.backup(1); return factory.createToken(GlslTokenId.RIGHT_ANGLE); } case '/': int c = input.read(); if(c == '/') { readRemainingLine(); return factory.createToken(GlslTokenId.COMMENT); }else if(c == '*') { return tokenizeMLComment(); }else if(c == '=') { return factory.createToken(GlslTokenId.DIV_ASSIGN); }else{ input.backup(1); return factory.createToken(GlslTokenId.SLASH); } case '#': readRemainingLine(); return factory.createToken(GlslTokenId.PREPROCESSOR); case ' ': case '\t': do{ character = input.read(); }while(character == ' ' || character == '\t'); input.backup(1); return factory.createToken(GlslTokenId.WHITESPACE); case '\r': input.consumeNewline(); case LexerInput.EOF: if(input.readLength() == 0) return null; case '\n': return factory.createToken(GlslTokenId.END_OF_LINE); default: if(Character.isDigit(character)) { return tokenizeNumber(); }else if(Character.isUnicodeIdentifierStart(character)) { return tokenizeName(); }else{ return factory.createToken(GlslTokenId.error); } } } @SuppressWarnings("fallthrough") private final void readRemainingLine() { int character = input.read(); while(character != LexerInput.EOF) { switch (character) { case '\r': input.consumeNewline(); case '\n': case LexerInput.EOF: return; } character = input.read(); } } private final Token<GlslTokenId> tokenizeMLComment() { int character = input.read(); while(character != LexerInput.EOF) { if(character == '*' && input.read() == '/') { return factory.createToken(GlslTokenId.ML_COMMENT); } character = input.read(); } return factory.createToken(GlslTokenId.ML_COMMENT); } private final Token<GlslTokenId> tokenizeNumber() { int character; do{ character = input.read(); }while(Character.isDigit(character)); if(character == '.') { do{ character = input.read(); }while(Character.isDigit(character)); if(character != 'f' && character != 'F') input.backup(1); return factory.createToken(GlslTokenId.FLOAT_LITERAL); }else{ if(character != 'u' && character != 'U') input.backup(1); // input.backup(1); return factory.createToken(GlslTokenId.INTEGER_LITERAL); } } private final Token<GlslTokenId> tokenizeName() { if(stringBuilder.length() > 0) stringBuilder.delete(0, stringBuilder.length()); // backup everything read input.backup(input.readLength()); // assamble token char c; while(Character.isUnicodeIdentifierPart(c = ((char)input.read()))) stringBuilder.append(c); if(stringBuilder.length() > 0) input.backup(1); // categorise token GLSLElementDescriptor[] desc = manager.getDesc(stringBuilder.toString()); if(desc != null) { if(desc[0].category != null) { if(desc[0].category == GLSLElementDescriptor.Category.BUILD_IN_FUNC) return factory.createToken(GlslTokenId.BUILD_IN_FUNC); if(desc[0].category == GLSLElementDescriptor.Category.BUILD_IN_VAR) return factory.createToken(GlslTokenId.BUILD_IN_VAR); return factory.createToken(GlslTokenId.KEYWORD); } } // check if token = function name int tokenEnd = input.readLength(); int character = input.read(); while(true) { switch (character) { case ' ': case '\t': case '\r': case '\n': character = input.read(); break; case '(': input.backup(input.readLength()-tokenEnd); return factory.createToken(GlslTokenId.FUNCTION); default: input.backup(input.readLength()-tokenEnd); return factory.createToken(GlslTokenId.IDENTIFIER); } } } public Object state() { // we don't need states return null; } public void release() { } }