/******************************************************************************* * Copyright (c) 2015 AKSW Xturtle Project, itemis AG (http://www.itemis.eu). * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html ******************************************************************************/package de.itemis.tooling.xturtle; import java.lang.reflect.Field; import org.antlr.runtime.BaseRecognizer; import org.antlr.runtime.CharStream; import org.antlr.runtime.RecognizerSharedState; public class LexerOverrider { private int tripleEndRule; private int numberRule; private int stringRule; private int idRule; private static final int DEFAULT_TOKEN_CHANNEL = BaseRecognizer.DEFAULT_TOKEN_CHANNEL; private boolean colonAsId=false; private RuntimeException getException(String rule, Throwable cause) { throw new RuntimeException( "cannot determine the index of rule " + rule, cause); } private int getRuleIndex(Class<?> lexerClass, String ruleName) { try { Field field = lexerClass.getField(ruleName); Object value = field.get(lexerClass); return (Integer) value; } catch (SecurityException e) { throw getException(ruleName, e); } catch (NoSuchFieldException e) { throw getException(ruleName, e); } catch (IllegalArgumentException e) { throw getException(ruleName, e); } catch (IllegalAccessException e) { throw getException(ruleName, e); } } public LexerOverrider(Class<?> lexerClass) { this.tripleEndRule = getRuleIndex(lexerClass, "RULE_TRIPELEND"); this.numberRule = getRuleIndex(lexerClass, "RULE_NUMBER"); this.stringRule = getRuleIndex(lexerClass, "RULE_STRING"); this.idRule = getRuleIndex(lexerClass, "RULE_ID"); } private void stateOK(RecognizerSharedState state, int rule) { state.type = rule; state.channel = DEFAULT_TOKEN_CHANNEL; state.failed = false; state.backtracking = 0; } private void resetColonId(){ colonAsId=false; } private boolean overrideTripleEndGeneration(CharStream input, RecognizerSharedState state) { if (input.LA(1) == '.') { int next = input.LA(2); switch (next) { case -1:// EOF case '\n': case ' ': case '\t': case '\r': case '@': case '<': case '#': input.consume(); stateOK(state, tripleEndRule); resetColonId(); return true; default: break; } } return false; } boolean isDigit(int c) { return c >= '0' && c <= '9'; } private void consumeExponent(CharStream input) { int e = input.LA(1); int signOrDigit = input.LA(2); if (e == 'e' || e == 'E') { if (signOrDigit == '+' || signOrDigit == '-') { if (isDigit(input.LA(3))) { input.consume();// e input.consume();// sign consumeDigits(input); } } else if (isDigit(signOrDigit)) { input.consume();// e consumeDigits(input); } } } private void consumeDigits(CharStream input) { while (isDigit(input.LA(1))) { input.consume(); } } private boolean isNumberContinuation(int c1, int c2, int c3){ if(isDigit(c1)){ return true; } else if(c1=='e'|| c1=='E'){ if(isDigit(c2)){ return true; }else if(c2=='+'||c2=='-'){ return isDigit(c3); } } return false; } private boolean overrideNumberGeneration(CharStream input, RecognizerSharedState state) { int nextChar = input.LA(1); int nextChar2 = input.LA(2); boolean result = false; if (isDigit(nextChar)) { result = true; consumeDigits(input); if (input.LA(1) == '.' && isNumberContinuation(input.LA(2), input.LA(3), input.LA(4))) { input.consume(); consumeDigits(input); } } else if (nextChar == '.' && isDigit(nextChar2)) { result = true; input.consume(); consumeDigits(input); } if (result) { consumeExponent(input); stateOK(state, numberRule); } return result; } private boolean endOfStringFound(int quoteCharacter, int length, CharStream input) { boolean result = true; for (int i = 0; i < length; i++) { result &= input.LA(i + 1) == quoteCharacter; } return result; } private void consumeString(int quoteCharacter, CharStream input, RecognizerSharedState state) { boolean isLong = input.LA(1) == quoteCharacter && input.LA(2) == quoteCharacter && input.LA(3) == quoteCharacter; int numberOfExpectedEndQuotes; boolean endReached = false; if (isLong) { numberOfExpectedEndQuotes = 3; } else { numberOfExpectedEndQuotes = 1; } for (int i = 0; i < numberOfExpectedEndQuotes; i++) { // start quotes input.consume(); } while (!endReached) { if (input.LA(1) == '\\') { input.consume(); input.consume(); } else if (endOfStringFound(quoteCharacter, numberOfExpectedEndQuotes, input)) { for (int i = 0; i < numberOfExpectedEndQuotes; i++) { // start quotes input.consume(); } endReached = true; } else if (input.LA(1) == CharStream.EOF) { endReached = true; state.failed = true; } else { input.consume(); } } } private boolean overrideString(CharStream input, RecognizerSharedState state) { int start = input.LA(1); boolean isString = start == '"' || start == '\''; if (isString) { consumeString(start, input, state); stateOK(state, stringRule); } return isString; } private boolean overrideAasID(CharStream input, RecognizerSharedState state) { if (input.LA(1) == 'a') { int next = input.LA(2); boolean aIsPartOfId = next == ':' || next == '.'; if (aIsPartOfId) { input.consume(); stateOK(state, idRule); return true; } } return false; } private boolean isUTF16high(int i) { // x10000-xeffff return i > 65535 && i < 983040; } private boolean overrideUTF16(CharStream input, RecognizerSharedState state) { boolean isUTF16 = false; while (isUTF16high(input.LA(1))) { input.consume(); isUTF16 = true; } if(isUTF16){ stateOK(state, idRule); } return isUTF16; } private void checkResetColonId(CharStream input){ switch (input.LA(1)) { case -1:// EOF case '\n': case ' ': case ',': case ';': case '[': case '\t': case '\r': case '#': resetColonId(); default: break; } } private boolean overrideColonAsID(CharStream input, RecognizerSharedState state) { if (input.LA(1) == ':') { if(colonAsId){ input.consume(); stateOK(state, idRule); return true; }else{ colonAsId=true; } } return false; } public boolean override(CharStream input, RecognizerSharedState state) { checkResetColonId(input); return overrideTripleEndGeneration(input, state) || overrideNumberGeneration(input, state) || overrideString(input, state) || overrideColonAsID(input, state) || overrideAasID(input, state) || overrideUTF16(input, state); } }